diff options
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/AccessRight.pm | 2 | ||||
-rw-r--r-- | FS/FS/Conf.pm | 4 | ||||
-rw-r--r-- | FS/FS/Cron/backup.pm | 2 | ||||
-rw-r--r-- | FS/FS/Cron/rt_tasks.pm | 2 | ||||
-rw-r--r-- | FS/FS/Record.pm | 4 | ||||
-rw-r--r-- | FS/FS/TaxEngine/suretax.pm | 11 | ||||
-rw-r--r-- | FS/FS/access_right.pm | 5 | ||||
-rw-r--r-- | FS/FS/access_user_session_log.pm | 124 | ||||
-rw-r--r-- | FS/FS/cust_main/Billing_Realtime.pm | 1 | ||||
-rw-r--r-- | FS/FS/cust_main/Import.pm | 29 | ||||
-rw-r--r-- | FS/FS/cust_main_Mixin.pm | 11 | ||||
-rw-r--r-- | FS/FS/part_export/broadband_shellcommands.pm | 41 | ||||
-rw-r--r-- | FS/FS/part_export/broadband_shellcommands_expect.pm | 19 | ||||
-rw-r--r-- | FS/FS/part_export/pbxware.pm | 4 | ||||
-rw-r--r-- | FS/FS/part_export/shellcommands.pm | 93 | ||||
-rw-r--r-- | FS/FS/part_export/shellcommands_expect.pm | 128 | ||||
-rw-r--r-- | FS/FS/part_export/vitelity.pm | 2 | ||||
-rwxr-xr-x | FS/bin/freeside-voipinnovations-cdrimport | 12 | ||||
-rw-r--r-- | FS/t/access_user_session_log.t | 5 |
19 files changed, 412 insertions, 87 deletions
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm index 161e466a2..ccabf27fd 100644 --- a/FS/FS/AccessRight.pm +++ b/FS/FS/AccessRight.pm @@ -329,7 +329,7 @@ tie my %rights, 'Tie::IxHash', 'Usage: Unrateable CDRs', 'Usage: Time worked', #gone in 4.x as a distinct ACL (for now?) { rightname=>'Employees: Commission Report', global=>1 }, - { rightname=>'Employees: Audit Report', global=>1 }, + { rightname=>'Employee Reports', global=>1 }, #{ rightname => 'List customers of all agents', global=>1 }, ], diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index bddeee932..715733667 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -5796,8 +5796,8 @@ and customer address. Include units.', { 'key' => 'logout-timeout', - 'section' => 'UI', - 'description' => 'If set, automatically log users out of the backoffice after this many minutes.', + 'section' => 'deprecated', + 'description' => 'Deprecated. Used to automatically log users out of the backoffice after this many minutes. Set session timeouts in employee groups instead.', 'type' => 'text', }, diff --git a/FS/FS/Cron/backup.pm b/FS/FS/Cron/backup.pm index 7d868c882..5276565c5 100644 --- a/FS/FS/Cron/backup.pm +++ b/FS/FS/Cron/backup.pm @@ -25,7 +25,7 @@ sub backup { my $ext; if ( driver_name eq 'Pg' ) { - system("pg_dump -Fc $database >/var/tmp/$database.Pg"); + system("pg_dump -Fc -T h_cdr -T h_queue -T h_queue_arg $database >/var/tmp/$database.Pg"); $ext = 'Pg'; } elsif ( driver_name eq 'mysql' ) { system("mysqldump $database >/var/tmp/$database.sql"); diff --git a/FS/FS/Cron/rt_tasks.pm b/FS/FS/Cron/rt_tasks.pm index 01ea0b5dd..077f23cc6 100644 --- a/FS/FS/Cron/rt_tasks.pm +++ b/FS/FS/Cron/rt_tasks.pm @@ -31,6 +31,8 @@ sub rt_daily { my $system = $FS::TicketSystem::system; return if !defined($system) || $system ne 'RT_Internal'; + system('/opt/rt3/sbin/rt-fulltext-indexer --quiet --limit 5400 &'); + # if -d or -y is in use, bail out. There's no reliable way to tell RT # to use an alternate system time. if ( $opt{'d'} or $opt{'y'} ) { diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index f2e9e6fba..479f9b1f1 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -2647,7 +2647,7 @@ sub ut_currency { =item ut_text COLUMN Check/untaint text. Alphanumerics, spaces, and the following punctuation -symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? / = [ ] < > +symbols are currently permitted: ! @ # $ % & ( ) - + ; : ' " , . ? / = [ ] < > ~ May not be null. If there is an error, returns the error, otherwise returns false. @@ -2661,7 +2661,7 @@ sub ut_text { # \p{Word} = alphanumerics, marks (diacritics), and connectors # see perldoc perluniprops $self->getfield($field) - =~ /^([\p{Word} \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]\<\>$money_char]+)$/ + =~ /^([\p{Word} \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]\<\>\~$money_char]+)$/ or return gettext('illegal_or_empty_text'). " $field: ". $self->getfield($field); $self->setfield($field,$1); diff --git a/FS/FS/TaxEngine/suretax.pm b/FS/FS/TaxEngine/suretax.pm index 1a00cdaaa..fe8764bf1 100644 --- a/FS/FS/TaxEngine/suretax.pm +++ b/FS/FS/TaxEngine/suretax.pm @@ -14,7 +14,7 @@ our $DEBUG = 1; # prints progress messages # $DEBUG = 2; # prints decoded request and response (noisy, be careful) # $DEBUG = 3; # prints raw response from the API, ridiculously unreadable -our $json = Cpanel::JSON::XS->new->pretty(1); +our $json = Cpanel::JSON::XS->new->pretty(0)->shrink(1); our %taxproduct_cache; @@ -328,13 +328,14 @@ sub make_taxlines { return; } - warn "sending SureTax request\n" if $DEBUG; + warn "encoding SureTax request\n" if $DEBUG; my $request_json = $json->encode($request); warn $request_json if $DEBUG > 1; my $host = $conf->config('suretax-hostname'); $host ||= 'testapi.taxrating.net'; + warn "sending SureTax request\n" if $DEBUG; # We are targeting the "V05" interface: # - accepts both telecom and general sales transactions # - produces results broken down by "invoice" (Freeside line item) @@ -346,8 +347,8 @@ sub make_taxlines { 'Accept' => 'application/json', ); + warn "received SureTax response\n" if $DEBUG; my $raw_response = $http_response->content; - warn "received response\n" if $DEBUG; warn $raw_response if $DEBUG > 2; my $response; if ( $raw_response =~ /^<\?xml/ ) { @@ -356,6 +357,8 @@ sub make_taxlines { $response = XMLin( $raw_response ); $raw_response = $response->{content}; } + + warn "decoding SureTax response\n" if $DEBUG; $response = eval { $json->decode($raw_response) } or die "$raw_response\n"; @@ -375,6 +378,7 @@ sub make_taxlines { } return if !$response->{GroupList}; + warn "creating FS objects from SureTax data\n" if $DEBUG; foreach my $taxable ( @{ $response->{GroupList} } ) { # each member of this array here corresponds to what SureTax calls an # "invoice" and we call a "line item". The invoice number is @@ -420,6 +424,7 @@ sub make_taxlines { }); } } + warn "TaxEngine/suretax.pm make_taxlines done; returning FS objects\n" if $DEBUG; return @elements; } diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm index 409b44136..4a360333e 100644 --- a/FS/FS/access_right.pm +++ b/FS/FS/access_right.pm @@ -155,6 +155,7 @@ sub _upgrade_data { # class method 'Refund payment' => [ 'Refund credit card payment', 'Refund Echeck payment' ], 'Regular void' => [ 'Void payments' ], 'Unvoid' => [ 'Unvoid payments', 'Unvoid invoices' ], + 'Employees: Audit Report' => [ 'Employee Reports' ], ); foreach my $oldright (keys %migrate) { @@ -233,9 +234,7 @@ sub _upgrade_data { # class method 'Usage: Unrateable CDRs', ], 'Provision customer service' => [ 'Edit password' ], - 'Financial reports' => [ 'Employees: Commission Report', - 'Employees: Audit Report', - ], + 'Financial reports' => 'Employee Reports', 'Change customer package' => 'Detach customer package', 'Services: Accounts' => 'Services: Cable Subscribers', 'Bulk change customer packages' => 'Bulk move customer services', diff --git a/FS/FS/access_user_session_log.pm b/FS/FS/access_user_session_log.pm new file mode 100644 index 000000000..d28ec8586 --- /dev/null +++ b/FS/FS/access_user_session_log.pm @@ -0,0 +1,124 @@ +package FS::access_user_session_log; +use base qw( FS::Record ); + +use strict; +#use FS::Record qw( qsearch qsearchs ); + +=head1 NAME + +FS::access_user_session_log - Object methods for access_user_session_log records + +=head1 SYNOPSIS + + use FS::access_user_session_log; + + $record = new FS::access_user_session_log \%hash; + $record = new FS::access_user_session_log { 'column' => 'value' }; + + $error = $record->insert; + + $error = $new_record->replace($old_record); + + $error = $record->delete; + + $error = $record->check; + +=head1 DESCRIPTION + +An FS::access_user_session_log object represents an log of an employee session. +FS::access_user_session_log inherits from FS::Record. The following fields +are currently supported: + +=over 4 + +=item sessionlognum + +primary key + +=item usernum + +usernum + +=item start_date + +start_date + +=item last_date + +last_date + +=item logout_date + +logout_date + +=item logout_type + +logout_type + + +=back + +=head1 METHODS + +=over 4 + +=item new HASHREF + +Creates a new log entry. To add the entry to the database, see L<"insert">. + +Note that this stores the hash reference, not a distinct copy of the hash it +points to. You can ask the object for a copy with the I<hash> method. + +=cut + +sub table { 'access_user_session_log'; } + +=item insert + +Adds this record to the database. If there is an error, returns the error, +otherwise returns false. + +=item delete + +Delete this record from the database. + +=item replace OLD_RECORD + +Replaces the OLD_RECORD with this one in the database. If there is an error, +returns the error, otherwise returns false. + +=item check + +Checks all fields to make sure this is a valid log entry. If there is +an error, returns the error, otherwise returns false. Called by the insert +and replace methods. + +=cut + +sub check { + my $self = shift; + + my $error = + $self->ut_number('usernum') + || $self->ut_numbern('start_date') + || $self->ut_numbern('last_date') + || $self->ut_numbern('logout_date') + || $self->ut_text('logout_type') + ; + return $error if $error; + + $self->SUPER::check; +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L<FS::Record> + +=cut + +1; + diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm index d62120b3f..f16752ba4 100644 --- a/FS/FS/cust_main/Billing_Realtime.pm +++ b/FS/FS/cust_main/Billing_Realtime.pm @@ -6,6 +6,7 @@ use vars qw( $realtime_bop_decline_quiet ); #ugh use Carp; use Data::Dumper; use Business::CreditCard 0.35; +use Business::OnlinePayment; use FS::UID qw( dbh myconnect ); use FS::Record qw( qsearch qsearchs ); use FS::payby; diff --git a/FS/FS/cust_main/Import.pm b/FS/FS/cust_main/Import.pm index 646476162..9624529fa 100644 --- a/FS/FS/cust_main/Import.pm +++ b/FS/FS/cust_main/Import.pm @@ -325,6 +325,7 @@ sub batch_import { my %svc_x = (); my %bill_location = (); my %ship_location = (); + my $cust_payby = ''; foreach my $field ( @fields ) { if ( $field =~ /^cust_pkg\.(pkgpart|setup|bill|susp|adjourn|expire|cancel)$/ ) { @@ -409,17 +410,24 @@ sub batch_import { if ( $cust_main{'payinfo'} =~ /^\s*(\d+\@[\d\.]+)\s*$/ ) { - $cust_main{'payby'} = 'CHEK'; - $cust_main{'payinfo'} = $1; + delete $cust_main{'payinfo'}; - } else { + $cust_payby = new FS::cust_payby { + 'payby' => 'CHEK', + 'payinfo' => $1, + }; - $cust_main{'payby'} = 'CARD'; + } elsif ($cust_main{'payinfo'} =~ /^\s*([AD]?)(.*)\s*$/) { - if ($cust_main{'payinfo'} =~ /^\s*([AD]?)(.*)\s*$/) { - $cust_main{'payby'} = 'DCRD' if $1 eq 'D'; - $cust_main{'payinfo'} = $2; - } + delete $cust_main{'payinfo'}; + + $cust_payby = new FS::cust_payby { + 'payby' => ($1 eq 'D') ? 'DCRD' : 'CARD', + 'payinfo' => $2, + 'paycvv' => delete $cust_main{'paycvv'}, + 'paydate' => delete $cust_main{'paydate'}, + 'payname' => $cust_main{'first'}. ' '. $cust_main{'last'}, + }; } @@ -502,7 +510,10 @@ sub batch_import { $hash{$cust_pkg} = \@svc_x; } - my $error = $cust_main->insert( \%hash, $invoicing_list ); + my %options = ('invoicing_list' => $invoicing_list); + $options{'cust_payby'} = [ $cust_payby ] if $cust_payby; + + my $error = $cust_main->insert( \%hash, %options ); if ( $error ) { $dbh->rollback if $oldAutoCommit; diff --git a/FS/FS/cust_main_Mixin.pm b/FS/FS/cust_main_Mixin.pm index 1ef5387c1..8b6569a74 100644 --- a/FS/FS/cust_main_Mixin.pm +++ b/FS/FS/cust_main_Mixin.pm @@ -262,6 +262,17 @@ sub cust_statuscolor { : '000000'; } +=item agent_name + +=cut + +sub agent_name { + my $self = shift; + $self->cust_linked + ? $self->cust_main->agent_name + : $self->cust_unlinked_msg; +} + =item prospect_sql =item active_sql diff --git a/FS/FS/part_export/broadband_shellcommands.pm b/FS/FS/part_export/broadband_shellcommands.pm index 44280a200..d3e495c45 100644 --- a/FS/FS/part_export/broadband_shellcommands.pm +++ b/FS/FS/part_export/broadband_shellcommands.pm @@ -70,7 +70,18 @@ sub _export_command { my $command = $self->option($action); return '' if $command =~ /^\s*$/; - #set variables for the command + my $command_string = $self->_export_subvars( $svc_broadband, $command ); + + $self->shellcommands_queue( $svc_broadband->svcnum, + user => $self->option('user')||'root', + host => $self->machine, + command => $command_string, + ); +} + +sub _export_subvars { + my( $self, $svc_broadband, $command ) = @_; + no strict 'vars'; { no strict 'refs'; @@ -85,20 +96,25 @@ sub _export_command { $locationnum = $cust_pkg ? $cust_pkg->locationnum : ''; $custnum = $cust_pkg ? $cust_pkg->custnum : ''; - #done setting variables for the command + eval(qq("$command")); +} - $self->shellcommands_queue( $svc_broadband->svcnum, +sub _export_replace { + my($self, $new, $old ) = (shift, shift, shift); + my $command = $self->option('replace'); + + my $command_string = $self->_export_subvars_replace( $new, $old, $command ); + + $self->shellcommands_queue( $new->svcnum, user => $self->option('user')||'root', host => $self->machine, - command => eval(qq("$command")), + command => $command_string, ); } -sub _export_replace { - my($self, $new, $old ) = (shift, shift, shift); - my $command = $self->option('replace'); +sub _export_subvars_replace { + my( $self, $new, $old, $command ) = @_; - #set variable for the command no strict 'vars'; { no strict 'refs'; @@ -120,15 +136,10 @@ sub _export_replace { $new_locationnum = $new_cust_pkg ? $new_cust_pkg->locationnum : ''; $new_custnum = $new_cust_pkg ? $new_cust_pkg->custnum : ''; - #done setting variables for the command - - $self->shellcommands_queue( $new->svcnum, - user => $self->option('user')||'root', - host => $self->machine, - command => eval(qq("$command")), - ); + eval(qq("$command")); } + #a good idea to queue anything that could fail or take any time sub shellcommands_queue { my( $self, $svcnum ) = (shift, shift); diff --git a/FS/FS/part_export/broadband_shellcommands_expect.pm b/FS/FS/part_export/broadband_shellcommands_expect.pm new file mode 100644 index 000000000..ec525d38a --- /dev/null +++ b/FS/FS/part_export/broadband_shellcommands_expect.pm @@ -0,0 +1,19 @@ +package FS::part_export::broadband_shellcommands_expect; +use base qw( FS::part_export::shellcommands_expect ); + +use strict; +use FS::part_export::broadband_shellcommands; + +our %info = %FS::part_export::shellcommands_expect::info; +$info{'svc'} = 'svc_broadband'; +$info{'desc'} = 'Real time export via remote SSH, with interactive ("Expect"-like) scripting, for svc_broadband services'; + +sub _export_subvars { + FS::part_export::broadband_shellcommands::_export_subvars(@_) +} + +sub _export_subvars_replace { + FS::part_export::broadband_shellcommands::_export_subvars_replace(@_) +} + +1; diff --git a/FS/FS/part_export/pbxware.pm b/FS/FS/part_export/pbxware.pm index 4373e7ad5..9458fca0c 100644 --- a/FS/FS/part_export/pbxware.pm +++ b/FS/FS/part_export/pbxware.pm @@ -137,7 +137,7 @@ sub import_cdrs { # page's IDs or something. my $uniqueid = md5_hex(join(',',@$row)); if ( FS::cdr->row_exists('uniqueid = ?', $uniqueid) ) { - warn "skipped duplicate row in page $page\n" if $DEBUG > 1; + warn "skipped duplicate row in page $page\n" if $DEBUG; next CDR; } @@ -186,7 +186,7 @@ local $ENV{'PERL_LWP_SSL_VERIFY_HOSTNAME'} = 0; ] ); warn "$me $method\n" if $DEBUG; - warn $request->as_string."\n" if $DEBUG > 1; + warn $request->as_string."\n" if $DEBUG; my $ua = LWP::UserAgent->new; my $response = $ua->request($request); diff --git a/FS/FS/part_export/shellcommands.pm b/FS/FS/part_export/shellcommands.pm index 647dc5f4d..775af17ae 100644 --- a/FS/FS/part_export/shellcommands.pm +++ b/FS/FS/part_export/shellcommands.pm @@ -4,6 +4,7 @@ use vars qw(@ISA %info); use Tie::IxHash; use Date::Format; use String::ShellQuote; +use Net::OpenSSH; use FS::part_export; use FS::Record qw( qsearch qsearchs ); @@ -296,7 +297,7 @@ sub _export_command_or_super { } else { $self->_export_command($action, @_); } -}; +} sub _export_command { my ( $self, $action, $svc_acct) = (shift, shift, shift); @@ -305,6 +306,41 @@ sub _export_command { return '' if $command =~ /^\s*$/; my $stdin = $self->option($action."_stdin"); + my( $command_string, $stdin_string ) = + $self->_export_subvars( $svc_acct, $command, $stdin ); + + $self->ssh_or_queue( $svc_acct, $command_string, $stdin_string ); +} + +sub ssh_or_queue { + my( $self, $svc_acct, $command_string, $stdin_string ) = @_; + + my @ssh_cmd_args = ( + user => $self->option('user') || 'root', + host => $self->svc_machine($svc_acct), + command => $command_string, + stdin_string => $stdin_string, + ignored_errors => $self->option('ignored_errors') || '', + ignore_all_errors => $self->option('ignore_all_errors'), + fail_on_output => $self->option('fail_on_output'), + ); + + if ( $self->option($action. '_no_queue') ) { + # discard return value just like freeside-queued. + eval { ssh_cmd(@ssh_cmd_args) }; + $error = $@; + $error = $error->full_message if ref $error; # Exception::Class::Base + return $error. + ' ('. $self->exporttype. ' to '. $self->svc_machine($svc_acct). ')' + if $error; + } else { + $self->shellcommands_queue( $svc_acct->svcnum, @ssh_cmd_args ); + } +} + +sub _export_subvars { + my( $self, $svc_acct, $command, $stdin ) = @_; + no strict 'vars'; { no strict 'refs'; @@ -412,27 +448,7 @@ sub _export_command { my $command_string = eval(qq("$command")); return "error filling in command: $@" if $@; - my @ssh_cmd_args = ( - user => $self->option('user') || 'root', - host => $self->svc_machine($svc_acct), - command => $command_string, - stdin_string => $stdin_string, - ignored_errors => $self->option('ignored_errors') || '', - ignore_all_errors => $self->option('ignore_all_errors'), - fail_on_output => $self->option('fail_on_output'), - ); - - if ( $self->option($action. '_no_queue') ) { - # discard return value just like freeside-queued. - eval { ssh_cmd(@ssh_cmd_args) }; - $error = $@; - $error = $error->full_message if ref $error; # Exception::Class::Base - return $error. - ' ('. $self->exporttype. ' to '. $self->svc_machine($svc_acct). ')' - if $error; - } else { - $self->shellcommands_queue( $svc_acct->svcnum, @ssh_cmd_args ); - } + ( $command_string, $stdin_string ); } sub _export_replace { @@ -440,6 +456,16 @@ sub _export_replace { my $command = $self->option('usermod'); return '' if $command =~ /^\s*$/; my $stdin = $self->option('usermod_stdin'); + + my( $command_string, $stdin_string ) = + $self->_export_subvars_replace( $new, $old, $command, $stdin ); + + $self->ssh_or_queue( $new, $command_string, $stdin_string ); +} + +sub _export_subvars_replace { + my( $self, $new, $old, $command, $stdin ) = @_; + no strict 'vars'; { no strict 'refs'; @@ -511,27 +537,7 @@ sub _export_replace { my $command_string = eval(qq("$command")); - my @ssh_cmd_args = ( - user => $self->option('user') || 'root', - host => $self->svc_machine($new), - command => $command_string, - stdin_string => $stdin_string, - ignored_errors => $self->option('ignored_errors') || '', - ignore_all_errors => $self->option('ignore_all_errors'), - fail_on_output => $self->option('fail_on_output'), - ); - - if($self->option('usermod_no_queue')) { - # discard return value just like freeside-queued. - eval { ssh_cmd(@ssh_cmd_args) }; - $error = $@; - $error = $error->full_message if ref $error; # Exception::Class::Base - return $error. ' ('. $self->exporttype. ' to '. $self->svc_machine($new). ')' - if $error; - } - else { - $self->shellcommands_queue( $new->svcnum, @ssh_cmd_args ); - } + ( $command_string, $stdin_string ); } #a good idea to queue anything that could fail or take any time @@ -545,7 +551,6 @@ sub shellcommands_queue { } sub ssh_cmd { #subroutine, not method - use Net::OpenSSH; my $opt = { @_ }; open my $def_in, '<', '/dev/null' or die "unable to open /dev/null\n"; my $ssh = Net::OpenSSH->new( diff --git a/FS/FS/part_export/shellcommands_expect.pm b/FS/FS/part_export/shellcommands_expect.pm new file mode 100644 index 000000000..c2a4118e2 --- /dev/null +++ b/FS/FS/part_export/shellcommands_expect.pm @@ -0,0 +1,128 @@ +package FS::part_export::shellcommands_expect; +use base qw( FS::part_export::shellcommands ); + +use strict; +use Tie::IxHash; +use Net::OpenSSH; +use Expect; +#use FS::Record qw( qsearch qsearchs ); + +tie my %options, 'Tie::IxHash', + 'user' => { label =>'Remote username', default=>'root' }, + 'useradd' => { label => 'Insert commands', type => 'textarea', }, + 'userdel' => { label => 'Delete commands', type => 'textarea', }, + 'usermod' => { label => 'Modify commands', type => 'textarea', }, + 'suspend' => { label => 'Suspend commands', type => 'textarea', }, + 'unsuspend' => { label => 'Unsuspend commands', type => 'textarea', }, + 'debug' => { label => 'Enable debugging', + type => 'checkbox', + value => 1, + }, +; + +our %info = ( + 'svc' => 'svc_acct', + 'desc' => 'Real time export via remote SSH, with interactive ("Expect"-like) scripting, for svc_acct services', + 'options' => \%options, + 'notes' => q[ +Interactively run commands via SSH in a remote terminal, like "Expect". In +most cases, you probably want a regular shellcommands (or broadband_shellcommands, etc.) export instead, unless +you have a specific need to interact with a terminal-based interface in an +"Expect"-like fashion. +<BR><BR> + +Each line specifies a string to match and a command to +run after that string is found, separated by the first space. For example, to +run "exit" after a prompt ending in "#" is sent, "# exit". You will need to +<a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.9:Documentation:Administration:SSH_Keys">setup SSH for unattended operation</a>. +<BR><BR> + +In commands, all variable substitutions of the regular shellcommands (or +broadband_shellcommands, etc.) export are available (use a backslash to escape +a literal $). +] +); + +sub _export_command { + my ( $self, $action, $svc_acct) = (shift, shift, shift); + my @lines = split("\n", $self->option($action) ); + + return '' unless @lines; + + my @commands = (); + foreach my $line (@lines) { + my($match, $command) = split(' ', $line, 2); + my( $command_string ) = $self->_export_subvars( $svc_acct, $command, '' ); + push @commands, [ $match, $command_string ]; + } + + $self->shellcommands_expect_queue( $svc_acct->svcnum, @commands ); +} + +sub _export_replace { + my( $self, $new, $old ) = (shift, shift, shift); + my @lines = split("\n", $self->option('replace') ); + + return '' unless @lines; + + my @commands = (); + foreach my $line (@lines) { + my($match, $command) = split(' ', $line, 2); + my( $command_string ) = $self->_export_subvars_replace( $new, $old, $command, '' ); + push @commands, [ $match, $command_string ]; + } + + $self->shellcommands_expect_queue( $new->svcnum, @commands ); +} + +sub shellcommands_expect_queue { + my( $self, $svcnum, @commands ) = @_; + + my $queue = new FS::queue { + 'svcnum' => $svcnum, + 'job' => "FS::part_export::shellcommands_expect::ssh_expect", + }; + $queue->insert( + user => $self->option('user') || 'root', + host => $self->machine, + debug => $self->option('debug'), + commands => \@commands, + ); +} + +sub ssh_expect { #subroutine, not method + my $opt = { @_ }; + + my $dest = $opt->{'user'}.'@'.$opt->{'host'}; + + open my $def_in, '<', '/dev/null' or die "unable to open /dev/null\n"; + my $ssh = Net::OpenSSH->new( $dest, 'default_stdin_fh' => $def_in ); + # ignore_all_errors doesn't override SSH connection/auth errors-- + # probably correct + die "Couldn't establish SSH connection to $dest: ". $ssh->error + if $ssh->error; + + my ($pty, $pid) = $ssh->open2pty + or die "Couldn't start a remote terminal session"; + my $expect = Expect->init($pty); + #not useful #$expect->debug($opt->{debug} ? 3 : 0); + + foreach my $line ( @{ $opt->{commands} } ) { + my( $match, $command ) = @$line; + + warn "Waiting for '$match'\n" if $opt->{debug}; + + my $matched = $expect->expect(30, $match); + unless ( $matched ) { + my $err = "Never saw '$match'\n"; + warn $err; + die $err; + } + warn "Running '$command'\n" if $opt->{debug}; + $expect->send("$command\n"); + } + + ''; +} + +1; diff --git a/FS/FS/part_export/vitelity.pm b/FS/FS/part_export/vitelity.pm index 332e45712..51bb0aab1 100644 --- a/FS/FS/part_export/vitelity.pm +++ b/FS/FS/part_export/vitelity.pm @@ -425,7 +425,7 @@ sub e911_send { my $e911_result = $self->vitelity_command('e911send', %e911send); - unless ( $e911_result =~ /^(missingdata|invalid)/i ) { + unless ( $e911_result =~ /status=(missingdata|invalid)/i ) { warn "Vitelity response: $e911_result" if $self->option('debug'); return ''; } diff --git a/FS/bin/freeside-voipinnovations-cdrimport b/FS/bin/freeside-voipinnovations-cdrimport index 23ea6bbdc..d64c8708f 100755 --- a/FS/bin/freeside-voipinnovations-cdrimport +++ b/FS/bin/freeside-voipinnovations-cdrimport @@ -4,7 +4,8 @@ use strict; use Getopt::Std; use Date::Format; use File::Temp 'tempdir'; -use Net::FTP; +use Net::SSLGlue::FTP; #at least until the Deb 9 transition is done, then + # regular Net::FTP has SSL support use FS::UID qw(adminsuidsetup datasrc dbh); use FS::cdr; use FS::cdr_batch; @@ -39,11 +40,14 @@ my $tempdir = tempdir( CLEANUP => !$opt_v ); my $format = 'voip_innovations'; my $hostname = 'customercdr.voipinnovations.com'; -my $ftp = Net::FTP->new($hostname, Debug => $opt_d) +my $ftp = Net::FTP->new($hostname, Passive => 1, Debug => $opt_d) or die "Can't connect to $hostname: $@\n"; +$ftp->starttls() + or die "TLS initialization failed: ". $ftp->message. "\n"; + $ftp->login($login, $password) - or die "Login failed: ".$ftp->message."\n"; + or die "Login failed: ". $ftp->message. "\n"; ### # get the file list @@ -51,7 +55,7 @@ $ftp->login($login, $password) warn "Retrieving directory listing\n" if $opt_v; -$ftp->cwd('/'); +#$ftp->cwd('/'); my @dirs = $ftp->ls(); warn scalar(@dirs)." directories found.\n" if $opt_v; # apply date range diff --git a/FS/t/access_user_session_log.t b/FS/t/access_user_session_log.t new file mode 100644 index 000000000..630637474 --- /dev/null +++ b/FS/t/access_user_session_log.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::access_user_session_log; +$loaded=1; +print "ok 1\n"; |