summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Record.pm54
-rw-r--r--FS/FS/Report/Table.pm2
-rw-r--r--FS/FS/contact.pm12
-rw-r--r--FS/FS/cust_main.pm82
-rw-r--r--FS/FS/deploy_zone.pm2
-rw-r--r--FS/FS/part_export/saisei.pm60
-rwxr-xr-xFS/FS/svc_broadband.pm6
7 files changed, 209 insertions, 9 deletions
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index 9f9b1e2fc..5048e4407 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -2999,6 +2999,60 @@ sub ut_enumn {
: '';
}
+=item ut_date COLUMN
+
+Check/untaint a column containing a date string.
+
+Date will be normalized to YYYY-MM-DD format
+
+=cut
+
+sub ut_date {
+ my ( $self, $field ) = @_;
+ my $value = $self->getfield( $field );
+
+ my @date = split /[\-\/]/, $value;
+ if ( scalar(@date) == 3 ) {
+ @date = @date[2,0,1] if $date[2] >= 1900;
+
+ local $@;
+ my $ymd;
+ eval {
+ # DateTime will die given invalid date
+ $ymd = DateTime->new(
+ year => $date[0],
+ month => $date[1],
+ day => $date[2],
+ )->ymd('-');
+ };
+
+ unless( $@ ) {
+ $self->setfield( $field, $ymd ) unless $value eq $ymd;
+ return '';
+ }
+
+ }
+ return "Illegal (date) field $field: $value";
+}
+
+=item ut_daten COLUMN
+
+Check/untaint a column containing a date string.
+
+Column may be null.
+
+Date will be normalized to YYYY-MM-DD format
+
+=cut
+
+sub ut_daten {
+ my ( $self, $field ) = @_;
+
+ $self->getfield( $field ) =~ /^()$/
+ ? $self->setfield( $field, '' )
+ : $self->ut_date( $field );
+}
+
=item ut_flag COLUMN
Check/untaint a column if it contains either an empty string or 'Y'. This
diff --git a/FS/FS/Report/Table.pm b/FS/FS/Report/Table.pm
index cef7813af..7c4f97309 100644
--- a/FS/FS/Report/Table.pm
+++ b/FS/FS/Report/Table.pm
@@ -1115,7 +1115,7 @@ sub calculate_churn_cust {
as suspended,
SUM((s_active = 0 and s_suspended > 0 and e_active > 0)::int)
as resumed,
- SUM((s_active > 0 and e_active = 0 and e_suspended = 0)::int)
+ SUM((e_active = 0 and e_cancelled > s_cancelled)::int)
as cancelled
FROM ($cust_sql) AS x
";
diff --git a/FS/FS/contact.pm b/FS/FS/contact.pm
index 4db3cdfd1..8f6b6a3b5 100644
--- a/FS/FS/contact.pm
+++ b/FS/FS/contact.pm
@@ -131,7 +131,6 @@ sub insert {
my $dbh = dbh;
my $error = $self->SUPER::insert;
- $error ||= $self->insert_password_history;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
@@ -191,6 +190,15 @@ sub insert {
}
}
+ if ( $self->get('password') ) {
+ my $error = $self->is_password_allowed($self->get('password'))
+ || $self->change_password($self->get('password'));
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
@@ -614,7 +622,7 @@ sub authenticate_password {
$hash eq $check_hash;
- } else {
+ } else {
return 0 if $self->_password eq '';
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 621f3d144..b103996a4 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2145,6 +2145,10 @@ sub check {
if !$import
&& !$ignore_expired_card
&& ( $y<$nowy || ( $y==$nowy && $1<$nowm ) );
+
+ if ( my $error = $self->ut_daten('paydate') ) {
+ return $error;
+ }
}
if ( $self->payname eq '' && $self->payby !~ /^(CHEK|DCHK)$/ &&
@@ -5643,8 +5647,86 @@ sub _upgrade_data { #class method
FS::Setup::enable_encryption();
}
+ $class->_upgrade_data_paydate_edgebug;
}
+=item _upgrade_data_paydate_edgebug
+
+Correct bad data injected into payment expire date column by Edge browser bug
+
+The month and year values may have an extra character injected into form POST
+data by Edge browser. It was possible for some bad month values to slip
+past data validation.
+
+If the stored value was out of range, it was causing payments screen to crash.
+We can detect and fix this by dropping the second digit.
+
+If the stored value is is 11 or 12, it's possible the user inputted a 1. In
+this case, the payment method will fail to authorize, but the record will
+not cause crashdumps for being out of range.
+
+In short, check for any expiration month > 12, and drop the extra digit
+
+=cut
+
+sub _upgrade_data_paydate_edgebug {
+ my $journal_label = 'cust_main_paydate_edgebug';
+ return if FS::upgrade_journal->is_done( $journal_label );
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+
+ for my $row (
+ FS::Record::qsearch(
+ cust_main => { paydate => { op => '!=', value => '' }}
+ )
+ ) {
+ next unless $row->ut_daten('paydate');
+
+ # paydate column stored in database has failed date validation
+ my $bad_paydate = $row->paydate;
+
+ my @date = split /[\-\/]/, $bad_paydate;
+ @date = @date[2,0,1] if $date[2] > 1900;
+
+ # Only autocorrecting when month > 12 - notify operator
+ unless ( $date[1] > 12 ) {
+ die sprintf(
+ 'Unable to correct bad paydate stored in cust_main row '.
+ 'custnum(%s) paydate(%s)',
+ $row->custnum,
+ $bad_paydate,
+ );
+ }
+
+ $date[1] = substr( $date[1], 0, 1 );
+ $row->paydate( join('-', @date ));
+
+ if ( my $error = $row->replace ) {
+ die sprintf(
+ 'Failed to autocorrect bad paydate stored in cust_main row '.
+ 'custnum(%s) paydate(%s) - error: %s',
+ $row->custnum,
+ $bad_paydate,
+ $error
+ );
+ }
+
+ warn sprintf(
+ 'Autocorrected bad paydate stored in cust_main row '.
+ "custnum(%s) old-paydate(%s) new-paydate(%s)\n",
+ $row->custnum,
+ $bad_paydate,
+ $row->paydate,
+ );
+
+ }
+
+ FS::upgrade_journal->set_done( $journal_label );
+ dbh->commit unless $oldAutoCommit;
+}
+
+
sub queueable_upgrade {
my $class = shift;
diff --git a/FS/FS/deploy_zone.pm b/FS/FS/deploy_zone.pm
index 6ad355f72..7c3a76ca5 100644
--- a/FS/FS/deploy_zone.pm
+++ b/FS/FS/deploy_zone.pm
@@ -6,7 +6,7 @@ use FS::Record qw( qsearch qsearchs dbh );
use Storable qw(thaw);
use MIME::Base64;
-use JSON qw(encode_json decode_json) ;
+use Cpanel::JSON::XS qw(encode_json decode_json);
use LWP::UserAgent;
use HTTP::Request::Common;
diff --git a/FS/FS/part_export/saisei.pm b/FS/FS/part_export/saisei.pm
index af77dfa78..6a42b94ba 100644
--- a/FS/FS/part_export/saisei.pm
+++ b/FS/FS/part_export/saisei.pm
@@ -201,12 +201,28 @@ sub _export_insert {
my $accesspoint = process_sector($self, $sector_opt);
return $self->api_error if $self->{'__saisei_error'};
+## get custnum and pkgpart from cust_pkg for virtual access point
+ my $cust_pkg = FS::Record::qsearchs({
+ 'table' => 'cust_pkg',
+ 'hashref' => { 'pkgnum' => $svc_broadband->{Hash}->{pkgnum}, },
+ });
+ my $virtual_ap_name = $cust_pkg->{Hash}->{custnum}.'_'.$cust_pkg->{Hash}->{pkgpart}.'_'.$svc_broadband->{Hash}->{speed_down}.'_'.$svc_broadband->{Hash}->{speed_up};
+
+ my $virtual_ap_opt = {
+ 'virtual_name' => $virtual_ap_name,
+ 'sector_name' => $sector_name,
+ 'virtual_uprate_limit' => $svc_broadband->{Hash}->{speed_up},
+ 'virtual_downrate_limit' => $svc_broadband->{Hash}->{speed_down},
+ };
+ my $virtual_ap = process_virtual_ap($self, $virtual_ap_opt);
+ return $self->api_error if $self->{'__saisei_error'};
+
## tie host to user add sector name as access point.
$self->api_add_host_to_user(
$user->{collection}->[0]->{name},
$rateplan->{collection}->[0]->{name},
$svc_broadband->{Hash}->{ip_addr},
- $accesspoint->{collection}->[0]->{name},
+ $virtual_ap->{collection}->[0]->{name},
) unless $self->{'__saisei_error'};
}
@@ -216,8 +232,8 @@ sub _export_insert {
sub _export_replace {
my ($self, $svc_broadband) = @_;
- $self->_export_insert($svc_broadband);
- return '';
+ my $error = $self->_export_insert($svc_broadband);
+ return $error;
}
sub _export_delete {
@@ -805,6 +821,44 @@ sub process_sector {
return $accesspoint;
}
+sub process_virtual_ap {
+ my ($self, $opt) = @_;
+
+ my $existing_virtual_ap;
+ my $virtual_name = $opt->{virtual_name};
+
+ #check if sector has been set up as an access point.
+ $existing_virtual_ap = $self->api_get_accesspoint($virtual_name);
+
+ # modify the existing virtual accesspoint if changing it. this should never happen
+ $self->api_modify_existing_accesspoint (
+ $virtual_name,
+ $opt->{sector_name},
+ $opt->{virtual_uprate_limit},
+ $opt->{virtual_downrate_limit},
+ ) if $existing_virtual_ap && $opt->{modify_existing};
+
+ #if virtual ap does not exist as an access point create it.
+ $self->api_create_accesspoint(
+ $virtual_name,
+ $opt->{virtual_uprate_limit},
+ $opt->{virtual_downrate_limit},
+ ) unless $existing_virtual_ap;
+
+my $update_sector;
+if ($existing_virtual_ap && ($existing_virtual_ap->{collection}->[0]->{uplink}->{link}->{name} ne $opt->{sector_name})) {
+ $update_sector = 1;
+}
+
+ # Attach newly created virtual ap to tower sector ap or if sector has changed.
+ $self->api_modify_accesspoint($virtual_name, $opt->{sector_name}) unless ($self->{'__saisei_error'} || ($existing_virtual_ap && !$update_sector));
+
+ # set access point to existing one or newly created one.
+ my $accesspoint = $existing_virtual_ap ? $existing_virtual_ap : $self->api_get_accesspoint($virtual_name);
+
+ return $accesspoint;
+}
+
sub export_provisioned_services {
my $job = shift;
my $param = thaw(decode_base64(shift));
diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm
index 11e3331a0..9cd085942 100755
--- a/FS/FS/svc_broadband.pm
+++ b/FS/FS/svc_broadband.pm
@@ -156,8 +156,8 @@ sub table_info {
disable_inventory => 1,
},
'serviceid' => 'Torrus serviceid', #but is should be hidden
- 'speed_test_up' => 'Speed test download (Kbps)',
- 'speed_test_down' => 'Speed test upload (Kbps)',
+ 'speed_test_up' => { 'label' => 'Speed test upload (Kbps)' },
+ 'speed_test_down' => { 'label' => 'Speed test download (Kbps)' },
'speed_test_latency' => 'Speed test latency (ms)',
},
};
@@ -364,6 +364,8 @@ sub check {
|| $self->ut_textn('description')
|| $self->ut_numbern('speed_up')
|| $self->ut_numbern('speed_down')
+ || $self->ut_numbern('speed_test_up')
+ || $self->ut_numbern('speed_test_down')
|| $self->ut_ipn('ip_addr')
|| $self->ut_hexn('mac_addr')
|| $self->ut_hexn('auth_key')