summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2013-12-10 20:59:42 -0800
committerIvan Kohler <ivan@freeside.biz>2013-12-10 20:59:42 -0800
commit430b2c784d2ee9ea5be00b821d2dbd27279ef132 (patch)
treec8a224219c1425bd3fa03b1a54487bf1f3de7449 /FS
parent6ced51db0e73603461591e54b7f606467e7c7af0 (diff)
parente1157f37c618a7b9cdb2793d61999458076dc51b (diff)
Merge branch 'master' of git.freeside.biz:/home/git/freeside
Conflicts: FS/FS/Mason.pm FS/MANIFEST
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/ClientAPI/MyAccount.pm5
-rw-r--r--FS/FS/Conf.pm6
-rw-r--r--FS/FS/IP_Mixin.pm11
-rw-r--r--FS/FS/Mason.pm1
-rw-r--r--FS/FS/Schema.pm12
-rwxr-xr-xFS/FS/addr_block.pm12
-rw-r--r--FS/FS/addr_range.pm259
-rw-r--r--FS/FS/part_export/bulkvs_e911.pm2
-rw-r--r--FS/FS/svc_Common.pm1
-rw-r--r--FS/MANIFEST2
-rwxr-xr-xFS/bin/freeside-setup43
-rw-r--r--FS/t/addr_range.t5
12 files changed, 308 insertions, 51 deletions
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm
index a58099184..aa21ac076 100644
--- a/FS/FS/ClientAPI/MyAccount.pm
+++ b/FS/FS/ClientAPI/MyAccount.pm
@@ -7,6 +7,7 @@ use subs qw( _cache _provision );
use IO::Scalar;
use Data::Dumper;
use Digest::MD5 qw(md5_hex);
+use Digest::SHA qw(sha512_hex);
use Date::Format;
use Time::Duration;
use Time::Local qw(timelocal_nocheck);
@@ -278,7 +279,7 @@ sub login {
my $session_id;
do {
- $session_id = md5_hex(md5_hex(time(). {}. rand(). $$))
+ $session_id = sha512_hex(time(). {}. rand(). $$)
} until ( ! defined _cache->get($session_id) ); #just in case
my $timeout = $conf->config('selfservice-session_timeout') || '1 hour';
@@ -2896,7 +2897,7 @@ sub reset_passwd {
my $reset_session_id;
do {
- $reset_session_id = md5_hex(md5_hex(time(). {}. rand(). $$))
+ $reset_session_id = sha512_hex(time(). {}. rand(). $$)
} until ( ! defined _cache->get("reset_passwd_$reset_session_id") ); #just in case
_cache->set( "reset_passwd_$reset_session_id", $reset_session, $timeout );
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 0eed8ee5d..301d972b3 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -2150,7 +2150,7 @@ and customer address. Include units.',
'section' => 'self-service',
'description' => 'Acceptable payment types for the signup server',
'type' => 'selectmultiple',
- 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB PREPAY PPAL BILL COMP) ],
+ 'select_enum' => [ qw(CARD DCRD CHEK DCHK PREPAY PPAL BILL COMP) ],
},
{
@@ -2538,7 +2538,7 @@ and customer address. Include units.',
'section' => 'billing',
'description' => 'Available payment types.',
'type' => 'selectmultiple',
- 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD PPAL COMP) ],
+ 'select_enum' => [ qw(CARD DCRD CHEK DCHK BILL CASH WEST MCRD PPAL COMP) ],
},
{
@@ -2546,7 +2546,7 @@ and customer address. Include units.',
'section' => 'UI',
'description' => 'Default payment type. HIDE disables display of billing information and sets customers to BILL.',
'type' => 'select',
- 'select_enum' => [ '', qw(CARD DCRD CHEK DCHK LECB BILL CASH WEST MCRD PPAL COMP HIDE) ],
+ 'select_enum' => [ '', qw(CARD DCRD CHEK DCHK BILL CASH WEST MCRD PPAL COMP HIDE) ],
},
{
diff --git a/FS/FS/IP_Mixin.pm b/FS/FS/IP_Mixin.pm
index fdeb51da7..b3c10528c 100644
--- a/FS/FS/IP_Mixin.pm
+++ b/FS/FS/IP_Mixin.pm
@@ -200,12 +200,21 @@ sub check_ip_addr {
return '' if $addr eq '';
my $na = $self->NetAddr
or return "Can't parse address '$addr'";
+ # if there's a chosen address block, check that the address is in it
if ( my $block = $self->addr_block ) {
if ( !$block->NetAddr->contains($na) ) {
return "Address $addr not in block ".$block->cidr;
}
}
- # this returns '' if the address is in use by $self.
+ # if the address is in any designated ranges, check that they don't
+ # disallow use
+ foreach my $range (FS::addr_range->any_contains($addr)) {
+ if ( !$range->allow_use ) {
+ return "Address $addr is in ".$range->desc." range ".$range->as_string;
+ }
+ }
+ # check that nobody else is sitting on the address
+ # (this returns '' if the address is in use by $self)
if ( my $dup = $self->is_used($self->ip_addr) ) {
return "Address $addr in use by $dup";
}
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index d228bb173..4e9c4f19b 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -361,6 +361,7 @@ if ( -e $addl_handler_use_file ) {
use FS::alarm_system;
use FS::alarm_type;
use FS::alarm_station;
+ use FS::addr_range;
# Sammath Naur
if ( $FS::Mason::addl_handler_use ) {
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 6403782f8..647e2b106 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -4278,6 +4278,18 @@ sub tables_hashref {
],
},
+ 'addr_range' => {
+ 'columns' => [
+ 'rangenum', 'serial', '', '', '', '',
+ 'start', 'varchar', '', 15, '', '',
+ 'length', 'int', '', '', '', '',
+ 'status', 'varchar', 'NULL', 32, '', '',
+ ],
+ 'primary_key' => 'rangenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
'svc_broadband' => {
'columns' => [
'svcnum', 'int', '', '', '', '',
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
index 6a62777be..1f4000b75 100755
--- a/FS/FS/addr_block.pm
+++ b/FS/FS/addr_block.pm
@@ -256,7 +256,17 @@ sub next_free_addr {
# just do a linear search of the block
my $freeaddr = $selfaddr->network + 1;
while ( $freeaddr < $selfaddr->broadcast ) {
- return $freeaddr unless $used{ $freeaddr->addr };
+ # also make sure it's not blocked from assignment by an address range
+ if ( !$used{$freeaddr->addr } ) {
+ my ($range) = grep { !$_->allow_use }
+ FS::addr_range->any_contains($freeaddr);
+ if ( !$range ) {
+ # then we've found a free address
+ return $freeaddr;
+ }
+ # otherwise, skip to the end of the range
+ $freeaddr = NetAddr::IP->new($range->end, $self->ip_netmask);
+ }
$freeaddr++;
}
return;
diff --git a/FS/FS/addr_range.pm b/FS/FS/addr_range.pm
new file mode 100644
index 000000000..18ae1e2ea
--- /dev/null
+++ b/FS/FS/addr_range.pm
@@ -0,0 +1,259 @@
+package FS::addr_range;
+
+use strict;
+use base qw( FS::Record );
+use vars qw( %status_desc
+ %status_allow_auto
+ %status_allow_use
+ );
+use FS::Record qw( qsearch qsearchs );
+use NetAddr::IP;
+
+# metadata about status strings:
+# how to describe them
+%status_desc = (
+ '' => '',
+ 'unavailable' => 'unavailable',
+);
+
+# whether addresses in this range are available for use
+%status_allow_use = (
+ '' => 1,
+ 'unavailable' => 0,
+);
+
+=head1 NAME
+
+FS::addr_range - Object methods for addr_range records
+
+=head1 SYNOPSIS
+
+ use FS::addr_range;
+
+ $record = new FS::addr_range \%hash;
+ $record = new FS::addr_range { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::addr_range object represents a contiguous range of IP
+addresses assigned to a certain purpose. Unlike L<FS::addr_block>,
+this isn't a routing block; the range doesn't have to be aligned on
+a subnet boundary, and doesn't have a gateway or broadcast address.
+It's just a range.
+
+=over 4
+
+=item rangenum - primary key
+
+=item start - starting address of the range, as a dotted quad
+
+=item length - number of addresses in the range, including start
+
+=item status - what to do with the addresses in this range; currently can
+only be "unavailable", which makes the addresses unavailable for assignment
+to any kind of service.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new range. To add the example 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 { 'addr_range'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=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.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('rangenum')
+ || $self->ut_ip('start')
+ || $self->ut_number('length')
+ || $self->ut_textn('status')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item end [ IPADDR ]
+
+Get/set the end IP address in the range. This isn't actually part of the
+record but it's convenient.
+
+=cut
+
+sub end {
+ my $self = shift;
+ # if there's no start address, just return nothing
+ my $start = NetAddr::IP->new($self->start, 0) or return '';
+
+ my $new = shift;
+ if ( $new ) {
+ my $end = NetAddr::IP->new($new, 0)
+ or die "bad end address $new";
+ if ( $end < $start ) {
+ $self->set('start', $end);
+ ($end, $start) = ($start, $end);
+ }
+ $self->set('length', $end - $start + 1);
+ return $end->addr;
+ }
+ my $end = $start + $self->get('length') - 1;
+ $end->addr;
+}
+
+=item contains IPADDR
+
+Checks whether IPADDR (a dotted-quad IPv4 address) is within the range.
+
+=cut
+
+sub contains {
+ my $self = shift;
+ my $addr = shift;
+ $addr = NetAddr::IP->new($addr, 0)
+ unless ref($addr) and UNIVERSAL::isa($addr, 'NetAddr::IP');
+ return 0 unless $addr;
+
+ my $start = NetAddr::IP->new($self->start, 0);
+
+ return ($addr >= $start and $addr - $start < $self->length) ? 1 : 0;
+}
+
+=item as_string
+
+Returns a readable string showing the address range.
+
+=cut
+
+sub as_string {
+ my $self = shift;
+ my $start = NetAddr::IP->new($self->start, 0);
+ my $end = $start + $self->length;
+
+ if ( $self->length == 1 ) {
+ # then just the address
+ return $self->start;
+ } else { # we have to get tricksy
+ my @end_octets = split('\.', $end->addr);
+ $start = ($start->numeric)[0] + 0;
+ $end = ($end->numeric)[0] + 0;
+ # which octets are different between start and end?
+ my $delta = $end ^ $start;
+ foreach (0xffffff, 0xffff, 0xff) {
+ if ( $delta <= $_ ) {
+ # then they are identical in the first 8/16/24 bits
+ shift @end_octets;
+ }
+ }
+ return $self->start . '-' . join('.', @end_octets);
+ }
+}
+
+=item desc
+
+Returns a semi-friendly description of the block status.
+
+=item allow_use
+
+Returns true if addresses in this range can be used by services, etc.
+
+=cut
+
+sub desc {
+ my $self = shift;
+ $status_desc{ $self->status };
+}
+
+sub allow_use {
+ my $self = shift;
+ $status_allow_use{ $self->status };
+}
+
+=back
+
+=head1 CLASS METHODS
+
+=sub any_contains IPADDR
+
+Returns all address ranges that contain IPADDR.
+
+=cut
+
+sub any_contains {
+ my $self = shift;
+ my $addr = shift;
+ return grep { $_->contains($addr) } qsearch('addr_range', {});
+}
+
+=head1 DEVELOPER NOTE
+
+L<NetAddr::IP> objects have netmasks. When using them to represent
+range endpoints, be sure to set the netmask to I<zero> so that math on
+the address doesn't stop at the subnet boundary. (The default is /32,
+which doesn't work very well. Address ranges ignore subnet boundaries.)
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::svc_IP_Mixin>, L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_export/bulkvs_e911.pm b/FS/FS/part_export/bulkvs_e911.pm
index c66305bc4..a8af3a055 100644
--- a/FS/FS/part_export/bulkvs_e911.pm
+++ b/FS/FS/part_export/bulkvs_e911.pm
@@ -57,7 +57,7 @@ sub _export_insert {
my ($self, $svc_phone) = @_;
my @login = $self->login;
- my $location = $svc_phone->cust_location
+ my $location = $svc_phone->cust_location_or_main
or return 'no e911 location defined for this phone service';
warn "$me validating address for svcnum ".$svc_phone->svcnum."\n"
diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm
index 56567e8ce..659255eaa 100644
--- a/FS/FS/svc_Common.pm
+++ b/FS/FS/svc_Common.pm
@@ -336,6 +336,7 @@ sub _check_duplcate { ''; }
sub preinsert_hook { ''; }
sub table_dupcheck_fields { (); }
sub prereplace_hook { ''; }
+sub prereplace_hook_first { ''; }
sub predelete_hook { ''; }
sub predelete_hook_first { ''; }
diff --git a/FS/MANIFEST b/FS/MANIFEST
index ba933996d..1f2dfccc4 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -735,3 +735,5 @@ FS/alarm_type.pm
t/alarm_type.t
FS/alarm_station.pm
t/alarm_station.t
+FS/addr_range.pm
+t/addr_range.t
diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup
index 07da88dea..a6908e172 100755
--- a/FS/bin/freeside-setup
+++ b/FS/bin/freeside-setup
@@ -37,51 +37,8 @@ getsecrets();
#needs to match FS::Record
my($dbdef_file) = "%%%FREESIDE_CONF%%%/dbdef.". datasrc;
-###
-
my $username_len = 32;
-#print "\n\n", <<END, ":";
-#Freeside tracks the RADIUS User-Name, check attribute Password and
-#reply attribute Framed-IP-Address for each user. You can specify additional
-#check and reply attributes (or you can add them later with the
-#fs-radius-add-check and fs-radius-add-reply programs).
-#
-#First enter any additional RADIUS check attributes you need to track for each
-#user, separated by whitespace.
-#END
-#my @check_attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
-# split(" ",&getvalue);
-#
-#print "\n\n", <<END, ":";
-#Now enter any additional reply attributes you need to track for each user,
-#separated by whitespace.
-#END
-#my @attributes = map { $attrib2db{lc($_)} or die "unknown attribute $_"; }
-# split(" ",&getvalue);
-#
-#print "\n\n", <<END, ":";
-#Do you wish to enable the tracking of a second, separate shipping/service
-#address?
-#END
-#my $ship = &_yesno;
-#
-#sub getvalue {
-# my($x)=scalar(<STDIN>);
-# chop $x;
-# $x;
-#}
-#
-#sub _yesno {
-# print " [y/N]:";
-# my $x = scalar(<STDIN>);
-# $x =~ /^y/i;
-#}
-
-#my @check_attributes = (); #add later
-#my @attributes = (); #add later
-#my $ship = $opt_s;
-
###
# create a dbdef object from the old data structure
###
diff --git a/FS/t/addr_range.t b/FS/t/addr_range.t
new file mode 100644
index 000000000..6747d6768
--- /dev/null
+++ b/FS/t/addr_range.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::addr_range;
+$loaded=1;
+print "ok 1\n";