| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #!/usr/bin/perl | 
| 2 |  |  |  |  |  |  | package Ed2k_link; | 
| 3 |  |  |  |  |  |  | our $VERSION = '20090428'; | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 2 |  |  | 2 |  | 103941 | use strict; | 
|  | 2 |  |  |  |  | 7 |  | 
|  | 2 |  |  |  |  | 551 |  | 
| 6 | 2 |  |  | 2 |  | 14 | use warnings; | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 161 |  | 
| 7 | 2 |  |  | 2 |  | 13 | use base qw(Exporter); | 
|  | 2 |  |  |  |  | 8 |  | 
|  | 2 |  |  |  |  | 1814 |  | 
| 8 |  |  |  |  |  |  | our @EXPORT = (); | 
| 9 |  |  |  |  |  |  | our @EXPORT_OK = (); | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 2 |  |  | 2 |  | 18 | use Carp qw(croak); | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 163 |  | 
| 12 | 2 |  |  | 2 |  | 11 | use File::Basename qw(); | 
|  | 2 |  |  |  |  | 5 |  | 
|  | 2 |  |  |  |  | 45 |  | 
| 13 | 2 |  |  | 2 |  | 8808 | use URI::Escape; | 
|  | 2 |  |  |  |  | 4240 |  | 
|  | 2 |  |  |  |  | 164 |  | 
| 14 | 2 |  |  | 2 |  | 2568 | use Digest::MD4 qw(md4_hex); | 
|  | 2 |  |  |  |  | 6586 |  | 
|  | 2 |  |  |  |  | 882 |  | 
| 15 | 2 |  |  | 2 |  | 3642 | use Digest::SHA1 qw(sha1); | 
|  | 2 |  |  |  |  | 7034 |  | 
|  | 2 |  |  |  |  | 200 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | use constant { | 
| 18 | 2 |  |  |  |  | 6136 | CHANK_SIZE => 9_728_000, | 
| 19 |  |  |  |  |  |  | BLOCK_SIZE => 184_320, | 
| 20 | 2 |  |  | 2 |  | 21 | }; | 
|  | 2 |  |  |  |  | 6 |  | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | sub encode_base32 { | 
| 23 | 0 |  |  | 0 | 0 | 0 | my %bits_to_char = qw# 00000 A 00001 B 00010 C 00011 D 00100 E 00101 F 00110 G 00111 H | 
| 24 |  |  |  |  |  |  | 01000 I 01001 J 01010 K 01011 L 01100 M 01101 N 01110 O 01111 P | 
| 25 |  |  |  |  |  |  | 10000 Q 10001 R 10010 S 10011 T 10100 U 10101 V 10110 W 10111 X | 
| 26 |  |  |  |  |  |  | 11000 Y 11001 Z 11010 2 11011 3 11100 4 11101 5 11110 6 11111 7 | 
| 27 |  |  |  |  |  |  | #; | 
| 28 | 0 |  |  |  |  | 0 | my ($source, $bits, $res) = shift; | 
| 29 | 0 |  |  |  |  | 0 | $bits .= unpack('B*', substr($source, $_, 1)) for 0 .. length($source) - 1; | 
| 30 |  |  |  |  |  |  | # generally $bits could be not 40 * k length and have to be padding. but not in our case | 
| 31 | 0 |  |  |  |  | 0 | $res .= $bits_to_char{$&} while $bits =~ m/.{5}/g; | 
| 32 | 0 |  |  |  |  | 0 | $res; | 
| 33 |  |  |  |  |  |  | } | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | sub define_base_trees_orientation { # l/r, array_ref, start_idx, end_idx | 
| 36 | 0 | 0 |  | 0 | 0 | 0 | if ($_[2] - $_[3] >= 0) { | 
|  |  | 0 |  |  |  |  |  | 
| 37 | 0 |  |  |  |  | 0 | $_[1][$_[2]] = $_[0]; | 
| 38 |  |  |  |  |  |  | } elsif ($_[2] + 1 == $_[3]) { | 
| 39 | 0 |  |  |  |  | 0 | $_[1][$_[2]] = 'l'; | 
| 40 | 0 |  |  |  |  | 0 | $_[1][$_[3]] = 'r'; | 
| 41 |  |  |  |  |  |  | } else { | 
| 42 | 0 |  |  |  |  | 0 | my $med = sprintf("%d", ($_[2] + $_[3]) / 2); | 
| 43 | 0 | 0 | 0 |  |  | 0 | $med-- if $_[0] eq 'r' && $_[2] + $_[3] == $med * 2; | 
| 44 | 0 |  |  |  |  | 0 | &define_base_trees_orientation('l', $_[1], $_[2], $med++); | 
| 45 | 0 |  |  |  |  | 0 | &define_base_trees_orientation('r', $_[1], $med, $_[3]); | 
| 46 |  |  |  |  |  |  | } | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | sub get_root_hash {		# l/r, array_ref, start_idx, end_idx | 
| 50 | 0 |  |  | 0 | 0 | 0 | my $med = $_[3]; | 
| 51 | 0 | 0 |  |  |  | 0 | if ($_[2] - $_[3] >= 0) { | 
|  |  | 0 |  |  |  |  |  | 
| 52 | 0 |  |  |  |  | 0 | return; | 
| 53 |  |  |  |  |  |  | } elsif ($_[3] - $_[2] > 1) { | 
| 54 | 0 |  |  |  |  | 0 | $med = sprintf("%d", ($_[2] + $_[3]) / 2); | 
| 55 | 0 | 0 | 0 |  |  | 0 | $med-- if $_[0] eq 'r' && $_[2] + $_[3] == $med * 2; | 
| 56 | 0 |  |  |  |  | 0 | &get_root_hash('l', $_[1], $_[2], $med++); | 
| 57 | 0 |  |  |  |  | 0 | &get_root_hash('r', $_[1], $med, $_[3]); | 
| 58 |  |  |  |  |  |  | } | 
| 59 | 0 |  |  |  |  | 0 | $_[1] -> [$_[2]] = sha1($_[1] -> [$_[2]], $_[1] -> [$med]); | 
| 60 |  |  |  |  |  |  | } | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | sub from_file { | 
| 63 | 0 |  |  | 0 | 1 | 0 | my $either = shift; | 
| 64 | 0 | 0 |  |  |  | 0 | %$either = () if ref $either; | 
| 65 | 0 |  |  |  |  | 0 | my $file = shift; | 
| 66 | 0 | 0 |  |  |  | 0 | return undef unless defined $file; | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | # file must exist and be not empty! | 
| 69 | 0 | 0 | 0 |  |  | 0 | return undef unless -f $file && -s $file; | 
| 70 |  |  |  |  |  |  |  | 
| 71 | 0 |  |  |  |  | 0 | my $self = { path_to_file => $file }; | 
| 72 | 0 |  |  |  |  | 0 | $self -> {filename} = File::Basename::fileparse($file); | 
| 73 |  |  |  |  |  |  | # emule doesn't escape #[]@$&+,;= | 
| 74 | 0 |  |  |  |  | 0 | $self -> {escaped_filename} = uri_escape_utf8($self -> {filename}, '^A-Za-z0-9\-_.!~*\'()#&+,;='); | 
| 75 |  |  |  |  |  |  | # []@$ | 
| 76 | 0 |  |  |  |  | 0 | $self -> {escaped_filename} =~ s/%5B/[/g; | 
| 77 | 0 |  |  |  |  | 0 | $self -> {escaped_filename} =~ s/%5D/]/g; | 
| 78 | 0 |  |  |  |  | 0 | $self -> {escaped_filename} =~ s/%40/\@/g; | 
| 79 | 0 |  |  |  |  | 0 | $self -> {escaped_filename} =~ s/%24/\$/g; | 
| 80 | 0 |  |  |  |  | 0 | $self -> {size} = -s $file; | 
| 81 |  |  |  |  |  |  | # hashes. step 1 | 
| 82 | 0 |  |  |  |  | 0 | my @aich_tree; | 
| 83 |  |  |  |  |  |  | { | 
| 84 | 0 |  |  |  |  | 0 | my $base_blocks = sprintf("%d", $self -> {size} / CHANK_SIZE); | 
|  | 0 |  |  |  |  | 0 |  | 
| 85 | 0 | 0 |  |  |  | 0 | $base_blocks-- if $self -> {size} == $base_blocks * CHANK_SIZE; | 
| 86 | 0 |  |  |  |  | 0 | &define_base_trees_orientation('l', \@aich_tree, 0, $base_blocks); | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | { | 
| 90 | 0 | 0 |  |  |  | 0 | open my $f, '<', $file or return undef; | 
|  | 0 |  |  |  |  | 0 |  | 
| 91 | 0 |  |  |  |  | 0 | binmode $f; | 
| 92 | 0 |  |  |  |  | 0 | my ($t, $readed_bytes); | 
| 93 | 0 |  |  |  |  | 0 | my $md4 = Digest::MD4 -> new; | 
| 94 | 0 |  |  |  |  | 0 | while (defined($readed_bytes = read $f, $t, CHANK_SIZE)) { | 
| 95 | 0 |  |  |  |  | 0 | $md4 -> add($t); | 
| 96 | 0 |  |  |  |  | 0 | $self -> {hash} .= $md4 -> clone -> digest; | 
| 97 | 0 |  |  |  |  | 0 | push @{$self -> {p}}, uc $md4 -> hexdigest; | 
|  | 0 |  |  |  |  | 0 |  | 
| 98 | 0 | 0 |  |  |  | 0 | if ($readed_bytes) { | 
| 99 | 0 |  |  |  |  | 0 | my $pos = 0; | 
| 100 | 0 |  |  |  |  | 0 | my @t_sha1; | 
| 101 | 0 |  |  |  |  | 0 | while ($pos < $readed_bytes) { | 
| 102 | 0 |  |  |  |  | 0 | push @t_sha1, sha1(substr($t, $pos, BLOCK_SIZE)); | 
| 103 | 0 |  |  |  |  | 0 | $pos += BLOCK_SIZE; | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  | # sha1 for chank | 
| 106 | 0 |  |  |  |  | 0 | &get_root_hash($aich_tree[$#{$self -> {p}}], \@t_sha1, 0, $#t_sha1); | 
|  | 0 |  |  |  |  | 0 |  | 
| 107 | 0 |  |  |  |  | 0 | $aich_tree[$#{$self -> {p}}] = $t_sha1[0]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 108 |  |  |  |  |  |  | } | 
| 109 | 0 | 0 |  |  |  | 0 | last if $readed_bytes != CHANK_SIZE; | 
| 110 |  |  |  |  |  |  | } | 
| 111 | 0 |  |  |  |  | 0 | close $f; | 
| 112 | 0 |  |  |  |  | 0 | return undef unless defined $readed_bytes | 
| 113 | 0 | 0 | 0 |  |  | 0 | && $self -> {size} == $#{$self -> {p}} * CHANK_SIZE + $readed_bytes; | 
| 114 |  |  |  |  |  |  | } | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | # hashes. step 2 | 
| 117 | 0 | 0 |  |  |  | 0 | if (@{$self -> {p}} == 1) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 118 | 0 |  |  |  |  | 0 | $self -> {hash} = $self -> {p}[0]; | 
| 119 |  |  |  |  |  |  | } else { | 
| 120 | 0 |  |  |  |  | 0 | $self -> {hash} = uc md4_hex($self -> {hash}); | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  | # aich hashset | 
| 123 | 0 |  |  |  |  | 0 | &get_root_hash('l', \@aich_tree, 0, $#aich_tree); | 
| 124 | 0 |  |  |  |  | 0 | $self -> {aich} = encode_base32($aich_tree[0]); | 
| 125 | 0 |  |  |  |  | 0 | $self -> {reliable} = 1; | 
| 126 |  |  |  |  |  |  |  | 
| 127 | 0 | 0 |  |  |  | 0 | if (ref $either) { | 
| 128 | 0 |  |  |  |  | 0 | %$either = %$self; | 
| 129 | 0 |  |  |  |  | 0 | 1; | 
| 130 |  |  |  |  |  |  | } else { | 
| 131 | 0 |  |  |  |  | 0 | bless $self, $either; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | sub from_link { | 
| 136 | 1 |  |  | 1 | 1 | 12 | my $either = shift; | 
| 137 | 1 | 50 |  |  |  | 5 | %$either = () if ref $either; | 
| 138 | 1 |  |  |  |  | 3 | my $link = shift; | 
| 139 | 1 | 50 |  |  |  | 4 | return undef unless defined $link; | 
| 140 | 1 | 50 |  |  |  | 14 | return undef unless $link =~ m#^ed2k://\|file\|([\d\D]+?)\|(\d+)\|([\da-f]{32})\|#i; | 
| 141 | 1 |  |  |  |  | 2 | my $self; | 
| 142 | 1 |  |  |  |  | 5 | $self -> {escaped_filename} = $1; | 
| 143 | 1 |  |  |  |  | 6 | $self -> {filename} = uri_unescape($1); | 
| 144 | 1 |  |  |  |  | 14 | $self -> {size} = $2; | 
| 145 | 1 |  |  |  |  | 5 | $self -> {hash} = uc $3; | 
| 146 | 1 |  |  |  |  | 4 | $self -> {reliable} = 1; | 
| 147 | 1 |  |  |  |  | 4 | $link = "|$'"; | 
| 148 | 1 | 50 |  |  |  | 4 | return undef unless $self -> {size}; | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | # complete hashset | 
| 151 | 1 | 50 |  |  |  | 4 | if ($link =~ m/\|p=([\d\D]*?)\|/) { | 
| 152 | 0 |  |  |  |  | 0 | my $t = uc $1; | 
| 153 | 0 |  |  |  |  | 0 | $link = "|$`$'"; | 
| 154 | 0 | 0 |  |  |  | 0 | return undef unless $t =~ m/^([\dA-F]{32}(:[\dA-F]{32})*)$/; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 0 |  |  |  |  | 0 | my @t = split ':', $1; | 
| 157 | 0 |  |  |  |  | 0 | $t = sprintf("%d", $self -> {size} / CHANK_SIZE); | 
| 158 | 0 | 0 |  |  |  | 0 | $t++ if $self -> {size} >= $t * CHANK_SIZE; | 
| 159 | 0 | 0 |  |  |  | 0 | return undef unless $t == @t; | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 0 | 0 |  |  |  | 0 | if (@t == 1) { | 
| 162 | 0 | 0 |  |  |  | 0 | return undef unless $self -> {hash} eq $t[0]; | 
| 163 |  |  |  |  |  |  | } else { | 
| 164 | 0 |  |  |  |  | 0 | my $t = ''; | 
| 165 | 0 |  |  |  |  | 0 | foreach my $bh (@t) { | 
| 166 | 0 |  |  |  |  | 0 | $t .= chr(hex($&)) while $bh =~ m/../g; | 
| 167 |  |  |  |  |  |  | } | 
| 168 | 0 | 0 |  |  |  | 0 | return undef unless $self -> {hash} eq uc md4_hex($t); | 
| 169 | 0 |  |  |  |  | 0 | $self -> {reliable} = 0; | 
| 170 |  |  |  |  |  |  | } | 
| 171 | 0 |  |  |  |  | 0 | $self -> {p} = \@t; | 
| 172 |  |  |  |  |  |  | } | 
| 173 | 1 | 50 | 33 |  |  | 19 | $self -> {p}[0] = $self -> {hash} if $self -> {size} < CHANK_SIZE && not exists $self -> {p}; | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | # aich | 
| 176 | 1 | 50 |  |  |  | 4 | if ($link =~ m/\|h=([\d\D]*?)\|/) { | 
| 177 | 0 |  |  |  |  | 0 | $self -> {aich} = uc $1; | 
| 178 | 0 |  |  |  |  | 0 | $link = "|$`$'"; | 
| 179 | 0 | 0 |  |  |  | 0 | return undef unless $self -> {aich} =~ m/^[A-Z2-7]{32}$/; | 
| 180 | 0 |  |  |  |  | 0 | $self -> {reliable} = 0; | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 1 | 50 |  |  |  | 4 | if (ref $either) { | 
| 184 | 0 |  |  |  |  | 0 | %$either = %$self; | 
| 185 | 0 |  |  |  |  | 0 | 1; | 
| 186 |  |  |  |  |  |  | } else { | 
| 187 | 1 |  |  |  |  | 6 | bless $self, $either; | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | sub ok { | 
| 192 | 1 | 50 |  | 1 | 1 | 1268 | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 193 | 1 |  | 33 |  |  | 21 | return exists $instance -> {escaped_filename} && exists $instance -> {size} && exists $instance -> {hash}; | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | sub filename { | 
| 197 | 1 | 50 |  | 1 | 1 | 5 | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 198 | 1 |  |  |  |  | 6 | $instance -> {filename}; | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | sub escaped_filename { | 
| 202 | 1 | 50 |  | 1 | 1 | 7 | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 203 | 1 |  |  |  |  | 6 | $instance -> {escaped_filename}; | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | sub filesize { | 
| 207 | 1 | 50 |  | 1 | 1 | 27 | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 208 | 1 |  |  |  |  | 7 | $instance -> {size}; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | sub hash { | 
| 212 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 213 | 0 |  |  |  |  |  | $instance -> {hash}; | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | sub has_complete_hashset { | 
| 217 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 218 | 0 | 0 |  |  |  |  | exists $instance -> {p} && @{$instance -> {p}}; | 
|  | 0 |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | } | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | sub complete_hashset { | 
| 222 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 223 | 0 |  |  |  |  |  | $instance -> has_complete_hashset ? | 
| 224 | 0 | 0 |  |  |  |  | join ':', @{$instance -> {p}} | 
| 225 |  |  |  |  |  |  | : undef; | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | sub has_aich { | 
| 229 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 230 | 0 |  |  |  |  |  | exists $instance -> {aich}; | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | sub aich { | 
| 234 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 235 | 0 |  |  |  |  |  | $instance -> {aich}; | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | sub link { | 
| 239 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 240 | 0 |  |  |  |  |  | my $optional = shift; | 
| 241 | 0 | 0 |  |  |  |  | return undef unless $instance -> ok; | 
| 242 | 0 |  |  |  |  |  | my $res = 'ed2k://|file|'.$instance -> escaped_filename.'|'.$instance -> filesize.'|'.$instance -> hash.'|'; | 
| 243 | 0 | 0 |  |  |  |  | if (defined $optional) { | 
| 244 |  |  |  |  |  |  | # complete hashset | 
| 245 | 0 | 0 | 0 |  |  |  | $res .= "p=" . $instance -> complete_hashset . '|' | 
|  |  |  | 0 |  |  |  |  | 
| 246 |  |  |  |  |  |  | if $optional =~ /p/ && $instance -> filesize >= CHANK_SIZE && $instance -> has_complete_hashset; | 
| 247 |  |  |  |  |  |  | # aich hashset | 
| 248 | 0 | 0 | 0 |  |  |  | $res .= 'h=' . $instance -> aich . '|' if $optional =~ /h/ && $instance -> has_aich; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 0 |  |  |  |  |  | $res .= '/'; | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | sub is_reliable { | 
| 254 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 255 | 0 |  |  |  |  |  | $instance -> {reliable}; | 
| 256 |  |  |  |  |  |  | } | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | sub set_reliable { | 
| 259 | 0 | 0 |  | 0 | 1 |  | ref(my $instance = shift) or croak "class usage! need to be instance usage"; | 
| 260 | 0 |  |  |  |  |  | $instance -> {reliable} = 1; | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | sub equal { | 
| 264 | 0 |  |  | 0 | 1 |  | my $class = shift; | 
| 265 | 0 | 0 |  |  |  |  | return undef unless @_ == 2; | 
| 266 | 0 |  |  |  |  |  | my $one = shift; | 
| 267 | 0 |  |  |  |  |  | my $two = shift; | 
| 268 | 0 |  | 0 |  |  |  | my $res = $one -> ok && $two -> ok && $one -> filesize == $two -> filesize && $one -> hash eq $two -> hash; | 
| 269 | 0 | 0 |  |  |  |  | return undef unless $res; | 
| 270 | 0 | 0 | 0 |  |  |  | $res = $one -> complete_hashset eq $two -> complete_hashset | 
| 271 |  |  |  |  |  |  | if $one -> has_complete_hashset && $two -> has_complete_hashset; | 
| 272 | 0 | 0 |  |  |  |  | return undef unless $res; | 
| 273 | 0 | 0 | 0 |  |  |  | $res = $one -> aich eq $two -> aich | 
| 274 |  |  |  |  |  |  | if $one -> has_aich && $two -> has_aich; | 
| 275 | 0 | 0 |  |  |  |  | return undef unless $res; | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | # cases with copying complete hash or aich and setting reliable flag | 
| 278 | 0 | 0 | 0 |  |  |  | if ($one -> is_reliable && $two -> is_reliable) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 279 | 0 | 0 | 0 |  |  |  | if ($one -> has_complete_hashset && !$two -> has_complete_hashset) { | 
|  |  | 0 | 0 |  |  |  |  | 
| 280 | 0 |  |  |  |  |  | $two -> {p} = $one -> {p}; | 
| 281 |  |  |  |  |  |  | } elsif (!$one -> has_complete_hashset && $two -> has_complete_hashset) { | 
| 282 | 0 |  |  |  |  |  | $one -> {p} = $two -> {p}; | 
| 283 |  |  |  |  |  |  | } | 
| 284 | 0 | 0 | 0 |  |  |  | if ($one -> has_aich && !$two -> has_aich) { | 
|  |  | 0 | 0 |  |  |  |  | 
| 285 | 0 |  |  |  |  |  | $two -> {aich} = $one -> {aich}; | 
| 286 |  |  |  |  |  |  | } elsif (!$one -> has_aich && $two -> has_aich) { | 
| 287 | 0 |  |  |  |  |  | $one -> {aich} = $two -> {aich}; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  | } elsif ($one -> is_reliable) { | 
| 290 | 0 |  |  |  |  |  | my $t = 0; | 
| 291 | 0 | 0 |  |  |  |  | if ($one -> has_complete_hashset) { | 
| 292 | 0 |  |  |  |  |  | $t++; | 
| 293 | 0 |  |  |  |  |  | $two -> {p} = $one -> {p}; | 
| 294 |  |  |  |  |  |  | } | 
| 295 | 0 | 0 |  |  |  |  | if ($one -> has_aich) { | 
| 296 | 0 |  |  |  |  |  | $t++; | 
| 297 | 0 |  |  |  |  |  | $two -> {aich} = $one -> {aich}; | 
| 298 |  |  |  |  |  |  | } | 
| 299 | 0 | 0 |  |  |  |  | $t-- if $two -> has_complete_hashset; | 
| 300 | 0 | 0 |  |  |  |  | $t-- if $two -> has_aich; | 
| 301 | 0 | 0 |  |  |  |  | $two -> set_reliable if $t >= 0; | 
| 302 |  |  |  |  |  |  | } elsif ($two -> is_reliable) { | 
| 303 | 0 |  |  |  |  |  | my $t = 0; | 
| 304 | 0 | 0 |  |  |  |  | if ($two -> has_complete_hashset) { | 
| 305 | 0 |  |  |  |  |  | $t++; | 
| 306 | 0 |  |  |  |  |  | $one -> {p} = $two -> {p}; | 
| 307 |  |  |  |  |  |  | } | 
| 308 | 0 | 0 |  |  |  |  | if ($two -> has_aich) { | 
| 309 | 0 |  |  |  |  |  | $t++; | 
| 310 | 0 |  |  |  |  |  | $one -> {aich} = $two -> {aich}; | 
| 311 |  |  |  |  |  |  | } | 
| 312 | 0 | 0 |  |  |  |  | $t-- if $one -> has_complete_hashset; | 
| 313 | 0 | 0 |  |  |  |  | $t-- if $one -> has_aich; | 
| 314 | 0 | 0 |  |  |  |  | $one -> set_reliable if $t >= 0; | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  |  | 
| 317 | 0 |  |  |  |  |  | $res; | 
| 318 |  |  |  |  |  |  | } | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | 1; | 
| 321 |  |  |  |  |  |  | __END__ |