default to a session cookie instead of setting an explicit timeout, weird timezone...
[freeside.git] / FS / FS / part_export / grandstream.pm
1 package FS::part_export::grandstream;
2
3 use base 'FS::part_export';
4 use vars qw($DEBUG $me %info $GAPSLITE_HOME $JAVA_HOME);
5 use URI;
6 use MIME::Base64;
7 use Tie::IxHash;
8 use IPC::Run qw(run);
9 use FS::CGI qw(rooturl);
10 use Carp qw(carp);
11
12 $DEBUG = 0;
13
14 $me = '[' . __PACKAGE__ . ']';
15 $GAPSLITE_HOME = '/usr/local/src/GS_CFG_GEN/';
16
17 my @java = qw( /usr/lib/jvm/default-java/ /usr/java/default/
18                /usr/lib/jvm/java-6-sun/
19                /usr/lib/jvm/java-1.4.2-gcj-4.1-1.4.2.0/
20              ); #add more common places distros and people put their JREs
21
22 $JAVA_HOME = (grep { -e $_ } @java)[0];
23
24 tie my %options, 'Tie::IxHash',
25   'upload'          => { label=>'Enable upload to TFTP server via SSH',
26                          type=>'checkbox',
27                        },
28   'user'            => { label=>'User name for SSH to TFTP server' },
29   'tftproot'        => { label=>'Directory in which to upload configuration' },
30   'java_home'       => { label=>'Path to java to be used',
31                          default=>$JAVA_HOME,
32                        },
33   'gapslite_home'   => { label=>'Path to grandstream configuration tool',
34                          default=>$GAPSLITE_HOME,
35                        },
36   'template'        => { label=>'Configuration template',
37                          type=>'textarea',
38                          notes=>'Type or paste the configuration template here',
39                        },
40 ;
41
42 %info = (
43   'svc'      => [ qw( part_device ) ], # svc_phone
44   'desc'     => 'Provision phone numbers to Grandstream Networks phones/ATAs',
45   'options'  => \%options,
46   'notes'    => 'Provision phone numbers to Grandstream Networks phones/ATAs.  Requires a Java runtime environment and the Grandstream configuration tool to be installed.',
47 );
48
49 sub rebless { shift; }
50
51 sub gs_create_config {
52   my($self, $mac, %opt) = (@_);
53
54   if ( $FS::svc_Common::noexport_hack ) {
55     carp 'gs_create_config() suppressed by noexport_hack'
56       if $self->option('debug') || $DEBUG;
57     return;
58   }
59
60   eval "use Net::SCP;";
61   die $@ if $@;
62
63   warn "gs_create_config called with mac of $mac\n" if $DEBUG;
64   $mac = sprintf('%012s', lc($mac));
65   my $dir = '%%%FREESIDE_CONF%%%/cache.'. $FS::UID::datasrc;
66
67   my $fh = new File::Temp(
68     TEMPLATE => "grandstream.$mac.XXXXXXXX",
69     DIR      => $dir,
70     UNLINK   => 0,
71   );
72
73   my $filename = $fh->filename;
74
75   #my $template = new Text::Template (
76   #  TYPE       => 'ARRAY',
77   #  SOURCE     => $self->option('template'),
78   #  DELIMITERS => $delimiters,
79   #  OUTPUT     => $fh,
80   #);
81
82   #$template->compile or die "Can't compile template: $Text::Template::ERROR\n";
83
84   #my $config = $template->fill_in( HASH => { mac_addr => $mac } );
85
86   print $fh $self->option('template') or die "print failed: $!";
87   close $fh;
88
89   #system( "export GAPSLITE_HOME=$GAPSLITE_HOME; export JAVA_HOME=$JAVA_HOME; ".
90   #        "cd $dir; $GAPSLITE_HOME/bin/encode.sh $mac $filename $dir/cfg$mac"
91   #      ) == 0
92   #  or die "grandstream encode failed: $!";
93   my $out_and_err = '';
94   my @cmd = ( "$JAVA_HOME/bin/java",
95               '-classpath', "$GAPSLITE_HOME/lib/gapslite.jar:$GAPSLITE_HOME/lib/bcprov-jdk14-124.jar:$GAPSLITE_HOME/config",
96               'com.grandstream.cmd.TextEncoder',
97               $mac, $filename, "$dir/cfg$mac",
98             );
99   run \@cmd, '>&', \$out_and_err
100     or die "grandstream encode failed: $out_and_err";
101
102   unlink $filename;
103
104   open my $encoded, "$dir/cfg$mac"  or die "open cfg$mac failed: $!";
105   
106   my $content;
107
108   if ($opt{upload}) {
109     if ($self->option('upload')) {
110       my $scp = new Net::SCP ( {
111         'host' => $self->machine,
112         'user' => $self->option('user'),
113         'cwd'  => $self->option('tftproot'),
114       } );
115
116       $scp->put( "$dir/cfg$mac" ) or die "upload failed: ". $scp->errstr;
117     }
118   } else {
119     local $/;
120     $content = <$encoded>;
121   }
122
123   close $encoded;
124   unlink "$dir/cfg$mac";
125
126   $content;
127 }
128
129 sub gs_create {
130   my($self, $mac) = (shift, shift);
131
132   return unless $mac;  # be more alarmed?  Or check upstream?
133
134   $self->gs_create_config($mac, 'upload' => 1);
135   '';
136 }
137
138 sub gs_delete {
139   my($self, $mac) = (shift, shift);
140
141   if ( $FS::svc_Common::noexport_hack ) {
142     carp 'gs_delete() suppressed by noexport_hack'
143       if $self->option('debug') || $DEBUG;
144     return;
145   }
146
147   $mac = sprintf('%012s', lc($mac));
148
149   ssh_cmd( user => $self->option('user'),
150            host => $self->machine,
151            command => 'rm',
152            args    => [ '-f', $self->option('tftproot'). "/cfg$mac" ],
153          );
154   '';
155
156 }
157
158 sub ssh_cmd { #subroutine, not method
159   use Net::SSH '0.08';
160   &Net::SSH::ssh_cmd( { @_ } );
161 }
162
163 sub _export_insert {
164 #  my( $self, $svc_phone ) = (shift, shift);
165 #  $self->gs_create($svc_phone->mac_addr);
166   '';
167 }
168
169 sub _export_replace {
170 #  my( $self, $new_svc, $old_svc ) = (shift, shift, shift);
171 #  $self->gs_delete($old_svc->mac_addr);
172 #  $self->gs_create($new_svc->mac_addr);
173   '';
174 }
175
176 sub _export_delete {
177 #  my( $self, $svc_phone ) = (shift, shift);
178 #  $self->gs_delete($svc_phone->mac_addr);
179   '';
180 }
181
182 sub _export_suspend {
183   '';
184 }
185
186 sub _export_unsuspend {
187   '';
188 }
189
190 sub export_device_insert {
191   my( $self, $svc_phone, $phone_device ) = (shift, shift, shift);
192   $self->gs_create($phone_device->mac_addr);
193   '';
194 }
195
196 sub export_device_delete {
197   my( $self, $svc_phone, $phone_device ) = (shift, shift, shift);
198   $self->gs_delete($phone_device->mac_addr);
199   '';
200 }
201
202 sub export_device_config {
203   my( $self, $svc_phone, $phone_device ) = (shift, shift, shift);
204
205   my $mac;
206 #  if ($phone_device) {
207     $mac = $phone_device->mac_addr;
208 #  } else {
209 #    $mac = $svc_phone->mac_addr;
210 #  }
211
212   return '' unless $mac;  # be more alarmed?  Or check upstream?
213
214   $self->gs_create_config($mac);
215 }
216
217
218 sub export_device_replace {
219   my( $self, $svc_phone, $new_svc_or_device, $old_svc_or_device ) =
220     (shift, shift, shift, shift);
221
222   $self->gs_delete($old_svc_or_device->mac_addr);
223   $self->gs_create($new_svc_or_device->mac_addr);
224   '';
225 }
226
227 # bad overloading?
228 sub export_links {
229   my($self, $svc_phone, $arrayref) = (shift, shift, shift);
230
231   return;  # remove if we actually support being an export for svc_phone;
232
233   my @deviceparts = map { $_->devicepart } $self->export_device;
234   my @devices = grep { my $part = $_->devicepart;
235                        scalar( grep { $_ == $part } @deviceparts );
236                      } $svc_phone->phone_device;
237
238   my $export = $self->exportnum;
239   my $fsurl = rooturl();
240   if (@devices) {
241     foreach my $device ( @devices ) {
242       next unless $device->mac_addr;
243       my $num = $device->devicenum;
244       push @$arrayref,
245         qq!<A HREF="$fsurl/misc/phone_device_config.html?exportnum=$export;devicenum=$num">!.
246         qq! Phone config </A>!;
247       }
248   } elsif ($svc_phone->mac_addr) {
249     my $num = $svc_phone->svcnum;
250     push @$arrayref,
251       qq!<A HREF="$fsurl/misc/phone_device_config.html?exportnum=$export;svcnum=$num">!.
252       qq! Phone config </A>!;
253   } #else
254   '';
255 }
256
257 sub export_device_links {
258   my($self, $svc_phone, $device, $arrayref) = (shift, shift, shift, shift);
259   warn "export_device_links $self $svc_phone $device $arrayref\n" if $DEBUG;
260   return unless $device && $device->mac_addr;
261   my $export = $self->exportnum;
262   my $fsurl = rooturl();
263   my $num = $device->devicenum;
264   push @$arrayref,
265     qq!<A HREF="$fsurl/misc/phone_device_config.html?exportnum=$export;devicenum=$num">!.
266     qq! Phone config </A>!;
267   '';
268 }
269
270 1;