File Coverage

blib/lib/Ipernity/API.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             # Iperntiy::API
2             #
3             # Contact: doomy [at] dokuleser [dot] org
4             # Copyright 2008 Winfried Neessen
5             #
6             # $Id$
7             # Last modified: [ 2011-01-13 12:32:46 ]
8              
9             ### Module definitions {{{
10             package Ipernity::API;
11 4     4   144512 use strict;
  4         12  
  4         144  
12 4     4   22 use warnings;
  4         9  
  4         164  
13 4     4   25 use Carp;
  4         12  
  4         354  
14 4     4   23 use Digest::MD5 qw(md5_hex);
  4         7  
  4         230  
15 4     4   1914 use Ipernity::API::Request;
  4         11  
  4         127  
16 4     4   25712 use LWP::UserAgent;
  4         128754  
  4         154  
17 4     4   7026 use XML::Simple;
  0            
  0            
18              
19             our @ISA = qw(LWP::UserAgent);
20             our $VERSION = '0.10';
21             # }}}
22              
23             ### Module constructor / new() {{{
24             sub new
25             {
26              
27             ### Define class and object
28             my $class = shift;
29             my $self = new LWP::UserAgent;
30              
31             ### Read arguments
32             my %args = @_;
33              
34             ## Assign arguments to object
35             foreach my $key ( keys %args )
36             {
37              
38             $self->{ 'args' }->{ $key } = $args{ $key };
39              
40             }
41            
42             ### For Ipernity we need an output format
43             unless( defined( $self->{ 'args' }->{ 'outputformat' } ) )
44             {
45              
46             $self->{ 'args' }->{ 'outputformat' } = 'xml';
47              
48             }
49              
50             ### The API key is mandatory
51             warn 'Please provide at least an API key' unless( defined( $self->{ 'args' }->{ 'api_key' } ) );
52              
53             ### Reference object to class
54             bless $self, $class;
55             return $self;
56              
57             }
58             # }}}
59              
60             ### Perform an API request / execute() {{{
61             sub execute
62             {
63              
64             ### Get object and arguments
65             my ( $self, %args ) = @_;
66              
67             ### Assign them to my object
68             foreach my $key ( keys %args )
69             {
70              
71             $self->{ 'request' }->{ $key } = $args{ $key };
72              
73             };
74              
75             ## Create a request object
76             my $request = Ipernity::API::Request->new( %{ $self->{ 'request' } } );
77              
78             ## Query the API object with the request
79             $self->execute_request( $request );
80              
81             }
82             # }}}
83              
84             ### Execute the API request and return a XML object / execute_hash() {{{
85             sub execute_hash
86             {
87              
88             ### Get object and request
89             my ( $self, %args ) = @_;
90              
91             ## Initialize placeholer for old format setting
92             my ( $oldformat );
93              
94             ### For XML output we need to force the output format to be XML
95             unless( lc( $self->{ 'args' }->{ 'outputformat' } ) eq 'xml' )
96             {
97              
98             ## Keep the old format type so we can restore it afterwards
99             $oldformat = $self->{ 'args' }->{ 'outputformat' };
100              
101             ## Set new output format
102             $self->{ 'args' }->{ 'outputformat' } = 'xml';
103              
104             }
105              
106             ### Execute the request and read response
107             my $response = $self->execute( %args )->{ '_content' };
108              
109             ### Generate a hashref out of the XML tree
110             my $xml = new XML::Simple;
111             my $xmlresult = $xml->XMLin(
112              
113             $response,
114             ForceContent => 1,
115             ForceArray => 1,
116              
117             );
118              
119             ### Check the status of the request
120             $self->CheckResponse( $xmlresult );
121              
122             ## Restore old outputformat
123             $self->{ 'args' }->{ 'outputformat' } = $oldformat if defined( $oldformat );
124              
125             ### Return the XML hashref
126             return $xmlresult;
127              
128             }
129             # }}}
130              
131             ### Information placeholder for execute_xml / execute_xml() {{{
132             sub execute_xml
133             {
134              
135             ## This function is deprecated
136             return "execute_xml() has been renamed to execute_hash()";
137              
138             }
139             # }}}
140              
141             ### Execute the API request / execute_request() {{{
142             sub execute_request
143             {
144              
145             ### Get object and request
146             my ( $self, $request ) = @_;
147              
148             ## Generate a valid URI path
149             $request->{ '_uri' }->path( $request->{ '_uri' }->path() . $request->{ 'args' }->{ 'method' } . '/' . $self->{ 'args' }->{ 'outputformat' } );
150              
151             ### Add API key and secret to the request
152             $request->{ 'args' }->{ 'api_key' } = $self->{ 'args' }->{ 'api_key' };
153             $request->{ 'args' }->{ 'api_sig' } = $self->signargs( $request->{ 'args' } ) if( defined( $self->{ 'args' }->{ 'secret' } ) );
154              
155             ### Encode the arguments and build a POST request
156             $request->encode();
157            
158             ### Call the API
159             my $response = $self->request( $request );
160              
161             ### Return the response
162             return $response;
163              
164             }
165             # }}}
166              
167             ### Sign arguments for authenticated call // signargs() {{{
168             sub signargs
169             {
170              
171             ### Get object
172             my ( $self, $args ) = @_;
173              
174             ## Initialize placeholer for signed arguments
175             my ( $signed_args );
176              
177             ### Sort arguments
178             foreach my $key ( sort { $a cmp $b } keys %{ $args } )
179             {
180              
181             ## Read value if it is set
182             my $val = $args->{ $key } ? $args->{ $key } : '';
183              
184             ## Skip the 'method'
185             next if $key eq 'method';
186              
187             ## Add key/value pair to sign arguments string
188             $signed_args .= $key . $val;
189              
190             }
191              
192             ## Add method if present
193             $signed_args .= $args->{ 'method' } if defined( $args->{ 'method' } );
194              
195             ## Add secret to the end
196             $signed_args .= $self->{ 'args' }->{ 'secret' };
197              
198             ### Return as MD5 Hex hash of signed arguments
199             return md5_hex( $signed_args );
200              
201             }
202             # }}}
203              
204             ### Fetch a Frob for the AuthToken request / fetchfrob() {{{
205             sub fetchfrob
206             {
207              
208             ### Get object and initalize frob
209             my $self = shift;
210             my $frob = {};
211              
212             ### Create an API request
213             my $response = $self->execute_hash(
214              
215             'method' => 'auth.getFrob',
216              
217             );
218              
219             ### Return the frob
220             return $response->{ 'auth' }->[0]->{ 'frob' }->[0]->{ 'content' };
221              
222             }
223             # }}}
224              
225             ### Build an AuthToken request URL / authurl {{{
226             sub authurl
227             {
228             ### Get object and arguments
229             my ( $self, %args ) = @_;
230              
231             ### Initalize placeholder for signed args
232             my ( $signed_args );
233              
234             ## Add api_key to provided arguements
235             $args{ 'api_key' } = $self->{ 'args' }->{ 'api_key' };
236              
237             ### Lets put the permissions into the main hash
238             foreach my $permkey ( %{ $args{ 'perms' } } )
239             {
240              
241             $args{ $permkey } = $args{ 'perms' }->{ $permkey };
242              
243             }
244              
245             ## Delete permissions from arguments
246             delete( $args{ 'perms' } );
247              
248             ### Sort arguments and add them to $api_sig
249             foreach my $key ( sort { $a cmp $b } keys %args )
250             {
251              
252             ## Skip if no key is defined
253             next unless( defined( $args{ $key } ) );
254              
255             ## Skip the method argument
256             next if $key eq 'method';
257              
258             ## Read value
259             my $val = $args{ $key } ? $args{ $key } : '';
260              
261             ## Add value/key to signed arguments list
262             $signed_args .= $key . $val;
263              
264             }
265              
266             ## Add method to signed arguments list
267             $signed_args .= $args{ 'method' } if defined( $args{ 'method' } );
268              
269             ## Add secret to signed arguments
270             $signed_args .= $self->{ 'args' }->{ 'secret' };
271              
272             ### Create MD5 hash out of the signed args
273             my $api_sig = md5_hex( $signed_args );
274              
275             ### Decide wether Auth URL to use
276             my $url = 'http://www.ipernity.com/apps/authorize';
277              
278             ### Build AuthURL
279             my $authurl = $url . '?api_key=' . $args{ 'api_key' };
280              
281             ## Add frob if defined
282             $authurl .= '&frob=' . $args{ 'frob' } if defined( $args{ 'frob' } );
283              
284             ## Add permission if any
285             foreach my $permission ( keys %args )
286             {
287              
288             ## Add permissions to AuthURL string
289             $authurl .= '&' . $permission . '=' . $args{ $permission } if $permission =~ /^perm_/;
290              
291             }
292              
293             ## Add API signature to AuthURL string
294             $authurl .= '&api_sig=' . $api_sig;
295              
296             ### Return the AuthURL
297             return $authurl;
298              
299             }
300             # }}}
301              
302             ### Fetch the AuthToken / authtoken {{{
303             sub authtoken
304             {
305              
306             ### Get object and frob
307             my ( $self, $frob ) = @_;
308              
309             ### Create an API request
310             my $response = $self->execute_hash(
311              
312             'method' => 'auth.getToken',
313             'frob' => $frob,
314              
315             );
316              
317             ### Let's safe the auth token and user information
318             $self->{ 'auth' }->{ 'authtoken' } = $response->{ 'auth' }->[0]->{ 'token' }->[0]->{ 'content' };
319             $self->{ 'auth' }->{ 'realname' } = $response->{ 'auth' }->[0]->{ 'user' }->[0]->{ 'realname' };
320             $self->{ 'auth' }->{ 'userid' } = $response->{ 'auth' }->[0]->{ 'user' }->[0]->{ 'user_id' };
321             $self->{ 'auth' }->{ 'username' } = $response->{ 'auth' }->[0]->{ 'user' }->[0]->{ 'username' };
322              
323             ### Return the AuthToken
324             return $response->{ 'auth' }->[0]->{ 'token' }->[0]->{ 'content' };
325              
326             }
327             # }}}
328              
329             ### Check the API status code and return an error if unsuccessfull // CheckResponse() {{{
330             sub CheckResponse
331             {
332             ### Get the object and XML hashref
333             my ( $self, $xmlhash ) = @_;
334              
335             ## Initialize placeholder for code and msg
336             my ( $code, $msg );
337              
338             ### Get the status;
339             my $status = $xmlhash->{ 'status' };
340              
341             ### We caught an error - let's die!
342             if( lc( $status ) ne 'ok' )
343             {
344            
345             ## Get code and message of the error
346             $code = $xmlhash->{ 'code' };
347             $msg = $xmlhash->{ 'message' };
348              
349             ## Croak the error
350             croak( 'An API call caught an unexpected error: ' . $msg . ' (Error Code: ' . $code . ')' );
351             }
352              
353             ### Otherwise everthing is fine
354             return undef;
355              
356             }
357             # }}}
358              
359             1;
360             __END__