File Coverage

blib/lib/Flickr/API.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Flickr::API;
2              
3 15     15   249964 use strict;
  15         20  
  15         337  
4 15     15   48 use warnings;
  15         18  
  15         284  
5 15     15   7800 use LWP::UserAgent;
  15         440588  
  15         384  
6 15     15   6228 use XML::Parser::Lite::Tree;
  15         53698  
  15         356  
7 15     15   6331 use XML::LibXML::Simple;
  0            
  0            
8             use Flickr::API::Request;
9             use Flickr::API::Response;
10             use Net::OAuth;
11             use Digest::MD5 qw(md5_hex);
12             use Scalar::Util qw(blessed);
13             use Encode qw(encode_utf8);
14             use Carp;
15             use Storable qw(store_fd retrieve_fd);
16              
17             our @ISA = qw(LWP::UserAgent);
18              
19             our $VERSION = '1.28';
20              
21             sub new {
22             my ($class, $args) = @_;
23              
24             my $self;
25             if ($args->{lwpobj}){
26             my $lwpobj = $args->{lwpobj};
27             if (defined($lwpobj)){
28             my $lwpobjtype = Scalar::Util::blessed($lwpobj);
29             if (defined($lwpobjtype)){
30             $self = $lwpobj;
31             @ISA = ($lwpobjtype);
32             }
33             }
34             }
35             $self = LWP::UserAgent->new unless $self;
36              
37             #
38             # If the args have consumer_key, handle as oauth
39             #
40             if (defined($args->{consumer_key})) {
41              
42             $self->{api_type} = 'oauth';
43             $self->{rest_uri} = $args->{rest_uri} || 'https://api.flickr.com/services/rest/';
44             $self->{auth_uri} = $args->{auth_uri} || 'https://api.flickr.com/services/oauth/authorize';
45             $self->{upload_uri} = $args->{upload_uri} || 'https://api.flickr.com/services/upload/';
46              
47             if (defined($args->{consumer_secret})) {
48              
49             #
50             # for the flickr api object
51             #
52             $self->{oauth_request} = 'consumer';
53             $self->{consumer_key} = $args->{consumer_key};
54             $self->{consumer_secret} = $args->{consumer_secret};
55             $self->{unicode} = $args->{unicode} || 0;
56             #
57             # for Net::OAuth Consumer Requests
58             #
59             $self->{oauth}->{request_method} = $args->{request_method} || 'GET';
60             $self->{oauth}->{request_url} = $self->{rest_uri};
61             $self->{oauth}->{consumer_secret} = $args->{consumer_secret};
62             $self->{oauth}->{consumer_key} = $args->{consumer_key};
63             $self->{oauth}->{nonce} = $args->{nonce} || _make_nonce();
64             $self->{oauth}->{signature_method} = $args->{signature_method} ||'HMAC-SHA1';
65             $self->{oauth}->{timestamp} = $args->{timestamp} || time;
66             $self->{oauth}->{version} = '1.0';
67             $self->{oauth}->{callback} = $args->{callback};
68              
69             }
70             else {
71              
72             carp "OAuth calls must have at least a consumer_key and a consumer_secret";
73             $self->_set_status(0,"OAuth call without consumer_secret");
74              
75             }
76              
77             if (defined($args->{token}) && defined($args->{token_secret})) {
78              
79             #
80             # If we have token/token secret then we are for protected resources
81             #
82             $self->{oauth}->{token_secret} = $args->{token_secret};
83             $self->{oauth}->{token} = $args->{token};
84             $self->{oauth_request} = 'protected resource';
85              
86             }
87              
88             #
89             # Preserve request and access tokens
90             #
91             if (defined($args->{request_token}) and
92             ref($args->{request_token}) eq 'Net::OAuth::V1_0A::RequestTokenResponse') {
93              
94             $self->{oauth}->{request_token} = $args->{request_token};
95              
96             }
97             if (defined($args->{access_token}) and
98             ref($args->{access_token}) eq 'Net::OAuth::AccessTokenResponse') {
99              
100             $self->{oauth}->{access_token} = $args->{access_token};
101              
102             }
103             }
104              
105             else {
106              
107             $self->{api_type} = 'flickr';
108             $self->{api_key} = $args->{api_key} || $args->{key};
109             $self->{api_secret} = $args->{api_secret} || $args->{secret};
110             $self->{rest_uri} = $args->{rest_uri} || 'https://api.flickr.com/services/rest/';
111             $self->{auth_uri} = $args->{auth_uri} || 'https://api.flickr.com/services/auth/';
112             $self->{upload_uri} = $args->{upload_uri} || 'https://api.flickr.com/services/upload/';
113             $self->{unicode} = $args->{unicode} || 0;
114              
115             if (defined($args->{key}) or defined ($self->{key})) {
116             delete $args->{key};
117             delete $self->{key};
118             # Silenty switch key to api_key until a later release
119             # carp "Argument 'key' is deprecated and has been changed to api_key";
120             }
121              
122             if (defined ($args->{secret}) or defined ($self->{secret})) {
123             delete $args->{secret};
124             delete $self->{secret};
125             # Silenty switch secret to api_secret until a later release
126             # carp "Argument 'secret' is deprecated and has been changed to api_secret";
127             }
128              
129             $self->{fauth}->{frob} = $args->{frob};
130             $self->{fauth}->{api_key} = $self->{api_key};
131             $self->{fauth}->{api_secret} = $self->{api_secret};
132             $self->{fauth}->{token} = $args->{token};
133              
134             carp "You must pass an API key or a Consumer key to the constructor" unless defined $self->{api_key};
135              
136             }
137              
138             eval {
139             require Compress::Zlib;
140              
141             $self->default_header('Accept-Encoding' => 'gzip');
142             };
143              
144             bless $self, $class;
145             $self->_clear_status();
146             $self->_initialize();
147             return $self;
148             }
149              
150              
151              
152             #
153             # Execution Methods
154             #
155              
156             sub execute_method {
157             my ($self, $method, $args) = @_;
158             my $request;
159              
160             if ($self->is_oauth) {
161              
162             #
163             # Consumer Request Params
164             #
165             my $oauth = {};
166              
167             $oauth->{nonce} = _make_nonce();
168             $oauth->{consumer_key} = $self->{oauth}->{consumer_key};
169             $oauth->{consumer_secret} = $self->{oauth}->{consumer_secret};
170             $oauth->{timestamp} = time;
171             $oauth->{signature_method} = $self->{oauth}->{signature_method};
172             $oauth->{version} = $self->{oauth}->{version};
173              
174             if (defined($args->{'token'}) or defined($args->{'token_secret'})) {
175              
176             carp "\ntoken and token_secret must be specified in Flickr::API->new() and are being discarded\n";
177             undef $args->{'token'};
178             undef $args->{'token_secret'};
179             }
180              
181             if (defined($args->{'consumer_key'}) or defined($args->{'consumer_secret'})) {
182              
183             carp "\nconsumer_key and consumer_secret must be specified in Flickr::API->new() and are being discarded\n";
184             undef $args->{'consumer_key'};
185             undef $args->{'consumer_secret'};
186             }
187              
188              
189             $oauth->{extra_params} = $args;
190             $oauth->{extra_params}->{method} = $method;
191              
192             #
193             # Protected resource params
194             #
195             if (defined($self->{oauth}->{token})) {
196              
197             $oauth->{token} = $self->{oauth}->{token};
198             $oauth->{token_secret} = $self->{oauth}->{token_secret};
199              
200             }
201              
202             $request = Flickr::API::Request->new({
203             'api_type' => 'oauth',
204             'method' => $method,
205             'args' => $oauth,
206             'rest_uri' => $self->{rest_uri},
207             'unicode' => $self->{unicode},
208             });
209             }
210             else {
211              
212             $request = Flickr::API::Request->new({
213             'api_type' => 'flickr',
214             'method' => $method,
215             'args' => $args,
216             'rest_uri' => $self->{rest_uri},
217             'unicode' => $self->{unicode},
218             });
219             }
220              
221             return $self->execute_request($request);
222              
223             }
224              
225             sub execute_request {
226             my ($self, $request) = @_;
227              
228             $request->{api_args}->{method} = $request->{api_method};
229              
230             unless ($self->is_oauth) { $request->{api_args}->{api_key} = $self->{api_key}; }
231              
232             if (defined($self->{api_secret}) && length($self->{api_secret})) {
233              
234             unless ($self->is_oauth) { $request->{api_args}->{api_sig} = $self->_sign_args($request->{api_args}); }
235              
236             }
237              
238             unless ($self->is_oauth) { $request->encode_args(); }
239              
240             my $response = $self->request($request);
241             bless $response, 'Flickr::API::Response';
242              
243             $response->init_flickr();
244              
245             if ($response->{_rc} != 200){
246             $response->set_fail(0, "API returned a non-200 status code ($response->{_rc})");
247             return $response;
248             }
249              
250             my $content = $response->decoded_content();
251             $content = $response->content() unless defined $content;
252              
253             my $xls = XML::LibXML::Simple->new(ForceArray => 0);
254             my $tree = XML::Parser::Lite::Tree::instance()->parse($content);
255              
256             my $hashref = $xls->XMLin($content,KeyAttr => []);
257              
258             my $rsp_node = $self->_find_tag($tree->{children});
259              
260             if ($rsp_node->{name} ne 'rsp'){
261             $response->set_fail(0, "API returned an invalid response");
262             return $response;
263             }
264              
265             if ($rsp_node->{attributes}->{stat} eq 'fail'){
266             my $fail_node = $self->_find_tag($rsp_node->{children});
267             if ($fail_node->{name} eq 'err'){
268             $response->set_fail($fail_node->{attributes}->{code}, $fail_node->{attributes}->{msg});
269             }
270             else {
271             $response->set_fail(0, "Method failed but returned no error code");
272             }
273             return $response;
274             }
275              
276             if ($rsp_node->{attributes}->{stat} eq 'ok'){
277             $response->set_ok($rsp_node,$hashref);
278             return $response;
279             }
280              
281             $response->set_fail(0, "API returned an invalid status code");
282             return $response;
283             }
284              
285              
286              
287             sub upload {
288             my ($self, $args) = @_;
289             my $upload;
290              
291             unless ($self->api_permissions() eq 'write' || $self->api_permissions() eq 'delete') {
292             croak "insufficient permission for upload";
293             }
294              
295             my %cfg = $self->export_config;
296             $cfg{'request_url'} = $self->{upload_uri};
297              
298             $upload = Flickr::API::Upload->new({
299             'photo' => $args,
300             'api' => \%cfg,
301             'api_type' => $self->api_type(),
302             });
303              
304             my $response = $self->request($upload);
305             bless $response, 'Flickr::API::Response';
306              
307             $response->init_flickr();
308              
309             if ($response->{_rc} != 200){
310             $response->set_fail(0, "Upload returned a non-200 status code ($response->{_rc})");
311             return $response;
312             }
313              
314             my $content = $response->decoded_content();
315             $content = $response->content() unless defined $content;
316              
317             my $xls = XML::LibXML::Simple->new(ForceArray => 0);
318             my $tree = XML::Parser::Lite::Tree::instance()->parse($content);
319              
320             my $hashref = $xls->XMLin($content,KeyAttr => []);
321              
322             my $rsp_node = $self->_find_tag($tree->{children});
323              
324             if ($rsp_node->{name} ne 'rsp'){
325             $response->set_fail(0, "Upload returned an invalid response");
326             return $response;
327             }
328              
329             if ($rsp_node->{attributes}->{stat} eq 'fail'){
330             my $fail_node = $self->_find_tag($rsp_node->{children});
331             if ($fail_node->{name} eq 'err'){
332             $response->set_fail($fail_node->{attributes}->{code}, $fail_node->{attributes}->{msg});
333             }
334             else {
335             $response->set_fail(0, "Upload failed but returned no error code");
336             }
337             return $response;
338             }
339              
340             if ($rsp_node->{attributes}->{stat} eq 'ok'){
341             $response->set_ok($rsp_node,$hashref);
342             return $response;
343             }
344              
345             $response->set_fail(0, "API returned an invalid status code");
346              
347             return $response;
348              
349             }
350              
351             #
352             # Persistent config methods
353             #
354              
355              
356             #
357             # Method to return hash of important Flickr or OAuth parameters.
358             # OAuth can also export meaningful subsets of parameters based
359             # on OAuth message type.
360             #
361             sub export_config {
362             my ($self, $type, $params) = @_;
363              
364             if ($self->is_oauth) {
365              
366             unless($params) { $params='do_it'; }
367              
368             my %oauth;
369              
370             if (defined($type)) {
371             if ($params =~ m/^m.*/i) {
372             %oauth = map { ($_) => undef } @{Net::OAuth->request($type)->all_message_params()};
373             }
374             elsif ($params =~ m/^a.*/i) {
375             %oauth = map { ($_) => undef } @{Net::OAuth->request($type)->all_api_params()};
376             }
377             else {
378             %oauth = map { ($_) => undef } @{Net::OAuth->request($type)->all_params()};
379             }
380             foreach my $param (keys %oauth) {
381             if (defined ($self->{oauth}->{$param})) { $oauth{$param} = $self->{oauth}->{$param}; }
382             }
383             return %oauth;
384             }
385             else {
386             return %{$self->{oauth}};
387             }
388             }
389             else {
390             return %{$self->{fauth}};
391             }
392              
393             }
394              
395             #
396             # Use perl core Storable to save important parameters.
397             #
398             sub export_storable_config {
399             my ($self,$file) = @_;
400              
401             open my $EXPORT, '>', $file or croak "\nCannot open $file for write: $!\n";
402             my %config = $self->export_config();
403             store_fd(\%config, $EXPORT);
404             close $EXPORT;
405             return;
406             }
407              
408             #
409             # Use perl core Storable for re-vivifying an API object from saved parameters
410             #
411             sub import_storable_config {
412             my ($class,$file) = @_;
413              
414             open my $IMPORT, '<', $file or croak "\nCannot open $file for read: $!\n";
415             my $config_ref = retrieve_fd($IMPORT);
416             close $IMPORT;
417             my $api = $class->new($config_ref);
418             return $api;
419             }
420              
421              
422              
423             #
424             # Preauthorization Methods
425             #
426             # Handle request token requests (process: REQUEST TOKEN, authorize, access token)
427             #
428             sub oauth_request_token {
429             my ($self, $args) = @_;
430              
431             my %oauth = %{$self->{oauth}};
432              
433             unless ($self->is_oauth) {
434             carp "\noauth_request_token called for Non-OAuth Flickr::API object\n";
435             return;
436             }
437             unless ($self->get_oauth_request_type() eq 'consumer') {
438             croak "\noauth_request_token called using protected resource Flickr::API object\n";
439             }
440              
441             $self->{oauth_request} = 'Request Token';
442             $oauth{request_url} = $args->{request_token_url} || 'https://api.flickr.com/services/oauth/request_token';
443             $oauth{callback} = $args->{callback} || 'https://127.0.0.1';
444              
445             $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
446              
447             my $orequest = Net::OAuth->request('Request Token')->new(%oauth);
448              
449             $orequest->sign;
450              
451             my $response = $self->get($orequest->to_url);
452              
453             my $content = $response->decoded_content();
454             $content = $response->content() unless defined $content;
455              
456             if ($content =~ m/^oauth_problem=(.+)$/) {
457              
458             carp "\nRequest token not granted: '",$1,"'\n";
459             $self->{oauth}->{request_token} = $1;
460             return $1;
461             }
462              
463             $self->{oauth}->{request_token} = Net::OAuth->response('request token')->from_post_body($content);
464             $self->{oauth}->{callback} = $oauth{callback};
465             return 'ok';
466             }
467              
468             #
469             # Participate in authorization (process: request token, AUTHORIZE, access token)
470             #
471             sub oauth_authorize_uri {
472              
473             my ($self, $args) = @_;
474              
475             unless ($self->is_oauth) {
476             carp "oauth_authorize_uri called for Non-OAuth Flickr::API object";
477             return;
478             }
479             my %oauth = %{$self->{oauth}};
480              
481             $self->{oauth_request} = 'User Authentication';
482             $oauth{perms} = lc($args->{perms}) || 'read';
483              
484             carp "\nThe 'perms' parameter must be one of: read, write, delete\n"
485             and return unless defined($oauth{perms}) && $oauth{perms} =~ /^(read|write|delete)$/;
486              
487             $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
488              
489             return $self->{auth_uri} .
490             '?oauth_token=' . $oauth{'request_token'}{'token'} .
491             '&perms=' . $oauth{perms};
492              
493             }
494              
495             #
496             # flickr preauthorization
497             #
498              
499             sub request_auth_url {
500             my ($self, $perms, $frob) = @_;
501              
502             if ($self->is_oauth) {
503              
504             carp "request_auth_url called for an OAuth instantiated Flickr::API";
505             return;
506              
507             }
508              
509             $perms = lc($perms);
510              
511             carp "\nThe 'perms' parameter must be one of: read, write, delete\n"
512             and return unless defined($perms) && $perms =~ /^(read|write|delete)$/;
513              
514             return unless defined $self->{api_secret} && length $self->{api_secret};
515              
516             my %fauth = (
517             'api_key' => $self->{api_key},
518             'perms' => $perms
519             );
520              
521             if ($frob) {
522             $fauth{frob} = $frob;
523             }
524              
525             my $sig = $self->_sign_args(\%fauth);
526             $fauth{api_sig} = $sig;
527              
528             my $uri = URI->new($self->{auth_uri});
529             $uri->query_form(%fauth);
530              
531             return $uri;
532             }
533              
534              
535             #
536             # Access Token (post authorization) Methods
537             #
538             # Handle access token requests (process: request token, authorize, ACCESS TOKEN)
539             #
540             sub oauth_access_token {
541              
542             my ($self, $args) = @_;
543              
544             unless ($self->is_oauth) {
545             carp "oauth_access_token called for Non-OAuth Flickr::API object";
546             return;
547             }
548             if ($args->{token} ne $self->{oauth}->{request_token}->{token}) {
549              
550             carp "Request token in API does not match token for access token request";
551             return;
552              
553             }
554              
555             #
556             # Stuff the values for the Net::OAuth factory
557             #
558             $self->{oauth}->{verifier} = $args->{verifier};
559             $self->{oauth}->{token} = $args->{token};
560             $self->{oauth}->{token_secret} = $self->{oauth}->{request_token}->{token_secret};
561              
562             my %oauth = %{$self->{oauth}};
563              
564             $oauth{request_url} = $args->{access_token_url} || 'https://api.flickr.com/services/oauth/access_token';
565              
566             $Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
567              
568             my $request = Net::OAuth->request('Access Token')->new(%oauth);
569              
570             $request->sign;
571              
572             my $response = $self->get($request->to_url);
573              
574             my $content = $response->decoded_content();
575             $content = $response->content() unless defined $content;
576              
577             if ($content =~ m/^oauth_problem=(.+)$/) {
578              
579             carp "\nAccess token not granted: '",$1,"'\n";
580             $self->{oauth}->{access_token} = $1;
581              
582             delete $self->{oauth}->{token}; # Not saving problematic request token
583             delete $self->{oauth}->{token_secret}; # token secret
584             delete $self->{oauth}->{verifier}; # and verifier copies
585              
586             return $1;
587              
588             }
589              
590             $self->{oauth}->{access_token} = Net::OAuth->response('access token')->from_post_body($content);
591             $self->{oauth}->{token} = $self->{oauth}->{access_token}->token();
592             $self->{oauth}->{token_secret} = $self->{oauth}->{access_token}->token_secret();
593              
594             delete $self->{oauth}->{request_token}; #No longer valid, anyway
595             delete $self->{oauth}->{verifier};
596              
597             return 'ok';
598              
599             }
600              
601             sub flickr_access_token {
602             my ($self,$frob) = @_;
603              
604             my $rsp = $self->execute_method('flickr.auth.getToken', {api_key => $self->{api_key}, frob => $frob });
605             my $response_ref = $rsp->as_hash();
606              
607             $self->{fauth}->{frob} = $frob;
608              
609             $self->{token} = $response_ref->{auth}->{token};
610             $self->{fauth}->{token} = $response_ref->{auth}->{token};
611              
612             $self->{fauth}->{user} = $response_ref->{auth}->{user};
613              
614             return $response_ref->{stat};
615              
616             }
617              
618              
619             #
620             # Utility methods
621             #
622              
623              
624             sub is_oauth {
625             my ($self) = @_;
626             if (defined $self->{api_type} and $self->{api_type} eq 'oauth') {
627             return 1;
628             }
629             else {
630             return 0;
631             }
632             }
633              
634              
635             sub get_oauth_request_type {
636             my ($self) = @_;
637              
638             if (defined $self->{api_type} and $self->{api_type} eq 'oauth') {
639             return $self->{oauth_request};
640             }
641             else {
642             return;
643             }
644             }
645              
646             sub api_type {
647             my ($self) = @_;
648              
649             return $self->{api_type};
650              
651             }
652              
653              
654             sub api_success {
655             my ($self) = @_;
656              
657             return $self->{flickr}->{status}->{api_success};
658              
659             }
660              
661              
662              
663              
664             sub api_message {
665             my ($self) = @_;
666              
667             return $self->{flickr}->{status}->{api_message};
668             }
669              
670              
671             sub api_permissions {
672             my ($self) = @_;
673             my $rsp;
674             my $check;
675             my $retval;
676              
677             if ($self->is_oauth) {
678              
679             if (defined($self->{oauth}->{perms})) {
680              
681             $self->_set_status(1,"Permissions retrieved from config.");
682              
683             }
684             else {
685              
686             $self->{oauth}->{perms} = 'none'; #preload no perms
687              
688             $rsp = $self->execute_method('flickr.auth.oauth.checkToken');
689              
690             if (!$rsp->success()) {
691              
692             $rsp->_propagate_status($self->{flickr}->{status});
693              
694             carp "\nUnable to validate OAuth token. Flickr error: ",
695             $self->{flickr}->{status}->{error_code}," - \"",
696             $self->{flickr}->{status}->{error_message},"\" \n";
697             delete $self->{oauth}->{perms};
698             $self->_set_status(0,"Unable to validate OAuth token, Flickr API call not successful.");
699              
700             }
701             else {
702              
703             $check = $rsp->as_hash();
704              
705             $self->{oauth}->{perms} = $check->{oauth}->{perms};
706             $self->_set_status(1,"Permissions retrieved from Flickr.");
707              
708             }
709             } # else not cached
710              
711             $retval = $self->{oauth}->{perms};
712              
713             } # is_oauth
714             else { # is_flickr
715              
716             if (defined($self->{fauth}->{perms})) {
717              
718             $self->_set_status(1,"Permissions retrieved from config.");
719              
720             }
721             else {
722              
723             $self->{fauth}->{perms} = 'none'; #preload no perms
724             $rsp = $self->execute_method('flickr.auth.checkToken',{'auth_token' => $self->{fauth}->{token}});
725              
726             if (!$rsp->success()) {
727              
728             $rsp->_propagate_status($self->{flickr}->{status});
729              
730             carp "\nUnable to validate Flickr token. Flickr error: ",
731             $self->{flickr}->{status}->{error_code}," - \"",
732             $self->{flickr}->{status}->{error_message},"\" \n";
733             delete $self->{fauth}->{perms};
734             $self->_set_status(0,"Unable to validate Flickr token, Flickr API call not successful.");
735              
736             }
737             else {
738              
739             $check = $rsp->as_hash();
740              
741             $self->{fauth}->{perms} = $check->{auth}->{perms};
742             $self->_set_status(1,"Permissions retrieved from Flickr.");
743             }
744             } # else not cached
745              
746             $retval = $self->{fauth}->{perms};
747              
748             } # else is_flickr
749              
750              
751             return $retval;
752              
753             }
754              
755              
756             #
757             # Private methods
758             #
759              
760             sub _sign_args {
761             my ($self, $args) = @_;
762              
763             if ($self->is_oauth) {
764              
765             carp "_sign_args called for an OAuth instantiated Flickr::API";
766             return;
767              
768             }
769              
770             my $sig = $self->{api_secret};
771              
772             foreach my $key (sort {$a cmp $b} keys %{$args}) {
773              
774             my $value = (defined($args->{$key})) ? $args->{$key} : "";
775             $sig .= $key . $value;
776             }
777              
778             return md5_hex(encode_utf8($sig)) if $self->{unicode};
779             return md5_hex($sig);
780             }
781              
782             sub _find_tag {
783             my ($self, $children) = @_;
784             for my $child(@{$children}){
785             return $child if $child->{type} eq 'element';
786             }
787             return {};
788             }
789              
790             sub _make_nonce {
791              
792             return md5_hex(rand);
793              
794             }
795             sub _export_api {
796             my ($self) = @_;
797             my $api = {};
798              
799             $api->{oauth} = $self->{oauth};
800             $api->{fauth} = $self->{fauth};
801             $api->{flickr} = $self->{flickr};
802              
803             $api->{api_type} = $self->{api_type};
804             $api->{api_key} = $self->{api_key};
805             $api->{api_secret} = $self->{api_secret};
806             $api->{rest_uri} = $self->{rest_uri};
807             $api->{unicode} = $self->{unicode};
808             $api->{auth_uri} = $self->{auth_uri};
809             $api->{upload_uri} = $self->{upload_uri};
810              
811             return $api;
812             }
813              
814              
815             sub _initialize {
816              
817             my ($self) = @_;
818             $self->_set_status(1,'Base API initialized');
819             return;
820              
821             }
822              
823             sub _full_status {
824              
825             my ($self) = @_;
826             return $self->{flickr}->{status};
827             }
828              
829             sub _clear_status {
830              
831             my ($self) = @_;
832              
833             # the API status
834             $self->_set_status(1,'');
835             # the propagated response status
836             $self->{flickr}->{status}->{_rc} = 0;
837             $self->{flickr}->{status}->{success} = 1; # initialize as successful
838             $self->{flickr}->{status}->{error_code} = 0;
839             $self->{flickr}->{status}->{error_message} = '';
840              
841             return;
842              
843             }
844              
845             sub _set_status {
846              
847             my ($self, $good, $msg) = @_;
848              
849             if ($good != 0) { $good = 1; }
850              
851             $self->{flickr}->{status}->{api_success} = $good;
852             $self->{flickr}->{status}->{api_message} = $msg;
853              
854             return;
855             }
856              
857              
858              
859             1;
860              
861             __END__