nibblebill integrateion, RT#19587
[freeside.git] / FS / FS / part_export / freeswitch.pm
1 package FS::part_export::freeswitch;
2 use base qw( FS::part_export );
3
4 use vars qw( %info ); # $DEBUG );
5 #use Data::Dumper;
6 use Tie::IxHash;
7 use Text::Template;
8 use FS::Record qw( qsearch ); #qsearchs );
9 use FS::svc_phone;
10 #use FS::Schema qw( dbdef );
11
12 #$DEBUG = 1;
13
14 tie my %options, 'Tie::IxHash',
15   'user'  => { label => 'SSH username', default=>'root', },
16   'directory' => { label   => 'Directory to store FreeSWITCH account XML files',
17                    default => '/usr/local/freeswitch/conf/directory/',
18                  },
19   #'domain'    => { label => 'Optional fixed SIP domain to use, overrides svc_phone domain', },
20   'reload'    => { label   => 'Reload command',
21                    default => '/usr/local/freeswitch/bin/fs_cli -x reloadxml',
22                  },
23   'user_template' => { label   => 'User XML configuration template',
24                        type    => 'textarea',
25                        default => <<'END',
26 <domain name="<% $domain %>">
27   <user id="<% $phonenum %>">
28     <params>
29       <param name="password" value="<% $sip_password %>"/>
30       <param name="nibble_account" value="<% $phonenum %>"/>
31       <param name="nibble_rate" value="<% $nibble_rate %>"/>
32     </params>
33   </user>
34 </domain>
35 END
36                      },
37 ;
38
39 %info = (
40   'svc'     => 'svc_phone',
41   'desc'    => 'Provision phone services to FreeSWITCH XML configuration files',
42   'options' => \%options,
43   'notes'   => <<'END',
44 Export XML account configuration files to FreeSWITCH, one per domain.
45 <br><br>
46 You will need to enable the svc_phone-domain configuration setting and
47 <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.9:Documentation:Administration:SSH_Keys">setup SSH for unattended operation</a>.
48 END
49 );
50
51 sub rebless { shift; }
52
53 sub _export_insert {
54   my( $self, $svc_phone ) = ( shift, shift );
55
56   $self->_export_rebuild_domain($svc_phone);
57
58 }
59
60 sub _export_replace {
61   my( $self, $new, $old ) = ( shift, shift, shift );
62
63   my $error = $self->_export_rebuild_domain($new);
64   return $error if $error;
65
66   if ( $new->domsvc ne $old->domsvc && $old->domsvc ) {
67     $error = $self->_export_rebuild_domain($old);
68     return $error if $error;
69   }
70
71   '';
72 }
73
74 sub _export_delete {
75   my( $self, $svc_phone ) = ( shift, shift );
76
77   $self->_export_rebuild_domain($svc_phone);
78 }
79
80 sub _export_rebuild_domain {
81   my($self, $svc_phone) = ( shift, shift );
82
83   eval "use Net::SCP;";
84   die $@ if $@;
85
86   #create and copy over file
87
88   my $tempdir = '%%%FREESIDE_CONF%%%/cache.'. $FS::UID::datasrc;
89
90   my $domain = $svc_phone->domain or return "domain required";
91
92   my $fh = new File::Temp(
93     TEMPLATE => "$tempdir/freeswitch.$domain.XXXXXXXX",
94     DIR      => $dir,
95     #UNLINK   => 0,
96   );
97
98   print $fh qq(<domain name="$domain">\n);
99
100   my @dom_svc_phone = qsearch( 'svc_phone', { 'domsvc'=>$svc_phone->domsvc } );
101
102   foreach my $dom_svc_phone (@dom_svc_phone) {
103
104     print $fh $self->freeswitch_template_fillin( $dom_svc_phone, 'user' )
105       or die "print to freeswitch template failed: $!";
106
107   }
108
109   print $fh qq(</domain>\n);
110   $fh->flush;
111
112   my $scp = new Net::SCP;
113   my $user = $self->option('user')||'root';
114   my $host = $self->machine;
115   my $dir = $self->option('directory');
116
117   $scp->scp( $fh->filename, "$user\@$host:$dir/$domain.xml" )
118     or return $scp->{errstr};
119
120   #signal freeswitch to reload config
121   $self->freeswitch_ssh( command => $self->option('reload') );
122
123   '';
124
125 }
126
127 sub freeswitch_template_fillin {
128   my( $self, $svc_phone, $template ) = (shift, shift, shift);
129
130   $template ||= 'user'; #?
131
132   #cache a %tt hash?
133   my $tt = new Text::Template (
134     TYPE       => 'STRING',
135     SOURCE     => $self->option($template.'_template'),
136     DELIMITERS => [ '<%', '%>' ],
137   );
138
139   my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
140   my $nibble_rate = $cust_pkg ? $cust_pkg->part_pkg->option('nibble_rate')
141                               : '';
142
143   #false lazinessish w/phone_shellcommands::_export_command
144   my %hash = (
145     'nibble_rate' => $nibble_rate,
146     map { $_ => $svc_phone->getfield($_) } $svc_phone->fields
147   );
148
149   #might as well do em all, they're all going in an XML file as attribs
150   foreach ( keys %hash ) {
151     $hash{$_} =~ s/'/&apos;/g;
152     $hash{$_} =~ s/"/&quot;/g;
153   }
154
155   $tt->fill_in(
156     HASH => \%hash,
157   );
158 }
159
160 ##a good idea to queue anything that could fail or take any time
161 #sub shellcommands_queue {
162 #  my( $self, $svcnum ) = (shift, shift);
163 #  my $queue = new FS::queue {
164 #    'svcnum' => $svcnum,
165 #    'job'    => "FS::part_export::freeswitch::ssh_cmd",
166 #  };
167 #  $queue->insert( @_ );
168 #}
169
170 sub freeswitch_ssh { #method
171   my $self = shift;
172   ssh_cmd( user    => $self->option('user')||'root',
173            host    => $self->machine,
174            @_,
175          );
176 }
177
178 sub ssh_cmd { #subroutine, not method
179   use Net::OpenSSH;
180   my $opt = { @_ };
181   open my $def_in, '<', '/dev/null' or die "unable to open /dev/null";
182   my $ssh = Net::OpenSSH->new( $opt->{'user'}.'@'.$opt->{'host'},
183                                default_stdin_fh => $def_in,
184                              );
185   die "Couldn't establish SSH connection: ". $ssh->error if $ssh->error;
186   my ($output, $errput) = $ssh->capture2( #{stdin_discard => 1},
187                                           $opt->{'command'}
188                                         );
189   die "Error running SSH command: ". $ssh->error if $ssh->error;
190
191   #who the fuck knows what freeswitch reload outputs, probably a fucking
192   # ascii advertisement for cluecon
193   #die $errput if $errput;
194   #die $output if $output;
195
196   '';
197 }
198
199 1;