File Coverage

blib/lib/Net/Google/SafeBrowsing3/Storage.pm
Criterion Covered Total %
statement 13 84 15.4
branch 1 6 16.6
condition 0 68 0.0
subroutine 4 24 16.6
pod 19 21 90.4
total 37 203 18.2


line stmt bran cond sub pod time code
1             package Net::Google::SafeBrowsing3::Storage;
2              
3              
4 1     1   4 use strict;
  1         1  
  1         23  
5 1     1   3 use warnings;
  1         1  
  1         20  
6              
7 1     1   4 use Carp;
  1         1  
  1         989  
8              
9              
10             our $VERSION = '0.1';
11              
12             =head1 NAME
13              
14             Net::Google::SafeBrowsing3::Storage - Base class for storing the Google Safe Browsing v2 database
15              
16             =head1 SYNOPSIS
17              
18             package Net::Google::SafeBrowsing3::Sqlite;
19              
20             use base 'Net::Google::SafeBrowsing3::Storage';
21              
22             =head1 DESCRIPTION
23              
24             This is the base class for implementing a storage mechanism for the Google Safe Browsing v3 database. See L for an example of implementation.
25              
26             This module cannot be used on its own as it does not actually store anything. All methods should redefined. Check the code to see which arguments are used, and what should be returned.
27              
28             =cut
29              
30              
31             =head1 CONSTRUCTOR
32              
33             =over 4
34              
35             =back
36              
37             =head2 new()
38              
39             Create a Net::Google::SafeBrowsing3::Storage object
40              
41             my $storage => Net::Google::SafeBrowsing3::Storage->new();
42              
43             =cut
44              
45             sub new {
46 1     1 1 2 my ($class, %args) = @_;
47              
48 1         2 my $self = {
49             %args,
50             };
51              
52 1 50       6 bless $self, $class or croak "Can't bless $class: $!";
53 1         3 return $self;
54             }
55              
56             =head1 PUBLIC FUNCTIONS
57              
58             =over 4
59              
60             =back
61              
62             =head2 add_chunks()
63              
64             Add chunk information to the local database
65              
66             $storage->add_chunks(type => 'a', chunknum => 2154, chunks => [{host => HEX, prefix => ''}], list => 'goog-malware-shavar');
67              
68             Does not return anything.
69              
70              
71             Arguments
72              
73             =over 4
74              
75             =item type
76              
77             Required. Type of chunk: 'a' (add chunk) or 's' (sub chunk).
78              
79             =item chunknum
80              
81             Required. Chunk number.
82              
83             =item chunks
84              
85             Required. Array of chunks
86              
87             For add chunks, each element of the array is an hash reference in the following format:
88              
89             {
90             host => HEX,
91             prefix => HEX
92             }
93              
94             For sub chunks, each element of the array is an hash reference in the following format:
95              
96             {
97             host => HEX,
98             prefix => HEX,
99             add_chunknum => INTEGER
100             }
101              
102             =item list
103              
104             Required. Google Safe Browsing list name.
105              
106              
107             =back
108              
109             =cut
110              
111             sub add_chunks {
112 0     0 1   my ($self, %args) = @_;
113 0   0       my $type = $args{type} || 'a';
114 0   0       my $chunknum = $args{chunknum} || 0;
115 0   0       my $chunks = $args{chunks} || [];
116 0   0       my $list = $args{'list'} || '';
117              
118              
119             # Save { type => $type, host => $chunk->{host}, prefix => $chunk->{prefix}, chunknum => $chunknum, list => $list }
120             }
121              
122             =head2 get_add_chunks()
123              
124             Returns a list of chunks for a given prefix for all lists.
125              
126             my @chunks = $storage->get_add_chunks(prefix => HEX);
127              
128              
129             Arguments
130              
131             =over 4
132              
133             =item hostkey.
134              
135             Required. Host key.
136              
137             =back
138              
139              
140             Return value
141              
142             =over 4
143              
144             Array of add chunks in the same format as described above:
145              
146             (
147             {
148             chunknum => 25121,
149             prefix => hex('2fc96b9f2fc96b9f2fc96b9f2fc96b9f'),
150             list => 'goog-malware-shavar'
151             },
152             {
153             chunknum => '25121',
154             prefix => hex('2fc96b9f'),
155             list => 'goog-malware-shavar'
156             },
157             );
158              
159             =back
160              
161             =cut
162              
163             sub get_add_chunks {
164 0     0 1   my ($self, %args) = @_;
165 0   0       my $prefix = $args{prefix} || '';
166              
167             return (
168             {
169 0           chunknum => 25121,
170             prefix => '',
171             list => 'goog-malware-shavar'
172             },
173             {
174             chunknum => '25121',
175             prefix => $self->ascii_to_hex('2fc96b9f'),
176             list => 'goog-malware-shavar'
177             },
178             );
179             }
180              
181             =head2 get_sub_chunks()
182              
183             Returns a list of sub chunks for a given prefix for all lists.
184              
185             my @chunks = $storage->get_sub_chunks(prefix => HEX);
186              
187              
188             Arguments
189              
190             =over 4
191              
192             =item hostkey
193              
194             Required. Host key.
195              
196             =back
197              
198              
199             Return value
200              
201             =over 4
202              
203             Array of add chunks in the same format as described above:
204              
205             (
206             {
207             chunknum => 37441,
208             prefix => '',
209             addchunknum => 23911,
210             list => 'goog-malware-shavar'
211             },
212             {
213             chunknum => 37441,
214             prefix => '',
215             addchunknum => 22107,
216             list => 'goog-malware-shavar'
217             },
218             );
219              
220             =back
221              
222             =cut
223              
224             sub get_sub_chunks {
225 0     0 1   my ($self, %args) = @_;
226 0   0       my $prefix = $args{prefix} || '';
227              
228              
229             return (
230             {
231 0           chunknum => 37441,
232             prefix => '',
233             addchunknum => 23911,
234             list => 'goog-malware-shavar'
235             },
236             {
237             chunknum => 37441,
238             prefix => '',
239             addchunknum => 22107,
240             list => 'goog-malware-shavar'
241             },
242             );
243             }
244              
245             =head2 get_add_chunks_nums()
246              
247             Returns a list of unique add chunk numbers for a specific list.
248              
249             B: this list should be sorted in ascendant order.
250              
251             my @ids = $storage->get_add_chunks_nums(list => 'goog-malware-shavar');
252              
253              
254             Arguments
255              
256             =over 4
257              
258             =item list
259              
260             Required. Google Safe Browsing list name
261              
262             =back
263              
264              
265             Return value
266              
267             =over 4
268              
269             Array of integers sorted in ascendant order:
270              
271             qw(25121 25122 25123 25124 25125 25126)
272              
273             =back
274              
275             =cut
276              
277             sub get_add_chunks_nums {
278 0     0 1   my ($self, %args) = @_;
279 0   0       my $list = $args{'list'} || '';
280              
281 0           return qw(25121 25122 25123 25124 25125 25126);
282             }
283              
284             =head2 get_sub_chunks_nums()
285              
286             Returns a list of unique sub chunk numbers for a specific list.
287              
288             B: this list should be sorted in ascendant order.
289              
290             my @ids = $storage->get_sub_chunks_nums(list => 'goog-malware-shavar');
291              
292              
293             Arguments
294              
295             =over 4
296              
297             =item list
298              
299             Required. Google Safe Browsing list name
300              
301             =back
302              
303              
304             Return value
305              
306             =over 4
307              
308             Array of integers sorted in ascendant order:
309              
310             qw(37441 37442 37443 37444 37445 37446 37447 37448 37449 37450)
311              
312             =back
313              
314             =cut
315              
316             sub get_sub_chunks_nums {
317 0     0 1   my ($self, %args) = @_;
318 0   0       my $list = $args{'list'} || '';
319            
320 0           return qw(37441 37442 37443 37444 37445 37446 37447 37448 37449 37450);
321             }
322              
323             =head2 delete_add_chunks()
324              
325             Delete add chunks from the local database
326              
327             $storage->delete_add_chunks(chunknums => [qw/37444 37445 37446/], list => 'goog-malware-shavar');
328              
329              
330             Arguments
331              
332             =over 4
333              
334             =item chunknums
335              
336             Required. Array of chunk numbers
337              
338             =item list
339              
340             Required. Google Safe Browsing list name
341              
342             =back
343              
344              
345             No return value
346              
347              
348             =cut
349              
350             sub delete_add_ckunks {
351 0     0 0   my ($self, %args) = @_;
352 0   0       my $chunknums = $args{chunknums} || [];
353 0   0       my $list = $args{'list'} || '';
354              
355 0           foreach my $num (@$chunknums) {
356             # DELETE FROM [...] WHERE chunknumber = $num AND list = $list
357             }
358             }
359              
360             =head2 delete_sub_chunks()
361              
362             Delete sub chunks from the local database
363              
364             $storage->delete_sub_chunks(chunknums => [qw/37444 37445 37446/], list => 'goog-malware-shavar');
365              
366              
367             Arguments
368              
369             =over 4
370              
371             =item chunknums
372              
373             Required. Array of chunk numbers
374              
375             =item list
376              
377             Required. Google Safe Browsing list name
378              
379             =back
380              
381              
382             No return value
383              
384              
385             =cut
386              
387             sub delete_sub_ckunks {
388 0     0 0   my ($self, %args) = @_;
389 0   0       my $chunknums = $args{chunknums} || [];
390 0   0       my $list = $args{'list'} || '';
391              
392 0           foreach my $num (@$chunknums) {
393             # DELETE FROM [...] WHERE chunknumber = $num AND list = $list
394             }
395             }
396              
397             =head2 get_full_hashes()
398              
399             Return a list of full hashes
400              
401             $storage->get_full_hashes(hash => AAAAAAAA..., list => 'goog-malware-shavar');
402              
403              
404             Arguments
405              
406             =over 4
407              
408             =item hash
409              
410             Required. 32-bit hash
411              
412              
413             =item list
414              
415             Required. Google Safe Browsing list name
416              
417             =back
418              
419             Return value
420              
421             =over 4
422              
423             Array of full hashes:
424              
425             ({ hash => HEX, type => 0 }, { hash => HEX, type => 1 }, { hash => HEX, type => 0 })
426              
427             =back
428              
429              
430             =cut
431              
432             sub get_full_hashes {
433 0     0 1   my ($self, %args) = @_;
434 0   0       my $hash = $args{hash} || '';
435 0   0       my $list = $args{list} || '';
436              
437             return (
438 0           { hash => $self->ascii_to_hex('eb9744c011d332ad9c92442d18d5a0f913328ad5623983822fc86fad1aab649d'), type => 0 },
439             { hash => $self->ascii_to_hex('2ae11a967a5517e24c7be3fa0b8f56e7a13358ce3b07556dc251bc6b650f0f59'), type => 1 }
440             );
441             }
442              
443             =head2 updated()
444              
445             Save information about a successful database update
446              
447             $storage->updated('time' => time(), wait => 1800, list => 'goog-malware-shavar');
448              
449              
450             Arguments
451              
452             =over 4
453              
454             =item time
455              
456             Required. Time of the update.
457              
458             =item wait
459              
460             Required. Number o seconds to wait before doing the next update.
461              
462             =item list
463              
464             Required. Google Safe Browsing list name.
465              
466             =back
467              
468              
469             No return value
470              
471             =cut
472              
473             sub updated {
474 0     0 1   my ($self, %args) = @_;
475 0   0       my $time = $args{'time'} || time();
476 0   0       my $wait = $args{'wait'} || 1800;
477 0   0       my $list = $args{'list'} || '';
478              
479             # INSERT INTO [...] (last, wait, errors, list) VALUES (?, ?, 0, ?)", $time, $wait, $list);
480             }
481              
482             =head2 update_error()
483              
484             Save information about a failed database update
485              
486             $storage->update_error('time' => time(), wait => 60, list => 'goog-malware-shavar', errors => 1);
487              
488              
489             Arguments
490              
491             =over 4
492              
493             =item time
494              
495             Required. Time of the update.
496              
497             =item wait
498              
499             Required. Number o seconds to wait before doing the next update.
500              
501             =item list
502              
503             Required. Google Safe Browsing list name.
504              
505             =item errors
506              
507             Required. Number of errors.
508              
509             =back
510              
511              
512             No return value
513              
514             =cut
515              
516             sub update_error {
517 0     0 1   my ($self, %args) = @_;
518 0   0       my $time = $args{'time'} || time();
519 0   0       my $list = $args{'list'} || '';
520 0   0       my $wait = $args{'wait'} || 60;
521 0   0       my $errors = $args{errors} || 1;
522              
523             # UPDATE updates SET last = $time, wait = $wait, errors = $errors, list = $list
524             }
525              
526             =head2 last_update()
527              
528             Return information about the last database update
529              
530             my $info = $storage->last_update(list => 'goog-malware-shavar');
531              
532              
533             Arguments
534              
535             =over 4
536              
537             =item list
538              
539             Required. Google Safe Browsing list name.
540              
541             =back
542              
543              
544             Return value
545              
546             =over 4
547              
548             Hash reference
549              
550             {
551             time => time(),
552             wait => 1800,
553             errors => 0
554             }
555              
556             =back
557              
558             =cut
559              
560             sub last_update {
561 0     0 1   my ($self, %args) = @_;
562 0   0       my $list = $args{'list'} || '';
563              
564 0           return {'time' => time(), 'wait' => 1800, errors => 0};
565             }
566              
567             =head2 add_full_hashes()
568              
569             Add full hashes to the local database
570              
571             $storage->add_full_hashes(timestamp => time(), full_hashes => [{life => 900, hash => HEX, list => 'goog-malware-shavar', type => 1}]);
572              
573              
574             Arguments
575              
576             =over 4
577              
578             =item timestamp
579              
580             Required. Time when the full hash was retrieved.
581              
582             =item full_hashes
583              
584             Required. Array of full hashes. Each element is an hash reference in the following format:
585              
586             {
587             life => INTEGER,
588             hash => HEX,
589             list => 'goog-malware-shavar',
590             type => 1
591             }
592              
593             =back
594              
595              
596             No return value
597              
598              
599             =cut
600              
601             sub add_full_hashes {
602 0     0 1   my ($self, %args) = @_;
603 0   0       my $timestamp = $args{timestamp} || time();
604 0   0       my $full_hashes = $args{full_hashes} || [];
605              
606 0           foreach my $hash (@$full_hashes) {
607             # INSERT INTO [...] (hash, list, timestamp, end,type ) VALUES ($hash->{chunknum}, $hash->{hash}, $hash->{list}, $timestamp, $timestamp + $hash->{life}, $hash->{type});
608             }
609             }
610              
611             =head2 full_hash_error()
612              
613             Save information about failed attempt to retrieve a full hash
614              
615             $storage->full_hash_error(timestamp => time(), prefix => HEX);
616              
617              
618             Arguments
619              
620             =over 4
621              
622             =item timestamp
623              
624             Required. Time when the Google returned an error.
625              
626             =item prefix
627              
628             Required. Host prefix.
629              
630             =back
631              
632              
633             No return value
634              
635              
636             =cut
637              
638             sub full_hash_error {
639 0     0 1   my ($self, %args) = @_;
640 0   0       my $timestamp = $args{timestamp} || time();
641 0   0       my $prefix = $args{prefix} || '';
642              
643             # Add 1 to existing error count
644             }
645              
646             =head2 full_hash_ok()
647              
648             Save information about a successful attempt to retrieve a full hash
649              
650             $storage->full_hash_ok(timestamp => time(), prefix => HEX);
651              
652              
653             Arguments
654              
655             =over 4
656              
657             =item timestamp
658              
659             Required. Time when the Google returned an error.
660              
661             =item prefix
662              
663             Required. Host prefix.
664              
665             =back
666              
667              
668             No return value
669              
670              
671             =cut
672              
673             sub full_hash_ok {
674 0     0 1   my ($self, %args) = @_;
675 0   0       my $timestamp = $args{timestamp} || time();
676 0   0       my $prefix = $args{prefix} || '';
677              
678             # UPDATE full_hashes_errors SET errors = 0, timestamp = $timestamp WHERE prefix = $prefix
679             }
680              
681             =head2 get_full_hash_error()
682              
683             Save information about a successful attempt to retrieve a full hash
684              
685             my $info = $storage->get_full_hash_error(prefix => HEX);
686              
687              
688             Arguments
689              
690             =over 4
691              
692             =item prefix
693              
694             Required. Host prefix.
695              
696             =back
697              
698              
699             Return value
700              
701             =over 4
702              
703             undef if there was no error
704              
705             Hash reference in the following format if there was an error:
706              
707             {
708             timestamp => time(),
709             errors => 3
710             }
711              
712             =back
713              
714              
715             =cut
716              
717             sub get_full_hash_error {
718 0     0 1   my ($self, %args) = @_;
719 0   0       my $prefix = $args{prefix} || '';
720              
721              
722             # no error
723 0           return undef;
724              
725             # some error
726             # return { timestamp => time(), errors => 3 }
727             }
728              
729              
730              
731             =head2 reset()
732              
733             Remove all local data
734              
735             $storage->delete_mac_keys();
736              
737              
738             Arguments
739              
740             =over 4
741              
742             =item list
743              
744             Required. Google Safe Browsing list name.
745              
746             =back
747              
748             No return value
749              
750             =cut
751              
752             sub reset {
753 0     0 1   my ($self, %args) = @_;
754 0   0       my $list = $args{'list'} || '';
755              
756             # DELETE FROM s_chunks WHERE list = $list
757             # DELETE FROM a_chunks WHERE list = $list
758             # DELETE FROM full_hashes WHERE list = $list
759             # DELETE FROM full_hashes_errors WHERE list = $list
760             # DELETE FROM updates WHERE list = $list
761             }
762              
763              
764             =head1 PRIVATE FUNCTIONS
765              
766             These functions are not intended for debugging purpose.
767              
768             =over 4
769              
770             =back
771              
772             =head2 hex_to_ascii()
773              
774             Transform hexadecimal strings to printable ASCII strings. Used mainly for debugging.
775              
776             print $storage->hex_to_ascii('hex value');
777              
778             =cut
779              
780             sub hex_to_ascii {
781 0     0 1   my ($self, $hex) = @_;
782              
783              
784 0           my $ascii = '';
785              
786 0           while (length $hex > 0) {
787 0           $ascii .= sprintf("%02x", ord( substr($hex, 0, 1, '') ) );
788             }
789              
790 0           return $ascii;
791             }
792              
793             =head2 ascii_to_hex()
794              
795             Transform ASCII strings to hexadecimal strings.
796              
797             print $storage->ascii_to_hex('ascii value');
798              
799             =cut
800              
801             sub ascii_to_hex {
802 0     0 1   my ($self, $ascii) = @_;
803              
804 0           my $hex = '';
805 0           for (my $i = 0; $i < int(length($ascii) / 2); $i++) {
806 0           $hex .= chr hex( substr($ascii, $i * 2, 2) );
807             }
808              
809 0           return $hex;
810             }
811              
812              
813             =head2 debug()
814              
815             Print debug output.
816              
817             =cut
818              
819             sub debug {
820 0     0 1   my ($self, @messages) = @_;
821              
822 0 0         print join('', @messages) if ($self->{debug} > 0);
823             }
824              
825              
826             =head2 error()
827              
828             Print error message.
829              
830             =cut
831              
832             sub error {
833 0     0 1   my ($self, $message) = @_;
834              
835 0 0 0       print "ERROR - ", $message if ($self->{debug} > 0 || $self->{errors} > 0);
836 0           $self->{last_error} = $message;
837             }
838              
839             =head1 CHANGELOG
840              
841             =over 4
842              
843              
844             =item 0.1
845              
846             Initial release.
847              
848             =back
849              
850             =head1 SEE ALSO
851              
852             See L for handling Google Safe Browsing v3.
853              
854             See L or L for an example of storing and managing the Google Safe Browsing database.
855              
856             Google Safe Browsing v3 API: L
857              
858             =head1 AUTHOR
859              
860             Julien Sobrier, Ejulien@sobrier.netE
861              
862             =head1 COPYRIGHT AND LICENSE
863              
864             Copyright (C) 2015 by Julien Sobrier
865              
866             This library is free software; you can redistribute it and/or modify
867             it under the same terms as Perl itself, either Perl version 5.8.8 or,
868             at your option, any later version of Perl 5 you may have available.
869              
870              
871             =cut
872              
873             1;