File Coverage

blib/lib/WWW/DirectAdmin/API.pm
Criterion Covered Total %
statement 30 121 24.7
branch 0 22 0.0
condition 0 16 0.0
subroutine 10 25 40.0
pod 13 13 100.0
total 53 197 26.9


line stmt bran cond sub pod time code
1             package WWW::DirectAdmin::API;
2              
3 1     1   23443 use 5.006;
  1         3  
  1         43  
4 1     1   6 use strict;
  1         1  
  1         34  
5 1     1   4 use warnings;
  1         6  
  1         47  
6              
7             our $VERSION = '0.02';
8              
9 1     1   4 use Carp;
  1         2  
  1         95  
10 1     1   132269 use Data::Dumper qw( Dumper );
  1         11991  
  1         107  
11 1     1   777 use HTML::Entities;
  1         5571  
  1         78  
12 1     1   813 use HTTP::Request;
  1         22728  
  1         31  
13 1     1   1014 use LWP::UserAgent ();
  1         22611  
  1         28  
14 1     1   890 use Params::Validate;
  1         11546  
  1         80  
15 1     1   9 use URI;
  1         2  
  1         1574  
16              
17             sub new {
18 0     0 1   my ( $class, %params ) = @_;
19 0           my $self = bless {}, $class;
20              
21 0   0       $self->{host} = delete $params{host}
22             || croak "Missing required 'host' parameter";
23            
24 0           $self->{port} = delete $params{port};
25              
26 0   0       $self->{username} = delete $params{username}
27             || croak "Missing required 'username' parameter";
28              
29 0   0       $self->{password} = delete $params{password}
30             || croak "Missing required 'password' parameter";
31              
32 0   0       $self->{ua} = delete $params{ua}
33             || LWP::UserAgent->new(
34             agent => __PACKAGE__ . "/$VERSION",
35             cookie_jar => {}
36             );
37              
38 0 0         $self->{scheme} = $params{https} ? 'https' : 'http';
39 0           $self->{domain} = delete $params{domain};
40 0   0       $self->{debug_level} = delete $params{debug} || 0;
41 0           $self->{error} = {};
42              
43 0           return $self;
44             }
45              
46             sub uri {
47 0     0 1   my $self = shift;
48              
49 0 0         if ( !$self->{uri} ) {
50 0 0         my $str = sprintf "%s://%s",
51             $self->{scheme},
52             $self->{host} . ( $self->{port} ? ":$self->{port}" : '' );
53              
54 0           my $uri = URI->new($str);
55              
56 0           $self->{uri} = $uri;
57             }
58              
59 0           return $self->{uri};
60             }
61              
62             sub debug_level {
63 0     0 1   my $self = shift;
64              
65 0 0         $self->{debug_level} = shift if @_;
66              
67 0           return $self->{debug_level};
68             }
69              
70             sub error {
71 0     0 1   my $self = shift;
72              
73 0 0         $self->{error} = shift if @_;
74              
75 0           return $self->{error};
76             }
77              
78             # NOTE: parameters for Reseller or User packages are different than Admin levels
79             sub _send_request {
80 0     0     my $self = shift;
81 0           my %p = validate @_,
82             {
83             command => 1,
84             method => { default => 'GET' },
85             domain => { default => $self->{domain} },
86             params => { default => {} }
87             };
88              
89 0           my $method = delete $p{method}; # POST or GET
90 0           my $params = delete $p{params};
91 0           my $uri = $self->uri;
92              
93 0           $uri->path_query( delete $p{command} );
94 0           $uri->query_form( %p, %{$params} );
  0            
95              
96 0           my $req = HTTP::Request->new( $method => $uri );
97              
98 0           $self->debug( "Sending request: " . $req->as_string . "\n" );
99              
100 0           $req->authorization_basic( $self->{username}, $self->{password} );
101              
102 0           return $self->_parse_response( $self->{ua}->request($req) );
103             }
104              
105             sub _parse_response {
106 0     0     my $self = shift;
107 0           my $resp = shift;
108              
109 0 0         croak "Failed to receive response from server"
110             unless $resp;
111              
112 0           $self->debug( "Received response: " . $resp->content );
113              
114 0 0         if ( $resp->content =~ /You cannot execute that command/ ) {
115 0           croak "Current user doesn't have correct authority level";
116             }
117              
118 0 0         if ( $resp->content =~ /error=1/ ) {
119 0           my $str = decode_entities( $resp->content );
120 0           my %error;
121              
122 0           foreach ( split '&', $str ) {
123 0           my ( $k, $v ) = split /\=/;
124 0           $error{$k} = $v;
125             }
126              
127 0           $self->error( \%error );
128              
129 0           croak "Response returned an error";
130             }
131              
132             # for now it looks like error=0 means good action
133             # but may need to check all calls for cases where it may be part of
134             # returned data.
135 0 0         if ( $resp->content =~ /error=0/ ) {
136 0           return 1;
137             }
138              
139             # return data containing goofy list[] format gets turned into list
140             # - this appears to be related to function type
141 0 0         if ( $resp->content =~ /list\[\]\=/ ) {
142              
143             # or $resp->as_string
144 0           return map { s/list\[\]\=//; $_; } split( '&', $resp->content );
  0            
  0            
145             }
146              
147 0           croak "Unknown return format: " . $resp->content;
148             }
149              
150             #
151             # Admin functions
152             #
153             sub get_users {
154 0     0 1   my $self = shift;
155 0           my %p = validate @_,
156             { reseller => 0, domain => { default => $self->{domain} } };
157 0           my $uri = $self->uri;
158              
159 0           $uri->path_query('CMD_API_SHOW_USERS');
160 0           $uri->query_form(%p);
161              
162 0           $self->debug( "Sending request: " . $uri->as_string );
163              
164 0           my $req = HTTP::Request->new( GET => $uri );
165 0           $req->authorization_basic( $self->{username}, $self->{password} );
166 0           return $self->_parse_response( $self->{ua}->request($req) );
167             }
168              
169             #
170             # User API Functions
171             #
172              
173             sub get_domains {
174 0     0 1   my $self = shift;
175 0           return $self->_send_request( command => 'CMD_API_SHOW_DOMAINS', @_ );
176             }
177              
178             sub get_subdomains {
179 0     0 1   my $self = shift;
180 0           return $self->_send_request( command => 'CMD_API_SUBDOMAINS', @_ );
181             }
182              
183             sub create_subdomain {
184 0     0 1   my $self = shift;
185 0           my %p = validate @_, { domain => 0, subdomain => 1 };
186              
187 0           return $self->_send_request(
188             command => 'CMD_API_SUBDOMAINS',
189             params => { %p, action => 'create' }
190             );
191             }
192              
193             sub delete_subdomain {
194 0     0 1   my $self = shift;
195 0           my %p = validate @_,
196             { domain => 0, subdomain => 1, contents => { default => 'yes' } };
197              
198             # ugh.... maybe support subdomain as list ref too?
199 0           $p{select0} = delete $p{subdomain};
200              
201             # remove directory for it too
202 0           return $self->_send_request(
203             command => 'CMD_API_SUBDOMAINS',
204             params => { %p, action => 'delete' }
205             );
206             }
207              
208             sub get_databases {
209 0     0 1   my $self = shift;
210              
211 0           return $self->_send_request( command => 'CMD_API_DATABASES', @_ );
212             }
213              
214             sub create_database {
215 0     0 1   my $self = shift;
216 0           my %p = validate @_, { name => 1, user => 1, passwd => 1, passwd2 => 1 };
217              
218 0           return $self->_send_request(
219             command => 'CMD_API_DATABASES',
220             params => { %p, action => 'create' }
221             );
222             }
223              
224             sub delete_database {
225 0     0 1   my $self = shift;
226 0           my %p = validate @_, { name => 1 };
227              
228 0           $p{'select0'} = delete $p{name};
229              
230 0           return $self->_send_request(
231             command => 'CMD_API_DATABASES',
232             params => { %p, action => 'delete' }
233             );
234             }
235              
236             sub debug {
237 0     0 1   my $self = shift;
238 0           my $func = ( caller(1) )[3];
239 0   0       my $msg = shift || 'here';
240              
241 0 0         return unless $self->debug_level;
242              
243 0           printf STDERR "[%s] %s: %s\n", scalar(localtime), $func, $msg;
244             }
245              
246             =head1 NAME
247              
248             WWW::DirectAdmin::API - Access the DirectAdmin API with Perl
249              
250             =head1 VERSION
251              
252             Version 0.01
253              
254             =head1 SYNOPSIS
255              
256             This will provide access to the DirectAdmin API. The DirectAdmin API has
257             three levels Admin, Reseller and User functions.
258              
259             At this time, this API only implements the User level functions. I am open
260             to adding others but at time of initial creation I didn't need those.
261              
262             Please read L for details.
263              
264             use WWW::DirectAdmin::API;
265              
266             my $da = WWW::DirectAdmin::API->new(
267             host => 'example.com',
268             user => 'username',
269             pass => 'password',
270             domain => 'example-example.com'
271             );
272              
273             my @domains = $da->get_domains;
274              
275             print "You have: ", join( ',', @domains ), "\n";
276              
277             my @subdomains = $da->get_subdomains;
278              
279             print "You have: ", join( ',', @subdomains ), "\n";
280              
281             =head1 METHODS
282              
283             =head2 new
284              
285             Creates new WWW::DirectAdmin::API object. Parameters are
286             passed in as name value pairs. e.g. host => 'example.com'
287              
288             =over 4
289              
290             =item * host
291              
292             =item * port (optional, default: 2222)
293              
294             =item * username
295              
296             =item * password
297              
298             =item * domain - user's require this for most user actions (e.g. example.com)
299              
300             =item * ua - L object (optional)
301              
302             =item * https - set to true to use HTTPS (default: false)
303              
304             =item * debug - Output debug logging (optional)
305              
306             =back
307              
308              
309             =head2 error
310              
311             Returns hash with error keys from API calls. This is not always populated
312             since maybe calls don't return error messages.
313              
314             Usage:
315              
316             if ( defined $da->error->{details} ) {
317             print "Error details: ", $da->error->{details}, "\n";
318             }
319              
320             These are possible keys:
321              
322             =over 4
323              
324             =item * text
325              
326             =item * details
327              
328             =back
329              
330              
331             =head2 uri
332              
333             Returns URI object
334              
335             =head2 debug_level( $boolean )
336              
337             Set debug level after object construction.
338              
339             At this time debugging can be enabled with '1' or disabled with '0'.
340              
341             =head1 USER LEVEL API
342              
343             User level API commands.
344              
345             All create or delete commands return true on success and throw exception in case of error.
346              
347             You can check C method for hash of error details.
348              
349             =head2 get_domains
350              
351             Returns list of domains
352              
353             my @domains = $da->get_domains;
354              
355             =head2 get_subdomains
356              
357             Returns list of subdomains
358              
359             my @subdomains = $da->get_subdomains;
360              
361             =head2 create_subdomain( subdomain => 'name' )
362              
363             Creates new subdomain
364              
365             if ( create_subdomain( subdomain => 'perlrocks' ) {
366             print "Created subdomain\n";
367             }
368             else {
369             print "Booo! failed to create subdomain\n";
370             print "Error: ", $da->error->{details}, "\n";
371             }
372              
373             Returns true on success, false on error
374              
375             =head2 delete_subdomain( subdomain => 'name', contents => 'yes|no' )
376              
377             Deletes subdomain and if contents are set to 'yes' (default) then directory underneath.
378              
379             =head2 get_databases
380              
381             Returns list of databases
382              
383             =head2 create_database( %params )
384              
385             Create new database with user
386              
387             Parameters
388              
389             =over 4
390              
391             =item * name - database name
392              
393             =item * user - database username (according to API doc will append current username to it)
394              
395             =item * passwd - password
396              
397             =item * passwd2 - confirm password
398              
399             =back
400              
401             =head2 delete_database( name => 'database name' )
402              
403             Deletes database
404              
405             B: Database names have current username automatically prefixed when created by DirectAdmin. In delete call I must include username prefix. e.g. 'username_dbname'
406              
407              
408             =head1 ADMIN LEVEL API
409              
410             Very little of this is implemented today. More to come in later releases.
411              
412             =head2 get_users
413              
414             Retrieves list of users
415              
416             =head1 AUTHOR
417              
418             Lee Carmichael, C<< >>
419              
420             =head1 BUGS
421              
422             Please report any bugs or feature requests to C, or through
423             the web interface at L. I will be notified, and then you'll
424             automatically be notified of progress on your bug as I make changes.
425              
426             =head1 SUPPORT
427              
428             You can find documentation for this module with the perldoc command.
429              
430             perldoc WWW::DirectAdmin::API
431              
432              
433             You can also look for information at:
434              
435             =over 4
436              
437             =item * DirectAdmin API L
438              
439             =item * RT: CPAN's request tracker (report bugs here)
440              
441             L
442              
443             =item * AnnoCPAN: Annotated CPAN documentation
444              
445             L
446              
447             =item * CPAN Ratings
448              
449             L
450              
451             =item * Search CPAN
452              
453             L
454              
455             =back
456              
457              
458             =head1 LICENSE AND COPYRIGHT
459              
460             Copyright 2012 Lee Carmichael.
461              
462             This program is free software; you can redistribute it and/or modify it
463             under the terms of either: the GNU General Public License as published
464             by the Free Software Foundation; or the Artistic License.
465              
466             See http://dev.perl.org/licenses/ for more information.
467              
468              
469             =cut
470              
471             1; # End of WWW::DirectAdmin::API