File Coverage

blib/lib/P2P/pDonkey/Meta.pm
Criterion Covered Total %
statement 501 685 73.1
branch 133 266 50.0
condition 35 72 48.6
subroutine 107 127 84.2
pod 0 58 0.0
total 776 1208 64.2


line stmt bran cond sub pod time code
1             # P2P::pDonkey::Meta.pm
2             #
3             # Copyright (c) 2003-2004 Alexey klimkin .
4             # All rights reserved.
5             # This program is free software; you can redistribute it and/or
6             # modify it under the same terms as Perl itself.
7             #
8             package P2P::pDonkey::Meta;
9              
10 5     5   6449 use 5.006;
  5         18  
  5         198  
11 5     5   28 use strict;
  5         8  
  5         148  
12 5     5   24 use warnings;
  5         8  
  5         1235  
13              
14             require Exporter;
15              
16             our $VERSION = '0.05';
17              
18             our @ISA = qw(Exporter);
19              
20             our %EXPORT_TAGS =
21             ( 'all' => [ qw(
22             MetaTagName
23              
24             DLS_HASHING
25             DLS_QUEUED
26             DLS_LOOKING
27             DLS_DOWNLOADING
28             DLS_PAUSED
29             DLS_IDS
30             DLS_NOSOURCES
31             DLS_DONE
32             DLS_HASHING2
33             DLS_ERRORLOADING
34             DLS_COMPLETING
35             DLS_COMPLETE
36             DLS_CORRUPTED
37             DLS_ERRORHASHING
38             DLS_TRANSFERRING
39             FPRI_LOW
40             FPRI_NORMAL
41             FPRI_HIGH
42             SPRI_LOW
43             SPRI_NORMAL
44             SPRI_HIGH
45              
46             SZ_FILEPART
47              
48             VT_STRING VT_INTEGER
49             ST_COMBINE ST_AND ST_OR ST_ANDNOT
50             ST_NAME
51             ST_META
52             ST_MINMAX ST_MIN ST_MAX
53              
54             TT_UNDEFINED
55             TT_NAME TT_SIZE TT_TYPE TT_FORMAT TT_COPIED TT_GAPSTART TT_GAPEND
56             TT_DESCRIPTION TT_PING TT_FAIL TT_PREFERENCE TT_PORT TT_IP TT_VERSION
57             TT_TEMPFILE TT_PRIORITY TT_STATUS TT_AVAILABILITY
58              
59             packB unpackB
60             packW unpackW
61             packD unpackD
62             packF unpackF
63             packS unpackS
64             packSList unpackSList
65             packHash unpackHash
66             packHashList unpackHashList
67              
68             packMetaTagName unpackMetaTagName
69             packMeta unpackMeta printMeta makeMeta sameMetaType
70             packMetaList unpackMetaList printMetaList
71             packMetaListU unpackMetaListU printMetaListU
72             MetaListU2MetaList MetaList2MetaListU
73              
74             packInfo unpackInfo makeClientInfo makeServerInfo printInfo
75             packInfoList unpackInfoList printInfoList
76            
77             packFileInfo unpackFileInfo makeFileInfo
78             packFileInfoList unpackFileInfoList makeFileInfoList
79              
80             packSearchQuery unpackSearchQuery matchSearchQuery makeSQLExpr
81              
82             packAddr unpackAddr printAddr idAddr
83             packAddrList unpackAddrList
84             ) ],
85             'tags' => [ qw(
86             SZ_FILEPART
87              
88             VT_STRING VT_INTEGER
89             ST_COMBINE ST_AND ST_OR ST_ANDNOT
90             ST_NAME
91             ST_META
92             ST_MINMAX ST_MIN ST_MAX
93              
94             TT_UNDEFINED
95             TT_NAME TT_SIZE TT_TYPE TT_FORMAT TT_COPIED TT_GAPSTART TT_GAPEND
96             TT_DESCRIPTION TT_PING TT_FAIL TT_PREFERENCE TT_PORT TT_IP TT_VERSION
97             TT_TEMPFILE TT_PRIORITY TT_STATUS TT_AVAILABILITY
98             ) ]
99             );
100              
101             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
102              
103             our @EXPORT = qw(
104            
105             );
106              
107              
108             # Preloaded methods go here.
109              
110 5     5   27 use Carp;
  5         8  
  5         374  
111 5     5   26 use File::Glob ':glob';
  5         18  
  5         1373  
112 5     5   4696 use Tie::IxHash;
  5         25563  
  5         161  
113 5     5   10139 use Digest::MD4 qw( md4_hex );
  5         5234  
  5         338  
114 5     5   37 use File::Basename;
  5         9  
  5         501  
115 5     5   28 use File::Find;
  5         9  
  5         285  
116 5     5   3659 use POSIX qw( ceil );
  5         20687  
  5         33  
117 5     5   8098 use P2P::pDonkey::Util qw( ip2addr );
  5         16  
  5         513  
118              
119             #use Video::Info;
120              
121             my $debug = 0;
122              
123             my @MetaTagName;
124             sub MetaTagName {
125 747     747 0 1893 return $MetaTagName[$_[0]];
126             }
127              
128             # --- Download status byte
129 5     5   29 use constant DLS_HASHING => 0x00;
  5         10  
  5         359  
130 5     5   26 use constant DLS_QUEUED => 0x01;
  5         8  
  5         209  
131 5     5   25 use constant DLS_LOOKING => 0x02;
  5         9  
  5         205  
132 5     5   24 use constant DLS_DOWNLOADING => 0x03;
  5         8  
  5         235  
133 5     5   26 use constant DLS_PAUSED => 0x04;
  5         42  
  5         214  
134 5     5   43 use constant DLS_IDS => 0x05;
  5         7  
  5         233  
135 5     5   25 use constant DLS_NOSOURCES => 0x06;
  5         7  
  5         212  
136 5     5   27 use constant DLS_DONE => 0x07;
  5         35  
  5         190  
137 5     5   24 use constant DLS_HASHING2 => 0x08;
  5         20  
  5         197  
138 5     5   24 use constant DLS_ERRORLOADING => 0x09;
  5         7  
  5         181  
139 5     5   23 use constant DLS_COMPLETING => 0x0a;
  5         8  
  5         301  
140 5     5   22 use constant DLS_COMPLETE => 0x0b;
  5         19  
  5         194  
141 5     5   25 use constant DLS_CORRUPTED => 0x0c;
  5         7  
  5         211  
142 5     5   32 use constant DLS_ERRORHASHING => 0x0d;
  5         7  
  5         175  
143 5     5   23 use constant DLS_TRANSFERRING => 0x0e;
  5         7  
  5         180  
144             # --- File priority
145 5     5   22 use constant FPRI_LOW => 0x00;
  5         6  
  5         176  
146 5     5   22 use constant FPRI_NORMAL => 0x01;
  5         6  
  5         170  
147 5     5   21 use constant FPRI_HIGH => 0x02;
  5         6  
  5         162  
148             # --- Server priority
149 5     5   22 use constant SPRI_LOW => 0x02;
  5         7  
  5         164  
150 5     5   22 use constant SPRI_NORMAL => 0x00;
  5         5  
  5         167  
151 5     5   21 use constant SPRI_HIGH => 0x01;
  5         7  
  5         195  
152              
153             # --- known sizes of pieces
154 5     5   21 use constant SZ_FILEPART => 9500*1024; # v 0.4.x
  5         7  
  5         194  
155 5     5   22 use constant SZ_B_FILEPART => 9728000;
  5         7  
  5         188  
156 5     5   26 use constant SZ_S_FILEPART => 486400;
  5         6  
  5         179  
157              
158             # -- tag bytes in file info
159 5     5   20 use constant FI_DESCRIPTION => 0x2;
  5         7  
  5         270  
160 5     5   21 use constant FI_PART_HASHES => 0x1;
  5         8  
  5         179  
161              
162             # --- value types
163 5     5   21 use constant VT_STRING => 0x02;
  5         9  
  5         200  
164 5     5   21 use constant VT_INTEGER => 0x03;
  5         6  
  5         184  
165 5     5   20 use constant VT_FLOAT => 0x04;
  5         8  
  5         176  
166             # --- search query constants
167             # - search type
168 5     5   21 use constant ST_COMBINE => 0x0;
  5         7  
  5         178  
169 5     5   25 use constant ST_NAME => 0x1;
  5         6  
  5         207  
170 5     5   21 use constant ST_META => 0x2;
  5         6  
  5         188  
171 5     5   22 use constant ST_MINMAX => 0x3;
  5         5  
  5         396  
172             # - search logic op for combined
173 5     5   24 use constant ST_AND => 0x0;
  5         8  
  5         204  
174 5     5   19 use constant ST_OR => 0x1;
  5         8  
  5         189  
175 5     5   27 use constant ST_ANDNOT => 0x2;
  5         6  
  5         186  
176             # - constants for ST_MINMAX
177 5     5   35 use constant ST_MIN => 0x1;
  5         7  
  5         203  
178 5     5   21 use constant ST_MAX => 0x2;
  5         6  
  5         196  
179             # --- tag types
180 5     5   22 use constant TT_UNDEFINED => 0x00;
  5         15  
  5         204  
181 5     5   23 use constant TT_NAME => 0x01;
  5         6  
  5         222  
182 5     5   22 use constant TT_SIZE => 0x02;
  5         44  
  5         192  
183 5     5   23 use constant TT_TYPE => 0x03; # Audio, Video, Image, Pro, Doc, Col
  5         7  
  5         204  
184 5     5   20 use constant TT_FORMAT => 0x04; # file extension
  5         6  
  5         186  
185 5     5   23 use constant TT_COPIED => 0x08;
  5         6  
  5         168  
186 5     5   27 use constant TT_GAPSTART => 0x09;
  5         7  
  5         179  
187 5     5   22 use constant TT_GAPEND => 0x0a;
  5         5  
  5         198  
188 5     5   23 use constant TT_DESCRIPTION => 0x0b;
  5         6  
  5         199  
189 5     5   22 use constant TT_PING => 0x0c;
  5         7  
  5         216  
190 5     5   22 use constant TT_FAIL => 0x0d;
  5         8  
  5         186  
191 5     5   26 use constant TT_PREFERENCE => 0x0e;
  5         7  
  5         222  
192 5     5   80 use constant TT_PORT => 0x0f;
  5         7  
  5         192  
193 5     5   56 use constant TT_IP => 0x10;
  5         8  
  5         212  
194 5     5   23 use constant TT_VERSION => 0x11;
  5         8  
  5         198  
195 5     5   23 use constant TT_TEMPFILE => 0x12;
  5         8  
  5         205  
196 5     5   53 use constant TT_PRIORITY => 0x13;
  5         8  
  5         193  
197 5     5   21 use constant TT_STATUS => 0x14;
  5         36  
  5         179  
198 5     5   21 use constant TT_AVAILABILITY => 0x15;
  5         5  
  5         54172  
199             $MetaTagName[TT_NAME] = 'Name';
200             $MetaTagName[TT_SIZE] = 'Size';
201             $MetaTagName[TT_TYPE] = 'Type';
202             $MetaTagName[TT_FORMAT] = 'Format';
203             $MetaTagName[TT_COPIED] = 'Copied';
204             $MetaTagName[TT_GAPSTART] = 'Gap start';
205             $MetaTagName[TT_GAPEND] = 'Gap end';
206             $MetaTagName[TT_DESCRIPTION] = 'Description';
207             $MetaTagName[TT_PING] = 'Ping';
208             $MetaTagName[TT_FAIL] = 'Fail';
209             $MetaTagName[TT_PREFERENCE] = 'Preference';
210             $MetaTagName[TT_PORT] = 'Port';
211             $MetaTagName[TT_IP] = 'IP';
212             $MetaTagName[TT_VERSION] = 'Version';
213             $MetaTagName[TT_TEMPFILE] = 'Temp file';
214             $MetaTagName[TT_PRIORITY] = 'Priority';
215             $MetaTagName[TT_STATUS] = 'Status';
216             $MetaTagName[TT_AVAILABILITY] = 'Availability';
217              
218             # basic pack/unpack functions
219             sub unpackB {
220 1133     1133 0 1141 my $res;
221 1133 100       1986 if (defined $_[1]) {
222 1132         2501 $res = unpack("x$_[1] C", $_[0]);
223 1132 50       2563 $_[1] += 1 if defined $res;
224             } else {
225 1         3 $res = unpack('C', $_[0]);
226             }
227 1133         2389 return $res;
228             }
229              
230             sub unpackW {
231 79     79 0 83 my $res;
232 79 100       135 if (defined $_[1]) {
233 78         172 $res = unpack("x$_[1] S", $_[0]);
234 78 50       185 $_[1] += 2 if defined $res;
235             } else {
236 1         4 $res = unpack('S', $_[0]);
237             }
238 79         197 return $res;
239             }
240              
241             sub unpackD {
242 708     708 0 922 my $res;
243 708 100       1204 if (defined $_[1]) {
244 707         1532 $res = unpack("x$_[1] L", $_[0]);
245 707 50       1704 $_[1] += 4 if defined $res;
246             } else {
247 1         4 $res = unpack('L', $_[0]);
248             }
249 708         1532 return $res;
250             }
251              
252             sub unpackF {
253 7     7 0 9 my $res;
254 7 50       13 if (defined $_[1]) {
255 7         20 $res = unpack("x$_[1] f", $_[0]);
256 7 50       21 $_[1] += 4 if defined $res;
257             } else {
258 0         0 $res = unpack('f', $_[0]);
259             }
260 7         13 return $res;
261             }
262              
263             sub unpackS {
264 1380     1380 0 1319 my ($res, $len);
265 1380 100       2025 if (defined $_[1]) {
266 1378 50       8367 defined($len = unpack("x$_[1] S", $_[0])) or return;
267 1378 50       4840 defined($res = unpack("x$_[1] x2 a$len", $_[0])) or return;
268 1378 50       2804 length($res) == $len or return;
269 1378         1463 $_[1] += 2;
270 1378         1651 $_[1] += $len;
271             } else {
272 2 50       8 defined($len = unpack('S', $_[0])) or return;
273 2         8 $res = unpack("x2 a$len", $_[0]);
274             }
275 1380         3378 return $res;
276             }
277              
278             sub unpackSList {
279 1     1 0 3 my (@res, $len, $s);
280 1         2 @res = ();
281 1 50       2 defined($len = &unpackW) or return;
282 1         4 while ($len--) {
283 2 50       3 defined($s = &unpackS) or return;
284 2         5 push @res, $s;
285             }
286 1         10 return \@res;
287             }
288              
289             sub unpackHash8 {
290 1042     1042 0 3473 my $res = unpack("x$_[1] H16", $_[0]);
291 1042 50       1904 length($res) == 16 or return;
292 1042         1141 $_[1] += 8;
293 1042         3996 return $res;
294             }
295              
296             sub unpackHash {
297 283     283 0 878 my $res = unpack("x$_[1] H32", $_[0]);
298 283 50       576 length($res) == 32 or return;
299 283         311 $_[1] += 16;
300 283         797 return $res;
301             }
302              
303             sub unpackHashList {
304 4     4 0 8 my ($n, @res, $hash);
305 4 50       12 defined($n = &unpackW) or return;
306 4         8 @res = ();
307 4         20 while ($n--) {
308 103 50       126 defined($hash = &unpackHash) or return;
309 103         242 push @res, $hash;
310             }
311 4         30 return \@res;
312             }
313              
314             #
315             sub packB {
316 88     88 0 368 return pack('C', shift);
317             }
318             sub packW {
319 5     5 0 21 return pack('S', shift);
320             }
321             sub packD {
322 28     28 0 115 return pack('L', shift);
323             }
324             sub packF {
325 0     0 0 0 return pack('f', shift);
326             }
327             sub packS {
328 60     60 0 289 return pack('Sa*', length $_[0], $_[0]);
329             }
330             sub packSList {
331 1     1 0 2 my ($l) = @_;
332 1         2 my ($res, $s);
333 1         4 $res = packW(scalar @$l);
334 1         3 foreach $s (@$l) {
335 2         4 $res .= packS($s);
336             }
337 1         5 return $res;
338             }
339             sub packHash8 {
340 0     0 0 0 return pack('H16', $_[0]);
341             }
342             sub packHash {
343 6     6 0 37 return pack('H32', $_[0]);
344             }
345             sub packHashList {
346 3     3 0 6 my ($l) = @_;
347 3         4 my ($res, $hash);
348 3         9 $res = packW(scalar @$l);
349 3         9 foreach $hash (@$l) {
350 2         5 $res .= packHash($hash);
351             }
352 3         18 return $res;
353             }
354              
355             # Meta Tag
356             sub makeMeta {
357 26     26 0 198 my ($st, $value, $name, $vt) = @_;
358 26 50       62 if ($st) {
359 26 100 100     254 if ($st == TT_NAME || $st == TT_DESCRIPTION
      100        
      100        
      100        
360             || $st == TT_TYPE || $st == TT_FORMAT
361             || $st == TT_TEMPFILE) {
362 11         20 $vt = VT_STRING;
363             } else {
364 15         19 $vt = VT_INTEGER;
365             }
366             }
367 26 50       47 confess "Value type is undefined" unless defined $vt;
368 26 50       75 return {Type => $st, ValType => $vt, Value => $value,
369             Name => $st ? MetaTagName($st) : $name};
370             }
371              
372             sub sameMetaType {
373 18     18 0 106 my ($m1, $m2) = @_;
374 18   33     236 return $m1 && $m2 && $m1->{ValType} == $m2->{ValType}
375             && ($m1->{Type}
376             ? $m1->{Type} == $m2->{Type}
377             : $m1->{Name} eq $m2->{Name});
378             }
379              
380             sub unpackMetaTagName {
381 828     828 0 798 my ($name, $st, $len);
382              
383 828 50       1165 defined($name = &unpackS) or return;
384 828 50       1632 ($len = length $name) or return; # length is not 0
385 828         809 $st = ord $name;
386              
387             # if ($st < _TT_LAST) { # special tag
388 828 100 100     3520 if ($st == TT_GAPEND || $st == TT_GAPSTART) {
    100          
389 10         26 $name = unpack('xa*', $name);
390             } elsif ($len == 1) {
391 716         1269 $name = MetaTagName($st);
392 716 100       1526 $name = sprintf("Unknown(0x%x)", $st) if !$name;
393             } else {
394 102         123 $st = TT_UNDEFINED;
395             }
396             # } else {
397             # $st = TT_UNDEFINED;
398             # }
399 828         3371 return {Type => $st, Name => $name};
400             }
401             sub packMetaTagName {
402 37     37 0 73 my ($meta) = @_;
403 37         86 my ($st, $name) = ($meta->{Type}, $meta->{Name});
404              
405 37 100 100     253 if ($st == TT_GAPSTART || $st == TT_GAPEND) {
    50          
406 2         11 $name = packB($st) . $name;
407             } elsif ($st) {
408 35         76 $name = packB($st);
409             }
410 37         96 return packS($name);
411             }
412              
413             sub unpackMeta {
414 825     825 0 833 my ($vt, $val, $meta);
415              
416 825 50       1179 defined($vt = &unpackB) or return;
417 825 50       1227 $meta = &unpackMetaTagName or return;
418              
419 825 100       1559 if ($vt == VT_STRING) {
    100          
    50          
420 546         780 $val = &unpackS;
421             } elsif ($vt == VT_INTEGER) {
422 272         422 $val = &unpackD;
423             } elsif ($vt == VT_FLOAT) {
424 7         24 $val = &unpackF;
425             } else {
426 0         0 return;
427             }
428 825 50       1727 defined($val) or return;
429              
430 825         1363 $meta->{ValType} = $vt;
431 825         1287 $meta->{Value} = $val;
432 825         2579 return $meta;
433             }
434             sub packMeta {
435 34     34 0 4842 my ($meta) = @_;
436 34         95 my ($vt, $val) = ($meta->{ValType}, $meta->{Value});
437 34         84 my $res = packB($vt) . packMetaTagName($meta);
438 34 100       120 if ($vt == VT_STRING) {
    50          
    0          
439 17         34 $res .= packS($val);
440             } elsif ($vt == VT_INTEGER) {
441 17         41 $res .= packD($val);
442             } elsif ($vt == VT_FLOAT) {
443 0         0 $res .= packF($val);
444             } else {
445 0         0 confess "Incorrect meta tag value type!\n";
446             }
447 34         138 return $res;
448             }
449             sub printMeta {
450 0     0 0 0 my ($m) = @_;
451 0 0       0 print $m->{Name}, ': ', ($m->{Type} == TT_IP ? ip2addr($m->{Value}) : $m->{Value});
452             }
453              
454             # list of references to meta tags
455             sub unpackMetaList {
456 143     143 0 140 my ($ntags, @res, $meta);
457 143 50       4812 defined($ntags = &unpackD) or return;
458 143         217 @res = ();
459 143         273 while ($ntags--) {
460 568 50       800 $meta = &unpackMeta or return;
461 568         1352 push @res, $meta;
462             }
463 143         375 return \@res;
464             }
465             sub packMetaList {
466 4     4 0 8 my ($l) = @_;
467 4         7 my ($res, $meta);
468 4         13 $res = packD(scalar @$l);
469 4         13 foreach $meta (@$l) {
470 16         36 $res .= packMeta($meta);
471             }
472 4         20 return $res;
473             }
474             sub printMetaList {
475 0     0 0 0 my ($l) = @_;
476 0         0 foreach my $m (@$l) {
477 0         0 print "\t";
478 0         0 printMeta($m);
479 0         0 print "\n";
480             }
481             }
482              
483             # hash of references to meta
484             sub unpackMetaListU {
485 71     71 0 72 my ($ntags, %res, $meta);
486              
487 71         273 tie %res, "Tie::IxHash";
488 71 50       948 defined($ntags = &unpackD) or return;
489 71         233 %res = ();
490 71         1146 while ($ntags--) {
491 239 50       2847 $meta = &unpackMeta or return;
492 239         1305 $res{$meta->{Name}} = $meta;
493             }
494 71         1178 return \%res;
495             }
496             sub packMetaListU {
497 0     0 0 0 my ($res, $meta);
498 0         0 my $ntags = 0;
499 0         0 $res = '';
500 0         0 while ((undef, $meta) = each %{$_[0]}) {
  0         0  
501 0         0 $res .= packMeta($meta);
502 0         0 $ntags++;
503             }
504 0         0 return packD($ntags) . $res;
505             }
506             sub printMetaListU {
507 0     0 0 0 my ($l) = @_;
508 0         0 foreach my $m (values %$l) {
509 0         0 print "\t";
510 0         0 printMeta($m);
511 0         0 print "\n";
512             }
513             }
514              
515             sub MetaList2MetaListU {
516 0     0 0 0 my ($l) = @_;
517 0         0 my %res;
518 0         0 tie %res, "Tie::IxHash";
519 0         0 foreach my $meta (@$l) {
520 0         0 $res{$meta->{Name}} = $meta;
521             }
522 0         0 return \%res;
523             }
524              
525             sub MetaListU2MetaList {
526 4     4 0 8 return [values %{$_[0]}];
  4         27  
527             }
528              
529             # client, server or search result info
530             sub unpackInfo {
531 0     0 0 0 my ($hash, $ip, $port, $meta);
532 0 0       0 defined($hash = &unpackHash) or return;
533 0 0       0 ($ip, $port) = &unpackAddr or return;
534 0 0       0 $meta = &unpackMetaListU or return;
535 0         0 return {Hash => $hash, IP => $ip, Port => $port, Meta => $meta};
536             }
537              
538             sub packInfo {
539 0     0 0 0 my ($d) = @_;
540 0         0 return packHash($d->{Hash}) . packAddr($d)
541             . packMetaListU($d->{Meta});
542             }
543              
544             sub unpackInfoList {
545 0     0 0 0 my ($nres, @res, $info);
546 0 0       0 defined($nres = &unpackD) or return;
547 0         0 @res = ();
548 0         0 while ($nres--) {
549 0 0       0 $info = &unpackInfo or return;
550 0         0 push @res, $info;
551             }
552 0         0 return \@res;
553             }
554              
555             sub packInfoList {
556 0     0 0 0 my ($l) = @_;
557 0         0 my ($res, $info);
558 0         0 $res = packD(scalar @$l);
559 0         0 foreach $info (@$l) {
560 0         0 $res .= packInfo($info);
561             }
562 0         0 return $res;
563             }
564              
565             sub printInfoList {
566 0     0 0 0 foreach my $i (@{$_[0]}) {
  0         0  
567 0         0 printInfo($i);
568             }
569             }
570              
571             sub makeClientInfo {
572 0     0 0 0 my ($ip, $port, $nick, $version) = @_;
573 0         0 my (%meta, $hash);;
574 0         0 $hash = md4_hex($nick);
575 0         0 tie %meta, "Tie::IxHash";
576 0         0 $meta{Name} = makeMeta(TT_NAME, $nick);
577 0         0 $meta{Version} = makeMeta(TT_VERSION, $version);
578 0         0 $meta{Port} = makeMeta(TT_PORT, $port);
579 0         0 return {Hash => $hash, IP => $ip, Port => $port, Meta => \%meta};
580             }
581              
582             sub makeServerInfo {
583 0     0 0 0 my ($ip, $port, $name, $description) = @_;
584 0         0 my (%meta, $hash);;
585 0         0 $hash = md4_hex($name);
586 0         0 tie %meta, "Tie::IxHash";
587 0         0 $meta{Name} = makeMeta(TT_NAME, $name);
588 0         0 $meta{Description} = makeMeta(TT_DESCRIPTION, $description);
589 0         0 return {Hash => $hash, IP => $ip, Port => $port, Meta => \%meta};
590             }
591              
592             sub printInfo {
593 0     0 0 0 my ($info) = @_;
594 0 0       0 $info or return;
595              
596 0 0       0 if (defined $info->{Date}) {
597 0         0 print "Date: ", scalar(localtime($info->{Date})), "\n";
598             }
599              
600 0 0       0 if (defined $info->{IP}) {
601 0         0 print "Address: ";
602 0         0 printAddr($info);
603 0         0 print "\n";
604             }
605              
606 0 0       0 if (defined $info->{Hash}) {
607 0         0 print "Hash: $info->{Hash}\n";
608             }
609              
610 0 0       0 if ($info->{Parts}) {
611 0         0 print "Parts:\n";
612 0         0 my $i = 0;
613 0         0 foreach my $parthash (@{$info->{Parts}}) {
  0         0  
614 0         0 print "\t$i: $parthash\n";
615 0         0 $i++;
616             }
617             }
618              
619 0 0       0 if ($info->{Parts8}) {
620 0         0 print "Parts8:\n";
621 0         0 my $i = 0;
622 0         0 foreach my $parthash (@{$info->{Parts8}}) {
  0         0  
623 0         0 print "\t$i: $parthash\n";
624 0         0 $i++;
625             }
626             }
627              
628 0 0       0 if ($info->{Gaps}) {
629 0         0 print "Gaps:\n";
630 0         0 my $gaps = $info->{Gaps};
631 0         0 for (my $i = 0; $i < @$gaps/2; $i += 2) {
632 0         0 print "\t$gaps->[$i] - $gaps->[$i+1]\n";
633             }
634             }
635              
636 0 0       0 if ($info->{Meta}) {
637 0         0 my ($name, $meta);
638 0         0 print "Meta:\n";
639 0         0 while (($name, $meta) = each %{$info->{Meta}}) {
  0         0  
640 0         0 print "\t$name: $meta->{Value}\n";
641             }
642             }
643             }
644              
645             # file info
646             sub unpackFileInfo {
647 140     140 0 162 my (%res, $metas, %tags, @gaps);
648 0         0 my $bb;
649              
650 140         195 $bb = &unpackB;
651 140 50       254 ($bb == FI_DESCRIPTION) or return;
652              
653 140 50       184 defined($res{Date} = &unpackD) or return;
654 140 50       239 defined($res{Hash} = &unpackHash) or return;
655 140 50       246 $metas = &unpackMetaList or return;
656              
657 140         628 tie %tags, "Tie::IxHash";
658 140         1758 foreach my $meta (@$metas) {
659 546 100 100     6448 if ($meta->{Type} == TT_GAPSTART || $meta->{Type} == TT_GAPEND) {
660 2         6 push @gaps, $meta->{Value};
661             } else {
662 544         2247 $tags{$meta->{Name}} = $meta;
663             }
664             }
665 140         1855 $res{Gaps} = [sort {$a <=> $b} @gaps];
  1         6  
666 140         242 $res{Meta} = \%tags;
667              
668 140         223 $bb = &unpackB;
669 140 100       329 if ($bb == FI_PART_HASHES) {
    100          
670 67         280 my $size = $tags{Size}{Value};
671 67 100       528 if ($size >= SZ_B_FILEPART) {
672 9         13 my @hashes;
673 9         60 for (my $i = 0; $i < ceil($size / SZ_B_FILEPART); $i++) {
674 36         56 push @hashes, &unpackHash;
675             }
676 9         20 $res{Parts} = \@hashes;
677 9 50       18 (&unpackB == FI_PART_HASHES) or return;
678             }
679 67 50       122 if ($size >= SZ_S_FILEPART) {
680 67         73 my @hashes8;
681 67         315 for (my $i = 0; $i < ceil($size / SZ_S_FILEPART); $i++) {
682 1042         1575 push @hashes8, &unpackHash8;
683             }
684 67         167 $res{Parts8} = \@hashes8;
685             }
686             } elsif ($bb == FI_DESCRIPTION) {
687 71 50       264 if (defined $_[1]) {
688 71         94 $_[1] -= 1;
689             }
690             }
691            
692 140         562 return \%res;
693             }
694              
695             sub packFileInfo {
696 2     2 0 11 my ($d) = @_;
697 2         3 my ($res, $metas);
698 2         8 $res = packB(FI_DESCRIPTION) . packD($d->{Date}) . packHash($d->{Hash});
699 2         11 $metas = MetaListU2MetaList($d->{Meta});
700 2 50 66     170 if ($d->{Gaps} and @{$d->{Gaps}}) {
  1         9  
701 0         0 my $gaps = $d->{Gaps};
702 0         0 my $ngaps = @$gaps / 2;
703 0         0 for (my ($i, $n) = (0, 0); $i < $ngaps; $i += 2, $n++) {
704 0         0 push @$metas, makeMeta(TT_GAPSTART, $gaps->[$i], $n);
705             }
706 0         0 for (my ($i, $n) = (0, 0); $i < $ngaps; $i += 2, $n++) {
707 0         0 push @$metas, makeMeta(TT_GAPEND, $gaps->[$i+1], $i);
708             }
709             }
710 2         10 $res .= packMetaList($metas);
711 2 50 66     13 if ($d->{Parts} and @{$d->{Parts}}) {
  1         10  
712 0         0 $res .= packB(FI_PART_HASHES);
713 0         0 foreach my $h ($d->{Parts}) {
714 0         0 $res .= packHash($h);
715             }
716             }
717 2 50 66     13 if ($d->{Parts8} and @{$d->{Parts8}}) {
  1         10  
718 0         0 $res .= packB(FI_PART_HASHES);
719 0         0 foreach my $h8 ($d->{Parts8}) {
720 0         0 $res .= packHash8($h8);
721             }
722             }
723 2         8 $res .= packB(0x0);
724 2         12 return $res;
725             }
726              
727             sub unpackFileInfoList {
728 1     1 0 22 my ($nres, @res, $info);
729 1 50       4 defined($nres = &unpackD) or return;
730 1         3 @res = ();
731 1         5 while ($nres--) {
732 138 50       208 $info = &unpackFileInfo or return;
733 138         402 push @res, $info;
734             }
735 1         6 return \@res;
736             }
737              
738             sub packFileInfoList {
739 0     0 0 0 my ($l) = @_;
740 0         0 my ($res, $info);
741 0         0 $res = packD(scalar @$l);;
742 0         0 foreach $info (@$l) {
743 0         0 $res .= packFileInfo($info);
744             }
745 0         0 return $res;
746             }
747              
748             sub makeFileInfo {
749 1     1 0 323 my ($path) = @_;
750 1         3 my ($base, $ext);
751 0         0 my ($context, %meta, $hash, $type);
752              
753 1         83 $path = bsd_glob($path, GLOB_TILDE);
754              
755 1 50 33     35 (-e $path && -r _) or return;
756              
757 1 50       6 print "Making info for $path\n" if $debug;
758              
759             # my $vinfo = Video::Info->new(-file => $path);
760             # if ($vinfo->type()) {
761             # print $vinfo->filename, "\n";
762             # print $vinfo->filesize(), "\n";
763             # print $vinfo->type(), "\n";
764             # print $vinfo->duration(), "\n";
765             # print $vinfo->minutes(), "\n";
766             # print $vinfo->MMSS(), "\n";
767             # print $vinfo->geometry(), "\n";
768             # print $vinfo->title(), "\n";
769             # print $vinfo->author(), "\n";
770             # print $vinfo->copyright(), "\n";
771             # print $vinfo->description(), "\n";
772             # print $vinfo->rating(), "\n";
773             # print $vinfo->packets(), "\n";
774             # }
775              
776 1         93 ($base, undef, $ext) = fileparse($path, '\..*');
777 1 50       9 $ext = unpack('xa*', $ext) if $ext; # skip first '.'
778 1 50       5 if ($ext) {
779 1         9 my %ft = qw(mp3 Audio avi Video gif Image iso Pro doc Doc);
780 1         10 $type = $ft{lc $ext};
781             }
782              
783 1         3 my ($size, $date);
784 1         29 $size = (stat _)[7];
785 1         5 $date = (stat _)[9];
786              
787 1         15 tie %meta, "Tie::IxHash";
788 1         34 $meta{Name} = makeMeta(TT_NAME, "$base.$ext");
789 1         38 $meta{Size} = makeMeta(TT_SIZE, $size);
790 1 50       28 $meta{Type} = makeMeta(TT_TYPE, $type) if $type;
791 1 50       28 $meta{Format} = makeMeta(TT_FORMAT, $ext) if $ext;
792              
793 1 50       87 open(HANDLE, $path) or return;
794 1         7 binmode(HANDLE);
795              
796 1         26 $context = new Digest::MD4;
797 1         82 $context->addfile(\*HANDLE);
798 1         11 $hash = $context->hexdigest;
799              
800 1         3 my $part;
801 1         4 my @parts = ();
802 1 50       7 if ($size > SZ_B_FILEPART) {
803 0         0 seek(HANDLE, 0, 0);
804 0         0 for (my $i = 0; $i < ceil($size / SZ_B_FILEPART); $i++) {
805 0         0 read(HANDLE, $part, SZ_B_FILEPART);
806 0         0 push @parts, md4_hex($part);
807             }
808             }
809 1         3 my @parts8 = ();
810 1 50       7 if ($size >= SZ_S_FILEPART) {
811 0         0 seek(HANDLE, 0, 0);
812 0         0 for (my $i = 0; $i < ceil($size / SZ_S_FILEPART); $i++) {
813 0         0 read(HANDLE, $part, SZ_S_FILEPART);
814 0         0 push @parts8, substr(md4_hex($part), 0, 16);
815             }
816             }
817            
818 1         54 close HANDLE;
819              
820 1         21 return {Date => $date, Hash => $hash, Parts => \@parts, Parts8 => \@parts8, Meta => \%meta, Path => $path};
821             }
822              
823             sub makeFileInfoList {
824 0     0 0 0 my (@res, $info);
825 0         0 @res = ();
826 0         0 foreach my $pattern (@_) {
827 0         0 my $globbed = bsd_glob($pattern, GLOB_TILDE);
828 0         0 print "ggg: $globbed\n";
829 0 0   0   0 find { wanted => sub { push(@res, makeFileInfo($File::Find::name)) if -f $File::Find::name}, no_chdir => 1 },
  0         0  
830             bsd_glob($pattern, GLOB_TILDE);
831             }
832 0         0 return \@res;
833             }
834              
835             # search query
836             sub unpackSearchQuery {
837 7     7 0 14 my ($t);
838 7 50       19 defined($t = &unpackB) or return;
839              
840 7 100       37 if ($t == ST_COMBINE) {
    100          
    100          
    50          
841 3         6 my ($op, $exp1, $exp2);
842 3 50       9 defined($op = &unpackB) or return;
843 3 50       25 $exp1 = &unpackSearchQuery or return;
844 3 50       9 $exp2 = &unpackSearchQuery or return;
845 3         23 return {Type => $t, Op => $op, Q1 => $exp1, Q2 => $exp2};
846              
847             } elsif ($t == ST_NAME) {
848 1         3 my $str;
849 1 50       4 defined($str = &unpackS) or return;
850 1         29 return {Type => $t, Value => $str};
851              
852             } elsif ($t == ST_META) {
853 1         2 my ($val, $metaname);
854 1 50       4 defined($val = &unpackS) or return;
855 1 50       11 $metaname = &unpackMetaTagName or return;
856 1         9 return {Type => $t, Value => $val, MetaName => $metaname};
857              
858             } elsif ($t == ST_MINMAX) {
859 2         5 my ($val, $metaname, $comp);
860 2 50       8 defined($val = &unpackD) or return;
861 2 50       5 defined($comp = &unpackB) or return;
862 2 50 66     23 ($comp == ST_MIN || $comp == ST_MAX) or return;
863 2 50       190 $metaname = &unpackMetaTagName or return;
864 2         15 return {Type => $t, Value => $val, Compare => $comp, MetaName => $metaname};
865              
866             } else {
867 0         0 return;
868             }
869             }
870             sub packSearchQuery {
871 7     7 0 316 my ($d) = @_;
872 7         11 my ($res, $t);
873 7         23 $res = packB($t = $d->{Type});
874              
875 7 100       49 if ($t == ST_COMBINE) {
    100          
    100          
    50          
876 3         11 return $res . packB($d->{Op})
877             . packSearchQuery($d->{Q1})
878             . packSearchQuery($d->{Q2});
879              
880             } elsif ($t == ST_NAME) {
881 1         4 return $res . packS($d->{Value});
882              
883             } elsif ($t == ST_META) {
884 1         5 return $res . packS($d->{Value}) . packMetaTagName($d->{MetaName});
885              
886             } elsif ($t == ST_MINMAX) {
887 2         9 return $res . packD($d->{Value}) . packB($d->{Compare})
888             . packMetaTagName($d->{MetaName});
889              
890             } else {
891 0         0 confess "Incorrect search query type!\n";
892             }
893             }
894              
895             sub makeSQLExpr {
896 14     14 0 48 my ($q, $ok, $fields) = @_;
897 14         29 my $t = $q->{Type};
898 14         25 my $nm;
899              
900 14 100       57 if ($t == ST_COMBINE) {
    100          
    100          
    50          
901 6         14 my $op = $q->{Op};
902              
903 6 100       27 if ($op == ST_AND) {
    100          
    50          
904 2         45 return makeSQLExpr($q->{Q1}, $ok, $fields) . ' AND ' . makeSQLExpr($q->{Q2}, $ok, $fields);
905             } elsif ($op == ST_OR) {
906 2         24 return '(' . makeSQLExpr($q->{Q1}, $ok, $fields) . ' OR ' . makeSQLExpr($q->{Q2}, $ok, $fields) . ')';
907             } elsif ($op == ST_ANDNOT) {
908 2         8 return makeSQLExpr($q->{Q1}, $ok, $fields) . ' AND NOT ' . makeSQLExpr($q->{Q2}, $ok, $fields);
909             } else {
910 0         0 $$ok = 0;
911 0         0 return '';
912             }
913              
914             } elsif ($t == ST_NAME) {
915 2         6 my $nm = MetaTagName(TT_NAME);
916 2         7 my $ft = $fields->{$nm};
917 2 50 33     17 if (defined $ft && $ft == VT_STRING) {
918 2         6 my $qval = $q->{Value};
919 2         7 $qval =~ s/'/''/g;
920 2         17 return "$nm LIKE '$qval'";
921             } else {
922 0         0 $$ok = 0;
923 0         0 return '';
924             }
925              
926             } elsif ($t == ST_META) {
927 2         7 my $nm = $q->{MetaName}->{Name};
928 2         7 my $ft = $fields->{$nm};
929 2 50 33     15 if (defined $ft && $ft == VT_STRING) {
930 2         5 my $qval = $q->{Value};
931 2         6 $qval =~ s/'/''/g;
932 2         19 return "$nm LIKE '$qval'";
933             } else {
934 0         0 $$ok = 0;
935 0         0 return '';
936             }
937              
938             } elsif ($t == ST_MINMAX) {
939 4         12 my $nm = $q->{MetaName}->{Name};
940 4         9 my $ft = $fields->{$nm};
941 4 50 33     39 if (defined $ft && $ft == VT_INTEGER) {
942 4 100       19 if ($q->{Compare} == ST_MIN) {
    50          
943 2         22 return "$nm >= $q->{Value}";
944             } elsif ($q->{Compare} == ST_MAX) {
945 2         21 return "$nm <= $q->{Value}";
946             } else {
947 0         0 $$ok = 0;
948 0         0 return '';
949             }
950             } else {
951 0         0 $$ok = 0;
952 0         0 return '';
953             }
954              
955             } else {
956 0         0 $$ok = 0;
957 0         0 return '';
958             }
959             }
960              
961             sub matchSearchQuery {
962 0     0 0 0 my ($q, $i) = @_;
963 0         0 my $t = $q->{Type};
964              
965 0 0       0 if ($t == ST_COMBINE) {
    0          
    0          
    0          
966 0         0 my $op = $q->{Op};
967              
968 0 0       0 if ($op == ST_AND) {
    0          
    0          
969 0   0     0 return matchSearchQuery($q->{Q1}, $i) && matchSearchQuery($q->{Q2}, $i);
970             } elsif ($op == ST_OR) {
971 0   0     0 return matchSearchQuery($q->{Q1}, $i) || matchSearchQuery($q->{Q2}, $i);
972             } elsif ($op == ST_ANDNOT) {
973 0   0     0 return matchSearchQuery($q->{Q1}, $i) && !matchSearchQuery($q->{Q2}, $i);
974             } else {
975 0         0 return;
976             }
977              
978             } elsif ($t == ST_NAME) {
979 0         0 my ($mm, $qval);
980 0         0 $qval = $q->{Value};
981 0         0 $mm = $i->{Meta}->{Name};
982              
983 0   0     0 return ($mm && $mm->{Value} =~ /$qval/);
984              
985             } elsif ($t == ST_META) {
986 0         0 my $mm = $i->{Meta}->{$q->{MetaName}->{Name}};
987              
988 0 0 0     0 return unless $mm && $mm->{ValType} == VT_STRING;
989              
990 0         0 return $mm->{Value} eq $q->{Value};
991              
992             } elsif ($t == ST_MINMAX) {
993 0         0 my $mm = $i->{Meta}->{$q->{MetaName}->{Name}};
994              
995 0 0 0     0 return unless $mm && $mm->{ValType} == VT_INTEGER;
996              
997 0 0       0 if ($q->{Compare} == ST_MIN) {
    0          
998 0         0 return $mm->{Value} >= $q->{Value};
999             } elsif ($q->{Compare} == ST_MAX) {
1000 0         0 return $mm->{Value} <= $q->{Value};
1001             } else {
1002 0         0 return;
1003             }
1004              
1005             } else {
1006 0         0 return;
1007             }
1008             }
1009              
1010             # list (ip1 port1 ip2 port2 ..)
1011             sub unpackAddrList {
1012 1     1 0 2 my ($snum, $ip, $port, @res);
1013              
1014 1 50       4 defined($snum = &unpackB) or return;
1015 1         2 @res = ();
1016 1         4 while ($snum--) {
1017 2 50       13 defined($ip = &unpackD) or return;
1018 2 50       4 defined($port = &unpackW) or return;
1019 2         7 push @res, $ip, $port;
1020             }
1021 1         11 return \@res;
1022             }
1023             sub packAddrList {
1024 1     1 0 3 my $l = shift;
1025 1         3 my $n = @$l / 2;
1026 1         13 return pack('C', $n) . pack('LS' x $n, @$l);
1027             }
1028              
1029             sub unpackAddr {
1030 71     71 0 79 my ($ip, $port);
1031 71 50       98 defined($ip = &unpackD) or return;
1032 71 50       105 defined($port = &unpackW) or return;
1033 71         253 return ($ip, $port);
1034             }
1035              
1036             sub packAddr {
1037 1     1 0 3 my ($p) = @_;
1038 1 50       5 if (ref $p) {
1039 0         0 return pack('LS', $p->{IP}, $p->{Port});
1040             } else {
1041 1         6 return pack('LS', @_);
1042             }
1043             }
1044              
1045             sub printAddr {
1046 0     0 0 0 my ($ip, $port) = @_;
1047 0 0 0     0 if (ref $ip && !defined $port) {
1048 0         0 print ip2addr($ip->{IP}), ':', $ip->{Port};
1049             } else {
1050 0         0 print ip2addr($ip), ':', $port;
1051             }
1052             }
1053              
1054             sub idAddr {
1055 69     69 0 98 my ($ip, $port) = @_;
1056 69 50 33     300 if (ref $ip && !defined $port) {
1057 69         538 return $ip->{IP}.':'.$ip->{Port};
1058             } else {
1059 0           return "$ip:$port";
1060             }
1061             }
1062              
1063             1;
1064             __END__