Reorganizing for Trac. perl-modules tip
authormahlon
Fri, 05 Sep 2008 05:15:48 +0000
branchperl-modules
changeset 0 315eb12b224b
Reorganizing for Trac.
bsd-jail-object/Changes
bsd-jail-object/MANIFEST
bsd-jail-object/META.yml
bsd-jail-object/Makefile.PL
bsd-jail-object/Object.pm
bsd-jail-object/README
bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/Makefile
bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/distinfo
bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/pkg-descr
bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/pkg-plist
bsd-jail-object/t/BSD-Jail-Object.t
net-opensrs/bad_XML
net-opensrs/trunk/Changes
net-opensrs/trunk/MANIFEST
net-opensrs/trunk/META.yml
net-opensrs/trunk/Makefile
net-opensrs/trunk/Makefile.PL
net-opensrs/trunk/README
net-opensrs/trunk/lib/Net/OpenSRS.pm
net-opensrs/trunk/old_changelog
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/Changes	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,6 @@
+0.01 Thu Aug  4 2006
+    - Initial release to CPAN.  Yay.
+
+0.02 Thu Aug  7 2006
+    - Check for root on create() and attach()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/MANIFEST	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,7 @@
+Changes
+Makefile.PL
+MANIFEST
+README
+t/BSD-Jail-Object.t
+Object.pm
+META.yml                                 Module meta-data (added by MakeMaker)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/META.yml	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,11 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         BSD-Jail-Object
+version:      0.01
+version_from: Object.pm
+installdirs:  site
+requires:
+    Inline:                        0.44
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/Makefile.PL	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,8 @@
+use 5.008006;
+use Inline::MakeMaker;
+WriteMakefile(
+    NAME           => 'BSD::Jail::Object',
+    VERSION_FROM   => 'Object.pm',
+    PREREQ_PM      => {},
+    AUTHOR         => 'Mahlon E. Smith <mahlon@martini.nu>',
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/Object.pm	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,412 @@
+
+package BSD::Jail::Object;
+use strict;
+use warnings;
+use vars qw/ @ISA @EXPORT_OK /;
+use Exporter;
+
+our $VERSION = '0.02';
+@ISA         = qw/ Exporter /;
+@EXPORT_OK   = qw/ jids /;
+
+use Inline C       => 'DATA',
+           NAME    => 'BSD::Jail::Object',
+           VERSION => '0.02';
+
+sub new
+{
+    my ( $class, $opts ) = @_;
+
+    my $self = {};
+    bless $self, $class;
+    return $self unless $opts;
+
+    if ( ref $opts eq 'HASH' ) {
+
+        # create a new jail
+
+        if ( $< ) {
+            $@ = "jail() requires root";
+            return;
+        }
+
+        unless ( $opts->{'path'}     &&
+                 $opts->{'hostname'} &&
+                 $opts->{'ip'} ) {
+            $@ = "Missing arguments to create() - need 'path', 'hostname', and 'ip'";
+            return;
+        }
+
+        my $jid = _create( $opts->{'path'}, $opts->{'hostname'}, $opts->{'ip'} )
+            or return;
+
+        $self->{'_data'} = [
+            $jid, $opts->{'ip'}, $opts->{'hostname'}, $opts->{'path'}
+        ];
+    
+        return $self;
+    }
+    else {
+
+        # this object should be linked to an existing jail
+        return $self->_init( $opts );
+
+    }
+}
+
+sub _init
+{
+    my $self = shift;
+    my $key  = shift;
+
+    return unless $key;
+
+    my ( @data, $type );
+    if ( $key =~ /^\d+$/ ) {
+        $type = 'jid';
+        @data = _find_jail( 0, $key );
+    }
+    elsif ( $key =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
+        $type = 'ip';
+        @data = _find_jail( 1, $key );
+    }
+    else {
+        $type = 'hostname';
+        @data = _find_jail( 2, $key );
+    }
+
+    unless ( scalar @data ) {
+        $@ = "No such jail $type: $key";
+        return;
+    }
+
+    $self->{'_data'} = \@data;
+    return $self;
+}
+
+sub jid       { shift()->{'_data'}->[0] }
+sub ip        { shift()->{'_data'}->[1] }
+sub hostname  { shift()->{'_data'}->[2] }
+sub path      { shift()->{'_data'}->[3] }
+
+sub attach
+{
+    my $self = shift;
+    return unless $self->jid;
+
+    if ( $< ) {
+        $@ = "jail_attach() requires root";
+        return;
+    }
+
+    return _attach( $self->jid );
+}
+
+sub jids
+{
+    return if ref $_[0]; # shouldn't be used as an object method
+
+    my %opts = @_;
+
+    my @jids = _find_jids();
+    return @jids unless $opts{'instantiate'};
+
+    map { $_ = __PACKAGE__->new( $_ ) } @jids;
+    return @jids;
+}
+
+1;
+
+__DATA__
+
+=pod
+
+=head1 DESCRIPTION
+
+This is an object oriented wrapper around the FreeBSD jail subsystem.
+
+A 5.x or higher FreeBSD system is required.
+
+=head1 SYNOPSIS
+
+Here is an exact replica of the 'jls' utility in just a few lines of perl:
+
+ use BSD::Jail::Object 'jids';
+
+ print "   JID  IP Address      Hostname                      Path\n";
+ printf "%6d  %-15.15s %-29.29s %.74s\n",
+        $_->jid, $_->ip, $_->hostname, $_->path foreach jids( instantiate => 1 );
+
+And here's 'jexec' (actually, a jexec that lets you optionally select by
+something other than jid):
+
+ my $j = BSD::Jail::Object->new( $ARGV[0] ) or die $@;
+ $j->attach && chdir('/') && exec $ARGV[1] or exit;
+
+=head1 EXAMPLES
+
+=over 4
+
+=item B<Create a new jail>
+
+ $options = {
+     path     => '/tmp',
+     ip       => '127.0.0.1',
+     hostname => 'example.com'
+ };
+
+ $j = BSD::Jail::Object->new( $options ) or die $@;
+
+=item B<Attach to an existing jail>
+
+ $j = BSD::Jail::Object->new( 'example.com' );
+ $j->attach;
+
+=item B<Do something in all jails>
+
+ foreach $j ( jids(instantiate => 1) ) {
+
+     if ( fork ) {
+         $j->attach;
+
+         #
+         # do something exciting
+         #
+
+         exit;
+     }
+ }
+
+=item B<Get information on a jail>
+
+(See the B<SYNOPSIS> section above)
+
+=back
+
+=head1 OBJECT METHODS
+
+=head2 new()
+
+Instantiate a new BSD::Jail::Object object, either by associating
+ourselves with an already running jail, or by creating a new one from
+scratch.
+
+To associate with an already active jail, I<new()> accepts a jid,
+hostname, or ip address.  Errors are placed into $@.
+
+ # existing jail, find by jid
+ $j = BSD::Jail::Object->new( 23 ) or die $@;
+
+ # existing jail, find by hostname
+ $j = BSD::Jail::Object->new( 'example.com' ) or die $@;
+
+ # existing jail, find by ip address
+ $j = BSD::Jail::Object->new( '127.0.0.1' ) or die $@;
+
+Note that if you're selecting a jail by hostname or IP, those aren't
+always unique values.  Two jails could be running with the same hostname
+or IP address - this module will always select the highest numbered jid
+in that case.  If you need to be sure you're in the 'right' jail when
+there are duplicates, select by JID.
+
+Create a new jail by passing a hash reference.  Required keys are
+'hostname', 'ip', and 'path'.  See the I<jail(8)> man page for specifics
+on these keys.
+
+ # create a new jail under /tmp
+ $j = BSD::Jail::Object->new({
+        hostname => 'example.com',
+        ip       => '127.0.0.1',
+        path     => '/tmp'
+ }) or die $@;
+
+=head2 jid()
+
+Get the current jail identifier.  JIDs are assigned sequentially from
+the kernel.
+
+=head2 hostname()
+
+Get the current jail hostname.
+
+=head2 path()
+
+Get the root path the jail was bound to.
+
+=head2 attach()
+
+Imprison ourselves within a jail.  Note that this generally requires
+root access, and is a one way operation.  Once the script process
+is imprisioned, there is no way to perform a jailbreak!  You'd need
+to I<fork()> if you intended to attach to more than one jail.  See
+I<EXAMPLES>.
+
+=head1 EXPORTABLE METHODS
+
+=head2 jids()
+
+Returns an array of active JIDs.  Can also return them as
+pre-instantiated objects by passing 'instantiate => 1' as an argument.
+
+ my @jail_jids    = jids();
+ my @jail_objects = jids( instantiate => 1 );
+
+Only exported upon request.
+
+=head1 ACKNOWLEDGEMENTS
+
+Most of the jail specific C code was based on work 
+by Mike Barcroft <mike@freebsd.org> and Poul-Henning Kamp <phk@freebsd.org>
+for the FreeBSD Project.
+
+=head1 AUTHOR
+
+Mahlon E. Smith I<mahlon@martini.nu> for Spime Solutions Group
+I<(www.spime.net)>
+
+=cut
+
+__C__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <sys/param.h>
+#include <sys/jail.h>
+
+size_t
+sysctl_len()
+{
+    size_t len;
+    if ( sysctlbyname( "security.jail.list", NULL, &len, NULL, 0 ) == -1 ) return 0;
+
+    return len;
+}
+
+// get jail structure from kernel
+struct xprison
+*get_xp()
+{
+    struct xprison *sxp, *xp;
+    size_t len;
+
+    len = sysctl_len();
+    if ( len <= 0 ) return NULL;
+
+    sxp = xp = malloc(len);
+    if ( sxp == NULL ) return NULL;
+
+    // populate the xprison list
+    if ( sysctlbyname( "security.jail.list", xp, &len, NULL, 0 ) == -1 ) {
+        if (errno == ENOMEM) {
+            free( sxp );
+            return NULL;
+        }
+        return NULL;
+    }
+
+    // check if kernel and userland is in sync
+    if ( len < sizeof(*xp) || len % sizeof(*xp) ||
+            xp->pr_version != XPRISON_VERSION ) {
+        warn("%s", "Kernel out of sync with userland");
+        return NULL;
+    }
+
+    free( sxp );
+    return xp;
+}
+
+// fetch a specific jail's information
+void
+_find_jail( int compare, char *string )
+{ 
+    struct xprison *xp;
+    struct in_addr in;
+    size_t i, len;
+    Inline_Stack_Vars;
+
+    Inline_Stack_Reset;
+    xp  = get_xp();
+    len = sysctl_len();
+
+    /*
+       compare == 0    jid
+       compare == 1    ip address
+       compare == 2    hostname
+    */
+
+    for (i = 0; i < len / sizeof(*xp); i++) {
+        in.s_addr = ntohl(xp->pr_ip);
+        if (
+                ( compare == 0 && xp->pr_id == atoi(string) )
+                ||
+                ( compare == 1 && strcmp( string, inet_ntoa(in) ) == 0 )
+                ||
+                ( compare == 2 && strcmp( string, xp->pr_host ) == 0 )
+           ) {
+            Inline_Stack_Push( sv_2mortal( newSViv( xp->pr_id ) ));
+            Inline_Stack_Push( sv_2mortal( newSVpvf( inet_ntoa(in) ) ));
+            Inline_Stack_Push( sv_2mortal( newSVpvf( xp->pr_host ) ));
+            Inline_Stack_Push( sv_2mortal( newSVpvf( xp->pr_path ) ));
+            break;
+        }
+        else {
+            xp++;
+        }
+    }
+
+    Inline_Stack_Done;
+}
+
+// return an array of all current jail ids
+void
+_find_jids()
+{ 
+    struct xprison *xp;
+    size_t i, len;
+    Inline_Stack_Vars;
+
+    Inline_Stack_Reset;
+    xp  = get_xp();
+    len = sysctl_len();
+
+    for (i = 0; i < len / sizeof(*xp); i++) {
+        Inline_Stack_Push( sv_2mortal( newSViv( xp->pr_id ) ));
+        xp++;
+    }
+
+    Inline_Stack_Done;
+}
+
+// attach to a jail
+int
+_attach( int jid )
+{
+    return ( jail_attach(jid) == -1 ? 0 : 1 );
+}
+
+// create a new jail
+int
+_create( char *path, char *hostname, char *ipaddr )
+{
+    struct in_addr ip;
+    struct jail    j;
+    int            jid;
+
+    if ( inet_aton( ipaddr, &ip ) == 0 ) return 0;
+    
+    j.path      = path;
+    j.hostname  = hostname;
+    j.ip_number = ntohl( ip.s_addr );
+    j.version   = 0;
+
+    if ( (jid = jail( &j )) == -1 ) return 0;
+
+    return jid;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/README	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,46 @@
+BSD-Jail-Object
+
+DESCRIPTION
+-----------
+
+This is an object oriented environment for FreeBSD jails.
+
+
+
+DEPENDENCIES
+------------
+
+This module requires these other modules and libraries:
+
+    The FreeBSD operating system, version 5.x or above
+
+
+
+INSTALLATION
+------------
+
+To install this module, run the following commands:
+
+    perl Makefile.PL
+    make
+    make test
+    make install
+
+
+
+SUPPORT AND DOCUMENTATION
+-------------------------
+
+After installing, you can find documentation for this module with the perldoc command.
+
+    perldoc BSD::Jail::Object
+
+
+
+COPYRIGHT AND LICENCE
+---------------------
+
+Copyright (C) 2006 Mahlon E. Smith
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/Makefile	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,36 @@
+# New ports collection makefile for:	sysutils/p5-BSD-Jail-Object
+# Date created:		2006-08-07
+# Whom:			Mahlon E. Smith <mahlon@martini.nu>
+#
+# $FreeBSD$
+#
+
+PORTNAME=	BSD-Jail-Object
+PORTVERSION=	0.02
+PORTREVISION=	1
+CATEGORIES=	sysutils perl5
+MASTER_SITES=	${MASTER_SITE_PERL_CPAN}
+MASTER_SITE_SUBDIR= ../../authors/id/M/MA/MAHLON
+PKGNAMEPREFIX=	p5-
+
+MAINTAINER=	mahlon@martini.nu
+COMMENT=	An object oriented perl interface to jail(2)
+
+BUILD_DEPENDS=\                                                                                                                           
+        ${SITE_PERL}/Inline.pm:${PORTSDIR}/devel/p5-Inline                                                                                
+                                                                                                                                          
+PERL_CONFIGURE= yes                                                                                                                       
+                                                                                                                                          
+MAN3=           BSD::Jail::Object.3                                                                                                       
+                                                                                                                                          
+.include <bsd.port.pre.mk>                                                                                                                
+                                                                                                                                          
+.if ${PERL_LEVEL} < 500806                                                                                                                
+IGNORE=         requires perl 5.8.6 or later. Install lang/perl5.8 then try again                                                         
+.endif                                                                                                                                    
+                                                                                                                                          
+.if ${OSVERSION} < 503100 # ref: jail.h v1.17                                                                                             
+IGNORE=         require xprison structure defined                                                                                         
+.endif                                                                                                                                    
+                                                                                                                                          
+.include <bsd.port.post.mk>         
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/distinfo	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,3 @@
+MD5 (BSD-Jail-Object-0.02.tar.gz) = 7cff670f41dd536b1b0313161e40d252
+SHA256 (BSD-Jail-Object-0.02.tar.gz) = 9ae2cdd9164e3d8737ffbe63b9655e64aeb74b596ca523ca54379ac80fd3b7f1
+SIZE (BSD-Jail-Object-0.02.tar.gz) = 4695
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/pkg-descr	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,18 @@
+This is an object oriented perl interface to the FreeBSD jail subsystem.
+
+Here's a replica of the 'jls' utility in just a few lines of perl:
+
+  use BSD::Jail::Object 'jids';
+
+  print "   JID  IP Address      Hostname                      Path\n";
+  printf "%6d  %-15.15s %-29.29s %.74s\n",
+      $_->jid, $_->ip, $_->hostname, $_->path foreach jids( instantiate => 1 );
+
+And here's 'jexec':
+
+  my $j = BSD::Jail::Object->new( $ARGV[0] ) or die $@;
+  $j->attach && chdir('/') && exec $ARGV[1] or exit;
+
+For more info please use 'perldoc' on the module.
+
+Author:	Mahlon E. Smith <mahlon@martini.nu>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/freebsd-port/p5-BSD-Jail-Object/pkg-plist	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,7 @@
+%%SITE_PERL%%/%%PERL_ARCH%%/BSD/Jail/Object.pm
+%%SITE_PERL%%/%%PERL_ARCH%%/auto/BSD/Jail/Object/.packlist
+%%SITE_PERL%%/%%PERL_ARCH%%/auto/BSD/Jail/Object/Object.bs
+%%SITE_PERL%%/%%PERL_ARCH%%/auto/BSD/Jail/Object/Object.so
+@dirrm %%SITE_PERL%%/%%PERL_ARCH%%/auto/BSD/Jail/Object
+@dirrmtry %%SITE_PERL%%/%%PERL_ARCH%%/auto/BSD/Jail/Object
+@dirrmtry %%SITE_PERL%%/%%PERL_ARCH%%/BSD/Jail/Object
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bsd-jail-object/t/BSD-Jail-Object.t	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,52 @@
+
+use Test::More;
+
+BEGIN
+{
+    if ( $^O !~ /^freebsd$/i ) {
+        plan skip_all => 'Module only usable under FreeBSD operating system';
+    }
+    else {
+        if ( $< ) {
+            plan skip_all => 'Must be root to run tests';
+        }
+        else {
+            plan tests => 7;
+            use_ok('BSD::Jail::Object');
+        }
+    }
+}
+
+# jail identifier we'll do our tests on
+my $jid;
+
+# all methods, public and private
+can_ok( 'BSD::Jail::Object', qw/
+    new _init 
+    jid ip hostname path
+    attach jids
+
+    sysctl_len _find_jail
+    _find_jids _attach _create
+/);
+
+# create
+my $j = BSD::Jail::Object->new({
+        path => '/tmp',
+        hostname => 'bsd-jail-object-tester',
+        ip => '127.0.0.1'
+});
+
+is( ref $j, 'BSD::Jail::Object', 'Object namespace' );
+is( $j->path, '/tmp', "path verify" );
+is( $j->ip, '127.0.0.1', "ip verify" );
+is( $j->hostname, 'bsd-jail-object-tester', "hostname verify" );
+like( $j->jid, qr/^\d+$/, 'jid is integer (' . $j->jid . ')' );
+
+# can't easily test attach()
+        
+# end of tests
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/bad_XML	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,190 @@
+<?xml version='1.0' encoding="UTF-8" standalone="no" ?>
+<!DOCTYPE OPS_envelope SYSTEM "ops.dtd">
+<OPS_envelope>
+ <header>
+  <version>0.9</version>
+  </header>
+ <body>
+  <data_block>
+   <dt_assoc>
+    <item key="protocol">XCP</item>
+    <item key="object">DOMAIN</item>
+    <item key="response_text">Command successful</item>
+    <item key="action">REPLY</item>
+    <item key="attributes">
+     <dt_assoc>
+      <item key="exp_domains">
+       <dt_array>
+        <item key="0">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">thonstad.org</item>
+          <item key="expiredate">2006-08-25 16:40:46</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="1">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">morelandneighborhooddental.com</item>
+          <item key="expiredate">2006-09-19 23:06:30</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="2">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">theriverfrontlodge.com</item>
+          <item key="expiredate">2006-09-23 13:34:51</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="3">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">balishmd.com</item>
+          <item key="expiredate">2006-09-29 19:54:32</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="4">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">bofzoo.com</item>
+          <item key="expiredate">2006-10-04 00:29:38</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="5">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">massagebylori.net</item>
+          <item key="expiredate">2006-10-18 17:33:35</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="6">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">jamminjj.com</item>
+          <item key="expiredate">2006-10-22 13:56:29</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="7">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">taylorgreymusic.com</item>
+          <item key="expiredate">2006-10-22 16:23:52</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="8">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">decisionpointmanagement.com</item>
+          <item key="expiredate">2006-10-24 13:08:56</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="9">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">morelanddental.com</item>
+          <item key="expiredate">2006-10-25 21:46:46</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="10">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">eve-dev.com</item>
+          <item key="expiredate">2006-10-27 13:15:07</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="11">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">scour.com</item>
+          <item key="expiredate">2006-11-02 00:00:00</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="12">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">statisticool.com</item>
+          <item key="expiredate">2006-11-02 23:56:04</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="13">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">woohaw.com</item>
+          <item key="expiredate">2006-11-08 15:04:10</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="14">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">souchi.com</item>
+          <item key="expiredate">2006-11-17 00:00:00</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="15">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">thelancefamily.com</item>
+          <item key="expiredate">2006-11-28 19:43:46</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="16">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">fat-kid.com</item>
+          <item key="expiredate">2006-11-30 02:23:28</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="17">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">opensystemshospitality.com</item>
+          <item key="expiredate">2006-12-01 19:08:54</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="18">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">joelskool.com</item>
+          <item key="expiredate">2006-12-09 12:59:02</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+        <item key="19">
+         <dt_assoc>
+          <item key="f_let_expire">N</item>
+          <item key="name">hillsboronaturalmedicine.com</item>
+          <item key="expiredate">2006-12-09 18:09:09</item>
+          <item key="f_auto_renew">N</item>
+         </dt_assoc>
+        </item>
+       </dt_array>
+      </item>
+      <item key="page">1</item>
+      <item key="remainder">0</item>
+      <item key="total">20</item>
+     </dt_assoc>
+    </item>
+    <item key="response_code">200</item>
+    <item key="is_success">1</item>
+   </dt_assoc>
+  </data_block>
+ </body>
+</OPS_envelope>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/Changes	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,6 @@
+0.01 Thu Feb 16 2006
+    - Initial release to CPAN.  Yay.
+
+0.02 Sat Apr 8 2006
+    - Allow countries to be two letter codes or the full country name.
+      Contributed by Sten Spans <sten@bit.nl>.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/MANIFEST	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,6 @@
+Changes
+MANIFEST
+META.yml # Will be created by "make dist"
+Makefile.PL
+README
+lib/Net/OpenSRS.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/META.yml	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,15 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#
+name:         Net-OpenSRS
+version:      0.02
+version_from: lib/Net/OpenSRS.pm
+installdirs:  site
+requires:
+    Date::Calc:                    0
+    Digest::MD5:                   0
+    LWP::UserAgent:                0
+    Test::More:                    0
+    XML::Simple:                   0
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/Makefile	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,814 @@
+# This Makefile is for the Net::OpenSRS extension to perl.
+#
+# It was generated automatically by MakeMaker version
+# 6.30 (Revision: Revision: 4535 ) from the contents of
+# Makefile.PL. Don't edit this file, edit Makefile.PL instead.
+#
+#       ANY CHANGES MADE HERE WILL BE LOST!
+#
+#   MakeMaker ARGV: ()
+#
+#   MakeMaker Parameters:
+
+#     ABSTRACT_FROM => q[lib/Net/OpenSRS.pm]
+#     AUTHOR => q[Mahlon E. Smith <mahlon@martini.nu>]
+#     NAME => q[Net::OpenSRS]
+#     PL_FILES => {  }
+#     PREREQ_PM => { Date::Calc=>q[0], Test::More=>q[0], LWP::UserAgent=>q[0], Digest::MD5=>q[0], XML::Simple=>q[0] }
+#     VERSION_FROM => q[lib/Net/OpenSRS.pm]
+#     clean => { FILES=>q[Net-OpenSRS-*] }
+#     dist => { COMPRESS=>q[gzip -9f], SUFFIX=>q[gz] }
+
+# --- MakeMaker post_initialize section:
+
+
+# --- MakeMaker const_config section:
+
+# These definitions are from config.sh (via /usr/local/lib/perl5/5.8.8/mach/Config.pm)
+
+# They may have been overridden via Makefile.PL or on the command line
+AR = ar
+CC = cc
+CCCDLFLAGS = -DPIC -fPIC
+CCDLFLAGS =   -Wl,-R/usr/local/lib/perl5/5.8.8/mach/CORE
+DLEXT = so
+DLSRC = dl_dlopen.xs
+LD = cc
+LDDLFLAGS = -shared  -L/usr/local/lib
+LDFLAGS =  -Wl,-E -L/usr/local/lib
+LIBC = 
+LIB_EXT = .a
+OBJ_EXT = .o
+OSNAME = freebsd
+OSVERS = 6.1-beta4
+RANLIB = :
+SITELIBEXP = /usr/local/lib/perl5/site_perl/5.8.8
+SITEARCHEXP = /usr/local/lib/perl5/site_perl/5.8.8/mach
+SO = so
+EXE_EXT = 
+FULL_AR = /usr/bin/ar
+VENDORARCHEXP = 
+VENDORLIBEXP = 
+
+
+# --- MakeMaker constants section:
+AR_STATIC_ARGS = cr
+DIRFILESEP = /
+DFSEP = $(DIRFILESEP)
+NAME = Net::OpenSRS
+NAME_SYM = Net_OpenSRS
+VERSION = 0.02
+VERSION_MACRO = VERSION
+VERSION_SYM = 0_02
+DEFINE_VERSION = -D$(VERSION_MACRO)=\"$(VERSION)\"
+XS_VERSION = 0.02
+XS_VERSION_MACRO = XS_VERSION
+XS_DEFINE_VERSION = -D$(XS_VERSION_MACRO)=\"$(XS_VERSION)\"
+INST_ARCHLIB = blib/arch
+INST_SCRIPT = blib/script
+INST_BIN = blib/bin
+INST_LIB = blib/lib
+INST_MAN1DIR = blib/man1
+INST_MAN3DIR = blib/man3
+MAN1EXT = 1
+MAN3EXT = 3
+INSTALLDIRS = site
+DESTDIR = 
+PREFIX = /usr/local
+PERLPREFIX = /usr/local
+SITEPREFIX = /usr/local
+VENDORPREFIX = 
+INSTALLPRIVLIB = /usr/local/lib/perl5/5.8.8
+DESTINSTALLPRIVLIB = $(DESTDIR)$(INSTALLPRIVLIB)
+INSTALLSITELIB = /usr/local/lib/perl5/site_perl/5.8.8
+DESTINSTALLSITELIB = $(DESTDIR)$(INSTALLSITELIB)
+INSTALLVENDORLIB = 
+DESTINSTALLVENDORLIB = $(DESTDIR)$(INSTALLVENDORLIB)
+INSTALLARCHLIB = /usr/local/lib/perl5/5.8.8/mach
+DESTINSTALLARCHLIB = $(DESTDIR)$(INSTALLARCHLIB)
+INSTALLSITEARCH = /usr/local/lib/perl5/site_perl/5.8.8/mach
+DESTINSTALLSITEARCH = $(DESTDIR)$(INSTALLSITEARCH)
+INSTALLVENDORARCH = 
+DESTINSTALLVENDORARCH = $(DESTDIR)$(INSTALLVENDORARCH)
+INSTALLBIN = /usr/local/bin
+DESTINSTALLBIN = $(DESTDIR)$(INSTALLBIN)
+INSTALLSITEBIN = /usr/local/bin
+DESTINSTALLSITEBIN = $(DESTDIR)$(INSTALLSITEBIN)
+INSTALLVENDORBIN = 
+DESTINSTALLVENDORBIN = $(DESTDIR)$(INSTALLVENDORBIN)
+INSTALLSCRIPT = /usr/local/bin
+DESTINSTALLSCRIPT = $(DESTDIR)$(INSTALLSCRIPT)
+INSTALLMAN1DIR = /usr/local/man/man1
+DESTINSTALLMAN1DIR = $(DESTDIR)$(INSTALLMAN1DIR)
+INSTALLSITEMAN1DIR = /usr/local/man/man1
+DESTINSTALLSITEMAN1DIR = $(DESTDIR)$(INSTALLSITEMAN1DIR)
+INSTALLVENDORMAN1DIR = 
+DESTINSTALLVENDORMAN1DIR = $(DESTDIR)$(INSTALLVENDORMAN1DIR)
+INSTALLMAN3DIR = /usr/local/lib/perl5/5.8.8/perl/man/man3
+DESTINSTALLMAN3DIR = $(DESTDIR)$(INSTALLMAN3DIR)
+INSTALLSITEMAN3DIR = /usr/local/lib/perl5/5.8.8/man/man3
+DESTINSTALLSITEMAN3DIR = $(DESTDIR)$(INSTALLSITEMAN3DIR)
+INSTALLVENDORMAN3DIR = 
+DESTINSTALLVENDORMAN3DIR = $(DESTDIR)$(INSTALLVENDORMAN3DIR)
+PERL_LIB = /usr/local/lib/perl5/5.8.8
+PERL_ARCHLIB = /usr/local/lib/perl5/5.8.8/mach
+LIBPERL_A = libperl.a
+FIRST_MAKEFILE = Makefile
+MAKEFILE_OLD = Makefile.old
+MAKE_APERL_FILE = Makefile.aperl
+PERLMAINCC = $(CC)
+PERL_INC = /usr/local/lib/perl5/5.8.8/mach/CORE
+PERL = /usr/local/bin/perl
+FULLPERL = /usr/local/bin/perl
+ABSPERL = $(PERL)
+PERLRUN = $(PERL)
+FULLPERLRUN = $(FULLPERL)
+ABSPERLRUN = $(ABSPERL)
+PERLRUNINST = $(PERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
+FULLPERLRUNINST = $(FULLPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
+ABSPERLRUNINST = $(ABSPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
+PERL_CORE = 0
+PERM_RW = 644
+PERM_RWX = 755
+
+MAKEMAKER   = /usr/local/lib/perl5/5.8.8/ExtUtils/MakeMaker.pm
+MM_VERSION  = 6.30
+MM_REVISION = Revision: 4535 
+
+# FULLEXT = Pathname for extension directory (eg Foo/Bar/Oracle).
+# BASEEXT = Basename part of FULLEXT. May be just equal FULLEXT. (eg Oracle)
+# PARENT_NAME = NAME without BASEEXT and no trailing :: (eg Foo::Bar)
+# DLBASE  = Basename part of dynamic library. May be just equal BASEEXT.
+FULLEXT = Net/OpenSRS
+BASEEXT = OpenSRS
+PARENT_NAME = Net
+DLBASE = $(BASEEXT)
+VERSION_FROM = lib/Net/OpenSRS.pm
+OBJECT = 
+LDFROM = $(OBJECT)
+LINKTYPE = dynamic
+BOOTDEP = 
+
+# Handy lists of source code files:
+XS_FILES = 
+C_FILES  = 
+O_FILES  = 
+H_FILES  = 
+MAN1PODS = 
+MAN3PODS = lib/Net/OpenSRS.pm
+
+# Where is the Config information that we are using/depend on
+CONFIGDEP = $(PERL_ARCHLIB)$(DFSEP)Config.pm $(PERL_INC)$(DFSEP)config.h
+
+# Where to build things
+INST_LIBDIR      = $(INST_LIB)/Net
+INST_ARCHLIBDIR  = $(INST_ARCHLIB)/Net
+
+INST_AUTODIR     = $(INST_LIB)/auto/$(FULLEXT)
+INST_ARCHAUTODIR = $(INST_ARCHLIB)/auto/$(FULLEXT)
+
+INST_STATIC      = 
+INST_DYNAMIC     = 
+INST_BOOT        = 
+
+# Extra linker info
+EXPORT_LIST        = 
+PERL_ARCHIVE       = 
+PERL_ARCHIVE_AFTER = 
+
+
+TO_INST_PM = lib/.arch-ids/=id \
+	lib/Net/.arch-ids/=id \
+	lib/Net/.arch-ids/OpenSRS.pm.id \
+	lib/Net/OpenSRS.pm
+
+PM_TO_BLIB = lib/Net/.arch-ids/OpenSRS.pm.id \
+	blib/lib/Net/.arch-ids/OpenSRS.pm.id \
+	lib/Net/OpenSRS.pm \
+	blib/lib/Net/OpenSRS.pm \
+	lib/.arch-ids/=id \
+	blib/lib/.arch-ids/=id \
+	lib/Net/.arch-ids/=id \
+	blib/lib/Net/.arch-ids/=id
+
+
+# --- MakeMaker platform_constants section:
+MM_Unix_VERSION = 1.50
+PERL_MALLOC_DEF = -DPERL_EXTMALLOC_DEF -Dmalloc=Perl_malloc -Dfree=Perl_mfree -Drealloc=Perl_realloc -Dcalloc=Perl_calloc
+
+
+# --- MakeMaker tool_autosplit section:
+# Usage: $(AUTOSPLITFILE) FileToSplit AutoDirToSplitInto
+AUTOSPLITFILE = $(ABSPERLRUN)  -e 'use AutoSplit;  autosplit($$ARGV[0], $$ARGV[1], 0, 1, 1)'
+
+
+
+# --- MakeMaker tool_xsubpp section:
+
+
+# --- MakeMaker tools_other section:
+SHELL = /bin/sh
+CHMOD = chmod
+CP = cp
+MV = mv
+NOOP = $(SHELL) -c true
+NOECHO = @
+RM_F = rm -f
+RM_RF = rm -rf
+TEST_F = test -f
+TOUCH = touch
+UMASK_NULL = umask 0
+DEV_NULL = > /dev/null 2>&1
+MKPATH = $(ABSPERLRUN) "-MExtUtils::Command" -e mkpath
+EQUALIZE_TIMESTAMP = $(ABSPERLRUN) "-MExtUtils::Command" -e eqtime
+ECHO = echo
+ECHO_N = echo -n
+UNINST = 0
+VERBINST = 0
+MOD_INSTALL = $(ABSPERLRUN) -MExtUtils::Install -e 'install({@ARGV}, '\''$(VERBINST)'\'', 0, '\''$(UNINST)'\'');'
+DOC_INSTALL = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e perllocal_install
+UNINSTALL = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e uninstall
+WARN_IF_OLD_PACKLIST = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e warn_if_old_packlist
+MACROSTART = 
+MACROEND = 
+USEMAKEFILE = -f
+FIXIN = $(PERLRUN) "-MExtUtils::MY" -e "MY->fixin(shift)"
+
+
+# --- MakeMaker makemakerdflt section:
+makemakerdflt: all
+	$(NOECHO) $(NOOP)
+
+
+# --- MakeMaker dist section:
+TAR = tar
+TARFLAGS = cvf
+ZIP = zip
+ZIPFLAGS = -r
+COMPRESS = gzip -9f
+SUFFIX = gz
+SHAR = shar
+PREOP = $(NOECHO) $(NOOP)
+POSTOP = $(NOECHO) $(NOOP)
+TO_UNIX = $(NOECHO) $(NOOP)
+CI = ci -u
+RCS_LABEL = rcs -Nv$(VERSION_SYM): -q
+DIST_CP = best
+DIST_DEFAULT = tardist
+DISTNAME = Net-OpenSRS
+DISTVNAME = Net-OpenSRS-0.02
+
+
+# --- MakeMaker macro section:
+
+
+# --- MakeMaker depend section:
+
+
+# --- MakeMaker cflags section:
+
+
+# --- MakeMaker const_loadlibs section:
+
+
+# --- MakeMaker const_cccmd section:
+
+
+# --- MakeMaker post_constants section:
+
+
+# --- MakeMaker pasthru section:
+
+PASTHRU = LIBPERL_A="$(LIBPERL_A)"\
+	LINKTYPE="$(LINKTYPE)"\
+	PREFIX="$(PREFIX)"
+
+
+# --- MakeMaker special_targets section:
+.SUFFIXES : .xs .c .C .cpp .i .s .cxx .cc $(OBJ_EXT)
+
+.PHONY: all config static dynamic test linkext manifest blibdirs clean realclean disttest distdir
+
+
+
+# --- MakeMaker c_o section:
+
+
+# --- MakeMaker xs_c section:
+
+
+# --- MakeMaker xs_o section:
+
+
+# --- MakeMaker top_targets section:
+all :: pure_all manifypods
+	$(NOECHO) $(NOOP)
+
+
+pure_all :: config pm_to_blib subdirs linkext
+	$(NOECHO) $(NOOP)
+
+subdirs :: $(MYEXTLIB)
+	$(NOECHO) $(NOOP)
+
+config :: $(FIRST_MAKEFILE) blibdirs
+	$(NOECHO) $(NOOP)
+
+help :
+	perldoc ExtUtils::MakeMaker
+
+
+# --- MakeMaker blibdirs section:
+blibdirs : $(INST_LIBDIR)$(DFSEP).exists $(INST_ARCHLIB)$(DFSEP).exists $(INST_AUTODIR)$(DFSEP).exists $(INST_ARCHAUTODIR)$(DFSEP).exists $(INST_BIN)$(DFSEP).exists $(INST_SCRIPT)$(DFSEP).exists $(INST_MAN1DIR)$(DFSEP).exists $(INST_MAN3DIR)$(DFSEP).exists
+	$(NOECHO) $(NOOP)
+
+# Backwards compat with 6.18 through 6.25
+blibdirs.ts : blibdirs
+	$(NOECHO) $(NOOP)
+
+$(INST_LIBDIR)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_LIBDIR)
+	$(NOECHO) $(CHMOD) 755 $(INST_LIBDIR)
+	$(NOECHO) $(TOUCH) $(INST_LIBDIR)$(DFSEP).exists
+
+$(INST_ARCHLIB)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_ARCHLIB)
+	$(NOECHO) $(CHMOD) 755 $(INST_ARCHLIB)
+	$(NOECHO) $(TOUCH) $(INST_ARCHLIB)$(DFSEP).exists
+
+$(INST_AUTODIR)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_AUTODIR)
+	$(NOECHO) $(CHMOD) 755 $(INST_AUTODIR)
+	$(NOECHO) $(TOUCH) $(INST_AUTODIR)$(DFSEP).exists
+
+$(INST_ARCHAUTODIR)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_ARCHAUTODIR)
+	$(NOECHO) $(CHMOD) 755 $(INST_ARCHAUTODIR)
+	$(NOECHO) $(TOUCH) $(INST_ARCHAUTODIR)$(DFSEP).exists
+
+$(INST_BIN)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_BIN)
+	$(NOECHO) $(CHMOD) 755 $(INST_BIN)
+	$(NOECHO) $(TOUCH) $(INST_BIN)$(DFSEP).exists
+
+$(INST_SCRIPT)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_SCRIPT)
+	$(NOECHO) $(CHMOD) 755 $(INST_SCRIPT)
+	$(NOECHO) $(TOUCH) $(INST_SCRIPT)$(DFSEP).exists
+
+$(INST_MAN1DIR)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_MAN1DIR)
+	$(NOECHO) $(CHMOD) 755 $(INST_MAN1DIR)
+	$(NOECHO) $(TOUCH) $(INST_MAN1DIR)$(DFSEP).exists
+
+$(INST_MAN3DIR)$(DFSEP).exists :: Makefile.PL
+	$(NOECHO) $(MKPATH) $(INST_MAN3DIR)
+	$(NOECHO) $(CHMOD) 755 $(INST_MAN3DIR)
+	$(NOECHO) $(TOUCH) $(INST_MAN3DIR)$(DFSEP).exists
+
+
+
+# --- MakeMaker linkext section:
+
+linkext :: $(LINKTYPE)
+	$(NOECHO) $(NOOP)
+
+
+# --- MakeMaker dlsyms section:
+
+
+# --- MakeMaker dynamic section:
+
+dynamic :: $(FIRST_MAKEFILE) $(INST_DYNAMIC) $(INST_BOOT)
+	$(NOECHO) $(NOOP)
+
+
+# --- MakeMaker dynamic_bs section:
+
+BOOTSTRAP =
+
+
+# --- MakeMaker dynamic_lib section:
+
+
+# --- MakeMaker static section:
+
+## $(INST_PM) has been moved to the all: target.
+## It remains here for awhile to allow for old usage: "make static"
+static :: $(FIRST_MAKEFILE) $(INST_STATIC)
+	$(NOECHO) $(NOOP)
+
+
+# --- MakeMaker static_lib section:
+
+
+# --- MakeMaker manifypods section:
+
+POD2MAN_EXE = $(PERLRUN) "-MExtUtils::Command::MM" -e pod2man "--"
+POD2MAN = $(POD2MAN_EXE)
+
+
+manifypods : pure_all  \
+	lib/Net/OpenSRS.pm \
+	lib/Net/OpenSRS.pm
+	$(NOECHO) $(POD2MAN) --section=3 --perm_rw=$(PERM_RW) \
+	  lib/Net/OpenSRS.pm $(INST_MAN3DIR)/Net::OpenSRS.$(MAN3EXT) 
+
+
+
+
+# --- MakeMaker processPL section:
+
+
+# --- MakeMaker installbin section:
+
+
+# --- MakeMaker subdirs section:
+
+# none
+
+# --- MakeMaker clean_subdirs section:
+clean_subdirs :
+	$(NOECHO) $(NOOP)
+
+
+# --- MakeMaker clean section:
+
+# Delete temporary files but do not touch installed files. We don't delete
+# the Makefile here so a later make realclean still has a makefile to use.
+
+clean :: clean_subdirs
+	- $(RM_F) \
+	  *$(LIB_EXT) core \
+	  core.[0-9] $(INST_ARCHAUTODIR)/extralibs.all \
+	  core.[0-9][0-9] $(BASEEXT).bso \
+	  pm_to_blib.ts core.[0-9][0-9][0-9][0-9] \
+	  $(BASEEXT).x $(BOOTSTRAP) \
+	  perl$(EXE_EXT) tmon.out \
+	  *$(OBJ_EXT) pm_to_blib \
+	  $(INST_ARCHAUTODIR)/extralibs.ld blibdirs.ts \
+	  core.[0-9][0-9][0-9][0-9][0-9] *perl.core \
+	  core.*perl.*.? $(MAKE_APERL_FILE) \
+	  perl $(BASEEXT).def \
+	  core.[0-9][0-9][0-9] mon.out \
+	  lib$(BASEEXT).def perlmain.c \
+	  perl.exe so_locations \
+	  $(BASEEXT).exp 
+	- $(RM_RF) \
+	  Net-OpenSRS-* blib 
+	- $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) $(DEV_NULL)
+
+
+# --- MakeMaker realclean_subdirs section:
+realclean_subdirs :
+	$(NOECHO) $(NOOP)
+
+
+# --- MakeMaker realclean section:
+# Delete temporary files (via clean) and also delete dist files
+realclean purge ::  clean realclean_subdirs
+	- $(RM_F) \
+	  $(MAKEFILE_OLD) $(FIRST_MAKEFILE) 
+	- $(RM_RF) \
+	  $(DISTVNAME) 
+
+
+# --- MakeMaker metafile section:
+metafile : create_distdir
+	$(NOECHO) $(ECHO) Generating META.yml
+	$(NOECHO) $(ECHO) '# http://module-build.sourceforge.net/META-spec.html' > META_new.yml
+	$(NOECHO) $(ECHO) '#XXXXXXX This is a prototype!!!  It will change in the future!!! XXXXX#' >> META_new.yml
+	$(NOECHO) $(ECHO) 'name:         Net-OpenSRS' >> META_new.yml
+	$(NOECHO) $(ECHO) 'version:      0.02' >> META_new.yml
+	$(NOECHO) $(ECHO) 'version_from: lib/Net/OpenSRS.pm' >> META_new.yml
+	$(NOECHO) $(ECHO) 'installdirs:  site' >> META_new.yml
+	$(NOECHO) $(ECHO) 'requires:' >> META_new.yml
+	$(NOECHO) $(ECHO) '    Date::Calc:                    0' >> META_new.yml
+	$(NOECHO) $(ECHO) '    Digest::MD5:                   0' >> META_new.yml
+	$(NOECHO) $(ECHO) '    LWP::UserAgent:                0' >> META_new.yml
+	$(NOECHO) $(ECHO) '    Test::More:                    0' >> META_new.yml
+	$(NOECHO) $(ECHO) '    XML::Simple:                   0' >> META_new.yml
+	$(NOECHO) $(ECHO) '' >> META_new.yml
+	$(NOECHO) $(ECHO) 'distribution_type: module' >> META_new.yml
+	$(NOECHO) $(ECHO) 'generated_by: ExtUtils::MakeMaker version 6.30' >> META_new.yml
+	-$(NOECHO) $(MV) META_new.yml $(DISTVNAME)/META.yml
+
+
+# --- MakeMaker signature section:
+signature :
+	cpansign -s
+
+
+# --- MakeMaker dist_basics section:
+distclean :: realclean distcheck
+	$(NOECHO) $(NOOP)
+
+distcheck :
+	$(PERLRUN) "-MExtUtils::Manifest=fullcheck" -e fullcheck
+
+skipcheck :
+	$(PERLRUN) "-MExtUtils::Manifest=skipcheck" -e skipcheck
+
+manifest :
+	$(PERLRUN) "-MExtUtils::Manifest=mkmanifest" -e mkmanifest
+
+veryclean : realclean
+	$(RM_F) *~ *.orig */*~ */*.orig
+
+
+
+# --- MakeMaker dist_core section:
+
+dist : $(DIST_DEFAULT) $(FIRST_MAKEFILE)
+	$(NOECHO) $(ABSPERLRUN) -l -e 'print '\''Warning: Makefile possibly out of date with $(VERSION_FROM)'\''' \
+	  -e '    if -e '\''$(VERSION_FROM)'\'' and -M '\''$(VERSION_FROM)'\'' < -M '\''$(FIRST_MAKEFILE)'\'';'
+
+tardist : $(DISTVNAME).tar$(SUFFIX)
+	$(NOECHO) $(NOOP)
+
+uutardist : $(DISTVNAME).tar$(SUFFIX)
+	uuencode $(DISTVNAME).tar$(SUFFIX) $(DISTVNAME).tar$(SUFFIX) > $(DISTVNAME).tar$(SUFFIX)_uu
+
+$(DISTVNAME).tar$(SUFFIX) : distdir
+	$(PREOP)
+	$(TO_UNIX)
+	$(TAR) $(TARFLAGS) $(DISTVNAME).tar $(DISTVNAME)
+	$(RM_RF) $(DISTVNAME)
+	$(COMPRESS) $(DISTVNAME).tar
+	$(POSTOP)
+
+zipdist : $(DISTVNAME).zip
+	$(NOECHO) $(NOOP)
+
+$(DISTVNAME).zip : distdir
+	$(PREOP)
+	$(ZIP) $(ZIPFLAGS) $(DISTVNAME).zip $(DISTVNAME)
+	$(RM_RF) $(DISTVNAME)
+	$(POSTOP)
+
+shdist : distdir
+	$(PREOP)
+	$(SHAR) $(DISTVNAME) > $(DISTVNAME).shar
+	$(RM_RF) $(DISTVNAME)
+	$(POSTOP)
+
+
+# --- MakeMaker distdir section:
+create_distdir :
+	$(RM_RF) $(DISTVNAME)
+	$(PERLRUN) "-MExtUtils::Manifest=manicopy,maniread" \
+		-e "manicopy(maniread(),'$(DISTVNAME)', '$(DIST_CP)');"
+
+distdir : create_distdir distmeta 
+	$(NOECHO) $(NOOP)
+
+
+
+# --- MakeMaker dist_test section:
+disttest : distdir
+	cd $(DISTVNAME) && $(ABSPERLRUN) Makefile.PL 
+	cd $(DISTVNAME) && $(MAKE) $(PASTHRU)
+	cd $(DISTVNAME) && $(MAKE) test $(PASTHRU)
+
+
+
+# --- MakeMaker dist_ci section:
+
+ci :
+	$(PERLRUN) "-MExtUtils::Manifest=maniread" \
+	  -e "@all = keys %{ maniread() };" \
+	  -e "print(qq{Executing $(CI) @all\n}); system(qq{$(CI) @all});" \
+	  -e "print(qq{Executing $(RCS_LABEL) ...\n}); system(qq{$(RCS_LABEL) @all});"
+
+
+# --- MakeMaker distmeta section:
+distmeta : create_distdir metafile
+	$(NOECHO) cd $(DISTVNAME) && $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{META.yml} => q{Module meta-data (added by MakeMaker)}}) } ' \
+	  -e '    or print "Could not add META.yml to MANIFEST: $${'\''@'\''}\n"'
+
+
+
+# --- MakeMaker distsignature section:
+distsignature : create_distdir
+	$(NOECHO) cd $(DISTVNAME) && $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{SIGNATURE} => q{Public-key signature (added by MakeMaker)}}) } ' \
+	  -e '    or print "Could not add SIGNATURE to MANIFEST: $${'\''@'\''}\n"'
+	$(NOECHO) cd $(DISTVNAME) && $(TOUCH) SIGNATURE
+	cd $(DISTVNAME) && cpansign -s
+
+
+
+# --- MakeMaker install section:
+
+install :: all pure_install doc_install
+	$(NOECHO) $(NOOP)
+
+install_perl :: all pure_perl_install doc_perl_install
+	$(NOECHO) $(NOOP)
+
+install_site :: all pure_site_install doc_site_install
+	$(NOECHO) $(NOOP)
+
+install_vendor :: all pure_vendor_install doc_vendor_install
+	$(NOECHO) $(NOOP)
+
+pure_install :: pure_$(INSTALLDIRS)_install
+	$(NOECHO) $(NOOP)
+
+doc_install :: doc_$(INSTALLDIRS)_install
+	$(NOECHO) $(NOOP)
+
+pure__install : pure_site_install
+	$(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site
+
+doc__install : doc_site_install
+	$(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site
+
+pure_perl_install ::
+	$(NOECHO) $(MOD_INSTALL) \
+		read $(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist \
+		write $(DESTINSTALLARCHLIB)/auto/$(FULLEXT)/.packlist \
+		$(INST_LIB) $(DESTINSTALLPRIVLIB) \
+		$(INST_ARCHLIB) $(DESTINSTALLARCHLIB) \
+		$(INST_BIN) $(DESTINSTALLBIN) \
+		$(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
+		$(INST_MAN1DIR) $(DESTINSTALLMAN1DIR) \
+		$(INST_MAN3DIR) $(DESTINSTALLMAN3DIR)
+	$(NOECHO) $(WARN_IF_OLD_PACKLIST) \
+		$(SITEARCHEXP)/auto/$(FULLEXT)
+
+
+pure_site_install ::
+	$(NOECHO) $(MOD_INSTALL) \
+		read $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist \
+		write $(DESTINSTALLSITEARCH)/auto/$(FULLEXT)/.packlist \
+		$(INST_LIB) $(DESTINSTALLSITELIB) \
+		$(INST_ARCHLIB) $(DESTINSTALLSITEARCH) \
+		$(INST_BIN) $(DESTINSTALLSITEBIN) \
+		$(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
+		$(INST_MAN1DIR) $(DESTINSTALLSITEMAN1DIR) \
+		$(INST_MAN3DIR) $(DESTINSTALLSITEMAN3DIR)
+	$(NOECHO) $(WARN_IF_OLD_PACKLIST) \
+		$(PERL_ARCHLIB)/auto/$(FULLEXT)
+
+pure_vendor_install ::
+	$(NOECHO) $(MOD_INSTALL) \
+		read $(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist \
+		write $(DESTINSTALLVENDORARCH)/auto/$(FULLEXT)/.packlist \
+		$(INST_LIB) $(DESTINSTALLVENDORLIB) \
+		$(INST_ARCHLIB) $(DESTINSTALLVENDORARCH) \
+		$(INST_BIN) $(DESTINSTALLVENDORBIN) \
+		$(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
+		$(INST_MAN1DIR) $(DESTINSTALLVENDORMAN1DIR) \
+		$(INST_MAN3DIR) $(DESTINSTALLVENDORMAN3DIR)
+
+doc_perl_install ::
+	$(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
+	-$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
+	-$(NOECHO) $(DOC_INSTALL) \
+		"Module" "$(NAME)" \
+		"installed into" "$(INSTALLPRIVLIB)" \
+		LINKTYPE "$(LINKTYPE)" \
+		VERSION "$(VERSION)" \
+		EXE_FILES "$(EXE_FILES)" \
+		>> $(DESTINSTALLARCHLIB)/perllocal.pod
+
+doc_site_install ::
+	$(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
+	-$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
+	-$(NOECHO) $(DOC_INSTALL) \
+		"Module" "$(NAME)" \
+		"installed into" "$(INSTALLSITELIB)" \
+		LINKTYPE "$(LINKTYPE)" \
+		VERSION "$(VERSION)" \
+		EXE_FILES "$(EXE_FILES)" \
+		>> $(DESTINSTALLARCHLIB)/perllocal.pod
+
+doc_vendor_install ::
+	$(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
+	-$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
+	-$(NOECHO) $(DOC_INSTALL) \
+		"Module" "$(NAME)" \
+		"installed into" "$(INSTALLVENDORLIB)" \
+		LINKTYPE "$(LINKTYPE)" \
+		VERSION "$(VERSION)" \
+		EXE_FILES "$(EXE_FILES)" \
+		>> $(DESTINSTALLARCHLIB)/perllocal.pod
+
+
+uninstall :: uninstall_from_$(INSTALLDIRS)dirs
+	$(NOECHO) $(NOOP)
+
+uninstall_from_perldirs ::
+	$(NOECHO) $(UNINSTALL) $(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist
+
+uninstall_from_sitedirs ::
+	$(NOECHO) $(UNINSTALL) $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist
+
+uninstall_from_vendordirs ::
+	$(NOECHO) $(UNINSTALL) $(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist
+
+
+# --- MakeMaker force section:
+# Phony target to force checking subdirectories.
+FORCE:
+	$(NOECHO) $(NOOP)
+
+
+# --- MakeMaker perldepend section:
+
+
+# --- MakeMaker makefile section:
+# We take a very conservative approach here, but it's worth it.
+# We move Makefile to Makefile.old here to avoid gnu make looping.
+$(FIRST_MAKEFILE) : Makefile.PL $(CONFIGDEP)
+	$(NOECHO) $(ECHO) "Makefile out-of-date with respect to $?"
+	$(NOECHO) $(ECHO) "Cleaning current config before rebuilding Makefile..."
+	-$(NOECHO) $(RM_F) $(MAKEFILE_OLD)
+	-$(NOECHO) $(MV)   $(FIRST_MAKEFILE) $(MAKEFILE_OLD)
+	- $(MAKE) $(USEMAKEFILE) $(MAKEFILE_OLD) clean $(DEV_NULL)
+	$(PERLRUN) Makefile.PL 
+	$(NOECHO) $(ECHO) "==> Your Makefile has been rebuilt. <=="
+	$(NOECHO) $(ECHO) "==> Please rerun the $(MAKE) command.  <=="
+	false
+
+
+
+# --- MakeMaker staticmake section:
+
+# --- MakeMaker makeaperl section ---
+MAP_TARGET    = perl
+FULLPERL      = /usr/local/bin/perl
+
+$(MAP_TARGET) :: static $(MAKE_APERL_FILE)
+	$(MAKE) $(USEMAKEFILE) $(MAKE_APERL_FILE) $@
+
+$(MAKE_APERL_FILE) : $(FIRST_MAKEFILE) pm_to_blib
+	$(NOECHO) $(ECHO) Writing \"$(MAKE_APERL_FILE)\" for this $(MAP_TARGET)
+	$(NOECHO) $(PERLRUNINST) \
+		Makefile.PL DIR= \
+		MAKEFILE=$(MAKE_APERL_FILE) LINKTYPE=static \
+		MAKEAPERL=1 NORECURS=1 CCCDLFLAGS=
+
+
+# --- MakeMaker test section:
+
+TEST_VERBOSE=0
+TEST_TYPE=test_$(LINKTYPE)
+TEST_FILE = test.pl
+TEST_FILES = 
+TESTDB_SW = -d
+
+testdb :: testdb_$(LINKTYPE)
+
+test :: $(TEST_TYPE)
+	$(NOECHO) $(ECHO) 'No tests defined for $(NAME) extension.'
+
+test_dynamic :: pure_all
+
+testdb_dynamic :: pure_all
+	PERL_DL_NONLAZY=1 $(FULLPERLRUN) $(TESTDB_SW) "-I$(INST_LIB)" "-I$(INST_ARCHLIB)" $(TEST_FILE)
+
+test_ : test_dynamic
+
+test_static :: test_dynamic
+testdb_static :: testdb_dynamic
+
+
+# --- MakeMaker ppd section:
+# Creates a PPD (Perl Package Description) for a binary distribution.
+ppd:
+	$(NOECHO) $(ECHO) '<SOFTPKG NAME="$(DISTNAME)" VERSION="0,02,0,0">' > $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '    <TITLE>$(DISTNAME)</TITLE>' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '    <ABSTRACT></ABSTRACT>' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '    <AUTHOR>Mahlon E. Smith &lt;mahlon@martini.nu&gt;</AUTHOR>' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '    <IMPLEMENTATION>' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <DEPENDENCY NAME="Date-Calc" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <DEPENDENCY NAME="Digest-MD5" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <DEPENDENCY NAME="LWP-UserAgent" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <DEPENDENCY NAME="Test-More" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <DEPENDENCY NAME="XML-Simple" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <OS NAME="$(OSNAME)" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <ARCHITECTURE NAME="i386-freebsd-64int" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '        <CODEBASE HREF="" />' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '    </IMPLEMENTATION>' >> $(DISTNAME).ppd
+	$(NOECHO) $(ECHO) '</SOFTPKG>' >> $(DISTNAME).ppd
+
+
+# --- MakeMaker pm_to_blib section:
+
+pm_to_blib : $(TO_INST_PM)
+	$(NOECHO) $(ABSPERLRUN) -MExtUtils::Install -e 'pm_to_blib({@ARGV}, '\''$(INST_LIB)/auto'\'', '\''$(PM_FILTER)'\'')' \
+	  lib/Net/.arch-ids/OpenSRS.pm.id blib/lib/Net/.arch-ids/OpenSRS.pm.id \
+	  lib/Net/OpenSRS.pm blib/lib/Net/OpenSRS.pm \
+	  lib/.arch-ids/=id blib/lib/.arch-ids/=id \
+	  lib/Net/.arch-ids/=id blib/lib/Net/.arch-ids/=id 
+	$(NOECHO) $(TOUCH) pm_to_blib
+
+
+# --- MakeMaker selfdocument section:
+
+
+# --- MakeMaker postamble section:
+
+
+# End.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/Makefile.PL	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,20 @@
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME          => 'Net::OpenSRS',
+    AUTHOR        => 'Mahlon E. Smith <mahlon@martini.nu>',
+    VERSION_FROM  => 'lib/Net/OpenSRS.pm',
+    ABSTRACT_FROM => 'lib/Net/OpenSRS.pm',
+    PL_FILES      => {},
+    PREREQ_PM     => {
+        'Test::More'     => 0,
+        'LWP::UserAgent' => 0,
+        'XML::Simple'    => 0,
+        'Digest::MD5'    => 0,
+        'Date::Calc'     => 0,
+    },
+    dist  => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+    clean => { FILES    => 'Net-OpenSRS-*' },
+);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/README	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,72 @@
+Net-OpenSRS
+
+DESCRIPTION
+
+This is a wrapper interface to the DNS portions of the Tucows OpenSRS
+HTTPS XML API.
+
+    The client library distributed by OpenSRS can be difficult to
+    integrate into a custom environment, and their web interface becomes
+    quickly tedious with heavy usage. This is a clean and relatively
+    quick library to perform the most common API methods described in the
+    OpenSRS API documentation.
+
+    For example -- after initial setup, registering a domain can be 
+    completed in one function call.  This makes Net::OpenSRS suitable
+    for easy SRS integration into your own environment.
+
+
+NOTES
+
+    This module requires some setup in the OpenSRS reseller environment
+    before it will work correctly.
+
+    - You need to have an OpenSRS account, of course.  If you aren't an
+      OpenSRS reseller, this module will be of limited use to you. :)
+
+    - The machine(s) using this module need to have their public IP 
+      addresses added to your 'Script API allow' list in the OpenSRS
+      web interface.  (You only need to do this once.)
+
+    - You need to generate your API keys - also in the the OpenSRS
+      web interface.  (These keys are used for reseller authentication.)
+
+    - For further information, please refer to the pod documentation.
+
+
+INSTALLATION
+
+To install this module, run the following commands:
+
+    perl Makefile.PL
+    make
+    make test
+    make install
+
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the perldoc command.
+
+    perldoc Net::OpenSRS
+
+You can also look for information at:
+
+    Search CPAN
+        http://search.cpan.org/dist/Net-OpenSRS
+
+    CPAN Request Tracker:
+        http://rt.cpan.org/NoAuth/Bugs.html?Dist=Net-OpenSRS
+
+    AnnoCPAN, annotated CPAN documentation:
+        http://annocpan.org/dist/Net-OpenSRS
+
+    CPAN Ratings:
+        http://cpanratings.perl.org/d/Net-OpenSRS
+
+COPYRIGHT AND LICENCE
+
+Copyright (C) 2006 Mahlon E. Smith
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/lib/Net/OpenSRS.pm	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,1234 @@
+=head1 Description
+
+This is a wrapper interface to the DNS portions of the Tucows OpenSRS
+HTTPS XML API.
+
+The client library distributed by OpenSRS can be difficult to integrate
+into a custom environment, and their web interface becomes quickly
+tedious with heavy usage. This is a clean and relatively quick library
+to perform the most common API methods described in the OpenSRS API
+documentation.
+
+=head1 Examples
+
+ use Net::OpenSRS;
+
+ my $key = 'Your_API_Key_From_The_Reseller_Interface';
+ my $srs = Net::OpenSRS->new();
+
+ $srs->environment('live');
+ $srs->set_key( $key );
+
+ $srs->set_manage_auth( 'manage_username', 'manage_password' );
+
+ my $cookie = $srs->get_cookie( 'spime.net' );
+ if ($cookie) {
+     print "Cookie:  $cookie\n";
+ } else {
+     print $srs->last_response() . "\n";
+ }
+
+ # do a batch of domain locks
+ $srs->bulk_lock([ 'example.com', 'example.net', ... ]);
+
+ # renew a domain
+ my $result = $srs->renew_domain( 'example.com' );
+ ...
+
+=head1 Notes
+
+=head2 Prerequisites
+
+This module requires some setup in the OpenSRS reseller environment
+before it will work correctly.
+
+=over 4
+
+=item Reseller account
+
+You need to have an OpenSRS account, of course.  If you aren't an
+OpenSRS reseller, this module will be of limited use to you. :)
+
+=item Script API network access
+
+The machine(s) using this module need to have their public IP addresses
+added to your 'Script API allow' list in the OpenSRS web interface.
+(You'll only need to do this once, assuming your IP doesn't change.)
+
+=item API key generation
+
+You'll need to pregenerate your API keys - also in the the OpenSRS web
+interface.  These keys are used for all reseller API authentication.
+
+=back
+
+=head2 Assumptions
+
+OpenSRS allows for a variety of ways to organize your domains.  Because
+of this, writing a 'one size fits all' module is rather difficult.
+Instead, we make a few assumptions regarding the way people use their
+OpenSRS reseller accounts.
+
+**** These assumptions will ultimately determine if this module is right for
+you!  Please read them carefully! ****
+
+=over 4
+
+=item Management 'master' account.
+
+We assume that all domains are under one global management owner
+account.  If customers want access to the management interface, we're
+operating under the idea that you create subaccounts for them -
+retainting the master account information for your own use.  (If you
+aren't doing this, it really makes things easier for you in the long
+run.)
+
+For example, 'spime.net' is my master management account.  Before doing
+any register_domain() calls, I call master_domain('spime.net') - then
+any transfers or registrations from that point forward are linked to
+'spime.net'.  If a customer wants access to the SRS web management
+interface, I can then just create a subaccount for just their domain,
+so I retain absolute control -- in the event a customer forgets their
+password, I'm covered.
+
+=item Usernames
+
+We assume that your management username 'master' account is identical to
+your reseller username, and just the passwords differ.
+
+=item Default registration info
+
+We assume you've properly set up default technical contact information,
+including your default nameservers, in the OpenSRS reseller web
+interface.
+
+=item Return codes
+
+Unless otherwise noted, all methods return true on success, false on
+failure, and undefined on caller error.
+
+=back
+
+=head2 Default environment
+
+This library defaults to the TEST environment. (horizon.)  Many API
+methods don't work in the test environment (SET COOKIE being the most
+notable example, as any API method relying on a cookie doesn't work
+either.)  Neither does batch processing.  Most everything else should be
+ok.  ( See environment() )
+
+=head2 The '$c' variable
+
+Many methods require customer information.  I leave the method of
+fetching this information entirely to you.  All examples below that show
+a $c variable expect a hashref (or object) that contain these keys:
+
+    my $c = {
+        firstname => 'John',
+        lastname  => 'Doe',
+        city      => 'Portland',
+        state     => 'Oregon',
+        country   => 'US',
+        address   => '555 Someplace Street',
+        email     => 'john@example.com',
+        phone     => '503-555-1212',
+        company   => 'n/a'
+    };
+
+=cut
+
+package Net::OpenSRS;
+
+use strict;
+use warnings;
+use LWP::UserAgent;
+use XML::Simple;
+use Digest::MD5;
+use Date::Calc qw/ Add_Delta_Days Today This_Year /;
+
+our $VERSION = '0.02';
+my $rv;
+*hash = \&Digest::MD5::md5_hex;
+
+#----------------------------------------------------------------------
+# utility methods
+#----------------------------------------------------------------------
+
+=head1 Utility methods
+
+=over 4
+
+=item new()
+
+ my $srs = Net::OpenSRS->new();
+
+Create a new Net::OpenSRS object.  There are no options for this
+method.
+
+=cut
+
+sub new
+{
+    my ($class, %opts) = @_;
+    my $self = {};
+    bless $self, $class;
+
+    $self->{config} = {
+        use_test_env  => 1,
+        debug         => 0,
+        master_domain => undef,
+
+        bulkhost => 'https://batch.opensrs.net:55443',
+
+        # reseller auth keys, as generated via the reseller website.
+        live => {
+            key  => undef,
+            host => 'https://rr-n1-tor.opensrs.net:55443',
+        },
+        test => {
+            key  => undef,
+            host => 'https://horizon.opensrs.net:55443',
+        }
+    };
+
+    return $self;
+}
+
+sub debug
+{
+    my $self = shift;
+    return unless $self->debug_level;
+    print STDERR shift() . "\n";
+}
+
+=item debug_level()
+
+Setting the debug level will print various pieces of information to
+STDERR when connecting to OpenSRS.  Use this if something isn't working
+the way you think it should be.
+
+=item 0
+
+Disable debugging.
+
+=item 1
+
+Print current environment, host, and HTTP response.
+
+=item 2
+
+Add XML request and response to output.
+
+=item 3
+
+Add SSL debugging to output.
+
+Debugging is off by default.  When called without an argument, returns
+the current debug level.
+
+=cut
+
+sub debug_level
+{
+    my ($self, $level) = @_;
+    return $self->{config}->{debug} unless $level;
+    $self->{config}->{debug} = $level;
+    return;
+}
+
+=item last_response()
+
+All Net::OpenSRS methods set the last OpenSRS API reply in a temporary
+variable.  You can view the contents of this variable using the
+last_response() method.
+
+Note that it is reset on each method call.
+
+Returns the last OpenSRS return code and result string, or if passed any
+true value, instead returns the full XML (parsed into a hashref) of the
+last OpenSRS return. (perfect for Data::Dumper)
+
+Examples:
+   200: Command Successful
+   400: Domain example.com does not exist with OpenSRS
+
+=cut
+
+sub last_response
+{
+    my ($self, $obj) = @_;
+    return $obj ? $rv : $self->{last_response} || '';
+}
+
+=item set_manage_auth()
+
+ $srs->set_manage_auth( $username, $password );
+
+Set the owner management username and password.  This is used to fetch
+cookies, and perform any API methods that require the management cookie.
+For specifics on this, see the OpenSRS API documentation.
+
+=cut
+
+sub set_manage_auth
+{
+    my ($self, $user, $pass) = @_;
+    return undef unless $user && $pass;
+    $self->{config}->{username} = $user;
+    $self->{config}->{password} = $pass;
+    return 1;
+}
+
+=item set_key()
+
+Tell the OpenSRS object what secret key to use for authentication.
+You can generate a new secret key by using the OpenSRS reseller web
+interface.  This key is required to perform any API functions.
+
+set_key() is affected by the current environment().  Calling the
+set_key() method while in the test environment only sets the key for the
+test environment - likewise for the live environment.  To set a key for
+the live environment, you need to call environment('live') B<first>.
+
+=cut
+
+sub set_key
+{
+    my ($self, $key) = @_;
+    return undef unless $key;
+    $self->{config}->{ $self->environment }->{key} = $key;
+    return 1;
+}
+
+=item environment()
+
+ my $env = $srs->environment;
+ $srs->environment('live');
+
+Without an argument, returns a string - either 'test', or 'live',
+depending on the environment the object is currently using.
+
+The test environment is the default.
+
+If passed an argument (either 'test' or 'live') - switches into the
+desired environment.  You will need to set_key() if you were previously
+using a different environment, or if you hadn't set_key() yet.
+
+=cut
+
+sub environment
+{
+    my ($self, $env) = @_;
+    return ($self->{config}->{use_test_env} ? 'test' : 'live')
+        unless $env && $env =~ /(test|live)/i;
+    $self->{config}->{use_test_env} = 
+        $1 eq 'test' ? 1 : 0;
+    return;
+}
+
+=item master_domain()
+
+ my $master = $srs->master_domain;
+ $srs->master_domain('spime.net');
+
+Without an argument, returns the currently set 'master domain' account.
+Otherwise, it sets the master domain.
+
+New transfers and registrations are linked under this domain, for
+centralized management.  See the 'Assumptions' section, above.
+
+=cut
+
+sub master_domain
+{
+    my ($self, $domain) = @_;
+    return $self->{config}->{master_domain} unless $domain;
+    $self->{config}->{master_domain} = $domain;
+    return;
+}
+
+# set last status messages/codes in $self,
+# for the benefit of the caller.
+sub _set_response
+{
+    my $self = shift;
+    $rv->{response_text} =~ s/Error: //;
+    $self->{last_response} = $rv->{response_code} . ": " . $rv->{response_text};
+    return;
+}
+
+#----------------------------------------------------------------------
+# SRS API methods
+#----------------------------------------------------------------------
+
+=back
+
+=head1 OpenSRS API methods
+
+=over 4
+
+=item bulk_lock() / bulk_unlock()
+
+Locks or unlocks up to 1000 domains at a time.
+
+ my $result = $srs->bulk_lock([ 'example.com', 'example.net' ]);
+
+Returns remote bulk queue id on successful batch submission.
+
+=cut
+
+sub bulk_lock
+{
+    my $self = shift;
+    return $self->_bulk_action( 'lock', @_ );
+}
+
+sub bulk_unlock
+{
+    my $self = shift;
+    return $self->_bulk_action( 'unlock', @_ );
+}
+
+sub _bulk_action
+{
+    my ( $self, $toggle, $domains ) = @_;
+    return undef unless $toggle =~ /lock|unlock/i && 
+                        ref $domains;
+    return undef if scalar @$domains >= 1000;
+
+    $rv = $self->make_request(
+        {
+            batch   => 1,
+            action  => 'submit',
+            object  => 'bulk_change',
+            attributes => {
+                change_type => 'domain_lock',
+                change_items => $domains,
+                op_type => lc $toggle,
+            }
+        }
+    );
+    return undef unless $rv;
+
+    $self->_set_response;
+    return $rv->{is_success} ? $rv->{bulk_change_req_id} : 0;
+}
+
+=item check_queued_request()
+
+ my $result = $srs->check_queued_request( $queue_id );
+
+Requires queue id - returned from batch methods such as bulk_lock().
+Always returns hashref of queue command on success.  
+Check $srs->last_response() for status progress.
+
+=cut
+
+sub check_queued_request
+{
+    my ( $self, $id ) = @_;
+    return undef unless $id;
+
+    $rv = $self->make_request(
+        {
+            action  => 'query_queued_request',
+            object  => 'domain',
+            attributes => {
+                request_id => $id,
+            }
+        }
+    );
+    return undef unless $rv;
+
+    $self->_set_response;
+    return $rv->{attributes}->{request_data};
+}
+
+=item check_transfer()
+
+ my $result = $srs->check_transfer( 'example.com' );
+
+Checks the status of a transfer in progress.  Returns hashref of
+'contact_email', 'status', and 'last_update_time' for a given domain
+transfer.  The 'status' key is always one of the following:
+
+        pending_owner  (waiting on owner confirmation)
+        pending_admin  (waiting on opensrs staff confirmation)
+        pending_registry  (waiting on register to complete)
+        completed  (transfer done)
+        cancelled  (reseller cancelled transfer in progress)
+        undefined  (no transfer in progress)
+
+If the domain in question has no transfer in progress - instead checks
+to see if the domain is capable of transfer.  Returns hashref of
+'transferrable' (boolean) and 'reason' (string).
+
+=cut
+
+sub check_transfer
+{
+    my ( $self, $domain ) = @_;
+    return undef unless $domain;
+
+    $rv = $self->make_request(
+        {
+            action     => 'check_transfer',
+            object     => 'domain',
+            attributes => {
+                domain              => $domain,
+                get_request_address => 1,
+            }
+        }
+    );
+    return undef unless $rv;
+
+    $self->_set_response;
+    if ( $rv->{attributes}->{status} ) {
+        return {
+            status           => $rv->{attributes}->{status},
+            last_update_time => $rv->{attributes}->{unixtime},
+            contact_email    => $rv->{attributes}->{request_address}
+        };
+    }
+    else {
+        return $rv->{attributes}; #(transferrable bool and reason)
+    }
+}
+
+=item get_cookie()
+
+OpenSRS management APIs require a cookie to be generated, and sent along
+with the API request.
+
+ $cookie = $srs->get_cookie( 'example.com ');
+ ($cookie, $expiration_date) = $srs->get_cookie( 'example.com ');
+
+Make sure you've set_manage_auth() before attempting any cookie required
+APIs.
+
+Returns cookie on success, undefined on error.  (Check error with
+last_response())
+
+In array context, returns cookie and expiration date of the domain.
+
+=cut
+
+sub get_cookie
+{
+    my ($self, $domain) = @_;
+    return undef unless $domain;
+    $rv = $self->make_request(
+        {
+            action     => 'set',
+            object     => 'cookie',
+            attributes => {
+                reg_username => $self->{config}->{username},
+                reg_password => $self->{config}->{password},
+                domain => $domain
+            }
+        }
+    );
+    return undef unless $rv;
+
+    $self->_set_response;
+    if ($rv->{is_success}) {
+        return
+          wantarray
+          ? ( $rv->{attributes}->{cookie}, $rv->{attributes}->{expiredate} )
+          : $rv->{attributes}->{cookie};
+    }
+    return undef;
+}
+
+=item get_expiring_domains()
+
+ my $results = $srs->get_expiring_domains( 60 );
+
+ Fetch and return OpenSRS hashref of expiring domains, within
+ the specified timeperiod.  (In days.)
+
+ Time period defaults to 30 days.
+
+=cut
+
+sub get_expiring_domains
+{
+    my ($self, $timeframe) = @_;
+    $timeframe ||= 30;
+
+    my $today   = join '-', map { sprintf( "%02d", $_ ) } Date::Calc::Today();
+    my $expdate = join '-', map { sprintf( "%02d", $_ ) }
+      Date::Calc::Add_Delta_Days( ( split '-', $today ), $timeframe );
+
+    $rv = $self->make_request(
+        {
+            action     => 'get_domains_by_expiredate',
+            object     => 'domain',
+            attributes => {
+                limit    => 1000,
+                exp_from => $today,
+                exp_to   => $expdate,
+            }
+        }
+    );
+    return undef unless $rv;
+
+    $self->_set_response;
+    return $rv->{attributes}->{exp_domains} if $rv->{is_success};
+    return undef;
+}
+
+=item is_available()
+
+Hey OpenSRS! Is this domain registered, or is it available?
+
+ my $result = $srs->is_available( 'example.com ');
+
+Returns true if the domain is available, false if it is already
+registered.
+
+=cut
+
+sub is_available
+{
+    my ($self, $domain) = @_;
+    return undef unless $domain;
+    $rv = $self->make_request(
+        {
+            action     => 'lookup',
+            object     => 'domain',
+            attributes => {
+                domain => $domain
+            }
+        }
+    );
+    return undef unless $rv;
+    $self->_set_response;
+    return undef unless $rv->{is_success};
+    return $rv->{response_code} == 210 ? 1 : 0;
+}
+
+=item register_domain()
+
+ my $result = $srs->register_domain( 'example.com', $c );
+
+Register a new domain.  Default nameserver and tech info used from
+OpenSRS settings.
+
+=cut
+
+sub register_domain
+{
+    my ($self, $domain, $c, $transfer) = @_;
+    return undef unless $domain;
+
+    # sanity checks
+    unless ($self->{config}->{username}) {
+        $self->debug("Management auth not set.");
+        return undef;
+    }
+    unless (ref $c) {
+        $self->debug("2nd arg must be a reference to customer info.");
+        return undef;
+    }
+
+    my $epp_phone = $c->{phone};
+    $epp_phone =~ s/[\.\-]//g;
+    $epp_phone = '+1.' . $epp_phone;
+
+    # blah, this sucks.
+    # it would be really nice if OpenSRS figured out the country -> code
+    # conversion on their end of things.
+    my %country_codes = (
+        'Afghanistan'                            => 'AF',
+        'Albania'                                => 'AL',
+        'Algeria'                                => 'DZ',
+        'American Samoa'                         => 'AS',
+        'Andorra'                                => 'AD',
+        'Angola'                                 => 'AO',
+        'Anguilla'                               => 'AI',
+        'Antarctica'                             => 'AQ',
+        'Antigua And Barbuda'                    => 'AG',
+        'Argentina'                              => 'AR',
+        'Armenia'                                => 'AM',
+        'Aruba'                                  => 'AW',
+        'Australia'                              => 'AU',
+        'Austria'                                => 'AT',
+        'Azerbaijan'                             => 'AZ',
+        'Bahamas'                                => 'BS',
+        'Bahrain'                                => 'BH',
+        'Bangladesh'                             => 'BD',
+        'Barbados'                               => 'BB',
+        'Belarus'                                => 'BY',
+        'Belgium'                                => 'BE',
+        'Belize'                                 => 'BZ',
+        'Benin'                                  => 'BJ',
+        'Bermuda'                                => 'BM',
+        'Bhutan'                                 => 'BT',
+        'Bolivia'                                => 'BO',
+        'Bosnia Hercegovina'                     => 'BA',
+        'Botswana'                               => 'BW',
+        'Bouvet Island'                          => 'BV',
+        'Brazil'                                 => 'BR',
+        'British Indian Ocean Territory'         => 'IO',
+        'Brunei Darussalam'                      => 'BN',
+        'Bulgaria'                               => 'BG',
+        'Burkina Faso'                           => 'BF',
+        'Burundi'                                => 'BI',
+        'Cambodia'                               => 'KH',
+        'Cameroon'                               => 'CM',
+        'Canada'                                 => 'CA',
+        'Cape Verde'                             => 'CV',
+        'Cayman Islands'                         => 'KY',
+        'Central African Republic'               => 'CF',
+        'Chad'                                   => 'TD',
+        'Chile'                                  => 'CL',
+        'China'                                  => 'CN',
+        'Christmas Island'                       => 'CX',
+        'Cocos (Keeling) Islands'                => 'CC',
+        'Colombia'                               => 'CO',
+        'Comoros'                                => 'KM',
+        'Congo'                                  => 'CG',
+        'Congo The Democratic Republic Of'       => 'CD',
+        'Cook Islands'                           => 'CK',
+        'Costa Rica'                             => 'CR',
+        'Cote D\'Ivoire'                         => 'CI',
+        'Croatia'                                => 'HR',
+        'Cuba'                                   => 'CU',
+        'Cyprus'                                 => 'CY',
+        'Czech Republic'                         => 'CZ',
+        'Denmark'                                => 'DK',
+        'Djibouti'                               => 'DJ',
+        'Dominica'                               => 'DM',
+        'Dominican Republic'                     => 'DO',
+        'Ecuador'                                => 'EC',
+        'Egypt'                                  => 'EG',
+        'El Salvador'                            => 'SV',
+        'Equatorial Guinea'                      => 'GQ',
+        'Eritrea'                                => 'ER',
+        'Estonia'                                => 'EE',
+        'Ethiopia'                               => 'ET',
+        'Falkland Islands (Malvinas)'            => 'FK',
+        'Faroe Islands'                          => 'FO',
+        'Fiji'                                   => 'FJ',
+        'Finland'                                => 'FI',
+        'France'                                 => 'FR',
+        'French Guiana'                          => 'GF',
+        'French Polynesia'                       => 'PF',
+        'French Southern Territories'            => 'TF',
+        'Gabon'                                  => 'GA',
+        'Gambia'                                 => 'GM',
+        'Georgia'                                => 'GE',
+        'Germany'                                => 'DE',
+        'Ghana'                                  => 'GH',
+        'Gibraltar'                              => 'GI',
+        'Greece'                                 => 'GR',
+        'Greenland'                              => 'GL',
+        'Grenada'                                => 'GD',
+        'Guadeloupe'                             => 'GP',
+        'Guam'                                   => 'GU',
+        'Guatemela'                              => 'GT',
+        'Guinea'                                 => 'GN',
+        'Guinea-Bissau'                          => 'GW',
+        'Guyana'                                 => 'GY',
+        'Haiti'                                  => 'HT',
+        'Heard and McDonald Islands'             => 'HM',
+        'Honduras'                               => 'HN',
+        'Hong Kong'                              => 'HK',
+        'Hungary'                                => 'HU',
+        'Iceland'                                => 'IS',
+        'India'                                  => 'IN',
+        'Indonesia'                              => 'ID',
+        'Iran (Islamic Republic Of)'             => 'IR',
+        'Iraq'                                   => 'IQ',
+        'Ireland'                                => 'IE',
+        'Israel'                                 => 'IL',
+        'Italy'                                  => 'IT',
+        'Jamaica'                                => 'JM',
+        'Japan'                                  => 'JP',
+        'Jordan'                                 => 'JO',
+        'Kazakhstan'                             => 'KZ',
+        'Kenya'                                  => 'KE',
+        'Kiribati'                               => 'KI',
+        'Korea, Democratic People\'s Republic Of' => 'KP',
+        'Korea, Republic Of'                     => 'KR',
+        'Kuwait'                                 => 'KW',
+        'Kyrgyzstan'                             => 'KG',
+        'Lao People\'s Democratic Republic'      => 'LA',
+        'Latvia'                                 => 'LV',
+        'Lebanon'                                => 'LB',
+        'Lesotho'                                => 'LS',
+        'Liberia'                                => 'LR',
+        'Libyan Arab Jamahiriya'                 => 'LY',
+        'Liechtenstein'                          => 'LI',
+        'Lithuania'                              => 'LT',
+        'Luxembourg'                             => 'LU',
+        'Macau'                                  => 'MO',
+        'Macedonia'                              => 'MK',
+        'Madagascar'                             => 'MG',
+        'Malawi'                                 => 'MW',
+        'Malaysia'                               => 'MY',
+        'Maldives'                               => 'MV',
+        'Mali'                                   => 'ML',
+        'Malta'                                  => 'MT',
+        'Marshall Islands'                       => 'MH',
+        'Martinique'                             => 'MQ',
+        'Mauritania'                             => 'MR',
+        'Mauritius'                              => 'MU',
+        'Mayotte'                                => 'YT',
+        'Mexico'                                 => 'MX',
+        'Micronesia, Federated States Of'        => 'FM',
+        'Moldova, Republic Of'                   => 'MD',
+        'Monaco'                                 => 'MC',
+        'Mongolia'                               => 'MN',
+        'Montserrat'                             => 'MS',
+        'Morocco'                                => 'MA',
+        'Mozambique'                             => 'MZ',
+        'Myanmar'                                => 'MM',
+        'Namibia'                                => 'NA',
+        'Nauru'                                  => 'NR',
+        'Nepal'                                  => 'NP',
+        'Netherlands'                            => 'NL',
+        'Netherlands Antilles'                   => 'AN',
+        'New Caledonia'                          => 'NC',
+        'New Zealand'                            => 'NZ',
+        'Nicaragua'                              => 'NI',
+        'Niger'                                  => 'NE',
+        'Nigeria'                                => 'NG',
+        'Niue'                                   => 'NU',
+        'Norfolk Island'                         => 'NF',
+        'Northern Mariana Islands'               => 'MP',
+        'Norway'                                 => 'NO',
+        'Oman'                                   => 'OM',
+        'Pakistan'                               => 'PK',
+        'Palau'                                  => 'PW',
+        'Palestine'                              => 'PS',
+        'Panama'                                 => 'PA',
+        'Papua New Guinea'                       => 'PG',
+        'Paraguay'                               => 'PY',
+        'Peru'                                   => 'PE',
+        'Philippines'                            => 'PH',
+        'Pitcairn'                               => 'PN',
+        'Poland'                                 => 'PL',
+        'Portugal'                               => 'PT',
+        'Puerto Rico'                            => 'PR',
+        'Qatar'                                  => 'QA',
+        'Reunion'                                => 'RE',
+        'Romania'                                => 'RO',
+        'Russian Federation'                     => 'RU',
+        'Rwanda'                                 => 'RW',
+        'Saint Helena'                           => 'SH',
+        'Saint Kitts And Nevis'                  => 'KN',
+        'Saint Lucia'                            => 'LC',
+        'Saint Pierre and Miquelon'              => 'PM',
+        'Saint Vincent and The Grenadines'       => 'VC',
+        'Samoa'                                  => 'WS',
+        'San Marino'                             => 'SM',
+        'Sao Tome and Principe'                  => 'ST',
+        'Saudi Arabia'                           => 'SA',
+        'Senegal'                                => 'SN',
+        'Serbia and Montenegro'                  => 'CS',
+        'Seychelles'                             => 'SC',
+        'Sierra Leone'                           => 'SL',
+        'Singapore'                              => 'SG',
+        'Slovakia'                               => 'SK',
+        'Slovenia'                               => 'SI',
+        'Solomon Islands'                        => 'SB',
+        'Somalia'                                => 'SO',
+        'South Africa'                           => 'ZA',
+        'South Georgia and The Sandwich Islands' => 'GS',
+        'Spain'                                  => 'ES',
+        'Sri Lanka'                              => 'LK',
+        'Sudan'                                  => 'SD',
+        'Suriname'                               => 'SR',
+        'Svalbard and Jan Mayen Islands'         => 'SJ',
+        'Swaziland'                              => 'SZ',
+        'Sweden'                                 => 'SE',
+        'Switzerland'                            => 'CH',
+        'Syrian Arab Republic'                   => 'SY',
+        'Taiwan'                                 => 'TW',
+        'Tajikista'                              => 'TJ',
+        'Tanzania, United Republic Of'           => 'TZ',
+        'Thailand'                               => 'TH',
+        'Timor-Leste'                            => 'TL',
+        'Togo'                                   => 'TG',
+        'Tokelau'                                => 'TK',
+        'Tonga'                                  => 'TO',
+        'Trinidad and Tobago'                    => 'TT',
+        'Tunisia'                                => 'TN',
+        'Turkey'                                 => 'TR',
+        'Turkmenistan'                           => 'TM',
+        'Turks and Caicos Islands'               => 'TC',
+        'Tuvalu'                                 => 'TV',
+        'Uganda'                                 => 'UG',
+        'Ukraine'                                => 'UA',
+        'United Arab Emirates'                   => 'AE',
+        'United Kingdom (GB)'                    => 'GB',
+        'United Kingdom (UK)'                    => 'UK',
+        'United States'                          => 'US',
+        'United States Minor Outlying Islands'   => 'UM',
+        'Uruguay'                                => 'UY',
+        'Uzbekistan'                             => 'UZ',
+        'Vanuatu'                                => 'VU',
+        'Vatican City State'                     => 'VA',
+        'Venezuela'                              => 'VE',
+        'Vietnam'                                => 'VN',
+        'Virgin Islands (British)'               => 'VG',
+        'Virgin Islands (U.S.)'                  => 'VI',
+        'Wallis and Futuna Islands'              => 'WF',
+        'Western Sahara'                         => 'EH',
+        'Yemen Republic of'                      => 'YE',
+        'Zambia'                                 => 'ZM',
+        'Zimbabwe'                               => 'ZW'
+    );  # end suckage
+
+    # attempt countryname translation if needed
+    if ( $c->{country} !~ m/^[A-Z]{2,3}$/ ) {
+    	$c->{country} = $country_codes{$c->{country}};
+
+        unless ( defined( $c->{country} ) ) {
+            $self->debug("Invalid country.");
+            return undef;
+        }
+    }
+
+    # build contact hashref from customer info.
+    my $contact_info = {
+        first_name  => $c->{firstname},
+        last_name   => $c->{lastname},
+        city        => $c->{city},
+        state       => $c->{state},
+        country     => $c->{country},
+        address1    => $c->{address},
+        postal_code => $c->{zip},
+        email       => $c->{email},
+        phone       => $epp_phone,
+        org_name    => $c->{company} || 'n/a',
+    };
+
+    $rv = $self->make_request(
+        {
+            action     => 'sw_register',
+            object     => 'domain',
+            attributes => {
+                domain              => $domain,
+                custom_nameservers  => 0,
+                custom_tech_contact => 0,
+                auto_renew          => 0,
+                period              => 1,
+                f_lock_domain       => 1,
+                contact_set         => {
+                    admin   => $contact_info,
+                    billing => $contact_info,
+                    owner   => $contact_info
+                },
+                reg_username => $self->{config}->{username},
+                reg_password => $self->{config}->{password},
+                reg_type   => $transfer ? 'transfer' : 'new',
+                reg_domain => $self->{config}->{master_domain}, # link domain to the 'master' account
+            }
+        }
+    );
+    $self->_set_response;
+    return $rv->{is_success};
+}
+
+=item renew_domain()
+
+ my $result = $srs->renew_domain( 'example.com', 1 );
+
+Renew a domain for a period of time in years. 1 year is the default.
+
+=cut
+
+sub renew_domain
+{
+    my ($self, $domain, $years) = @_;
+    return undef unless $domain;
+    $years ||= 1;
+
+    # sanity checks
+    unless ($self->{config}->{username}) {
+        $self->debug("Management auth not set.");
+        return undef;
+    }
+
+    # get current expiration year (why do they need this, again?)
+    my (undef, $expiration) = $self->get_cookie( $domain );
+    $expiration = $1 if $expiration =~ /^(\d{4})-/;
+    $expiration ||= Date::Calc::This_Year();
+    
+    $rv = $self->make_request(
+        {
+            action     => 'renew',
+            object     => 'domain',
+            attributes => {
+                domain                => $domain,
+                auto_renew            => 0,
+                handle                => 'process',
+                period                => $years,
+                currentexpirationyear => $expiration,
+            }
+        }
+    );
+    $self->_set_response;
+    return $rv->{is_success};
+}
+
+=item revoke_domain()
+
+Revoke a previously registered domain.  This only works if the domain is
+still within the grace period as defined by the registrar.
+Requires you to have called set_manage_auth() B<first>.
+
+ my $result = $srs->revoke_domain( 'example.com' );
+
+Returns true if the revoke is successful, false otherwise.
+Returns undefined on error.
+
+=cut
+
+sub revoke_domain
+{
+    my ($self, $domain) = @_;
+    return undef unless $domain;
+    unless ($self->{config}->{username}) {
+        $self->debug("Management auth not set.");
+        return undef;
+    }
+    $rv = $self->make_request(
+        {
+            action     => 'revoke',
+            object     => 'domain',
+            attributes => {
+                reseller => $self->{config}->{username},
+                domain => $domain,
+            }
+        }
+    );
+    $self->_set_response;
+    return $rv->{is_success};
+}
+
+=item transfer_domain()
+
+ my $result = $srs->transfer_domain( 'example.com', $c );
+
+Transfer a domain under your control.
+Returns true on success, false on failure, and undefined on caller error.
+
+=cut
+
+sub transfer_domain
+{
+    my $self = shift;
+    return $self->register_domain( @_, 1 );
+}
+
+=item make_request()
+
+This method is the real workhorse of this module.  If any OpenSRS API
+isn't explicity implemented in this module as a method call (such as
+get_cookie(), bulk_lock(), etc), you can use make_request() to build and send
+the API yourself.
+
+Examples:
+
+ my $result = $srs->make_request(
+     {
+         batch   => 1,
+         action  => 'submit',
+         object  => 'bulk_change',
+         attributes => {
+             change_type => 'domain_lock',
+             change_items => [ 'example.com', 'example.net' ],
+             op_type => 'lock',
+         }
+     }
+ );
+
+ my $result = $srs->make_request(
+     {
+         action     => 'lookup',
+         object     => 'domain',
+         attributes => {
+             domain => 'example.com'
+         }
+     }
+ );
+
+Returns a hashref containing parsed XML results from OpenSRS.
+
+Example return:
+
+ {
+     'protocol' => 'XCP',
+     'object' => 'DOMAIN',
+     'response_text' => 'Domain taken',
+     'action' => 'REPLY',
+     'response_code' => '211',
+     'attributes' => {
+         'status' => 'taken',
+         'match' => {}
+     },
+     'is_success' => '1'
+ }
+
+=cut
+
+# build opensrs xml protocol string.  submit.
+# convert xml response to data structure, and return.
+sub make_request
+{
+    my ($self, $data) = @_;
+    return undef unless ref $data;
+
+    $self->debug("Using " . $self->environment . " environment.");
+
+    my $key  = $self->{config}->{ $self->environment }->{key};
+    my $host = $self->{config}->{ $self->environment }->{host};
+    $ENV{HTTPS_DEBUG} = 1 if $self->debug_level > 2;
+
+    unless ($key) {
+        $self->debug("Authentication key not set.");
+        return undef;
+    }
+
+    my $action = uc $data->{action};
+    my $object = uc $data->{object};
+
+    # build our XML request.
+    # lets not bother with anything super fancy, 
+    # everything but the item keys are always static anyway.
+    my $xml;
+    $xml = <<XML;
+<?xml version='1.0' encoding="UTF-8" standalone="no" ?>
+<!DOCTYPE OPS_envelope SYSTEM "ops.dtd">
+<OPS_envelope>
+<header><version>0.9</version></header>
+<body>
+<data_block>
+<dt_assoc>
+  <item key="protocol">XCP</item>
+  <item key="action">$action</item>
+  <item key="object">$object</item>
+XML
+
+    $xml .= "  <item key=\"cookie\">$data->{cookie}</item>\n" if $data->{cookie};
+
+$xml .= <<XML;
+  <item key="attributes">
+    <dt_assoc>
+XML
+
+    foreach (sort keys %{ $data->{attributes} }) {
+        my $val = $data->{attributes}->{$_};
+        $xml .= $self->_format( $val, 4 );
+    }
+    $xml .= <<XML;
+    </dt_assoc>
+  </item>
+</dt_assoc>
+</data_block>
+</body>
+</OPS_envelope>
+XML
+
+    # whoof, ok.  got our request built.  lets ship it off.
+    if ($self->debug_level > 1) {
+        $self->debug("\nClient Request XML:\n" . '-' x 30);
+        $self->debug($xml);
+    }
+
+    $host = $self->{config}->{bulkhost} if $data->{batch};
+    $self->debug("Making request to $host...");
+    my $ua = LWP::UserAgent->new( timeout => 20, agent => "Net::OpenSRS/$VERSION" );
+    unless ($ua) {
+        $self->debug("Unable to contact remote host.");
+        return undef;
+    }
+
+    my $res = $ua->post( 
+        $host,
+        'Content-Type' => 'text/xml',
+        'X-Username'   => $self->{config}->{username},
+        'X-Signature'  => hash( hash( $xml, $key ), $key ),
+        'Content'      => $xml
+    );
+
+    my $struct;
+    if ( $res->is_success ) {
+        $self->debug("HTTP result: " . $res->status_line);
+        eval { $struct = XML::Simple::XMLin( $res->content); };
+
+        if ($self->debug_level > 1) {
+            $self->debug("\nOpenSRS Response XML:\n" . '-' x 30);
+            $self->debug($res->content);
+            $self->debug('');
+        }
+
+        # get the struct looking just how we want it.
+        # (de-nastify it.)
+        $xml = XML::Simple::XMLout( $struct->{body}->{data_block}->{dt_assoc}->{item} );
+        $struct = XML::Simple::XMLin( $xml );
+        $xml = XML::Simple::XMLout( $struct->{attributes}->{item} );
+        $struct->{attributes} = XML::Simple::XMLin( $xml );
+    }
+    else {
+        $self->debug("HTTP error: " . $res->status_line);
+        return undef;
+    }
+
+    $rv = $struct;
+    $self->_set_response;
+    return $self->last_response(1);
+}
+
+# format perl structs into opensrs XML
+sub _format
+{
+    my ($self, $val, $indent) = @_;
+    my $xml;
+
+    $indent ||= 6;
+    my $sp = ' ' x $indent;
+
+    if ( ref $val eq 'ARRAY' ) {
+        my $c = 0;
+        $xml .= "$sp<item key=\"$_\">\n";
+        $xml .= "$sp  <dt_array>\n";
+        foreach (sort @$val) {
+            $xml .= "$sp    <item key=\"$c\">$_</item>\n";
+            $c++;
+        }
+        $xml .= "$sp  </dt_array>\n";
+        $xml .= "$sp</item>\n";
+    }
+
+    elsif ( ref $val eq 'HASH' ) {
+        $xml .= "$sp<item key=\"$_\">\n";
+        $xml .= "$sp<dt_assoc>\n";
+        foreach (sort keys %$val) {
+            $xml .= $self->_format( $val->{$_} );
+        }
+        $xml .= "$sp</dt_assoc>\n";
+        $xml .= "$sp</item>\n";
+    }
+
+    else {
+        $xml .= "$sp<item key=\"$_\">$val</item>\n";
+    }
+
+    return $xml;
+}
+
+=back
+
+=head1 Author
+
+Mahlon E. Smith I<mahlon@martini.nu> for Spime Solutions Group
+I<(www.spime.net)>
+
+=cut
+
+1;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/net-opensrs/trunk/old_changelog	Fri Sep 05 05:15:48 2008 +0000
@@ -0,0 +1,46 @@
+# do not edit -- automatically generated by arch changelog
+# arch-tag: automatic-ChangeLog--mahlon@martini.nu--2003-code/net-opensrs--main--0.1
+#
+
+2006-04-08 22:28:27 GMT	Mahlon E. Smith <mahlon@martini.nu>	patch-2
+
+    Summary:
+      Bumped version to 0.02
+    Revision:
+      net-opensrs--main--0.1--patch-2
+
+
+    modified files:
+     Changes META.yml lib/Net/OpenSRS.pm
+
+
+2006-04-08 22:25:25 GMT	Mahlon E. Smith <mahlon@martini.nu>	patch-1
+
+    Summary:
+      Saner country handling
+    Revision:
+      net-opensrs--main--0.1--patch-1
+
+    * Allow countries to be two letter codes or the full country name.
+      Contributed by Sten Spans <sten@bit.nl>.
+    
+
+    modified files:
+     META.yml lib/Net/OpenSRS.pm
+
+
+2006-02-17 19:25:37 GMT	Mahlon E. Smith <mahlon@martini.nu>	base-0
+
+    Summary:
+      initial import
+    Revision:
+      net-opensrs--main--0.1--base-0
+
+    
+    (automatically generated log message)
+
+    new files:
+     Changes MANIFEST META.yml Makefile.PL README
+     lib/Net/OpenSRS.pm
+
+