line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
2
|
|
|
2
|
|
42244
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
120
|
|
2
|
2
|
|
|
2
|
|
14
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
70
|
|
3
|
2
|
|
|
2
|
|
1495
|
use utf8; |
|
2
|
|
|
|
|
29
|
|
|
2
|
|
|
|
|
10
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Ed2k_link; |
6
|
|
|
|
|
|
|
$Ed2k_link::VERSION = '20160412'; |
7
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
113
|
use Carp (); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
37
|
|
9
|
2
|
|
|
2
|
|
10
|
use File::Basename (); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
33
|
|
10
|
2
|
|
|
2
|
|
1238
|
use URI::Escape (); |
|
2
|
|
|
|
|
3008
|
|
|
2
|
|
|
|
|
59
|
|
11
|
2
|
|
|
2
|
|
1321
|
use Encode::Locale (); |
|
2
|
|
|
|
|
31106
|
|
|
2
|
|
|
|
|
56
|
|
12
|
2
|
|
|
2
|
|
13
|
use Encode (); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
36
|
|
13
|
2
|
|
|
2
|
|
1245
|
use Digest::MD4 (); |
|
2
|
|
|
|
|
1732
|
|
|
2
|
|
|
|
|
58
|
|
14
|
2
|
|
|
2
|
|
1408
|
use Digest::SHA (); |
|
2
|
|
|
|
|
7396
|
|
|
2
|
|
|
|
|
87
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
use constant { |
17
|
2
|
|
|
|
|
6247
|
CHUNK_SIZE => 9_728_000, |
18
|
|
|
|
|
|
|
BLOCK_SIZE => 184_320, |
19
|
2
|
|
|
2
|
|
14
|
}; |
|
2
|
|
|
|
|
3
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 NAME |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
Ed2k_link - module for creating eD2K links and working with them. |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 VERSION |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
version 20160412 |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 SYNOPSIS |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
use Ed2k_link (); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
print Ed2k_link -> from_file( 'c:\\temp\\new_movie.mkv' ) -> link( 'h' ) . "\n"; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
my $emule = Ed2k_link -> from_file( 'eMule0.49c.zip' ) or die 'something wrong with file!'); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
my $sources = Ed2k_link -> from_link( 'ed2k://|file|eMule0.49c.zip|2868871|0F88EEFA9D8AD3F43DABAC9982D2450C|/' ) or die 'incorrect link!'; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
$sources -> from_link( 'ed2k://|file|eMule0.49c-Sources.zip|5770302|195B6D8286BF184C3CC0665148D746CF|/' ) or die 'incorrect link!'; |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
print $emule -> link( 'h' ) if $emule -> filesize <= 10 * 1024 * 1024, "\n"; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
if ( Ed2k_link -> equal( $emule, $sources ) { |
44
|
|
|
|
|
|
|
printf "files %s and %s are equal\n"; |
45
|
|
|
|
|
|
|
$emule -> filename, |
46
|
|
|
|
|
|
|
$sources -> filename; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
print Ed2k_link -> from_file( '/somethere/cool_file.txt' ) -> link('hp'); |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head1 DESCRIPTION |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Ed2k_link module for creating eD2K links from files with correct hash, AICH hash and complete hashset fields. |
54
|
|
|
|
|
|
|
Also it can work with already created links (e. g. from textfile). |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=cut |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _encode_base32 { |
59
|
0
|
|
|
0
|
|
0
|
my %bits_to_char = qw# 00000 A 00001 B 00010 C 00011 D 00100 E 00101 F 00110 G 00111 H |
60
|
|
|
|
|
|
|
01000 I 01001 J 01010 K 01011 L 01100 M 01101 N 01110 O 01111 P |
61
|
|
|
|
|
|
|
10000 Q 10001 R 10010 S 10011 T 10100 U 10101 V 10110 W 10111 X |
62
|
|
|
|
|
|
|
11000 Y 11001 Z 11010 2 11011 3 11100 4 11101 5 11110 6 11111 7 |
63
|
|
|
|
|
|
|
#; |
64
|
0
|
|
|
|
|
0
|
my ($source, $bits, $res) = shift; |
65
|
0
|
|
|
|
|
0
|
$bits .= unpack('B*', substr($source, $_, 1)) for 0 .. length($source) - 1; |
66
|
|
|
|
|
|
|
# generally $bits length could be not 40 * k and there has to be padding. not our case |
67
|
0
|
|
|
|
|
0
|
$res .= $bits_to_char{$&} while $bits =~ m/.{5}/g; |
68
|
0
|
|
|
|
|
0
|
$res; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub _define_base_trees_orientation { # l/r, array_ref, start_idx, end_idx |
72
|
0
|
0
|
|
0
|
|
0
|
if ($_[2] - $_[3] >= 0) { |
|
|
0
|
|
|
|
|
|
73
|
0
|
|
|
|
|
0
|
$_[1][$_[2]] = $_[0]; |
74
|
|
|
|
|
|
|
} elsif ($_[2] + 1 == $_[3]) { |
75
|
0
|
|
|
|
|
0
|
$_[1][$_[2]] = 'l'; |
76
|
0
|
|
|
|
|
0
|
$_[1][$_[3]] = 'r'; |
77
|
|
|
|
|
|
|
} else { |
78
|
0
|
|
|
|
|
0
|
my $med = sprintf("%d", ($_[2] + $_[3]) / 2); |
79
|
0
|
0
|
0
|
|
|
0
|
-- $med if $_[ 0 ] eq 'r' && $_[ 2 ] + $_[ 3 ] == $med * 2; |
80
|
0
|
|
|
|
|
0
|
&_define_base_trees_orientation( 'l', $_[ 1 ], $_[ 2 ], $med ); |
81
|
0
|
|
|
|
|
0
|
&_define_base_trees_orientation( 'r', $_[ 1 ], ++ $med, $_[ 3 ] ); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _get_root_hash { # l/r, array_ref, start_idx, end_idx |
86
|
0
|
|
|
0
|
|
0
|
my $med = $_[3]; |
87
|
0
|
0
|
|
|
|
0
|
if ($_[2] - $_[3] >= 0) { |
|
|
0
|
|
|
|
|
|
88
|
0
|
|
|
|
|
0
|
return; |
89
|
|
|
|
|
|
|
} elsif ($_[3] - $_[2] > 1) { |
90
|
0
|
|
|
|
|
0
|
$med = sprintf("%d", ($_[2] + $_[3]) / 2); |
91
|
0
|
0
|
0
|
|
|
0
|
-- $med if $_[ 0 ] eq 'r' && $_[ 2 ] + $_[ 3 ] == $med * 2; |
92
|
0
|
|
|
|
|
0
|
&_get_root_hash( 'l', |
93
|
|
|
|
|
|
|
$_[ 1 ], |
94
|
|
|
|
|
|
|
$_[ 2 ], |
95
|
|
|
|
|
|
|
$med |
96
|
|
|
|
|
|
|
); |
97
|
0
|
|
|
|
|
0
|
&_get_root_hash( 'r', |
98
|
|
|
|
|
|
|
$_[ 1 ], |
99
|
|
|
|
|
|
|
++ $med, |
100
|
|
|
|
|
|
|
$_[ 3 ] |
101
|
|
|
|
|
|
|
); |
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
0
|
|
|
|
|
0
|
$_[ 1 ] -> [ $_[ 2 ] ] = Digest::SHA::sha1( $_[ 1 ] -> [ $_[ 2 ] ], |
105
|
|
|
|
|
|
|
$_[ 1 ] -> [ $med ] |
106
|
|
|
|
|
|
|
); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head1 CLASS METHODS |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=head2 from_file |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Can be called as class or instance method: |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
my $t = Ed2k_link -> from_file( 'file_1.txt' ) or die 'error!'; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
$t -> from_file( 'file_2.txt' ) or die 'error!'; |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
Creates all fields of eD2K link including hash, AICH hashset, complete hashset. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Filename should be a character string (as opposed to octet string). In case of any error returns undef and object doesn't hold any link information. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Sets Reliable flag to true. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub from_file { |
128
|
0
|
|
|
0
|
1
|
0
|
my $either = shift; |
129
|
0
|
0
|
|
|
|
0
|
%$either = () if ref $either; |
130
|
0
|
|
|
|
|
0
|
my $file = shift; # string of characters (not an octet stream) |
131
|
0
|
0
|
|
|
|
0
|
return undef unless defined $file; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# file must exist and be not empty! |
134
|
0
|
|
|
|
|
0
|
my $filename_to_access = Encode::encode( locale_fs => $file ); |
135
|
0
|
0
|
0
|
|
|
0
|
return undef unless -f $filename_to_access && -s _; |
136
|
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
0
|
my $self = { path_to_file => $file, |
138
|
|
|
|
|
|
|
size => -s _, |
139
|
|
|
|
|
|
|
filename => File::Basename::fileparse( $file ), |
140
|
|
|
|
|
|
|
}; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# emule doesn't escape #[]@$&+,;= |
143
|
0
|
|
|
|
|
0
|
$self -> {escaped_filename} = URI::Escape::uri_escape_utf8( $self -> {filename}, '^A-Za-z0-9\-_.!~*\'()#&+,;=' ); |
144
|
|
|
|
|
|
|
# []@$ |
145
|
0
|
|
|
|
|
0
|
$self -> {escaped_filename} =~ s/%5B/[/g; |
146
|
0
|
|
|
|
|
0
|
$self -> {escaped_filename} =~ s/%5D/]/g; |
147
|
0
|
|
|
|
|
0
|
$self -> {escaped_filename} =~ s/%40/\@/g; |
148
|
0
|
|
|
|
|
0
|
$self -> {escaped_filename} =~ s/%24/\$/g; |
149
|
|
|
|
|
|
|
# hashes. step 1 |
150
|
0
|
|
|
|
|
0
|
my @aich_tree; |
151
|
|
|
|
|
|
|
{ |
152
|
0
|
|
|
|
|
0
|
my $base_blocks = sprintf("%d", $self -> {size} / CHUNK_SIZE); |
153
|
0
|
0
|
|
|
|
0
|
-- $base_blocks if $self -> {size} == $base_blocks * CHUNK_SIZE; |
154
|
0
|
|
|
|
|
0
|
&_define_base_trees_orientation( 'l', \ @aich_tree, 0, $base_blocks ); |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
{ |
158
|
0
|
0
|
|
|
|
0
|
open my $f, '<', $filename_to_access |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
159
|
|
|
|
|
|
|
or die sprintf( 'cannot open %s for reading: %s', |
160
|
|
|
|
|
|
|
$file, |
161
|
|
|
|
|
|
|
$!, |
162
|
|
|
|
|
|
|
); |
163
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
0
|
binmode $f; |
165
|
0
|
|
|
|
|
0
|
my ($t, $readed_bytes); |
166
|
0
|
|
|
|
|
0
|
my $md4 = Digest::MD4 -> new; |
167
|
0
|
|
|
|
|
0
|
while (defined($readed_bytes = read $f, $t, CHUNK_SIZE)) { |
168
|
0
|
|
|
|
|
0
|
$md4 -> add($t); |
169
|
0
|
|
|
|
|
0
|
$self -> {hash} .= $md4 -> clone -> digest; |
170
|
0
|
|
|
|
|
0
|
push @{$self -> {p}}, uc $md4 -> hexdigest; |
|
0
|
|
|
|
|
0
|
|
171
|
0
|
0
|
|
|
|
0
|
if ($readed_bytes) { |
172
|
0
|
|
|
|
|
0
|
my $pos = 0; |
173
|
0
|
|
|
|
|
0
|
my @t_sha1; |
174
|
0
|
|
|
|
|
0
|
while ($pos < $readed_bytes) { |
175
|
0
|
|
|
|
|
0
|
push @t_sha1, Digest::SHA::sha1( substr( $t, $pos, BLOCK_SIZE ) ); |
176
|
0
|
|
|
|
|
0
|
$pos += BLOCK_SIZE; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
# sha1 for chunk |
179
|
0
|
|
|
|
|
0
|
&_get_root_hash( $aich_tree[ $#{ $self -> {p} } ], |
|
0
|
|
|
|
|
0
|
|
180
|
|
|
|
|
|
|
\ @t_sha1, |
181
|
|
|
|
|
|
|
0, |
182
|
|
|
|
|
|
|
$#t_sha1 |
183
|
|
|
|
|
|
|
); |
184
|
0
|
|
|
|
|
0
|
$aich_tree[$#{$self -> {p}}] = $t_sha1[0]; |
|
0
|
|
|
|
|
0
|
|
185
|
|
|
|
|
|
|
} |
186
|
0
|
0
|
|
|
|
0
|
last if $readed_bytes != CHUNK_SIZE; |
187
|
|
|
|
|
|
|
} |
188
|
0
|
|
|
|
|
0
|
close $f; |
189
|
|
|
|
|
|
|
return undef unless defined $readed_bytes |
190
|
0
|
0
|
0
|
|
|
0
|
&& $self -> {size} == $#{$self -> {p}} * CHUNK_SIZE + $readed_bytes; |
|
0
|
|
|
|
|
0
|
|
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
# hashes. step 2 |
194
|
0
|
0
|
|
|
|
0
|
if (@{$self -> {p}} == 1) { |
|
0
|
|
|
|
|
0
|
|
195
|
0
|
|
|
|
|
0
|
$self -> {hash} = $self -> {p}[0]; |
196
|
|
|
|
|
|
|
} else { |
197
|
0
|
|
|
|
|
0
|
$self -> {hash} = uc Digest::MD4::md4_hex( $self -> {hash} ); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
# aich hashset |
200
|
0
|
|
|
|
|
0
|
&_get_root_hash( 'l', |
201
|
|
|
|
|
|
|
\ @aich_tree, |
202
|
|
|
|
|
|
|
0, |
203
|
|
|
|
|
|
|
$#aich_tree |
204
|
|
|
|
|
|
|
); |
205
|
0
|
|
|
|
|
0
|
$self -> {aich} = _encode_base32( $aich_tree[ 0 ] ); |
206
|
0
|
|
|
|
|
0
|
$self -> {reliable} = 1; |
207
|
|
|
|
|
|
|
|
208
|
0
|
0
|
|
|
|
0
|
if (ref $either) { |
209
|
0
|
|
|
|
|
0
|
%$either = %$self; |
210
|
0
|
|
|
|
|
0
|
1; |
211
|
|
|
|
|
|
|
} else { |
212
|
0
|
|
|
|
|
0
|
bless $self, $either; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=head2 from_link |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
Can be called as class or object method: |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
my $tl = Ed2k_link -> from_link( 'ed2k://|file|eMule0.49c.zip|2868871|0F88EEFA9D8AD3F43DABAC9982D2450C|/' ) |
221
|
|
|
|
|
|
|
or die 'incorrect link!'; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
$t1 = from_link( 'ed2k://|file|eMule0.49c-Sources.zip|5770302|195B6D8286BF184C3CC0665148D746CF|/' ) |
224
|
|
|
|
|
|
|
or die 'incorrect link!'; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
Takes mandatory (filename/size/hash) and optional (AICH hash, complete hashset) fields from the link. |
227
|
|
|
|
|
|
|
Checks some correctness of fields (acceptable symbols, length, ...). |
228
|
|
|
|
|
|
|
If link in parameter has complete hashset, checks compliance between hash and complete hashset. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
In case of any incorrectness returns undef and object doesn't hold any link information. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
If link in parameter has AICH and/or complete hashset, sets Reliable flag to false. Otherwise it's true. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=cut |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub from_link { |
237
|
1
|
|
|
1
|
1
|
81367
|
my $either = shift; |
238
|
1
|
50
|
|
|
|
5
|
%$either = () if ref $either; |
239
|
1
|
|
|
|
|
2
|
my $link = shift; |
240
|
1
|
50
|
|
|
|
3
|
return undef unless defined $link; |
241
|
1
|
50
|
|
|
|
11
|
return undef unless $link =~ m#^ed2k://\|file\|([\d\D]+?)\|(\d+)\|([\da-f]{32})\|#i; |
242
|
1
|
|
|
|
|
8
|
my $self = { escaped_filename => $1, |
243
|
|
|
|
|
|
|
size => $2, |
244
|
|
|
|
|
|
|
hash => uc $3, |
245
|
|
|
|
|
|
|
filename => Encode::decode( 'UTF-8', URI::Escape::uri_unescape( $1 ) ), |
246
|
|
|
|
|
|
|
reliable => 1, |
247
|
|
|
|
|
|
|
}; |
248
|
|
|
|
|
|
|
|
249
|
1
|
|
|
|
|
208
|
$link = "|$'"; |
250
|
1
|
50
|
|
|
|
5
|
return undef unless $self -> {size}; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# complete hashset |
253
|
1
|
50
|
|
|
|
3
|
if ($link =~ m/\|p=([\d\D]*?)\|/) { |
254
|
0
|
|
|
|
|
0
|
my $t = uc $1; |
255
|
0
|
|
|
|
|
0
|
$link = "|$`$'"; |
256
|
0
|
0
|
|
|
|
0
|
return undef unless $t =~ m/^([\dA-F]{32}(:[\dA-F]{32})*)$/; |
257
|
|
|
|
|
|
|
|
258
|
0
|
|
|
|
|
0
|
my @t = split ':', $1; |
259
|
0
|
|
|
|
|
0
|
$t = sprintf("%d", $self -> {size} / CHUNK_SIZE); |
260
|
0
|
0
|
|
|
|
0
|
++ $t if $self -> {size} >= $t * CHUNK_SIZE; |
261
|
0
|
0
|
|
|
|
0
|
return undef unless $t == @t; |
262
|
|
|
|
|
|
|
|
263
|
0
|
0
|
|
|
|
0
|
if (@t == 1) { |
264
|
0
|
0
|
|
|
|
0
|
return undef unless $self -> {hash} eq $t[0]; |
265
|
|
|
|
|
|
|
} else { |
266
|
0
|
|
|
|
|
0
|
my $t = ''; |
267
|
0
|
|
|
|
|
0
|
foreach my $bh (@t) { |
268
|
0
|
|
|
|
|
0
|
$t .= chr(hex($&)) while $bh =~ m/../g; |
269
|
|
|
|
|
|
|
} |
270
|
0
|
0
|
|
|
|
0
|
return undef unless $self -> {hash} eq uc Digest::MD4::md4_hex( $t ); |
271
|
0
|
|
|
|
|
0
|
$self -> {reliable} = 0; |
272
|
|
|
|
|
|
|
} |
273
|
0
|
|
|
|
|
0
|
$self -> {p} = \@t; |
274
|
|
|
|
|
|
|
} |
275
|
1
|
50
|
33
|
|
|
11
|
$self -> {p}[0] = $self -> {hash} if $self -> {size} < CHUNK_SIZE && not exists $self -> {p}; |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# aich |
278
|
1
|
50
|
|
|
|
4
|
if ($link =~ m/\|h=([\d\D]*?)\|/) { |
279
|
0
|
|
|
|
|
0
|
$self -> {aich} = uc $1; |
280
|
0
|
|
|
|
|
0
|
$link = "|$`$'"; |
281
|
0
|
0
|
|
|
|
0
|
return undef unless $self -> {aich} =~ m/^[A-Z2-7]{32}$/; |
282
|
0
|
|
|
|
|
0
|
$self -> {reliable} = 0; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
1
|
50
|
|
|
|
3
|
if (ref $either) { |
286
|
0
|
|
|
|
|
0
|
%$either = %$self; |
287
|
0
|
|
|
|
|
0
|
$either; |
288
|
|
|
|
|
|
|
} else { |
289
|
1
|
|
|
|
|
4
|
bless $self, $either; |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head2 ok |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Instance only method. Returns true if object was successfully created and holds all required fields; |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
&do_something() if $t1 -> ok; |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=cut |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub ok { |
302
|
1
|
50
|
|
1
|
1
|
551
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
303
|
1
|
|
33
|
|
|
13
|
return exists $instance -> {escaped_filename} && exists $instance -> {size} && exists $instance -> {hash}; |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=head2 filename |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
Instance method. Returns filename as character string: |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
print $t -> filename; |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=cut |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub filename { |
315
|
1
|
50
|
|
1
|
1
|
4
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
316
|
1
|
|
|
|
|
5
|
$instance -> {filename}; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=head2 escaped_filename |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
Instance method. Returns escaped filename (as in link); |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
print $t -> escaped_filename; |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=cut |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub escaped_filename { |
328
|
1
|
50
|
|
1
|
1
|
4
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
329
|
1
|
|
|
|
|
4
|
$instance -> {escaped_filename}; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=head2 filesize |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
Instance method. Returns filesize; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=cut |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub filesize { |
339
|
1
|
50
|
|
1
|
1
|
5
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
340
|
1
|
|
|
|
|
4
|
$instance -> {size}; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=head2 hash |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
Instance method. Returns hash field from link; |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
=cut |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub hash { |
350
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
351
|
0
|
|
|
|
|
|
$instance -> {hash}; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head2 has_complete_hashset |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Instance method. Returns true if object has complete hashset, false otherwise; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=cut |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub has_complete_hashset { |
361
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
362
|
0
|
0
|
|
|
|
|
exists $instance -> {p} && @{$instance -> {p}}; |
|
0
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head2 complete_hashset |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Instance method. Returns complete hashset if object has it. undef otherwise; |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=cut |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub complete_hashset { |
372
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
373
|
|
|
|
|
|
|
$instance -> has_complete_hashset ? |
374
|
0
|
0
|
|
|
|
|
join ':', @{$instance -> {p}} |
|
0
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
: undef; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=head2 has_aich |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
Instance method. Returns true if object has aich hash, false otherwise; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=cut |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
sub has_aich { |
385
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
386
|
0
|
|
|
|
|
|
exists $instance -> {aich}; |
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=head2 aich |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
Instance method. Returns AICH hash if object has it. undef otherwise; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
=cut |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
sub aich { |
396
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
397
|
0
|
|
|
|
|
|
$instance -> {aich}; |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=head2 link |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Instance only method. Returns string representation of link. Can have parameter with options: |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
h - include AICH hash if available. Recommended. |
405
|
|
|
|
|
|
|
p - include complete hashset if available. |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
my $link1 = $t -> link; |
408
|
|
|
|
|
|
|
my $link_with_aich = $t -> link( 'h' ); |
409
|
|
|
|
|
|
|
my $link_with_hashset = $t -> link( 'p' ); |
410
|
|
|
|
|
|
|
my $iron_link = $t -> link( 'hp' ); |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=cut |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub link { |
415
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
416
|
0
|
|
|
|
|
|
my $optional = shift; |
417
|
0
|
0
|
|
|
|
|
return undef unless $instance -> ok; |
418
|
|
|
|
|
|
|
|
419
|
0
|
|
|
|
|
|
my @part = ( 'ed2k://|file', |
420
|
|
|
|
|
|
|
$instance -> escaped_filename, |
421
|
|
|
|
|
|
|
$instance -> filesize, |
422
|
|
|
|
|
|
|
$instance -> hash, |
423
|
|
|
|
|
|
|
); |
424
|
|
|
|
|
|
|
|
425
|
0
|
0
|
|
|
|
|
if ( defined $optional ) { |
426
|
|
|
|
|
|
|
# complete hashset |
427
|
0
|
0
|
0
|
|
|
|
push @part, |
|
|
|
0
|
|
|
|
|
428
|
|
|
|
|
|
|
'p=' . $instance -> complete_hashset |
429
|
|
|
|
|
|
|
if index( $optional, 'p' ) != -1 |
430
|
|
|
|
|
|
|
&& $instance -> filesize >= CHUNK_SIZE |
431
|
|
|
|
|
|
|
&& $instance -> has_complete_hashset; |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# aich hashset |
434
|
0
|
0
|
0
|
|
|
|
push @part, |
435
|
|
|
|
|
|
|
'h=' . $instance -> aich |
436
|
|
|
|
|
|
|
if index( $optional, 'h' ) != -1 |
437
|
|
|
|
|
|
|
&& $instance -> has_aich; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
0
|
|
|
|
|
|
join '|', @part, '/'; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head2 is_reliable |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Instance method. Returns true if object is reliable, false otherwise; |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=cut |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
sub is_reliable { |
450
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
451
|
0
|
|
|
|
|
|
$instance -> {reliable}; |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 set_reliable |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Instance method. Sets Reliable flag for object. Use it very carefully, or you could end up with fake link |
457
|
|
|
|
|
|
|
that doesn't reference any file and you won't be able to download anything with them. |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
Carefully means: you got string link from someone, who you trust. Or you previously created it from file |
460
|
|
|
|
|
|
|
by yourself and saved somethere and now you're reading those links from file of database. |
461
|
|
|
|
|
|
|
Such usage of this method is appropriated; |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=cut |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
sub set_reliable { |
466
|
0
|
0
|
|
0
|
1
|
|
ref(my $instance = shift) or Carp::croak "class usage! need to be instance usage"; |
467
|
0
|
|
|
|
|
|
$instance -> {reliable} = 1; |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=head2 equal |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
Class only method. |
473
|
|
|
|
|
|
|
Compares two Ed2k_link objects by complex rules. Returns true if they point to the same file. |
474
|
|
|
|
|
|
|
Could fill some fields of one object with other's objects fields. Also can set Reliable flag. |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
print "hey! they are the same!" if Ed2k_link -> equal($t1, $t2); |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=cut |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
sub equal { |
481
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
482
|
0
|
0
|
|
|
|
|
return undef unless @_ == 2; |
483
|
0
|
|
|
|
|
|
my $one = shift; |
484
|
0
|
|
|
|
|
|
my $two = shift; |
485
|
0
|
|
0
|
|
|
|
my $res = $one -> ok && $two -> ok && $one -> filesize == $two -> filesize && $one -> hash eq $two -> hash; |
486
|
0
|
0
|
|
|
|
|
return undef unless $res; |
487
|
0
|
0
|
0
|
|
|
|
$res = $one -> complete_hashset eq $two -> complete_hashset |
488
|
|
|
|
|
|
|
if $one -> has_complete_hashset && $two -> has_complete_hashset; |
489
|
0
|
0
|
|
|
|
|
return undef unless $res; |
490
|
0
|
0
|
0
|
|
|
|
$res = $one -> aich eq $two -> aich |
491
|
|
|
|
|
|
|
if $one -> has_aich && $two -> has_aich; |
492
|
0
|
0
|
|
|
|
|
return undef unless $res; |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# cases with copying complete hash or aich and setting reliable flag |
495
|
0
|
0
|
0
|
|
|
|
if ($one -> is_reliable && $two -> is_reliable) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
496
|
0
|
0
|
0
|
|
|
|
if ($one -> has_complete_hashset && !$two -> has_complete_hashset) { |
|
|
0
|
0
|
|
|
|
|
497
|
0
|
|
|
|
|
|
$two -> {p} = $one -> {p}; |
498
|
|
|
|
|
|
|
} elsif (!$one -> has_complete_hashset && $two -> has_complete_hashset) { |
499
|
0
|
|
|
|
|
|
$one -> {p} = $two -> {p}; |
500
|
|
|
|
|
|
|
} |
501
|
0
|
0
|
0
|
|
|
|
if ($one -> has_aich && !$two -> has_aich) { |
|
|
0
|
0
|
|
|
|
|
502
|
0
|
|
|
|
|
|
$two -> {aich} = $one -> {aich}; |
503
|
|
|
|
|
|
|
} elsif (!$one -> has_aich && $two -> has_aich) { |
504
|
0
|
|
|
|
|
|
$one -> {aich} = $two -> {aich}; |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
} elsif ($one -> is_reliable) { |
507
|
0
|
|
|
|
|
|
my $t = 0; |
508
|
0
|
0
|
|
|
|
|
if ($one -> has_complete_hashset) { |
509
|
0
|
|
|
|
|
|
++ $t; |
510
|
0
|
|
|
|
|
|
$two -> {p} = $one -> {p}; |
511
|
|
|
|
|
|
|
} |
512
|
0
|
0
|
|
|
|
|
if ($one -> has_aich) { |
513
|
0
|
|
|
|
|
|
++ $t; |
514
|
0
|
|
|
|
|
|
$two -> {aich} = $one -> {aich}; |
515
|
|
|
|
|
|
|
} |
516
|
0
|
0
|
|
|
|
|
-- $t if $two -> has_complete_hashset; |
517
|
0
|
0
|
|
|
|
|
-- $t if $two -> has_aich; |
518
|
0
|
0
|
|
|
|
|
$two -> set_reliable if $t >= 0; |
519
|
|
|
|
|
|
|
} elsif ($two -> is_reliable) { |
520
|
0
|
|
|
|
|
|
my $t = 0; |
521
|
0
|
0
|
|
|
|
|
if ($two -> has_complete_hashset) { |
522
|
0
|
|
|
|
|
|
++ $t; |
523
|
0
|
|
|
|
|
|
$one -> {p} = $two -> {p}; |
524
|
|
|
|
|
|
|
} |
525
|
0
|
0
|
|
|
|
|
if ($two -> has_aich) { |
526
|
0
|
|
|
|
|
|
++ $t; |
527
|
0
|
|
|
|
|
|
$one -> {aich} = $two -> {aich}; |
528
|
|
|
|
|
|
|
} |
529
|
0
|
0
|
|
|
|
|
-- $t if $one -> has_complete_hashset; |
530
|
0
|
0
|
|
|
|
|
-- $t if $one -> has_aich; |
531
|
0
|
0
|
|
|
|
|
$one -> set_reliable if $t >= 0; |
532
|
|
|
|
|
|
|
} |
533
|
|
|
|
|
|
|
|
534
|
0
|
|
|
|
|
|
$res; |
535
|
|
|
|
|
|
|
} |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
1; |
538
|
|
|
|
|
|
|
__END__ |