0
|
1 |
=head1 Description
|
|
2 |
|
|
3 |
This is a wrapper interface to the DNS portions of the Tucows OpenSRS
|
|
4 |
HTTPS XML API.
|
|
5 |
|
|
6 |
The client library distributed by OpenSRS can be difficult to integrate
|
|
7 |
into a custom environment, and their web interface becomes quickly
|
|
8 |
tedious with heavy usage. This is a clean and relatively quick library
|
|
9 |
to perform the most common API methods described in the OpenSRS API
|
|
10 |
documentation.
|
|
11 |
|
|
12 |
=head1 Examples
|
|
13 |
|
|
14 |
use Net::OpenSRS;
|
|
15 |
|
|
16 |
my $key = 'Your_API_Key_From_The_Reseller_Interface';
|
|
17 |
my $srs = Net::OpenSRS->new();
|
|
18 |
|
|
19 |
$srs->environment('live');
|
|
20 |
$srs->set_key( $key );
|
|
21 |
|
|
22 |
$srs->set_manage_auth( 'manage_username', 'manage_password' );
|
|
23 |
|
|
24 |
my $cookie = $srs->get_cookie( 'spime.net' );
|
|
25 |
if ($cookie) {
|
|
26 |
print "Cookie: $cookie\n";
|
|
27 |
} else {
|
|
28 |
print $srs->last_response() . "\n";
|
|
29 |
}
|
|
30 |
|
|
31 |
# do a batch of domain locks
|
|
32 |
$srs->bulk_lock([ 'example.com', 'example.net', ... ]);
|
|
33 |
|
|
34 |
# renew a domain
|
|
35 |
my $result = $srs->renew_domain( 'example.com' );
|
|
36 |
...
|
|
37 |
|
|
38 |
=head1 Notes
|
|
39 |
|
|
40 |
=head2 Prerequisites
|
|
41 |
|
|
42 |
This module requires some setup in the OpenSRS reseller environment
|
|
43 |
before it will work correctly.
|
|
44 |
|
|
45 |
=over 4
|
|
46 |
|
|
47 |
=item Reseller account
|
|
48 |
|
|
49 |
You need to have an OpenSRS account, of course. If you aren't an
|
|
50 |
OpenSRS reseller, this module will be of limited use to you. :)
|
|
51 |
|
|
52 |
=item Script API network access
|
|
53 |
|
|
54 |
The machine(s) using this module need to have their public IP addresses
|
|
55 |
added to your 'Script API allow' list in the OpenSRS web interface.
|
|
56 |
(You'll only need to do this once, assuming your IP doesn't change.)
|
|
57 |
|
|
58 |
=item API key generation
|
|
59 |
|
|
60 |
You'll need to pregenerate your API keys - also in the the OpenSRS web
|
|
61 |
interface. These keys are used for all reseller API authentication.
|
|
62 |
|
|
63 |
=back
|
|
64 |
|
|
65 |
=head2 Assumptions
|
|
66 |
|
|
67 |
OpenSRS allows for a variety of ways to organize your domains. Because
|
|
68 |
of this, writing a 'one size fits all' module is rather difficult.
|
|
69 |
Instead, we make a few assumptions regarding the way people use their
|
|
70 |
OpenSRS reseller accounts.
|
|
71 |
|
|
72 |
**** These assumptions will ultimately determine if this module is right for
|
|
73 |
you! Please read them carefully! ****
|
|
74 |
|
|
75 |
=over 4
|
|
76 |
|
|
77 |
=item Management 'master' account.
|
|
78 |
|
|
79 |
We assume that all domains are under one global management owner
|
|
80 |
account. If customers want access to the management interface, we're
|
|
81 |
operating under the idea that you create subaccounts for them -
|
|
82 |
retainting the master account information for your own use. (If you
|
|
83 |
aren't doing this, it really makes things easier for you in the long
|
|
84 |
run.)
|
|
85 |
|
|
86 |
For example, 'spime.net' is my master management account. Before doing
|
|
87 |
any register_domain() calls, I call master_domain('spime.net') - then
|
|
88 |
any transfers or registrations from that point forward are linked to
|
|
89 |
'spime.net'. If a customer wants access to the SRS web management
|
|
90 |
interface, I can then just create a subaccount for just their domain,
|
|
91 |
so I retain absolute control -- in the event a customer forgets their
|
|
92 |
password, I'm covered.
|
|
93 |
|
|
94 |
=item Usernames
|
|
95 |
|
|
96 |
We assume that your management username 'master' account is identical to
|
|
97 |
your reseller username, and just the passwords differ.
|
|
98 |
|
|
99 |
=item Default registration info
|
|
100 |
|
|
101 |
We assume you've properly set up default technical contact information,
|
|
102 |
including your default nameservers, in the OpenSRS reseller web
|
|
103 |
interface.
|
|
104 |
|
|
105 |
=item Return codes
|
|
106 |
|
|
107 |
Unless otherwise noted, all methods return true on success, false on
|
|
108 |
failure, and undefined on caller error.
|
|
109 |
|
|
110 |
=back
|
|
111 |
|
|
112 |
=head2 Default environment
|
|
113 |
|
|
114 |
This library defaults to the TEST environment. (horizon.) Many API
|
|
115 |
methods don't work in the test environment (SET COOKIE being the most
|
|
116 |
notable example, as any API method relying on a cookie doesn't work
|
|
117 |
either.) Neither does batch processing. Most everything else should be
|
|
118 |
ok. ( See environment() )
|
|
119 |
|
|
120 |
=head2 The '$c' variable
|
|
121 |
|
|
122 |
Many methods require customer information. I leave the method of
|
|
123 |
fetching this information entirely to you. All examples below that show
|
|
124 |
a $c variable expect a hashref (or object) that contain these keys:
|
|
125 |
|
|
126 |
my $c = {
|
|
127 |
firstname => 'John',
|
|
128 |
lastname => 'Doe',
|
|
129 |
city => 'Portland',
|
|
130 |
state => 'Oregon',
|
|
131 |
country => 'US',
|
|
132 |
address => '555 Someplace Street',
|
|
133 |
email => 'john@example.com',
|
|
134 |
phone => '503-555-1212',
|
|
135 |
company => 'n/a'
|
|
136 |
};
|
|
137 |
|
|
138 |
=cut
|
|
139 |
|
|
140 |
package Net::OpenSRS;
|
|
141 |
|
|
142 |
use strict;
|
|
143 |
use warnings;
|
|
144 |
use LWP::UserAgent;
|
|
145 |
use XML::Simple;
|
|
146 |
use Digest::MD5;
|
|
147 |
use Date::Calc qw/ Add_Delta_Days Today This_Year /;
|
|
148 |
|
|
149 |
our $VERSION = '0.02';
|
|
150 |
my $rv;
|
|
151 |
*hash = \&Digest::MD5::md5_hex;
|
|
152 |
|
|
153 |
#----------------------------------------------------------------------
|
|
154 |
# utility methods
|
|
155 |
#----------------------------------------------------------------------
|
|
156 |
|
|
157 |
=head1 Utility methods
|
|
158 |
|
|
159 |
=over 4
|
|
160 |
|
|
161 |
=item new()
|
|
162 |
|
|
163 |
my $srs = Net::OpenSRS->new();
|
|
164 |
|
|
165 |
Create a new Net::OpenSRS object. There are no options for this
|
|
166 |
method.
|
|
167 |
|
|
168 |
=cut
|
|
169 |
|
|
170 |
sub new
|
|
171 |
{
|
|
172 |
my ($class, %opts) = @_;
|
|
173 |
my $self = {};
|
|
174 |
bless $self, $class;
|
|
175 |
|
|
176 |
$self->{config} = {
|
|
177 |
use_test_env => 1,
|
|
178 |
debug => 0,
|
|
179 |
master_domain => undef,
|
|
180 |
|
|
181 |
bulkhost => 'https://batch.opensrs.net:55443',
|
|
182 |
|
|
183 |
# reseller auth keys, as generated via the reseller website.
|
|
184 |
live => {
|
|
185 |
key => undef,
|
|
186 |
host => 'https://rr-n1-tor.opensrs.net:55443',
|
|
187 |
},
|
|
188 |
test => {
|
|
189 |
key => undef,
|
|
190 |
host => 'https://horizon.opensrs.net:55443',
|
|
191 |
}
|
|
192 |
};
|
|
193 |
|
|
194 |
return $self;
|
|
195 |
}
|
|
196 |
|
|
197 |
sub debug
|
|
198 |
{
|
|
199 |
my $self = shift;
|
|
200 |
return unless $self->debug_level;
|
|
201 |
print STDERR shift() . "\n";
|
|
202 |
}
|
|
203 |
|
|
204 |
=item debug_level()
|
|
205 |
|
|
206 |
Setting the debug level will print various pieces of information to
|
|
207 |
STDERR when connecting to OpenSRS. Use this if something isn't working
|
|
208 |
the way you think it should be.
|
|
209 |
|
|
210 |
=item 0
|
|
211 |
|
|
212 |
Disable debugging.
|
|
213 |
|
|
214 |
=item 1
|
|
215 |
|
|
216 |
Print current environment, host, and HTTP response.
|
|
217 |
|
|
218 |
=item 2
|
|
219 |
|
|
220 |
Add XML request and response to output.
|
|
221 |
|
|
222 |
=item 3
|
|
223 |
|
|
224 |
Add SSL debugging to output.
|
|
225 |
|
|
226 |
Debugging is off by default. When called without an argument, returns
|
|
227 |
the current debug level.
|
|
228 |
|
|
229 |
=cut
|
|
230 |
|
|
231 |
sub debug_level
|
|
232 |
{
|
|
233 |
my ($self, $level) = @_;
|
|
234 |
return $self->{config}->{debug} unless $level;
|
|
235 |
$self->{config}->{debug} = $level;
|
|
236 |
return;
|
|
237 |
}
|
|
238 |
|
|
239 |
=item last_response()
|
|
240 |
|
|
241 |
All Net::OpenSRS methods set the last OpenSRS API reply in a temporary
|
|
242 |
variable. You can view the contents of this variable using the
|
|
243 |
last_response() method.
|
|
244 |
|
|
245 |
Note that it is reset on each method call.
|
|
246 |
|
|
247 |
Returns the last OpenSRS return code and result string, or if passed any
|
|
248 |
true value, instead returns the full XML (parsed into a hashref) of the
|
|
249 |
last OpenSRS return. (perfect for Data::Dumper)
|
|
250 |
|
|
251 |
Examples:
|
|
252 |
200: Command Successful
|
|
253 |
400: Domain example.com does not exist with OpenSRS
|
|
254 |
|
|
255 |
=cut
|
|
256 |
|
|
257 |
sub last_response
|
|
258 |
{
|
|
259 |
my ($self, $obj) = @_;
|
|
260 |
return $obj ? $rv : $self->{last_response} || '';
|
|
261 |
}
|
|
262 |
|
|
263 |
=item set_manage_auth()
|
|
264 |
|
|
265 |
$srs->set_manage_auth( $username, $password );
|
|
266 |
|
|
267 |
Set the owner management username and password. This is used to fetch
|
|
268 |
cookies, and perform any API methods that require the management cookie.
|
|
269 |
For specifics on this, see the OpenSRS API documentation.
|
|
270 |
|
|
271 |
=cut
|
|
272 |
|
|
273 |
sub set_manage_auth
|
|
274 |
{
|
|
275 |
my ($self, $user, $pass) = @_;
|
|
276 |
return undef unless $user && $pass;
|
|
277 |
$self->{config}->{username} = $user;
|
|
278 |
$self->{config}->{password} = $pass;
|
|
279 |
return 1;
|
|
280 |
}
|
|
281 |
|
|
282 |
=item set_key()
|
|
283 |
|
|
284 |
Tell the OpenSRS object what secret key to use for authentication.
|
|
285 |
You can generate a new secret key by using the OpenSRS reseller web
|
|
286 |
interface. This key is required to perform any API functions.
|
|
287 |
|
|
288 |
set_key() is affected by the current environment(). Calling the
|
|
289 |
set_key() method while in the test environment only sets the key for the
|
|
290 |
test environment - likewise for the live environment. To set a key for
|
|
291 |
the live environment, you need to call environment('live') B<first>.
|
|
292 |
|
|
293 |
=cut
|
|
294 |
|
|
295 |
sub set_key
|
|
296 |
{
|
|
297 |
my ($self, $key) = @_;
|
|
298 |
return undef unless $key;
|
|
299 |
$self->{config}->{ $self->environment }->{key} = $key;
|
|
300 |
return 1;
|
|
301 |
}
|
|
302 |
|
|
303 |
=item environment()
|
|
304 |
|
|
305 |
my $env = $srs->environment;
|
|
306 |
$srs->environment('live');
|
|
307 |
|
|
308 |
Without an argument, returns a string - either 'test', or 'live',
|
|
309 |
depending on the environment the object is currently using.
|
|
310 |
|
|
311 |
The test environment is the default.
|
|
312 |
|
|
313 |
If passed an argument (either 'test' or 'live') - switches into the
|
|
314 |
desired environment. You will need to set_key() if you were previously
|
|
315 |
using a different environment, or if you hadn't set_key() yet.
|
|
316 |
|
|
317 |
=cut
|
|
318 |
|
|
319 |
sub environment
|
|
320 |
{
|
|
321 |
my ($self, $env) = @_;
|
|
322 |
return ($self->{config}->{use_test_env} ? 'test' : 'live')
|
|
323 |
unless $env && $env =~ /(test|live)/i;
|
|
324 |
$self->{config}->{use_test_env} =
|
|
325 |
$1 eq 'test' ? 1 : 0;
|
|
326 |
return;
|
|
327 |
}
|
|
328 |
|
|
329 |
=item master_domain()
|
|
330 |
|
|
331 |
my $master = $srs->master_domain;
|
|
332 |
$srs->master_domain('spime.net');
|
|
333 |
|
|
334 |
Without an argument, returns the currently set 'master domain' account.
|
|
335 |
Otherwise, it sets the master domain.
|
|
336 |
|
|
337 |
New transfers and registrations are linked under this domain, for
|
|
338 |
centralized management. See the 'Assumptions' section, above.
|
|
339 |
|
|
340 |
=cut
|
|
341 |
|
|
342 |
sub master_domain
|
|
343 |
{
|
|
344 |
my ($self, $domain) = @_;
|
|
345 |
return $self->{config}->{master_domain} unless $domain;
|
|
346 |
$self->{config}->{master_domain} = $domain;
|
|
347 |
return;
|
|
348 |
}
|
|
349 |
|
|
350 |
# set last status messages/codes in $self,
|
|
351 |
# for the benefit of the caller.
|
|
352 |
sub _set_response
|
|
353 |
{
|
|
354 |
my $self = shift;
|
|
355 |
$rv->{response_text} =~ s/Error: //;
|
|
356 |
$self->{last_response} = $rv->{response_code} . ": " . $rv->{response_text};
|
|
357 |
return;
|
|
358 |
}
|
|
359 |
|
|
360 |
#----------------------------------------------------------------------
|
|
361 |
# SRS API methods
|
|
362 |
#----------------------------------------------------------------------
|
|
363 |
|
|
364 |
=back
|
|
365 |
|
|
366 |
=head1 OpenSRS API methods
|
|
367 |
|
|
368 |
=over 4
|
|
369 |
|
|
370 |
=item bulk_lock() / bulk_unlock()
|
|
371 |
|
|
372 |
Locks or unlocks up to 1000 domains at a time.
|
|
373 |
|
|
374 |
my $result = $srs->bulk_lock([ 'example.com', 'example.net' ]);
|
|
375 |
|
|
376 |
Returns remote bulk queue id on successful batch submission.
|
|
377 |
|
|
378 |
=cut
|
|
379 |
|
|
380 |
sub bulk_lock
|
|
381 |
{
|
|
382 |
my $self = shift;
|
|
383 |
return $self->_bulk_action( 'lock', @_ );
|
|
384 |
}
|
|
385 |
|
|
386 |
sub bulk_unlock
|
|
387 |
{
|
|
388 |
my $self = shift;
|
|
389 |
return $self->_bulk_action( 'unlock', @_ );
|
|
390 |
}
|
|
391 |
|
|
392 |
sub _bulk_action
|
|
393 |
{
|
|
394 |
my ( $self, $toggle, $domains ) = @_;
|
|
395 |
return undef unless $toggle =~ /lock|unlock/i &&
|
|
396 |
ref $domains;
|
|
397 |
return undef if scalar @$domains >= 1000;
|
|
398 |
|
|
399 |
$rv = $self->make_request(
|
|
400 |
{
|
|
401 |
batch => 1,
|
|
402 |
action => 'submit',
|
|
403 |
object => 'bulk_change',
|
|
404 |
attributes => {
|
|
405 |
change_type => 'domain_lock',
|
|
406 |
change_items => $domains,
|
|
407 |
op_type => lc $toggle,
|
|
408 |
}
|
|
409 |
}
|
|
410 |
);
|
|
411 |
return undef unless $rv;
|
|
412 |
|
|
413 |
$self->_set_response;
|
|
414 |
return $rv->{is_success} ? $rv->{bulk_change_req_id} : 0;
|
|
415 |
}
|
|
416 |
|
|
417 |
=item check_queued_request()
|
|
418 |
|
|
419 |
my $result = $srs->check_queued_request( $queue_id );
|
|
420 |
|
|
421 |
Requires queue id - returned from batch methods such as bulk_lock().
|
|
422 |
Always returns hashref of queue command on success.
|
|
423 |
Check $srs->last_response() for status progress.
|
|
424 |
|
|
425 |
=cut
|
|
426 |
|
|
427 |
sub check_queued_request
|
|
428 |
{
|
|
429 |
my ( $self, $id ) = @_;
|
|
430 |
return undef unless $id;
|
|
431 |
|
|
432 |
$rv = $self->make_request(
|
|
433 |
{
|
|
434 |
action => 'query_queued_request',
|
|
435 |
object => 'domain',
|
|
436 |
attributes => {
|
|
437 |
request_id => $id,
|
|
438 |
}
|
|
439 |
}
|
|
440 |
);
|
|
441 |
return undef unless $rv;
|
|
442 |
|
|
443 |
$self->_set_response;
|
|
444 |
return $rv->{attributes}->{request_data};
|
|
445 |
}
|
|
446 |
|
|
447 |
=item check_transfer()
|
|
448 |
|
|
449 |
my $result = $srs->check_transfer( 'example.com' );
|
|
450 |
|
|
451 |
Checks the status of a transfer in progress. Returns hashref of
|
|
452 |
'contact_email', 'status', and 'last_update_time' for a given domain
|
|
453 |
transfer. The 'status' key is always one of the following:
|
|
454 |
|
|
455 |
pending_owner (waiting on owner confirmation)
|
|
456 |
pending_admin (waiting on opensrs staff confirmation)
|
|
457 |
pending_registry (waiting on register to complete)
|
|
458 |
completed (transfer done)
|
|
459 |
cancelled (reseller cancelled transfer in progress)
|
|
460 |
undefined (no transfer in progress)
|
|
461 |
|
|
462 |
If the domain in question has no transfer in progress - instead checks
|
|
463 |
to see if the domain is capable of transfer. Returns hashref of
|
|
464 |
'transferrable' (boolean) and 'reason' (string).
|
|
465 |
|
|
466 |
=cut
|
|
467 |
|
|
468 |
sub check_transfer
|
|
469 |
{
|
|
470 |
my ( $self, $domain ) = @_;
|
|
471 |
return undef unless $domain;
|
|
472 |
|
|
473 |
$rv = $self->make_request(
|
|
474 |
{
|
|
475 |
action => 'check_transfer',
|
|
476 |
object => 'domain',
|
|
477 |
attributes => {
|
|
478 |
domain => $domain,
|
|
479 |
get_request_address => 1,
|
|
480 |
}
|
|
481 |
}
|
|
482 |
);
|
|
483 |
return undef unless $rv;
|
|
484 |
|
|
485 |
$self->_set_response;
|
|
486 |
if ( $rv->{attributes}->{status} ) {
|
|
487 |
return {
|
|
488 |
status => $rv->{attributes}->{status},
|
|
489 |
last_update_time => $rv->{attributes}->{unixtime},
|
|
490 |
contact_email => $rv->{attributes}->{request_address}
|
|
491 |
};
|
|
492 |
}
|
|
493 |
else {
|
|
494 |
return $rv->{attributes}; #(transferrable bool and reason)
|
|
495 |
}
|
|
496 |
}
|
|
497 |
|
|
498 |
=item get_cookie()
|
|
499 |
|
|
500 |
OpenSRS management APIs require a cookie to be generated, and sent along
|
|
501 |
with the API request.
|
|
502 |
|
|
503 |
$cookie = $srs->get_cookie( 'example.com ');
|
|
504 |
($cookie, $expiration_date) = $srs->get_cookie( 'example.com ');
|
|
505 |
|
|
506 |
Make sure you've set_manage_auth() before attempting any cookie required
|
|
507 |
APIs.
|
|
508 |
|
|
509 |
Returns cookie on success, undefined on error. (Check error with
|
|
510 |
last_response())
|
|
511 |
|
|
512 |
In array context, returns cookie and expiration date of the domain.
|
|
513 |
|
|
514 |
=cut
|
|
515 |
|
|
516 |
sub get_cookie
|
|
517 |
{
|
|
518 |
my ($self, $domain) = @_;
|
|
519 |
return undef unless $domain;
|
|
520 |
$rv = $self->make_request(
|
|
521 |
{
|
|
522 |
action => 'set',
|
|
523 |
object => 'cookie',
|
|
524 |
attributes => {
|
|
525 |
reg_username => $self->{config}->{username},
|
|
526 |
reg_password => $self->{config}->{password},
|
|
527 |
domain => $domain
|
|
528 |
}
|
|
529 |
}
|
|
530 |
);
|
|
531 |
return undef unless $rv;
|
|
532 |
|
|
533 |
$self->_set_response;
|
|
534 |
if ($rv->{is_success}) {
|
|
535 |
return
|
|
536 |
wantarray
|
|
537 |
? ( $rv->{attributes}->{cookie}, $rv->{attributes}->{expiredate} )
|
|
538 |
: $rv->{attributes}->{cookie};
|
|
539 |
}
|
|
540 |
return undef;
|
|
541 |
}
|
|
542 |
|
|
543 |
=item get_expiring_domains()
|
|
544 |
|
|
545 |
my $results = $srs->get_expiring_domains( 60 );
|
|
546 |
|
|
547 |
Fetch and return OpenSRS hashref of expiring domains, within
|
|
548 |
the specified timeperiod. (In days.)
|
|
549 |
|
|
550 |
Time period defaults to 30 days.
|
|
551 |
|
|
552 |
=cut
|
|
553 |
|
|
554 |
sub get_expiring_domains
|
|
555 |
{
|
|
556 |
my ($self, $timeframe) = @_;
|
|
557 |
$timeframe ||= 30;
|
|
558 |
|
|
559 |
my $today = join '-', map { sprintf( "%02d", $_ ) } Date::Calc::Today();
|
|
560 |
my $expdate = join '-', map { sprintf( "%02d", $_ ) }
|
|
561 |
Date::Calc::Add_Delta_Days( ( split '-', $today ), $timeframe );
|
|
562 |
|
|
563 |
$rv = $self->make_request(
|
|
564 |
{
|
|
565 |
action => 'get_domains_by_expiredate',
|
|
566 |
object => 'domain',
|
|
567 |
attributes => {
|
|
568 |
limit => 1000,
|
|
569 |
exp_from => $today,
|
|
570 |
exp_to => $expdate,
|
|
571 |
}
|
|
572 |
}
|
|
573 |
);
|
|
574 |
return undef unless $rv;
|
|
575 |
|
|
576 |
$self->_set_response;
|
|
577 |
return $rv->{attributes}->{exp_domains} if $rv->{is_success};
|
|
578 |
return undef;
|
|
579 |
}
|
|
580 |
|
|
581 |
=item is_available()
|
|
582 |
|
|
583 |
Hey OpenSRS! Is this domain registered, or is it available?
|
|
584 |
|
|
585 |
my $result = $srs->is_available( 'example.com ');
|
|
586 |
|
|
587 |
Returns true if the domain is available, false if it is already
|
|
588 |
registered.
|
|
589 |
|
|
590 |
=cut
|
|
591 |
|
|
592 |
sub is_available
|
|
593 |
{
|
|
594 |
my ($self, $domain) = @_;
|
|
595 |
return undef unless $domain;
|
|
596 |
$rv = $self->make_request(
|
|
597 |
{
|
|
598 |
action => 'lookup',
|
|
599 |
object => 'domain',
|
|
600 |
attributes => {
|
|
601 |
domain => $domain
|
|
602 |
}
|
|
603 |
}
|
|
604 |
);
|
|
605 |
return undef unless $rv;
|
|
606 |
$self->_set_response;
|
|
607 |
return undef unless $rv->{is_success};
|
|
608 |
return $rv->{response_code} == 210 ? 1 : 0;
|
|
609 |
}
|
|
610 |
|
|
611 |
=item register_domain()
|
|
612 |
|
|
613 |
my $result = $srs->register_domain( 'example.com', $c );
|
|
614 |
|
|
615 |
Register a new domain. Default nameserver and tech info used from
|
|
616 |
OpenSRS settings.
|
|
617 |
|
|
618 |
=cut
|
|
619 |
|
|
620 |
sub register_domain
|
|
621 |
{
|
|
622 |
my ($self, $domain, $c, $transfer) = @_;
|
|
623 |
return undef unless $domain;
|
|
624 |
|
|
625 |
# sanity checks
|
|
626 |
unless ($self->{config}->{username}) {
|
|
627 |
$self->debug("Management auth not set.");
|
|
628 |
return undef;
|
|
629 |
}
|
|
630 |
unless (ref $c) {
|
|
631 |
$self->debug("2nd arg must be a reference to customer info.");
|
|
632 |
return undef;
|
|
633 |
}
|
|
634 |
|
|
635 |
my $epp_phone = $c->{phone};
|
|
636 |
$epp_phone =~ s/[\.\-]//g;
|
|
637 |
$epp_phone = '+1.' . $epp_phone;
|
|
638 |
|
|
639 |
# blah, this sucks.
|
|
640 |
# it would be really nice if OpenSRS figured out the country -> code
|
|
641 |
# conversion on their end of things.
|
|
642 |
my %country_codes = (
|
|
643 |
'Afghanistan' => 'AF',
|
|
644 |
'Albania' => 'AL',
|
|
645 |
'Algeria' => 'DZ',
|
|
646 |
'American Samoa' => 'AS',
|
|
647 |
'Andorra' => 'AD',
|
|
648 |
'Angola' => 'AO',
|
|
649 |
'Anguilla' => 'AI',
|
|
650 |
'Antarctica' => 'AQ',
|
|
651 |
'Antigua And Barbuda' => 'AG',
|
|
652 |
'Argentina' => 'AR',
|
|
653 |
'Armenia' => 'AM',
|
|
654 |
'Aruba' => 'AW',
|
|
655 |
'Australia' => 'AU',
|
|
656 |
'Austria' => 'AT',
|
|
657 |
'Azerbaijan' => 'AZ',
|
|
658 |
'Bahamas' => 'BS',
|
|
659 |
'Bahrain' => 'BH',
|
|
660 |
'Bangladesh' => 'BD',
|
|
661 |
'Barbados' => 'BB',
|
|
662 |
'Belarus' => 'BY',
|
|
663 |
'Belgium' => 'BE',
|
|
664 |
'Belize' => 'BZ',
|
|
665 |
'Benin' => 'BJ',
|
|
666 |
'Bermuda' => 'BM',
|
|
667 |
'Bhutan' => 'BT',
|
|
668 |
'Bolivia' => 'BO',
|
|
669 |
'Bosnia Hercegovina' => 'BA',
|
|
670 |
'Botswana' => 'BW',
|
|
671 |
'Bouvet Island' => 'BV',
|
|
672 |
'Brazil' => 'BR',
|
|
673 |
'British Indian Ocean Territory' => 'IO',
|
|
674 |
'Brunei Darussalam' => 'BN',
|
|
675 |
'Bulgaria' => 'BG',
|
|
676 |
'Burkina Faso' => 'BF',
|
|
677 |
'Burundi' => 'BI',
|
|
678 |
'Cambodia' => 'KH',
|
|
679 |
'Cameroon' => 'CM',
|
|
680 |
'Canada' => 'CA',
|
|
681 |
'Cape Verde' => 'CV',
|
|
682 |
'Cayman Islands' => 'KY',
|
|
683 |
'Central African Republic' => 'CF',
|
|
684 |
'Chad' => 'TD',
|
|
685 |
'Chile' => 'CL',
|
|
686 |
'China' => 'CN',
|
|
687 |
'Christmas Island' => 'CX',
|
|
688 |
'Cocos (Keeling) Islands' => 'CC',
|
|
689 |
'Colombia' => 'CO',
|
|
690 |
'Comoros' => 'KM',
|
|
691 |
'Congo' => 'CG',
|
|
692 |
'Congo The Democratic Republic Of' => 'CD',
|
|
693 |
'Cook Islands' => 'CK',
|
|
694 |
'Costa Rica' => 'CR',
|
|
695 |
'Cote D\'Ivoire' => 'CI',
|
|
696 |
'Croatia' => 'HR',
|
|
697 |
'Cuba' => 'CU',
|
|
698 |
'Cyprus' => 'CY',
|
|
699 |
'Czech Republic' => 'CZ',
|
|
700 |
'Denmark' => 'DK',
|
|
701 |
'Djibouti' => 'DJ',
|
|
702 |
'Dominica' => 'DM',
|
|
703 |
'Dominican Republic' => 'DO',
|
|
704 |
'Ecuador' => 'EC',
|
|
705 |
'Egypt' => 'EG',
|
|
706 |
'El Salvador' => 'SV',
|
|
707 |
'Equatorial Guinea' => 'GQ',
|
|
708 |
'Eritrea' => 'ER',
|
|
709 |
'Estonia' => 'EE',
|
|
710 |
'Ethiopia' => 'ET',
|
|
711 |
'Falkland Islands (Malvinas)' => 'FK',
|
|
712 |
'Faroe Islands' => 'FO',
|
|
713 |
'Fiji' => 'FJ',
|
|
714 |
'Finland' => 'FI',
|
|
715 |
'France' => 'FR',
|
|
716 |
'French Guiana' => 'GF',
|
|
717 |
'French Polynesia' => 'PF',
|
|
718 |
'French Southern Territories' => 'TF',
|
|
719 |
'Gabon' => 'GA',
|
|
720 |
'Gambia' => 'GM',
|
|
721 |
'Georgia' => 'GE',
|
|
722 |
'Germany' => 'DE',
|
|
723 |
'Ghana' => 'GH',
|
|
724 |
'Gibraltar' => 'GI',
|
|
725 |
'Greece' => 'GR',
|
|
726 |
'Greenland' => 'GL',
|
|
727 |
'Grenada' => 'GD',
|
|
728 |
'Guadeloupe' => 'GP',
|
|
729 |
'Guam' => 'GU',
|
|
730 |
'Guatemela' => 'GT',
|
|
731 |
'Guinea' => 'GN',
|
|
732 |
'Guinea-Bissau' => 'GW',
|
|
733 |
'Guyana' => 'GY',
|
|
734 |
'Haiti' => 'HT',
|
|
735 |
'Heard and McDonald Islands' => 'HM',
|
|
736 |
'Honduras' => 'HN',
|
|
737 |
'Hong Kong' => 'HK',
|
|
738 |
'Hungary' => 'HU',
|
|
739 |
'Iceland' => 'IS',
|
|
740 |
'India' => 'IN',
|
|
741 |
'Indonesia' => 'ID',
|
|
742 |
'Iran (Islamic Republic Of)' => 'IR',
|
|
743 |
'Iraq' => 'IQ',
|
|
744 |
'Ireland' => 'IE',
|
|
745 |
'Israel' => 'IL',
|
|
746 |
'Italy' => 'IT',
|
|
747 |
'Jamaica' => 'JM',
|
|
748 |
'Japan' => 'JP',
|
|
749 |
'Jordan' => 'JO',
|
|
750 |
'Kazakhstan' => 'KZ',
|
|
751 |
'Kenya' => 'KE',
|
|
752 |
'Kiribati' => 'KI',
|
|
753 |
'Korea, Democratic People\'s Republic Of' => 'KP',
|
|
754 |
'Korea, Republic Of' => 'KR',
|
|
755 |
'Kuwait' => 'KW',
|
|
756 |
'Kyrgyzstan' => 'KG',
|
|
757 |
'Lao People\'s Democratic Republic' => 'LA',
|
|
758 |
'Latvia' => 'LV',
|
|
759 |
'Lebanon' => 'LB',
|
|
760 |
'Lesotho' => 'LS',
|
|
761 |
'Liberia' => 'LR',
|
|
762 |
'Libyan Arab Jamahiriya' => 'LY',
|
|
763 |
'Liechtenstein' => 'LI',
|
|
764 |
'Lithuania' => 'LT',
|
|
765 |
'Luxembourg' => 'LU',
|
|
766 |
'Macau' => 'MO',
|
|
767 |
'Macedonia' => 'MK',
|
|
768 |
'Madagascar' => 'MG',
|
|
769 |
'Malawi' => 'MW',
|
|
770 |
'Malaysia' => 'MY',
|
|
771 |
'Maldives' => 'MV',
|
|
772 |
'Mali' => 'ML',
|
|
773 |
'Malta' => 'MT',
|
|
774 |
'Marshall Islands' => 'MH',
|
|
775 |
'Martinique' => 'MQ',
|
|
776 |
'Mauritania' => 'MR',
|
|
777 |
'Mauritius' => 'MU',
|
|
778 |
'Mayotte' => 'YT',
|
|
779 |
'Mexico' => 'MX',
|
|
780 |
'Micronesia, Federated States Of' => 'FM',
|
|
781 |
'Moldova, Republic Of' => 'MD',
|
|
782 |
'Monaco' => 'MC',
|
|
783 |
'Mongolia' => 'MN',
|
|
784 |
'Montserrat' => 'MS',
|
|
785 |
'Morocco' => 'MA',
|
|
786 |
'Mozambique' => 'MZ',
|
|
787 |
'Myanmar' => 'MM',
|
|
788 |
'Namibia' => 'NA',
|
|
789 |
'Nauru' => 'NR',
|
|
790 |
'Nepal' => 'NP',
|
|
791 |
'Netherlands' => 'NL',
|
|
792 |
'Netherlands Antilles' => 'AN',
|
|
793 |
'New Caledonia' => 'NC',
|
|
794 |
'New Zealand' => 'NZ',
|
|
795 |
'Nicaragua' => 'NI',
|
|
796 |
'Niger' => 'NE',
|
|
797 |
'Nigeria' => 'NG',
|
|
798 |
'Niue' => 'NU',
|
|
799 |
'Norfolk Island' => 'NF',
|
|
800 |
'Northern Mariana Islands' => 'MP',
|
|
801 |
'Norway' => 'NO',
|
|
802 |
'Oman' => 'OM',
|
|
803 |
'Pakistan' => 'PK',
|
|
804 |
'Palau' => 'PW',
|
|
805 |
'Palestine' => 'PS',
|
|
806 |
'Panama' => 'PA',
|
|
807 |
'Papua New Guinea' => 'PG',
|
|
808 |
'Paraguay' => 'PY',
|
|
809 |
'Peru' => 'PE',
|
|
810 |
'Philippines' => 'PH',
|
|
811 |
'Pitcairn' => 'PN',
|
|
812 |
'Poland' => 'PL',
|
|
813 |
'Portugal' => 'PT',
|
|
814 |
'Puerto Rico' => 'PR',
|
|
815 |
'Qatar' => 'QA',
|
|
816 |
'Reunion' => 'RE',
|
|
817 |
'Romania' => 'RO',
|
|
818 |
'Russian Federation' => 'RU',
|
|
819 |
'Rwanda' => 'RW',
|
|
820 |
'Saint Helena' => 'SH',
|
|
821 |
'Saint Kitts And Nevis' => 'KN',
|
|
822 |
'Saint Lucia' => 'LC',
|
|
823 |
'Saint Pierre and Miquelon' => 'PM',
|
|
824 |
'Saint Vincent and The Grenadines' => 'VC',
|
|
825 |
'Samoa' => 'WS',
|
|
826 |
'San Marino' => 'SM',
|
|
827 |
'Sao Tome and Principe' => 'ST',
|
|
828 |
'Saudi Arabia' => 'SA',
|
|
829 |
'Senegal' => 'SN',
|
|
830 |
'Serbia and Montenegro' => 'CS',
|
|
831 |
'Seychelles' => 'SC',
|
|
832 |
'Sierra Leone' => 'SL',
|
|
833 |
'Singapore' => 'SG',
|
|
834 |
'Slovakia' => 'SK',
|
|
835 |
'Slovenia' => 'SI',
|
|
836 |
'Solomon Islands' => 'SB',
|
|
837 |
'Somalia' => 'SO',
|
|
838 |
'South Africa' => 'ZA',
|
|
839 |
'South Georgia and The Sandwich Islands' => 'GS',
|
|
840 |
'Spain' => 'ES',
|
|
841 |
'Sri Lanka' => 'LK',
|
|
842 |
'Sudan' => 'SD',
|
|
843 |
'Suriname' => 'SR',
|
|
844 |
'Svalbard and Jan Mayen Islands' => 'SJ',
|
|
845 |
'Swaziland' => 'SZ',
|
|
846 |
'Sweden' => 'SE',
|
|
847 |
'Switzerland' => 'CH',
|
|
848 |
'Syrian Arab Republic' => 'SY',
|
|
849 |
'Taiwan' => 'TW',
|
|
850 |
'Tajikista' => 'TJ',
|
|
851 |
'Tanzania, United Republic Of' => 'TZ',
|
|
852 |
'Thailand' => 'TH',
|
|
853 |
'Timor-Leste' => 'TL',
|
|
854 |
'Togo' => 'TG',
|
|
855 |
'Tokelau' => 'TK',
|
|
856 |
'Tonga' => 'TO',
|
|
857 |
'Trinidad and Tobago' => 'TT',
|
|
858 |
'Tunisia' => 'TN',
|
|
859 |
'Turkey' => 'TR',
|
|
860 |
'Turkmenistan' => 'TM',
|
|
861 |
'Turks and Caicos Islands' => 'TC',
|
|
862 |
'Tuvalu' => 'TV',
|
|
863 |
'Uganda' => 'UG',
|
|
864 |
'Ukraine' => 'UA',
|
|
865 |
'United Arab Emirates' => 'AE',
|
|
866 |
'United Kingdom (GB)' => 'GB',
|
|
867 |
'United Kingdom (UK)' => 'UK',
|
|
868 |
'United States' => 'US',
|
|
869 |
'United States Minor Outlying Islands' => 'UM',
|
|
870 |
'Uruguay' => 'UY',
|
|
871 |
'Uzbekistan' => 'UZ',
|
|
872 |
'Vanuatu' => 'VU',
|
|
873 |
'Vatican City State' => 'VA',
|
|
874 |
'Venezuela' => 'VE',
|
|
875 |
'Vietnam' => 'VN',
|
|
876 |
'Virgin Islands (British)' => 'VG',
|
|
877 |
'Virgin Islands (U.S.)' => 'VI',
|
|
878 |
'Wallis and Futuna Islands' => 'WF',
|
|
879 |
'Western Sahara' => 'EH',
|
|
880 |
'Yemen Republic of' => 'YE',
|
|
881 |
'Zambia' => 'ZM',
|
|
882 |
'Zimbabwe' => 'ZW'
|
|
883 |
); # end suckage
|
|
884 |
|
|
885 |
# attempt countryname translation if needed
|
|
886 |
if ( $c->{country} !~ m/^[A-Z]{2,3}$/ ) {
|
|
887 |
$c->{country} = $country_codes{$c->{country}};
|
|
888 |
|
|
889 |
unless ( defined( $c->{country} ) ) {
|
|
890 |
$self->debug("Invalid country.");
|
|
891 |
return undef;
|
|
892 |
}
|
|
893 |
}
|
|
894 |
|
|
895 |
# build contact hashref from customer info.
|
|
896 |
my $contact_info = {
|
|
897 |
first_name => $c->{firstname},
|
|
898 |
last_name => $c->{lastname},
|
|
899 |
city => $c->{city},
|
|
900 |
state => $c->{state},
|
|
901 |
country => $c->{country},
|
|
902 |
address1 => $c->{address},
|
|
903 |
postal_code => $c->{zip},
|
|
904 |
email => $c->{email},
|
|
905 |
phone => $epp_phone,
|
|
906 |
org_name => $c->{company} || 'n/a',
|
|
907 |
};
|
|
908 |
|
|
909 |
$rv = $self->make_request(
|
|
910 |
{
|
|
911 |
action => 'sw_register',
|
|
912 |
object => 'domain',
|
|
913 |
attributes => {
|
|
914 |
domain => $domain,
|
|
915 |
custom_nameservers => 0,
|
|
916 |
custom_tech_contact => 0,
|
|
917 |
auto_renew => 0,
|
|
918 |
period => 1,
|
|
919 |
f_lock_domain => 1,
|
|
920 |
contact_set => {
|
|
921 |
admin => $contact_info,
|
|
922 |
billing => $contact_info,
|
|
923 |
owner => $contact_info
|
|
924 |
},
|
|
925 |
reg_username => $self->{config}->{username},
|
|
926 |
reg_password => $self->{config}->{password},
|
|
927 |
reg_type => $transfer ? 'transfer' : 'new',
|
|
928 |
reg_domain => $self->{config}->{master_domain}, # link domain to the 'master' account
|
|
929 |
}
|
|
930 |
}
|
|
931 |
);
|
|
932 |
$self->_set_response;
|
|
933 |
return $rv->{is_success};
|
|
934 |
}
|
|
935 |
|
|
936 |
=item renew_domain()
|
|
937 |
|
|
938 |
my $result = $srs->renew_domain( 'example.com', 1 );
|
|
939 |
|
|
940 |
Renew a domain for a period of time in years. 1 year is the default.
|
|
941 |
|
|
942 |
=cut
|
|
943 |
|
|
944 |
sub renew_domain
|
|
945 |
{
|
|
946 |
my ($self, $domain, $years) = @_;
|
|
947 |
return undef unless $domain;
|
|
948 |
$years ||= 1;
|
|
949 |
|
|
950 |
# sanity checks
|
|
951 |
unless ($self->{config}->{username}) {
|
|
952 |
$self->debug("Management auth not set.");
|
|
953 |
return undef;
|
|
954 |
}
|
|
955 |
|
|
956 |
# get current expiration year (why do they need this, again?)
|
|
957 |
my (undef, $expiration) = $self->get_cookie( $domain );
|
|
958 |
$expiration = $1 if $expiration =~ /^(\d{4})-/;
|
|
959 |
$expiration ||= Date::Calc::This_Year();
|
|
960 |
|
|
961 |
$rv = $self->make_request(
|
|
962 |
{
|
|
963 |
action => 'renew',
|
|
964 |
object => 'domain',
|
|
965 |
attributes => {
|
|
966 |
domain => $domain,
|
|
967 |
auto_renew => 0,
|
|
968 |
handle => 'process',
|
|
969 |
period => $years,
|
|
970 |
currentexpirationyear => $expiration,
|
|
971 |
}
|
|
972 |
}
|
|
973 |
);
|
|
974 |
$self->_set_response;
|
|
975 |
return $rv->{is_success};
|
|
976 |
}
|
|
977 |
|
|
978 |
=item revoke_domain()
|
|
979 |
|
|
980 |
Revoke a previously registered domain. This only works if the domain is
|
|
981 |
still within the grace period as defined by the registrar.
|
|
982 |
Requires you to have called set_manage_auth() B<first>.
|
|
983 |
|
|
984 |
my $result = $srs->revoke_domain( 'example.com' );
|
|
985 |
|
|
986 |
Returns true if the revoke is successful, false otherwise.
|
|
987 |
Returns undefined on error.
|
|
988 |
|
|
989 |
=cut
|
|
990 |
|
|
991 |
sub revoke_domain
|
|
992 |
{
|
|
993 |
my ($self, $domain) = @_;
|
|
994 |
return undef unless $domain;
|
|
995 |
unless ($self->{config}->{username}) {
|
|
996 |
$self->debug("Management auth not set.");
|
|
997 |
return undef;
|
|
998 |
}
|
|
999 |
$rv = $self->make_request(
|
|
1000 |
{
|
|
1001 |
action => 'revoke',
|
|
1002 |
object => 'domain',
|
|
1003 |
attributes => {
|
|
1004 |
reseller => $self->{config}->{username},
|
|
1005 |
domain => $domain,
|
|
1006 |
}
|
|
1007 |
}
|
|
1008 |
);
|
|
1009 |
$self->_set_response;
|
|
1010 |
return $rv->{is_success};
|
|
1011 |
}
|
|
1012 |
|
|
1013 |
=item transfer_domain()
|
|
1014 |
|
|
1015 |
my $result = $srs->transfer_domain( 'example.com', $c );
|
|
1016 |
|
|
1017 |
Transfer a domain under your control.
|
|
1018 |
Returns true on success, false on failure, and undefined on caller error.
|
|
1019 |
|
|
1020 |
=cut
|
|
1021 |
|
|
1022 |
sub transfer_domain
|
|
1023 |
{
|
|
1024 |
my $self = shift;
|
|
1025 |
return $self->register_domain( @_, 1 );
|
|
1026 |
}
|
|
1027 |
|
|
1028 |
=item make_request()
|
|
1029 |
|
|
1030 |
This method is the real workhorse of this module. If any OpenSRS API
|
|
1031 |
isn't explicity implemented in this module as a method call (such as
|
|
1032 |
get_cookie(), bulk_lock(), etc), you can use make_request() to build and send
|
|
1033 |
the API yourself.
|
|
1034 |
|
|
1035 |
Examples:
|
|
1036 |
|
|
1037 |
my $result = $srs->make_request(
|
|
1038 |
{
|
|
1039 |
batch => 1,
|
|
1040 |
action => 'submit',
|
|
1041 |
object => 'bulk_change',
|
|
1042 |
attributes => {
|
|
1043 |
change_type => 'domain_lock',
|
|
1044 |
change_items => [ 'example.com', 'example.net' ],
|
|
1045 |
op_type => 'lock',
|
|
1046 |
}
|
|
1047 |
}
|
|
1048 |
);
|
|
1049 |
|
|
1050 |
my $result = $srs->make_request(
|
|
1051 |
{
|
|
1052 |
action => 'lookup',
|
|
1053 |
object => 'domain',
|
|
1054 |
attributes => {
|
|
1055 |
domain => 'example.com'
|
|
1056 |
}
|
|
1057 |
}
|
|
1058 |
);
|
|
1059 |
|
|
1060 |
Returns a hashref containing parsed XML results from OpenSRS.
|
|
1061 |
|
|
1062 |
Example return:
|
|
1063 |
|
|
1064 |
{
|
|
1065 |
'protocol' => 'XCP',
|
|
1066 |
'object' => 'DOMAIN',
|
|
1067 |
'response_text' => 'Domain taken',
|
|
1068 |
'action' => 'REPLY',
|
|
1069 |
'response_code' => '211',
|
|
1070 |
'attributes' => {
|
|
1071 |
'status' => 'taken',
|
|
1072 |
'match' => {}
|
|
1073 |
},
|
|
1074 |
'is_success' => '1'
|
|
1075 |
}
|
|
1076 |
|
|
1077 |
=cut
|
|
1078 |
|
|
1079 |
# build opensrs xml protocol string. submit.
|
|
1080 |
# convert xml response to data structure, and return.
|
|
1081 |
sub make_request
|
|
1082 |
{
|
|
1083 |
my ($self, $data) = @_;
|
|
1084 |
return undef unless ref $data;
|
|
1085 |
|
|
1086 |
$self->debug("Using " . $self->environment . " environment.");
|
|
1087 |
|
|
1088 |
my $key = $self->{config}->{ $self->environment }->{key};
|
|
1089 |
my $host = $self->{config}->{ $self->environment }->{host};
|
|
1090 |
$ENV{HTTPS_DEBUG} = 1 if $self->debug_level > 2;
|
|
1091 |
|
|
1092 |
unless ($key) {
|
|
1093 |
$self->debug("Authentication key not set.");
|
|
1094 |
return undef;
|
|
1095 |
}
|
|
1096 |
|
|
1097 |
my $action = uc $data->{action};
|
|
1098 |
my $object = uc $data->{object};
|
|
1099 |
|
|
1100 |
# build our XML request.
|
|
1101 |
# lets not bother with anything super fancy,
|
|
1102 |
# everything but the item keys are always static anyway.
|
|
1103 |
my $xml;
|
|
1104 |
$xml = <<XML;
|
|
1105 |
<?xml version='1.0' encoding="UTF-8" standalone="no" ?>
|
|
1106 |
<!DOCTYPE OPS_envelope SYSTEM "ops.dtd">
|
|
1107 |
<OPS_envelope>
|
|
1108 |
<header><version>0.9</version></header>
|
|
1109 |
<body>
|
|
1110 |
<data_block>
|
|
1111 |
<dt_assoc>
|
|
1112 |
<item key="protocol">XCP</item>
|
|
1113 |
<item key="action">$action</item>
|
|
1114 |
<item key="object">$object</item>
|
|
1115 |
XML
|
|
1116 |
|
|
1117 |
$xml .= " <item key=\"cookie\">$data->{cookie}</item>\n" if $data->{cookie};
|
|
1118 |
|
|
1119 |
$xml .= <<XML;
|
|
1120 |
<item key="attributes">
|
|
1121 |
<dt_assoc>
|
|
1122 |
XML
|
|
1123 |
|
|
1124 |
foreach (sort keys %{ $data->{attributes} }) {
|
|
1125 |
my $val = $data->{attributes}->{$_};
|
|
1126 |
$xml .= $self->_format( $val, 4 );
|
|
1127 |
}
|
|
1128 |
$xml .= <<XML;
|
|
1129 |
</dt_assoc>
|
|
1130 |
</item>
|
|
1131 |
</dt_assoc>
|
|
1132 |
</data_block>
|
|
1133 |
</body>
|
|
1134 |
</OPS_envelope>
|
|
1135 |
XML
|
|
1136 |
|
|
1137 |
# whoof, ok. got our request built. lets ship it off.
|
|
1138 |
if ($self->debug_level > 1) {
|
|
1139 |
$self->debug("\nClient Request XML:\n" . '-' x 30);
|
|
1140 |
$self->debug($xml);
|
|
1141 |
}
|
|
1142 |
|
|
1143 |
$host = $self->{config}->{bulkhost} if $data->{batch};
|
|
1144 |
$self->debug("Making request to $host...");
|
|
1145 |
my $ua = LWP::UserAgent->new( timeout => 20, agent => "Net::OpenSRS/$VERSION" );
|
|
1146 |
unless ($ua) {
|
|
1147 |
$self->debug("Unable to contact remote host.");
|
|
1148 |
return undef;
|
|
1149 |
}
|
|
1150 |
|
|
1151 |
my $res = $ua->post(
|
|
1152 |
$host,
|
|
1153 |
'Content-Type' => 'text/xml',
|
|
1154 |
'X-Username' => $self->{config}->{username},
|
|
1155 |
'X-Signature' => hash( hash( $xml, $key ), $key ),
|
|
1156 |
'Content' => $xml
|
|
1157 |
);
|
|
1158 |
|
|
1159 |
my $struct;
|
|
1160 |
if ( $res->is_success ) {
|
|
1161 |
$self->debug("HTTP result: " . $res->status_line);
|
|
1162 |
eval { $struct = XML::Simple::XMLin( $res->content); };
|
|
1163 |
|
|
1164 |
if ($self->debug_level > 1) {
|
|
1165 |
$self->debug("\nOpenSRS Response XML:\n" . '-' x 30);
|
|
1166 |
$self->debug($res->content);
|
|
1167 |
$self->debug('');
|
|
1168 |
}
|
|
1169 |
|
|
1170 |
# get the struct looking just how we want it.
|
|
1171 |
# (de-nastify it.)
|
|
1172 |
$xml = XML::Simple::XMLout( $struct->{body}->{data_block}->{dt_assoc}->{item} );
|
|
1173 |
$struct = XML::Simple::XMLin( $xml );
|
|
1174 |
$xml = XML::Simple::XMLout( $struct->{attributes}->{item} );
|
|
1175 |
$struct->{attributes} = XML::Simple::XMLin( $xml );
|
|
1176 |
}
|
|
1177 |
else {
|
|
1178 |
$self->debug("HTTP error: " . $res->status_line);
|
|
1179 |
return undef;
|
|
1180 |
}
|
|
1181 |
|
|
1182 |
$rv = $struct;
|
|
1183 |
$self->_set_response;
|
|
1184 |
return $self->last_response(1);
|
|
1185 |
}
|
|
1186 |
|
|
1187 |
# format perl structs into opensrs XML
|
|
1188 |
sub _format
|
|
1189 |
{
|
|
1190 |
my ($self, $val, $indent) = @_;
|
|
1191 |
my $xml;
|
|
1192 |
|
|
1193 |
$indent ||= 6;
|
|
1194 |
my $sp = ' ' x $indent;
|
|
1195 |
|
|
1196 |
if ( ref $val eq 'ARRAY' ) {
|
|
1197 |
my $c = 0;
|
|
1198 |
$xml .= "$sp<item key=\"$_\">\n";
|
|
1199 |
$xml .= "$sp <dt_array>\n";
|
|
1200 |
foreach (sort @$val) {
|
|
1201 |
$xml .= "$sp <item key=\"$c\">$_</item>\n";
|
|
1202 |
$c++;
|
|
1203 |
}
|
|
1204 |
$xml .= "$sp </dt_array>\n";
|
|
1205 |
$xml .= "$sp</item>\n";
|
|
1206 |
}
|
|
1207 |
|
|
1208 |
elsif ( ref $val eq 'HASH' ) {
|
|
1209 |
$xml .= "$sp<item key=\"$_\">\n";
|
|
1210 |
$xml .= "$sp<dt_assoc>\n";
|
|
1211 |
foreach (sort keys %$val) {
|
|
1212 |
$xml .= $self->_format( $val->{$_} );
|
|
1213 |
}
|
|
1214 |
$xml .= "$sp</dt_assoc>\n";
|
|
1215 |
$xml .= "$sp</item>\n";
|
|
1216 |
}
|
|
1217 |
|
|
1218 |
else {
|
|
1219 |
$xml .= "$sp<item key=\"$_\">$val</item>\n";
|
|
1220 |
}
|
|
1221 |
|
|
1222 |
return $xml;
|
|
1223 |
}
|
|
1224 |
|
|
1225 |
=back
|
|
1226 |
|
|
1227 |
=head1 Author
|
|
1228 |
|
|
1229 |
Mahlon E. Smith I<mahlon@martini.nu> for Spime Solutions Group
|
|
1230 |
I<(www.spime.net)>
|
|
1231 |
|
|
1232 |
=cut
|
|
1233 |
|
|
1234 |
1;
|