File Coverage

blib/lib/API/Plesk.pm
Criterion Covered Total %
statement 89 112 79.4
branch 20 44 45.4
condition 9 30 30.0
subroutine 17 20 85.0
pod 3 7 42.8
total 138 213 64.7


line stmt bran cond sub pod time code
1              
2             package API::Plesk;
3              
4 16     16   4357935 use strict;
  16         41  
  16         623  
5 16     16   95 use warnings;
  16         31  
  16         508  
6              
7 16     16   86 use Carp;
  16         34  
  16         1052  
8 16     16   157 use Data::Dumper;
  16         28  
  16         690  
9              
10 16     16   23145 use HTTP::Request;
  16         4766534  
  16         1409  
11 16     16   49273 use LWP::UserAgent;
  16         8109462  
  16         660  
12 16     16   15390 use XML::Fast;
  16         13800213  
  16         1215  
13 16     16   14699 use version;
  16         38898  
  16         145  
14              
15 16     16   15838 use API::Plesk::Response;
  16         46  
  16         24458  
16              
17             our $VERSION = '2.03';
18              
19             # creates accessors to components
20             # can support old interface of API::Plesk
21             init_components(
22             # new
23             customer => [['1.6.3.0', 'Customer']],
24             webspace => [['1.6.3.0', 'Webspace']],
25             site => [['1.6.3.0', 'Site']],
26             subdomain => [['1.6.3.0', 'Subdomain']],
27             site_alias => [['1.6.3.0', 'SiteAlias']],
28             sitebuilder => [['1.6.3.0', 'SiteBuilder']],
29             ftp_user => [['1.6.3.0', 'FTPUser']],
30             service_plan => [['1.6.3.0', 'ServicePlan']],
31             service_plan_addon => [['1.6.3.0', 'ServicePlanAddon']],
32             database => [['1.6.3.0', 'Database']],
33             webuser => [['1.6.3.0', 'WebUser']],
34             dns => [['1.6.3.0', 'DNS']],
35             mail => [['1.6.3.0', 'Mail']],
36             user => [['1.6.3.0', 'User']],
37              
38             # old
39             Accounts => [['1.5.0.0', 'Accounts']],
40             Domains => [['1.5.0.0', 'Domains']],
41             );
42              
43             # constructor
44             sub new {
45 17     17 1 1581 my $class = shift;
46 17   66     138 $class = ref ($class) || $class;
47              
48 17         273 my $self = {
49             username => '',
50             password => '',
51             secret_key => '',
52             url => '',
53             api_version => '1.6.3.1',
54             debug => 0,
55             timeout => 30,
56             (@_)
57             };
58              
59 17 50       86 if (!$self->{secret_key}) {
60 17 50       97 confess "Required username!" unless $self->{username};
61 17 50       71 confess "Required password!" unless $self->{password};
62             }
63 17 50       67 confess "Required url!" unless $self->{url};
64              
65 17         85 return bless $self, $class;
66             }
67              
68             # sends request to Plesk API
69             sub send {
70 1     1 1 3 my ( $self, $operator, $operation, $data, %params ) = @_;
71              
72 1 50 33     13 confess "Wrong request data!" unless $data && ref $data;
73              
74 1         5 my $xml = { $operator => { $operation => $data } };
75              
76 1         10 $xml = $self->render_xml($xml);
77              
78 1 50       8 warn "REQUEST $operator => $operation\n$xml" if $self->{debug};
79              
80 1         6 my ($response, $error) = $self->xml_http_req($xml);
81              
82 1 50       4 warn "RESPONSE $operator => $operation => $error\n$response" if $self->{debug};
83              
84 1 50       4 unless ( $error ) {
85 1         10 $response = xml2hash $response, array => [$operation, 'result', 'property'];
86             }
87              
88 1         109 return API::Plesk::Response->new(
89             operator => $operator,
90             operation => $operation,
91             response => $response,
92             error => $error,
93             );
94             }
95              
96 0     0 0 0 sub bulk_send { confess "Not implemented!" }
97              
98             # Send xml request to plesk api
99             sub xml_http_req {
100 0     0 1 0 my ($self, $xml) = @_;
101              
102             # HTTP::Request undestends only bytes
103 0 0       0 utf8::encode($xml) if utf8::is_utf8($xml);
104              
105 0         0 my $ua = new LWP::UserAgent( parse_head => 0 );
106 0         0 my $req = new HTTP::Request POST => $self->{url};
107              
108 0 0       0 if ($self->{secret_key}) {
109 0         0 $req->push_header(':KEY', $self->{secret_key});
110             } else {
111 0         0 $req->push_header(':HTTP_AUTH_LOGIN', $self->{username});
112 0         0 $req->push_header(':HTTP_AUTH_PASSWD', $self->{password});
113             }
114 0         0 $req->content_type('text/xml; charset=UTF-8');
115 0         0 $req->content($xml);
116              
117             # LWP6 hack to prevent verification of hostname
118 0 0       0 $ua->ssl_opts(verify_hostname => 0) if $ua->can('ssl_opts');
119              
120 0 0 0     0 warn $req->as_string if defined $self->{debug} && $self->{debug} > 1;
121              
122 0         0 my $res = eval {
123 0     0   0 local $SIG{ALRM} = sub { die "connection timeout" };
  0         0  
124 0         0 alarm $self->{timeout};
125 0         0 $ua->request($req);
126             };
127 0         0 alarm 0;
128              
129 0 0 0     0 warn $res->as_string if defined $self->{debug} && $self->{debug} > 1;
130              
131 0 0 0     0 return ('', 'connection timeout')
      0        
      0        
132             if !$res || $@ || ref $res && $res->status_line =~ /connection timeout/;
133              
134 0 0       0 return $res->is_success() ?
135             ($res->content(), '') :
136             ('', $res->status_line);
137             }
138              
139              
140             # renders xml packet for request
141             sub render_xml {
142 3     3 0 903 my ($self, $hash) = @_;
143              
144 3         13 my $xml = _render_xml($hash);
145              
146 3         18 $xml = qq|$xml|;
147              
148 3         41 $xml;
149             }
150              
151             # renders xml from hash
152             sub _render_xml {
153 20     20   37 my ( $hash ) = @_;
154              
155 20 100       46 return $hash unless ref $hash;
156              
157 19         26 my $xml = '';
158              
159 19         219 for my $tag ( keys %$hash ) {
160 20         236 my $value = $hash->{$tag};
161 20 100       71 if ( ref $value eq 'HASH' ) {
    100          
    100          
162 3         26 $value = _render_xml($value);
163             }
164             elsif ( ref $value eq 'ARRAY' ) {
165 4         6 my $tmp;
166 4         17 $tmp .= _render_xml($_) for ( @$value );
167 4         9 $value = $tmp;
168             }
169             elsif ( ref $value eq 'CODE' ) {
170 2         5 $value = _render_xml(&$value);
171             }
172              
173 20 100 100     258 if ( !defined $value or $value eq '' ) {
174 2         10 $xml .= "<$tag/>";
175             }
176             else {
177 18         245 $xml .= "<$tag>$value";
178             }
179             }
180              
181 19         62 $xml;
182             }
183              
184              
185             # initialize components
186             sub init_components {
187 16     16 0 237 my ( %c ) = @_;
188 16         446 my $caller = caller;
189              
190 16         97 for my $alias ( keys %c ) {
191              
192 256         367 my $classes = $c{$alias};
193              
194             my $sub = sub {
195 34     34   921 my( $self ) = @_;
196 34   66     297 $self->{"_$alias"} ||= $self->load_component($classes);
197 34   33     369 return $self->{"_$alias"} || confess "Not implemented!";
198 256         2019 };
199              
200 16     16   134 no strict 'refs';
  16         40  
  16         16891  
201              
202 256         399 *{"$caller\::$alias"} = $sub;
  256         1118  
203              
204              
205             }
206              
207             }
208              
209             # loads component package and creates object
210             sub load_component {
211 14     14 0 35 my ( $self, $classes ) = @_;
212 14         216 my $version = version->parse($self->{api_version});
213              
214 14         49 for my $item ( @$classes ) {
215              
216             # select compitable version of component
217 14 50       215 if ( $version >= $item->[0] ) {
218              
219 14         53 my $pkg = 'API::Plesk::' . $item->[1];
220              
221 14         43 my $module = "$pkg.pm";
222 14         96 $module =~ s/::/\//g;
223              
224 14         33 local $@;
225 14         49 eval { require $module };
  14         2441  
226 14 50       70 if ( $@ ) {
227 0         0 confess "Failed to load $pkg: $@";
228             }
229              
230 14         186 return $pkg->new(plesk => $self);
231              
232             }
233              
234             }
235              
236             }
237              
238             1;
239              
240             __END__