File Coverage

lib/WWW/A9Toolbar.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2              
3             package WWW::A9Toolbar;
4              
5 1     1   1962 use strict;
  1         2  
  1         45  
6             # use Data::Dumper;
7             # use Data::Dump::Streamer 'Dumper';
8 1     1   6848 use WWW::Mechanize;
  1         304052  
  1         46  
9 1     1   2047 use WWW::Mechanize::FormFiller;
  1         2031  
  1         42  
10 1     1   9 use URI;
  1         2  
  1         24  
11 1     1   945 use URI::URL;
  1         5361  
  1         67  
12 1     1   1131 use URI::QueryParam;
  1         905  
  1         43  
13 1     1   2126 use Net::DNS;
  1         158099  
  1         163  
14 1     1   446 use XML::Simple;
  0            
  0            
15             use List::MoreUtils 'apply';
16              
17             our $VERSION = '0.01';
18              
19             sub new
20             {
21             my ($class, $args) = @_;
22              
23             $args = check_new_options($args);
24             my $self = bless($args, $class);
25              
26             $self->{toolbarapiurl} = 'http://client.a9.com/api/toolbarapi/toolbar';
27             $self->{jspurl} = 'http://a9.com/-/search/updBm.jsp';
28             $self->{lastbookmarkfetch} = 0;
29             $self->connect() if($self->{connect});
30              
31             return $self;
32             }
33              
34             sub check_new_options
35             {
36             my ($args) = @_;
37              
38             # check for email, password and connect
39             return $args;
40             }
41              
42             sub connect
43             {
44             my ($self) = @_;
45              
46             if(!$self->{email} || !$self->{password})
47             {
48             warn "No email or password specified!";
49             return;
50             }
51              
52             if($self->{agent})
53             {
54             warn "Already connected!";
55             return;
56             }
57              
58             my $agent = WWW::Mechanize->new( autocheck => 1 );
59             $self->{agent} = $agent;
60              
61             my $formfiller = WWW::Mechanize::FormFiller->new();
62             $agent->env_proxy();
63             my $cookies = HTTP::Cookies->new();
64             $agent->cookie_jar($cookies);
65              
66             $agent->get('http://a9.com/-/sign-in/a9SignIn.jsp?ss=1');
67             $agent->form(1) if $agent->forms and scalar @{$agent->forms};
68             $formfiller->add_filler( 'email' => Fixed => $self->{email} );
69             $formfiller->add_filler( 'password' => Fixed => $self->{password} );
70             $formfiller->fill_form($agent->current_form);
71             $agent->submit();
72             $self->{cookies} = $cookies;
73             $self->scan_cookies();
74              
75             ## What happens on error?
76              
77             return 1;
78             }
79              
80             sub customer_id
81             {
82             my ($self) = @_;
83              
84             return $self->{'customer-id'} if($self->{'customer-id'});
85              
86             if(!$self->{cookies})
87             {
88             warn "No cookies found, not connected?";
89             return undef;
90             }
91            
92             $self->scan_cookies();
93              
94             return $self->{'customer-id'} if($self->{'customer-id'});
95              
96             return;
97             }
98              
99             sub scan_cookies
100             {
101             my ($self) = @_;
102              
103             return unless($self->{cookies});
104              
105             my ($id, $prefs, $name, $perm);
106              
107             my $scansub = sub {
108             my ($version, $key, $val, $path, $domain, $port, $path_spec, $secure, $expires, $discard, $hash) = @_;
109             if($domain eq 'a9.com')
110             {
111             $prefs = $val if($key eq 'a9Prefs');
112             $id = $val if($key eq 'a9id');
113             $name = $val if($key eq 'a9name');
114             $perm = $val if($key eq 'a9Perm');
115             }
116             };
117              
118             $self->{cookies}->scan( $scansub );
119              
120             $self->{'customer-id'} = $id;
121             $self->{prefs} = $prefs;
122             $self->{name} = $name;
123             $self->{perm} = $perm;
124             }
125              
126             sub get_userdata
127             {
128             my ($self) = @_;
129              
130             return $self->{userdata} if($self->{userdata});
131            
132             my $uri = URI->new($self->{toolbarapiurl}, 'http');
133             my $method = 'basicuserdataunique';
134             $uri->query_param('method' => $method);
135             $uri->query_param('customer-id' => $self->customer_id());
136             # print $uri->as_string(), "\n";
137             $self->{agent}->get($uri->as_string());
138            
139             # print $self->{agent}->content(), "\n";
140             my $xmlobj = XMLin($self->{agent}->content());
141              
142             my $columns = $xmlobj->{columns};
143             my $newcolumns;
144             while($columns =~ s/\{id:"([^"]+)", name:"([^"]+)"\}//)
145             {
146             push @$newcolumns, {id => $1, name => $2};
147             }
148             $xmlobj->{columns} = $newcolumns;
149              
150             $self->{userdata} = $xmlobj;
151             return $xmlobj;
152             }
153              
154             sub get_bookmarks
155             {
156             my ($self, $args) = @_;
157             $args->{since} = $self->{lastbookmarkfetch} || 0
158             if(!defined $args->{since});
159             if($args->{since} < $self->{lastbookmarkfetch})
160             {
161             return $self->{bookmarks};
162             }
163             $args->{since} *= 1000 if($args->{since} < 9999999999);
164              
165             my $uri = URI->new($self->{toolbarapiurl}, 'http');
166             my $method = 'GetBookmarksChangedSinceTime';
167             $uri->query_param('method' => $method);
168             $uri->query_param('customer-id' => $self->customer_id());
169             $uri->query_param('bmtimestamp' => $args->{since});
170             $uri->query_param('useAPI' => 2);
171             $uri->query_param('timestamp' => $args->{since});
172             $uri->query_param('clientid' => 2);
173             $uri->query_param('passback' => 1);
174              
175             $self->{agent}->get($uri->as_string());
176              
177             my $xmlobj = XMLin($self->{agent}->content(), ForceArray => ['BookmarkEntry']);
178             $self->{lastbookmarkfetch} = $xmlobj->{'a9'}{'getbookmarkschangedsincetime'}{'LastReturnedTimestamp'};
179              
180             foreach my $bm (@{$xmlobj->{'a9'}{'getbookmarkschangedsincetime'}{'BookmarkEntry'}})
181             {
182             $self->{bookmarks}{$bm->{guid}} = $bm;
183             }
184              
185             my $prev;
186             foreach my $b (values %{$self->{bookmarks}})
187             {
188             # $b = $bm->{guid};
189             $b->{previous} = $prev;
190             $prev->{next} = $b;
191             $prev = $b;
192             }
193             $prev->{next} = undef;
194              
195             # print Dumper($self->{bookmarks});
196              
197             $self->{bookmarks} ||= {};
198              
199             return values %{$self->{bookmarks}};
200             # print Dumper($self->{bookmarks});
201             }
202              
203             sub find_bookmarks
204             {
205             my ($self, $args) = @_;
206              
207             if(!$args->{title} && !$args->{url})
208             {
209             warn "Can't find bookmark without url or title";
210             return;
211             }
212             $args->{title} ||= qr//;
213             $args->{url} ||= qr//;
214              
215             $self->get_bookmarks() if(!$self->{bookmarks});
216              
217             my @bookmarks = map {$_->{guid} } $self->{bookmarks};
218             @bookmarks = grep { ( $_->{title} =~ /$args->{title}/ ||
219             ($_->{url} && $_->{url} =~ /$args->{url}/)) &&
220             $_->{deleted} eq 'false' }
221             @bookmarks;
222              
223             @bookmarks = apply { delete $_->{next}; delete $_->{previous} } @bookmarks;
224              
225             return @bookmarks;
226              
227             }
228              
229             sub add_bookmark
230             {
231             my ($self, $args) = @_;
232             # print Dumper($args);
233             my ($title, $url, $type, $parent, $before)
234             = @{$args}{qw/title url type parent before/};
235            
236             if(!$self->{bookmarks})
237             {
238             warn "Call get_bookmarks before trying to add any bookmarks.";
239             return undef;
240             }
241              
242             if($type ne 'folder' && $type ne 'url')
243             {
244             warn "Wrong type $type passed to get_bookmarks, use 'url' or 'folder'";
245             return undef;
246             }
247              
248             $parent ||= {guid => 0};
249              
250             my @sorted = sort {$a->{ordinal} <=> $b->{ordinal}}
251             values(%{$self->{bookmarks}});
252              
253             my $after = $before->{previous} if($before);
254             if(!$before)
255             {
256             $before = $sorted[0];
257             $after->{ordinal} = 0;
258             }
259              
260             my $ordinal = $before->{ordinal} -
261             ($before->{ordinal} - $after->{ordinal})/2;
262              
263             my $method = 'AddBookmark';
264             my $uri = URI->new($self->{toolbarapiurl}, 'http');
265             $uri->query_param('method' => $method);
266             $uri->query_param('customer-id' => $self->customer_id());
267             $uri->query_param('passback' => 1);
268              
269             my %vars = ('clientid' => 2,
270             'parentguid' => $parent->{guid} || 0,
271             'ordinal' => $ordinal,
272             'bmtype' => $type,
273             'title' => $title,
274             'url' => $url || '',
275             'useAPI' => 2);
276              
277             $self->{agent}->post($uri->as_string(), \%vars);
278              
279             # print Dumper($self->{agent}->content);
280             my $xmlresponse = XMLin($self->{agent}->content);
281              
282             # print Dumper($xmlresponse);
283             if($xmlresponse->{status}{code} == 200)
284             {
285             # woo it worked
286             $self->get_bookmarks({since => $self->{lastbookmarkfetch}});
287             return $xmlresponse->{a9}{addbookmark};
288             }
289              
290             warn "Failed to create bookmark n add_bookmark, " .
291             $xmlresponse->{status}{code} .
292             ' ' . $xmlresponse->{status}{text};
293             return undef;
294             }
295              
296             sub delete_bookmark
297             {
298             my ($self, $args) = @_;
299              
300             if(ref($args) ne 'ARRAY')
301             {
302             $args = [ $args ];
303             }
304              
305             foreach my $bk (@$args)
306             {
307             if(!$bk->{guid})
308             {
309             warn "Bookmark without GUID, skipping";
310             return undef;
311             }
312             my $method = 'deletebookmark';
313             my $uri = URI->new($self->{jspurl}, 'http');
314             $uri->query_param('method', $method);
315             $uri->query_param('clientid', 1);
316             $uri->query_param('guid', $bk->{guid});
317             # print "Running", $uri->as_string(), "\n";
318             $self->{agent}->get($uri->as_string());
319             my $xmlresult = XMLin($self->{agent}->content);
320             # print Dumper($xmlresult);
321             if($xmlresult->{status} ne 'success')
322             {
323             warn "Failed to delete " . $bk->{title};
324             last;
325             }
326             }
327              
328             $self->get_bookmarks({since => $self->{lastbookmarkfetch}});
329             return 1;
330             }
331              
332             sub get_diary_entries
333             {
334             my ($self, $args) = @_;
335              
336             my $method = 'AllDiary';
337             my $uri = URI->new($self->{toolbarapiurl}, 'http');
338             $uri->query_param('method', $method);
339            
340             $self->{agent}->get($uri->as_string());
341             my $xmlresponse = XMLin($self->{agent}->content);
342              
343             print Dumper($xmlresponse);
344              
345             ## POST!
346             ## wants a url?
347             }
348              
349             sub add_diary_entry
350             {
351             my ($self, $args) = @_;
352            
353             if(!$args->{url} || !$args->{text} || !$args->{title})
354             {
355             warn "add_diary_entry needs a url and a text argument";
356             return;
357             }
358              
359             my $diaryuri = URI->new($args->{url})->canonical();
360             my $domain = $diaryuri->host();
361             my $res = new Net::DNS::Resolver;
362             $res->tcp_timeout(10);
363             my $resolved;
364             do
365             {
366             $domain =~ s/(.+?)\.//;
367             $resolved = $res->query($domain, 'SOA');
368             } until($resolved && ($resolved->answer)[0]->name eq $domain);
369              
370             my $method = 'AddDiaryEntry';
371             my $uri = URI->new($self->{toolbarapiurl}, 'http');
372             $uri->query_param('method', $method);
373             $uri->query_param('customer-id', $self->customer_id());
374             $uri->query_param('passback', 1);
375              
376             my %qvars = ('url' => $args->{url},
377             'domain' => $domain,
378             'shortannot' => $args->{text},
379             'clientid' => 2,
380             'pagetitle' => $args->{title},
381             'longannot' => $args->{text},
382             'toolbarVer' => '1.3.1.154',
383             'debug' => 'true',
384             );
385              
386             $self->{agent}->post($uri->as_string(), \%qvars);
387              
388             my $xmlresponse = XMLin($self->{agent}->content);
389              
390             return 1 if($xmlresponse->{status}{code} == 200);
391              
392             warn "Can't create diary entry: " . $xmlresponse->{status}{text};
393             return undef;
394             }
395              
396             sub remove_diary_entry
397             {
398             my ($self, $args) = @_;
399              
400             if(!$args->{url})
401             {
402             warn "remove_diary_entry: URL argument missing";
403             return;
404             }
405              
406             my $method = 'removeEntry';
407             my $uri = URI->new('http://diary.a9.com/-/diary/', 'http');
408             $uri->query_param('method', $method);
409             $uri->query_param('url', $args->{url});
410             $self->{agent}->get($uri->as_string);
411              
412             }
413              
414              
415             1;
416              
417              
418             __END__