Defer use of Net::OpenSRS so that failure to install the module doesn't stop
[freeside.git] / FS / FS / part_export / domreg_opensrs.pm
1 package FS::part_export::domreg_opensrs;
2
3 use vars qw(@ISA %info %options $conf);
4 use Tie::IxHash;
5 use FS::Record qw(qsearchs qsearch);
6 use FS::Conf;
7 use FS::part_export::null;
8 use FS::svc_domain;
9 use FS::part_pkg;
10
11 =head1 NAME
12
13 FS::part_export::domreg_opensrs - Register or transfer domains with Tucows OpenSRS
14
15 =head1 DESCRIPTION
16
17 This module handles registering and transferring domains using a registration service provider (RSP) account
18 at Tucows OpenSRS, an ICANN-approved domain registrar.
19
20 As a part_export, this module can be designated for use with svc_domain services.  When the svc_domain object
21 is inserted into the Freeside database, registration or transferring of the domain may be initiated, depending
22 on the setting of the svc_domain's action field.
23
24 =over 4
25
26 =item N - Register the domain
27
28 =item M - Transfer the domain
29
30 =item I - Ignore the domain for registration purposes
31
32 =back
33
34 =cut
35
36 @ISA = qw(FS::part_export::null);
37
38 my @tldlist = qw/com net org biz info name mobi at be ca cc ch cn de dk es eu fr it mx nl tv uk us/;
39
40 tie %options, 'Tie::IxHash',
41   'username'     => { label => 'Reseller user name at OpenSRS',
42                       },
43   'privatekey'   => { label => 'Private key',
44                       },
45   'password'     => { label => 'Password for management account',
46                       },
47   'masterdomain' => { label => 'Master domain at OpenSRS',
48                       },
49   'debug_level'  => { label => 'Net::OpenSRS debug level',
50                       type => 'select',
51                       options => [ 0, 1, 2, 3 ],
52                       default => 0 },
53 #  'register'     => { label => 'Use for registration',
54 #                      type => 'checkbox',
55 #                      default => '1' },
56 #  'transfer'     => { label => 'Use for transfer',
57 #                      type => 'checkbox',
58 #                      default => '1' },
59   'tlds'         => { label => 'Use this export for these top-level domains (TLDs)',
60                       type => 'select',
61                       multi => 1,
62                       size => scalar(@tldlist),
63                       options => [ @tldlist ],
64                       default => 'com net org' },
65 ;
66
67 %info = (
68   'svc'     => 'svc_domain',
69   'desc'    => 'Domain registration via Tucows OpenSRS',
70   'options' => \%options,
71   'notes'   => <<'END'
72 Registers and transfers domains via the <a href="http://opensrs.com/">Tucows OpenSRS</a> registrar (using <a href="http://search.cpan.org/dist/Net-OpenSRS">Net::OpenSRS</a>).
73 All of the Net::OpenSRS restrictions apply:
74 <UL>
75   <LI>You must have a reseller account with Tucows.
76   <LI>You must add the public IP address of the Freeside server to the 'Script API allow' list in the OpenSRS web interface.
77   <LI>You must generate an API access key in the OpenSRS web interface and enter it below.
78   <LI>All domains are managed using the same user name and password, but you can create sub-accounts for clients.
79   <LI>The user name must be the same as your OpenSRS reseller ID.
80   <LI>You must enter a master domain that all other domains are associated with.  That domain must be registered through your OpenSRS account.
81 </UL>
82 Some top-level domains offered by OpenSRS have additional business rules not supported by this export. These TLDs cannot be registered or transfered with this export.
83 <BR><BR>Use these buttons for some useful presets:
84 <UL>
85   <LI>
86     <INPUT TYPE="button" VALUE="OpenSRS Live System (rr-n1-tor.opensrs.net)" onClick='
87       document.dummy.machine.value = "rr-n1-tor.opensrs.net";
88       this.form.machine.value = "rr-n1-tor.opensrs.net";
89     '>
90   <LI>
91     <INPUT TYPE="button" VALUE="OpenSRS Test System (horizon.opensrs.net)" onClick='
92       document.dummy.machine.value = "horizon.opensrs.net";
93       this.form.machine.value = "horizon.opensrs.net";
94     '>
95 </UL>
96 END
97 );
98
99 install_callback FS::UID sub { 
100   $conf = new FS::Conf;
101 };
102
103 =head1 METHODS
104
105 =over 4
106
107 =item format_tel
108
109 Reformats a phone number according to registry rules.  Currently Freeside stores phone numbers
110 in NANPA format and the registry prefers "+CCC.NPANPXNNNN"
111
112 =cut
113
114 sub format_tel {
115   my $tel = shift;
116
117   #if ($tel =~ /^(\d{3})-(\d{3})-(\d{4})( x(\d+))?$/) {
118   if ($tel =~ /^(\d{3})-(\d{3})-(\d{4})$/) {
119     $tel = "+1.$1$2$3";
120 #    if $tel .= "$4" if $4;
121   }
122   return $tel;
123 }
124
125 sub gen_contact_info
126 {
127   my ($co)=@_;
128
129   my @invoicing_list = $co->invoicing_list_emailonly;
130   if ( $conf->exists('emailinvoiceautoalways')
131        || $conf->exists('emailinvoiceauto') && ! @invoicing_list
132        || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
133     push @invoicing_list, $co->all_emails;
134   }
135
136   my $email = ($conf->exists('business-onlinepayment-email-override'))
137               ? $conf->config('business-onlinepayment-email-override')
138               : $invoicing_list[0];
139
140   my $c = {
141     firstname => $co->first,
142     lastname  => $co->last,
143     company   => $co->company,
144     address   => $co->address1,
145     city      => $co->city(),
146     state     => $co->state(),
147     zip       => $co->zip(),
148     country   => uc($co->country()),
149     email     => $email,
150     #phone     => format_tel($co->daytime()),
151     phone     => $co->daytime() || $co->night,
152   };
153   return $c;
154 }
155
156 sub validate_contact_info {
157   my $c = shift;
158
159   my %fields = (
160     firstname => "first name",
161     lastname => "last name",
162     address => "street address",
163     city => "city", 
164     state => "state",
165     zip => "ZIP/postal code",
166     country => "country",
167     email => "email address",
168     phone => "phone number",
169   );
170   my @err = ();
171   foreach (keys %fields) {
172     if (!defined($c->{$_}) || !$c->{$_}) {
173       push @err, $fields{$_};
174     }
175   }
176   if (scalar(@err) > 0) {
177     return "Contact information needs: " . join(', ', @err);
178   }
179   undef;
180 }
181
182 sub testmode {
183   my $self = shift;
184
185   return 'live' if $self->machine eq "rr-n1-tor.opensrs.net";
186   return 'test' if $self->machine eq "horizon.opensrs.net";
187   undef;
188 }
189
190 sub _export_insert {
191   my( $self, $svc_domain ) = ( shift, shift );
192
193   return if $svc_domain->action eq 'I';  # Ignoring registration, just doing DNS
194
195   eval "use Net::OpenSRS;";
196   return $@ if $@;
197
198   # Get the TLD of the new domain
199   my @bits = split /\./, $svc_domain->domain;
200
201   return "Can't register subdomains: " . $svc_domain->domain if scalar(@bits) != 2;
202
203   my $tld = pop @bits;
204
205   # See if it's one this export supports
206   my @tlds = split /\s+/, $self->option('tlds');
207   @tlds =  map { s/\.//; $_ } @tlds;
208   return "Can't register top-level domain $tld, restricted to: " . $self->option('tlds') if ! grep { $_ eq $tld } @tlds;
209
210   my $cust_main = $svc_domain->cust_svc->cust_pkg->cust_main;
211
212   my $c = gen_contact_info($cust_main);
213
214   my $err = validate_contact_info($c);
215   return $err if $err;
216
217   my $srs = Net::OpenSRS->new();
218
219   $srs->debug_level( $self->option('debug_level') ); # Output should be in the Apache error log
220
221   $srs->environment( $self->testmode() );
222   $srs->set_key( $self->option('privatekey') );
223
224   $srs->set_manage_auth( $self->option('username'), $self->option('password') );
225
226   my $cookie = $srs->get_cookie( $self->option('masterdomain') );
227   if (!$cookie) {
228      return "Unable to get cookie at OpenSRS: " . $srs->last_response();
229   }
230
231   if ($svc_domain->action eq 'N') {
232 #    return "Domain registration not enabled" if !$self->option('register');
233     return $srs->last_response() if !$srs->register_domain( $svc_domain->domain, $c);
234   } elsif ($svc_domain->action eq 'M') {
235 #    return "Domain transfer not enabled" if !$self->option('transfer');
236     return $srs->last_response() if !$srs->transfer_domain( $svc_domain->domain, $c);
237   } else {
238     return "Unknown domain action " . $svc_domain->action;
239   }
240
241   return ''; # Should only get here if register or transfer succeeded
242
243 }
244
245 ## Domain registration exports do nothing on replace.  Mainly because we haven't decided what they should do.
246 #sub _export_replace {
247 #  my( $self, $new, $old ) = (shift, shift, shift);
248 #
249 #  return '';
250 #
251 #}
252
253 ## Domain registration exports do nothing on delete.  You're just removing the domain from Freeside, not the registry
254 #sub _export_delete {
255 #  my( $self, $svc_domain ) = ( shift, shift );
256 #
257 #  return '';
258 #}
259
260 sub registrar {
261   return {
262         name => 'OpenSRS',
263   };
264 }
265
266 =back
267
268 =head1 SEE ALSO
269
270 L<FS::part_export_option>, L<FS::export_svc>, L<FS::svc_domain>,
271 L<FS::Record>, schema.html from the base documentation.
272
273 =cut
274
275 1;
276