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