File Coverage

blib/lib/Net/Google/SafeBrowsing2/Storage.pm
Criterion Covered Total %
statement 13 90 14.4
branch 1 2 50.0
condition 0 76 0.0
subroutine 4 26 15.3
pod 20 23 86.9
total 38 217 17.5


line stmt bran cond sub pod time code
1             package Net::Google::SafeBrowsing2::Storage;
2              
3              
4 1     1   3 use strict;
  1         1  
  1         23  
5 1     1   4 use warnings;
  1         1  
  1         24  
6              
7 1     1   3 use Carp;
  1         2  
  1         875  
8              
9              
10             our $VERSION = '0.4';
11              
12             =head1 NAME
13              
14             Net::Google::SafeBrowsing2::Storage - Base class for storing the Google Safe Browsing v2 database
15              
16             =head1 SYNOPSIS
17              
18             package Net::Google::SafeBrowsing2::Sqlite;
19              
20             use base 'Net::Google::SafeBrowsing2::Storage';
21              
22             =head1 DESCRIPTION
23              
24             This is the base class for implementing a storage mechanism for the Google Safe Browsing v2 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::SafeBrowsing2::Storage object
40              
41             my $storage => Net::Google::SafeBrowsing2::Storage->new();
42              
43             =cut
44              
45             sub new {
46 1     1 1 15 my ($class, %args) = @_;
47              
48 1         1 my $self = {
49             %args,
50             };
51              
52 1 50       7 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 host key for all lists.
125              
126             my @chunks = $storage->get_add_chunks(hostkey => 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             hostkey => hex('12345678'),
150             prefix => '',
151             list => 'goog-malware-shavar'
152             },
153             {
154             chunknum => '25121',
155             hostkey => hex('12345678'),
156             prefix => hex('2fc96b9f'),
157             list => 'goog-malware-shavar'
158             },
159             );
160              
161             =back
162              
163             =cut
164              
165             sub get_add_chunks {
166 0     0 1   my ($self, %args) = @_;
167 0   0       my $hostkey = $args{hostkey} || '';
168              
169             return (
170             {
171 0           chunknum => 25121,
172             prefix => '',
173             hostkey => $hostkey,
174             list => 'goog-malware-shavar'
175             },
176             {
177             chunknum => '25121',
178             prefix => $self->ascii_to_hex('2fc96b9f'),
179             hostkey => $hostkey,
180             list => 'goog-malware-shavar'
181             },
182             );
183             }
184              
185             =head2 get_sub_chunks()
186              
187             Returns a list of sub chunks for a given host key for all lists.
188              
189             my @chunks = $storage->get_sub_chunks(hostkey => HEX);
190              
191              
192             Arguments
193              
194             =over 4
195              
196             =item hostkey
197              
198             Required. Host key.
199              
200             =back
201              
202              
203             Return value
204              
205             =over 4
206              
207             Array of add chunks in the same format as described above:
208              
209             (
210             {
211             chunknum => 37441,
212             prefix => '',
213             addchunknum => 23911,
214             list => 'goog-malware-shavar'
215             },
216             {
217             chunknum => 37441,
218             prefix => '',
219             addchunknum => 22107,
220             list => 'goog-malware-shavar'
221             },
222             );
223              
224             =back
225              
226             =cut
227              
228             sub get_sub_chunks {
229 0     0 1   my ($self, %args) = @_;
230 0   0       my $hostkey = $args{hostkey} || '';
231              
232              
233             return (
234             {
235 0           chunknum => 37441,
236             prefix => '',
237             addchunknum => 23911,
238             list => 'goog-malware-shavar'
239             },
240             {
241             chunknum => 37441,
242             prefix => '',
243             addchunknum => 22107,
244             list => 'goog-malware-shavar'
245             },
246             );
247             }
248              
249             =head2 get_add_chunks_nums()
250              
251             Returns a list of unique add chunk numbers for a specific list.
252              
253             B: this list should be sorted in ascendant order.
254              
255             my @ids = $storage->get_add_chunks_nums(list => 'goog-malware-shavar');
256              
257              
258             Arguments
259              
260             =over 4
261              
262             =item list
263              
264             Required. Google Safe Browsing list name
265              
266             =back
267              
268              
269             Return value
270              
271             =over 4
272              
273             Array of integers sorted in ascendant order:
274              
275             qw(25121 25122 25123 25124 25125 25126)
276              
277             =back
278              
279             =cut
280              
281             sub get_add_chunks_nums {
282 0     0 1   my ($self, %args) = @_;
283 0   0       my $list = $args{'list'} || '';
284              
285 0           return qw(25121 25122 25123 25124 25125 25126);
286             }
287              
288             =head2 get_sub_chunks_nums()
289              
290             Returns a list of unique sub chunk numbers for a specific list.
291              
292             B: this list should be sorted in ascendant order.
293              
294             my @ids = $storage->get_sub_chunks_nums(list => 'goog-malware-shavar');
295              
296              
297             Arguments
298              
299             =over 4
300              
301             =item list
302              
303             Required. Google Safe Browsing list name
304              
305             =back
306              
307              
308             Return value
309              
310             =over 4
311              
312             Array of integers sorted in ascendant order:
313              
314             qw(37441 37442 37443 37444 37445 37446 37447 37448 37449 37450)
315              
316             =back
317              
318             =cut
319              
320             sub get_sub_chunks_nums {
321 0     0 1   my ($self, %args) = @_;
322 0   0       my $list = $args{'list'} || '';
323            
324 0           return qw(37441 37442 37443 37444 37445 37446 37447 37448 37449 37450);
325             }
326              
327             =head2 delete_add_chunks()
328              
329             Delete add chunks from the local database
330              
331             $storage->delete_add_chunks(chunknums => [qw/37444 37445 37446/], list => 'goog-malware-shavar');
332              
333              
334             Arguments
335              
336             =over 4
337              
338             =item chunknums
339              
340             Required. Array of chunk numbers
341              
342             =item list
343              
344             Required. Google Safe Browsing list name
345              
346             =back
347              
348              
349             No return value
350              
351              
352             =cut
353              
354             sub delete_add_ckunks {
355 0     0 0   my ($self, %args) = @_;
356 0   0       my $chunknums = $args{chunknums} || [];
357 0   0       my $list = $args{'list'} || '';
358              
359 0           foreach my $num (@$chunknums) {
360             # DELETE FROM [...] WHERE chunknumber = $num AND list = $list
361             }
362             }
363              
364             =head2 delete_sub_chunks()
365              
366             Delete sub chunks from the local database
367              
368             $storage->delete_sub_chunks(chunknums => [qw/37444 37445 37446/], list => 'goog-malware-shavar');
369              
370              
371             Arguments
372              
373             =over 4
374              
375             =item chunknums
376              
377             Required. Array of chunk numbers
378              
379             =item list
380              
381             Required. Google Safe Browsing list name
382              
383             =back
384              
385              
386             No return value
387              
388              
389             =cut
390              
391             sub delete_sub_ckunks {
392 0     0 0   my ($self, %args) = @_;
393 0   0       my $chunknums = $args{chunknums} || [];
394 0   0       my $list = $args{'list'} || '';
395              
396 0           foreach my $num (@$chunknums) {
397             # DELETE FROM [...] WHERE chunknumber = $num AND list = $list
398             }
399             }
400              
401             =head2 get_full_hashes()
402              
403             Return a list of full hashes
404              
405             $storage->get_full_hashes(chunknum => 37444, timestamp => time() - 45 * 60 * 60, list => 'goog-malware-shavar');
406              
407              
408             Arguments
409              
410             =over 4
411              
412             =item chunknum
413              
414             Required. Add chunk number
415              
416             =item timestamp
417              
418             Required. Request hashes retrieved after this timestamp value.
419              
420             =item list
421              
422             Required. Google Safe Browsing list name
423              
424             =back
425              
426             Return value
427              
428             =over 4
429              
430             Array of full hashes:
431              
432             (HEX, HEX, HEX)
433              
434             =back
435              
436              
437             =cut
438              
439             sub get_full_hashes {
440 0     0 1   my ($self, %args) = @_;
441 0   0       my $chunknum = $args{chunknum} || 0;
442 0   0       my $timestamp = $args{timestamp} || 0;
443 0   0       my $list = $args{list} || '';
444              
445             return (
446 0           $self->ascii_to_hex('eb9744c011d332ad9c92442d18d5a0f913328ad5623983822fc86fad1aab649d'),
447             $self->ascii_to_hex('2ae11a967a5517e24c7be3fa0b8f56e7a13358ce3b07556dc251bc6b650f0f59')
448             );
449             }
450              
451             =head2 updated()
452              
453             Save information about a successful database update
454              
455             $storage->updated('time' => time(), wait => 1800, list => 'goog-malware-shavar');
456              
457              
458             Arguments
459              
460             =over 4
461              
462             =item time
463              
464             Required. Time of the update.
465              
466             =item wait
467              
468             Required. Number o seconds to wait before doing the next update.
469              
470             =item list
471              
472             Required. Google Safe Browsing list name.
473              
474             =back
475              
476              
477             No return value
478              
479             =cut
480              
481             sub updated {
482 0     0 1   my ($self, %args) = @_;
483 0   0       my $time = $args{'time'} || time();
484 0   0       my $wait = $args{'wait'} || 1800;
485 0   0       my $list = $args{'list'} || '';
486              
487             # INSERT INTO [...] (last, wait, errors, list) VALUES (?, ?, 0, ?)", $time, $wait, $list);
488             }
489              
490             =head2 update_error()
491              
492             Save information about a failed database update
493              
494             $storage->update_error('time' => time(), wait => 60, list => 'goog-malware-shavar', errors => 1);
495              
496              
497             Arguments
498              
499             =over 4
500              
501             =item time
502              
503             Required. Time of the update.
504              
505             =item wait
506              
507             Required. Number o seconds to wait before doing the next update.
508              
509             =item list
510              
511             Required. Google Safe Browsing list name.
512              
513             =item errors
514              
515             Required. Number of errors.
516              
517             =back
518              
519              
520             No return value
521              
522             =cut
523              
524             sub update_error {
525 0     0 1   my ($self, %args) = @_;
526 0   0       my $time = $args{'time'} || time();
527 0   0       my $list = $args{'list'} || '';
528 0   0       my $wait = $args{'wait'} || 60;
529 0   0       my $errors = $args{errors} || 1;
530              
531             # UPDATE updates SET last = $time, wait = $wait, errors = $errors, list = $list
532             }
533              
534             =head2 last_update()
535              
536             Return information about the last database update
537              
538             my $info = $storage->last_update(list => 'goog-malware-shavar');
539              
540              
541             Arguments
542              
543             =over 4
544              
545             =item list
546              
547             Required. Google Safe Browsing list name.
548              
549             =back
550              
551              
552             Return value
553              
554             =over 4
555              
556             Hash reference
557              
558             {
559             time => time(),
560             wait => 1800,
561             errors => 0
562             }
563              
564             =back
565              
566             =cut
567              
568             sub last_update {
569 0     0 1   my ($self, %args) = @_;
570 0   0       my $list = $args{'list'} || '';
571              
572 0           return {'time' => time(), 'wait' => 1800, errors => 0};
573             }
574              
575             =head2 add_full_hashes()
576              
577             Add full hashes to the local database
578              
579             $storage->add_full_hashes(timestamp => time(), full_hashes => [{chunknum => 2154, hash => HEX, list => 'goog-malware-shavar'}]);
580              
581              
582             Arguments
583              
584             =over 4
585              
586             =item timestamp
587              
588             Required. Time when the full hash was retrieved.
589              
590             =item full_hashes
591              
592             Required. Array of full hashes. Each element is an hash reference in the following format:
593              
594             {
595             chunknum => INTEGER,
596             hash => HEX,
597             list => 'goog-malware-shavar'
598             }
599              
600             =back
601              
602              
603             No return value
604              
605              
606             =cut
607              
608             sub add_full_hashes {
609 0     0 1   my ($self, %args) = @_;
610 0   0       my $timestamp = $args{timestamp} || time();
611 0   0       my $full_hashes = $args{full_hashes} || [];
612              
613 0           foreach my $hash (@$full_hashes) {
614             # INSERT INTO [...] (num, hash, list, timestamp) VALUES ($hash->{chunknum}, $hash->{hash}, $hash->{list}, $timestamp);
615             }
616             }
617              
618             =head2 delete_full_hashes()
619              
620             Delete full hashes from the local database
621              
622             $storage->delete_full_hashes(chunknums => [qw/2154 2156 2158/], list => 'goog-malware-shavar');
623              
624              
625             Arguments
626              
627             =over 4
628              
629             =item chunknums
630              
631             Required. Array of chunk numbers.
632              
633             =item list
634              
635             Required. Google Safe Browsing list name.
636              
637             =back
638              
639              
640             No return value
641              
642              
643             =cut
644              
645             sub delete_full_hashes {
646 0     0 1   my ($self, %args) = @_;
647 0   0       my $chunknums = $args{chunknums} || [];
648 0   0       my $list = $args{list} || croak "Missing list name\n";
649              
650 0           foreach my $num (@$chunknums) {
651             # "DELETE FROM [...] WHERE num = $num AND list = $list
652             }
653             }
654              
655             =head2 full_hash_error()
656              
657             Save information about failed attempt to retrieve a full hash
658              
659             $storage->full_hash_error(timestamp => time(), prefix => HEX);
660              
661              
662             Arguments
663              
664             =over 4
665              
666             =item timestamp
667              
668             Required. Time when the Google returned an error.
669              
670             =item prefix
671              
672             Required. Host prefix.
673              
674             =back
675              
676              
677             No return value
678              
679              
680             =cut
681              
682             sub full_hash_error {
683 0     0 1   my ($self, %args) = @_;
684 0   0       my $timestamp = $args{timestamp} || time();
685 0   0       my $prefix = $args{prefix} || '';
686              
687             # Add 1 to existing error count
688             }
689              
690             =head2 full_hash_ok()
691              
692             Save information about a successful attempt to retrieve a full hash
693              
694             $storage->full_hash_ok(timestamp => time(), prefix => HEX);
695              
696              
697             Arguments
698              
699             =over 4
700              
701             =item timestamp
702              
703             Required. Time when the Google returned an error.
704              
705             =item prefix
706              
707             Required. Host prefix.
708              
709             =back
710              
711              
712             No return value
713              
714              
715             =cut
716              
717             sub full_hash_ok {
718 0     0 1   my ($self, %args) = @_;
719 0   0       my $timestamp = $args{timestamp} || time();
720 0   0       my $prefix = $args{prefix} || '';
721              
722             # UPDATE full_hashes_errors SET errors = 0, timestamp = $timestamp WHERE prefix = $prefix
723             }
724              
725             =head2 get_full_hash_error()
726              
727             Save information about a successful attempt to retrieve a full hash
728              
729             my $info = $storage->get_full_hash_error(prefix => HEX);
730              
731              
732             Arguments
733              
734             =over 4
735              
736             =item prefix
737              
738             Required. Host prefix.
739              
740             =back
741              
742              
743             Return value
744              
745             =over 4
746              
747             undef if there was no error
748              
749             Hash reference in the following format if there was an error:
750              
751             {
752             timestamp => time(),
753             errors => 3
754             }
755              
756             =back
757              
758              
759             =cut
760              
761             sub get_full_hash_error {
762 0     0 1   my ($self, %args) = @_;
763 0   0       my $prefix = $args{prefix} || '';
764              
765              
766             # no error
767 0           return undef;
768              
769             # some error
770             # return { timestamp => time(), errors => 3 }
771             }
772              
773             =head2 get_mac_keys()
774              
775             Retrieve the Message Authentication Code (MAC) keys.
776              
777             my $keys = $storage->get_mac_keys();
778              
779              
780             No arguments
781              
782              
783             Return value
784              
785             =over 4
786              
787              
788             Hash reference in the following format:
789              
790             {
791             client_key => '',
792             wrapped_key => ''
793             }
794              
795             =back
796              
797             =cut
798              
799             sub get_mac_keys {
800 0     0 1   my ($self, %args) = @_;
801              
802              
803 0           return { client_key => '', wrapped_key => '' }
804             }
805              
806             =head2 delete_add_keys()
807              
808             Add the Message Authentication Code (MAC) keys.
809              
810             $storage->delete_mac_keys(client_key => 'KEY', wrapped_key => 'KEY');
811              
812              
813             Arguments
814              
815             =over 4
816              
817             =item client_key
818              
819             Required. Client key.
820              
821             =item wrapped_key
822              
823             Required. Wrapped key.
824              
825             =back
826              
827             No return value
828              
829             =cut
830              
831             sub add_mac_keys {
832 0     0 0   my ($self, %args) = @_;
833 0   0       my $client_key = $args{client_key} || '';
834 0   0       my $wrapped_key = $args{wrapped_key} || '';
835              
836             # INSERT INTO ...
837              
838             }
839              
840             =head2 delete_mac_keys()
841              
842             Delete the Message Authentication Code (MAC) keys.
843              
844             $storage->delete_mac_keys();
845              
846              
847             No arguments
848              
849             No return value
850              
851             =cut
852              
853             sub delete_mac_keys {
854 0     0 1   my ($self, %args) = @_;
855              
856             # # DELETE FROM mac_keys WHERE 1
857             }
858              
859              
860             =head2 reset()
861              
862             Remove all local data
863              
864             $storage->delete_mac_keys();
865              
866              
867             Arguments
868              
869             =over 4
870              
871             =item list
872              
873             Required. Google Safe Browsing list name.
874              
875             =back
876              
877             No return value
878              
879             =cut
880              
881             sub reset {
882 0     0 1   my ($self, %args) = @_;
883 0   0       my $list = $args{'list'} || '';
884              
885             # DELETE FROM s_chunks WHERE list = $list
886             # DELETE FROM a_chunks WHERE list = $list
887             # DELETE FROM full_hashes WHERE list = $list
888             # DELETE FROM full_hashes_errors WHERE list = $list
889             # DELETE FROM updates WHERE list = $list
890             }
891              
892              
893             =head1 PRIVATE FUNCTIONS
894              
895             These functions are not intended for debugging purpose.
896              
897             =over 4
898              
899             =back
900              
901             =head2 hex_to_ascii()
902              
903             Transform hexadecimal strings to printable ASCII strings. Used mainly for debugging.
904              
905             print $storage->hex_to_ascii('hex value');
906              
907             =cut
908              
909             sub hex_to_ascii {
910 0     0 1   my ($self, $hex) = @_;
911              
912              
913 0           my $ascii = '';
914              
915 0           while (length $hex > 0) {
916 0           $ascii .= sprintf("%02x", ord( substr($hex, 0, 1, '') ) );
917             }
918              
919 0           return $ascii;
920             }
921              
922             =head2 ascii_to_hex()
923              
924             Transform ASCII strings to hexadecimal strings.
925              
926             print $storage->ascii_to_hex('ascii value');
927              
928             =cut
929              
930             sub ascii_to_hex {
931 0     0 1   my ($self, $ascii) = @_;
932              
933 0           my $hex = '';
934 0           for (my $i = 0; $i < int(length($ascii) / 2); $i++) {
935 0           $hex .= chr hex( substr($ascii, $i * 2, 2) );
936             }
937              
938 0           return $hex;
939             }
940              
941             =head1 CHANGELOG
942              
943             =over 4
944              
945             =item 0.4
946              
947             Add reset mehtod to empty local database.
948              
949             =item 0.3
950              
951             Return the hostkey as part of the add chunks (get_add_chunks).
952              
953             =item 0.2
954              
955             Add functions to store and retrieve Message Authentication Code (MAC) keys.
956              
957             =back
958              
959             =head1 SEE ALSO
960              
961             See L for handling Google Safe Browsing v2.
962              
963             See L or L for an example of storing and managing the Google Safe Browsing database.
964              
965             Google Safe Browsing v2 API: L
966              
967             =head1 AUTHOR
968              
969             Julien Sobrier, Ejsobrier@zscaler.comE or Ejulien@sobrier.netE
970              
971             =head1 COPYRIGHT AND LICENSE
972              
973             Copyright (C) 2011 by Julien Sobrier
974              
975             This library is free software; you can redistribute it and/or modify
976             it under the same terms as Perl itself, either Perl version 5.8.8 or,
977             at your option, any later version of Perl 5 you may have available.
978              
979              
980             =cut
981              
982             1;