escape ("percent-encode") : and @ in netsapeins URLs, RT#12447
[freeside.git] / FS / FS / part_export / netsapiens.pm
1 package FS::part_export::netsapiens;
2
3 use vars qw(@ISA $me %info);
4 use URI; #needed?
5 use URI::Escape;
6 use MIME::Base64;
7 use Tie::IxHash;
8 use FS::part_export;
9
10 @ISA = qw(FS::part_export);
11 $me = '[FS::part_export::netsapiens]';
12
13 tie my %options, 'Tie::IxHash',
14   'login'           => { label=>'NetSapiens tac2 User API username' },
15   'password'        => { label=>'NetSapiens tac2 User API password' },
16   'url'             => { label=>'NetSapiens tac2 User URL' },
17   'device_login'    => { label=>'NetSapiens tac2 Device API username' },
18   'device_password' => { label=>'NetSapiens tac2 Device API password' },
19   'device_url'      => { label=>'NetSapiens tac2 Device URL' },
20   'domain'          => { label=>'NetSapiens Domain' },
21   'debug'           => { label=>'Enable debugging', type=>'checkbox' },
22 ;
23
24 %info = (
25   'svc'      => [ 'svc_phone', ], # 'part_device',
26   'desc'     => 'Provision phone numbers to NetSapiens',
27   'options'  => \%options,
28   'notes'    => <<'END'
29 Requires installation of
30 <a href="http://search.cpan.org/dist/REST-Client">REST::Client</a>
31 from CPAN.
32 END
33 );
34
35 sub rebless { shift; }
36
37 sub ns_command {
38   my $self = shift;
39   $self->_ns_command('', @_);
40 }
41
42 sub ns_device_command { 
43   my $self = shift;
44   $self->_ns_command('device_', @_);
45 }
46
47 sub _ns_command {
48   my( $self, $prefix, $method, $command ) = splice(@_,0,4);
49
50   eval 'use REST::Client';
51   die $@ if $@;
52
53   my $ns = new REST::Client 'host'=>$self->option($prefix.'url');
54
55   my @args = ( $command );
56
57   if ( $method eq 'PUT' ) {
58     my $content = $ns->buildQuery( { @_ } );
59     $content =~ s/^\?//;
60     push @args, $content;
61   } elsif ( $method eq 'GET' ) {
62     $args[0] .= $ns->buildQuery( { @_ } );
63   }
64
65   warn "$me $method ". $self->option($prefix.'url'). join(', ', @args). "\n"
66     if $self->option('debug');
67
68   my $auth = encode_base64( $self->option($prefix.'login'). ':'.
69                             $self->option($prefix.'password')    );
70   push @args, { 'Authorization' => "Basic $auth" };
71
72   $ns->$method( @args );
73   $ns;
74 }
75
76 sub ns_domain {
77   my($self, $svc_phone) = (shift, shift);
78   $svc_phone->domain || $self->option('domain');
79 }
80
81 sub ns_subscriber {
82   my($self, $svc_phone) = (shift, shift);
83
84   my $domain = $self->ns_domain($svc_phone);
85   my $phonenum = $svc_phone->phonenum;
86
87   "/domains_config/$domain/subscriber_config/$phonenum";
88 }
89
90 sub ns_registrar {
91   my($self, $svc_phone) = (shift, shift);
92
93   $self->ns_subscriber($svc_phone).
94     '/registrar_config/'. uri_escape($self->ns_devicename($svc_phone));
95 }
96
97 sub ns_devicename {
98   my( $self, $svc_phone ) = (shift, shift);
99
100   my $domain = $self->ns_domain($svc_phone);
101   #my $countrycode = $svc_phone->countrycode;
102   my $phonenum    = $svc_phone->phonenum;
103
104   #"sip:$countrycode$phonenum\@$domain";
105   "sip:$phonenum\@$domain";
106 }
107
108 sub ns_dialplan {
109   my($self, $svc_phone) = (shift, shift);
110
111   #my $countrycode = $svc_phone->countrycode;
112   my $phonenum    = $svc_phone->phonenum;
113
114   #"/dialplans/DID+Table/dialplan_config/sip:$countrycode$phonenum\@*"
115   "/dialplans/DID+Table/dialplan_config/". uri_escape("sip:$phonenum\@*")
116 }
117
118 sub ns_device {
119   my($self, $svc_phone, $phone_device ) = (shift, shift, shift);
120
121   #my $countrycode = $svc_phone->countrycode;
122   #my $phonenum    = $svc_phone->phonenum;
123
124   "/phones_config/". lc($phone_device->mac_addr);
125 }
126
127 sub ns_create_or_update {
128   my($self, $svc_phone, $dial_policy) = (shift, shift, shift);
129
130   my $domain = $self->ns_domain($svc_phone);
131   #my $countrycode = $svc_phone->countrycode;
132   my $phonenum    = $svc_phone->phonenum;
133
134   my( $firstname, $lastname );
135   if ( $svc_phone->phone_name =~ /^\s*(\S+)\s+(\S.*\S)\s*$/ ) {
136     $firstname = $1;
137     $lastname  = $2;
138   } else {
139     #deal w/unaudited netsapiens services?
140     my $cust_main = $svc_phone->cust_svc->cust_pkg->cust_main;
141     $firstname = $cust_main->get('first');
142     $lastname  = $cust_main->get('last');
143   }
144
145   # Piece 1 (already done) - User creation
146
147   my $ns = $self->ns_command( 'PUT', $self->ns_subscriber($svc_phone), 
148     'subscriber_login' => $phonenum.'@'.$domain,
149     'firstname'        => $firstname,
150     'lastname'         => $lastname,
151     'subscriber_pin'   => $svc_phone->pin,
152     'dial_plan'        => 'Default', #config?
153     'dial_policy'      => $dial_policy,
154   );
155
156   if ( $ns->responseCode !~ /^2/ ) {
157      return $ns->responseCode. ' '.
158             join(', ', $self->ns_parse_response( $ns->responseContent ) );
159   }
160
161   #Piece 2 - sip device creation 
162
163   my $ns2 = $self->ns_command( 'PUT', $self->ns_registrar($svc_phone),
164     'termination_match' => $self->ns_devicename($svc_phone)
165   );
166
167   if ( $ns2->responseCode !~ /^2/ ) {
168      return $ns2->responseCode. ' '.
169             join(', ', $self->ns_parse_response( $ns2->responseContent ) );
170   }
171
172   #Piece 3 - DID mapping to user
173
174   my $ns3 = $self->ns_command( 'PUT', $self->ns_dialplan($svc_phone),
175     'to_user' => $phonenum,
176     'to_host' => $domain,
177   );
178
179   if ( $ns3->responseCode !~ /^2/ ) {
180      return $ns3->responseCode. ' '.
181             join(', ', $self->ns_parse_response( $ns3->responseContent ) );
182   }
183
184   '';
185 }
186
187 sub ns_delete {
188   my($self, $svc_phone) = (shift, shift);
189
190   my $ns = $self->ns_command( 'DELETE', $self->ns_subscriber($svc_phone) );
191
192   #delete other things?
193
194   if ( $ns->responseCode !~ /^2/ ) {
195      return $ns->responseCode. ' '.
196             join(', ', $self->ns_parse_response( $ns->responseContent ) );
197   }
198
199   '';
200
201 }
202
203 sub ns_parse_response {
204   my( $self, $content ) = ( shift, shift );
205
206   #try to screen-scrape something useful
207   tie my %hash, Tie::IxHash;
208   while ( $content =~ s/^.*?<p>\s*<b>(.+?)<\/b>\s*(.+?)\s*<\/p>//is ) {
209     ( $hash{$1} = $2 ) =~ s/^\s*<(\w+)>(.+?)<\/\1>/$2/is;
210   }
211
212   %hash;
213 }
214
215 sub _export_insert {
216   my($self, $svc_phone) = (shift, shift);
217   $self->ns_create_or_update($svc_phone, 'Permit All');
218 }
219
220 sub _export_replace {
221   my( $self, $new, $old ) = (shift, shift, shift);
222   return "can't change phonenum with NetSapiens (unprovision and reprovision?)"
223     if $old->phonenum ne $new->phonenum;
224   $self->_export_insert($new);
225 }
226
227 sub _export_delete {
228   my( $self, $svc_phone ) = (shift, shift);
229
230   $self->ns_delete($svc_phone);
231 }
232
233 sub _export_suspend {
234   my( $self, $svc_phone ) = (shift, shift);
235   $self->ns_create_or_update($svc_phone, 'Deny');
236 }
237
238 sub _export_unsuspend {
239   my( $self, $svc_phone ) = (shift, shift);
240   #$self->ns_create_or_update($svc_phone, 'Permit All');
241   $self->_export_insert($svc_phone);
242 }
243
244 sub export_device_insert {
245   my( $self, $svc_phone, $phone_device ) = (shift, shift, shift);
246
247   my $domain = $self->ns_domain($svc_phone);
248   my $countrycode = $svc_phone->countrycode;
249   my $phonenum    = $svc_phone->phonenum;
250   #my $device = $self->ns_devicename($svc_phone);
251
252   my $ns = $self->ns_device_command(
253     'PUT', $self->ns_device($svc_phone, $phone_device),
254       'line1_enable' => 'yes',
255       'device1'      => $self->ns_devicename($svc_phone),
256       'line1_ext'    => $phonenum,
257 ,
258       #'line2_enable' => 'yes',
259       #'device2'      =>
260       #'line2_ext'    =>
261
262       #'notes' => 
263       'server'       => 'SiPbx',
264       'domain'       => $domain,
265
266       'brand'        => $phone_device->part_device->devicename,
267       
268   );
269
270   if ( $ns->responseCode !~ /^2/ ) {
271      return $ns->responseCode. ' '.
272             join(', ', $self->ns_parse_response( $ns->responseContent ) );
273   }
274
275   '';
276
277 }
278
279 sub export_device_delete {
280   my( $self, $svc_phone, $phone_device ) = (shift, shift, shift);
281
282   my $ns = $self->ns_device_command(
283     'DELETE', $self->ns_device($svc_phone, $phone_device),
284   );
285
286   if ( $ns->responseCode !~ /^2/ ) {
287      return $ns->responseCode. ' '.
288             join(', ', $self->ns_parse_response( $ns->responseContent ) );
289   }
290
291   '';
292
293 }
294
295
296 sub export_device_replace {
297   my( $self, $svc_phone, $new_phone_device, $old_phone_device ) =
298     (shift, shift, shift, shift);
299
300   #?
301   $self->export_device_insert( $svc_phone, $new_phone_device );
302
303 }
304
305 sub export_links {
306   my($self, $svc_phone, $arrayref) = (shift, shift, shift);
307   #push @$arrayref, qq!<A HREF="http://example.com/~!. $svc_phone->username.
308   #                 qq!">!. $svc_phone->username. qq!</A>!;
309   '';
310 }
311
312 1;