#
#    W_node.pm:     convert ny's node addresses.
#                                                    Tomo.M(2006/06/04)
package W_node;

require Exporter;
@ISA = qw (Exporter);
@EXPORT = qw ( new
	       toHash toAddr
	       to_hash to_addr
	       );
use Crypt::RC4;
use strict 'vars';
use vars qw ( $RC4_KEY $CK_MASK );

$RC4_KEY = "opiewf6ascxlv";
$CK_MASK = 0xff;

sub new {
    my $this = shift;
    my $class = ref($this) || $this;
    my $self = {};
    bless $self, $class;

    if ($_[0]) {
	$self->set(@_);
    }
    return($self);
}

sub set {
    my $this = shift;
    my %args;

    unless (@_ % 2) {
	%args = @_;
    } else {
	chomp($_[0]);
	if ($_[0] =~ /^\@/) {
	    $args{Hash} = $_[0];
	} else {
	    $args{Addr} = $_[0];
	}
    }

    if (exists($args{Hash})) {
        $this->{Hash} = $args{Hash};
    }
    if (exists($args{Addr})) {
        $this->{Addr} = $args{Addr};
    }
    $this;
}

sub get {
    my $this = shift;
    my $key = shift;

    return($this->{$key});
}

sub toHash {
    my $this = shift;
    my $arg = shift;
    my ($addr, $key, $cksum, $ctx, $hash);

    $addr = $arg || $this->get('Addr');
    return undef unless $addr;

    $cksum = 0;
    foreach my $i ( split(//, $addr) ) {
        $cksum += ord($i);
    }
    $cksum &= $CK_MASK;
    ($key = $RC4_KEY) =~ s/^./chr($cksum)/e;

    $ctx = Crypt::RC4->new($key);
    my $estr = chr($cksum) . $ctx->RC4($addr);
    my $str = join ('', map { sprintf "%02x", $_ } unpack("C*", $estr));
    $hash = '@' . $str;

    $this->set('Hash' => $hash);
    return($hash);
}

sub toAddr {
    my $this = shift;
    my $arg = shift;
    my ($hash, $key, $cksum, $ctx, $addr, $str);

    $hash = $arg || $this->get('Hash');
    return undef unless $hash;

    chomp($hash);
    $hash =~ s/^\@(..)//;
    $cksum = hex($1);

    ($key = $RC4_KEY) =~ s/^./chr($cksum)/e;

    $str = pack('H*', $hash);
    $ctx = Crypt::RC4->new($key);
    $addr = $ctx->RC4($str);

    my $c = 0;
    foreach my $i ( split(//, $addr) ) {
        $c += ord($i);
    }
    return undef if (($c & 0xff) != $cksum);

    $this->set('Addr' => $addr);
    return($addr);
}

sub to_hash {
    return(__PACKAGE__->new(@_)->toHash());
}

sub to_addr {
    return(__PACKAGE__->new(@_)->toAddr());
}

1;

__END__

=head1 NAME

W_node - winny's Node address manipulation.

=head1 SYNOPSIS

    use W_node;

    $hash = to_hash("127.0.0.1:12345");
    $addr = to_addr('@ee52ac473f516e9b47e5a55fa3f73734');

=head1 DESCRIPTION

This module manipulates winny's hashed node descriptions. convert
IP:PORT string to '@-hex' hashed form and vice versa.

=head1 METHODS

=over 4

=item to_hash ( IP:PORT )

Returns the result of conversion from normal IP:PORT form.

=item to_addr ( @-hex )

Returns the Host:Port string from @-hex'ed Node string.

=back

=head1 SEE ALSO


=head1 AUTHOR

Tomo.M <tomoyuki at pobox.com>

=head1 COPYRIGHT

Copyright (c) 2006 Tomo.M. All rights reserved.

=cut
