use strict;
use vars qw( @ISA $nossh_hack $conf $dir_prefix @shells $usernamemin
- $usernamemax $passwordmin $username_letter $username_letterfirst
+ $usernamemax $passwordmin $passwordmax
+ $username_ampersand $username_letter $username_letterfirst
$username_noperiod $username_uppercase
$shellmachine $useradd $usermod $userdel $mydomain
$cyrus_server $cyrus_admin_user $cyrus_admin_pass
$usernamemin = $conf->config('usernamemin') || 2;
$usernamemax = $conf->config('usernamemax');
$passwordmin = $conf->config('passwordmin') || 6;
+ $passwordmax = $conf->config('passwordmax') || 8;
if ( $shellmachine ) {
if ( $conf->exists('shellmachine-useradd') ) {
$useradd = join("\n", $conf->config('shellmachine-useradd') )
$username_letterfirst = $conf->exists('username-letterfirst');
$username_noperiod = $conf->exists('username-noperiod');
$username_uppercase = $conf->exists('username-uppercase');
+ $username_ampersand = $conf->exists('username-ampersand');
$mydomain = $conf->config('domain');
if ( $conf->exists('cyrus') ) {
($cyrus_server, $cyrus_admin_user, $cyrus_admin_pass) =
$cyrus_admin_user = '';
$cyrus_admin_pass = '';
}
- if ( $conf->exists('icradius_secrets') ) {
- $icradius_dbh = DBI->connect($conf->config('icradius_secrets'))
- or die $DBI::errstr;
+ if ( $conf->exists('icradiusmachines') ) {
+ if ( $conf->exists('icradius_secrets') ) {
+ #need some sort of late binding so it's only connected to when
+ # actually used, hmm
+ $icradius_dbh = DBI->connect($conf->config('icradius_secrets'))
+ or die $DBI::errstr;
+ } else {
+ $icradius_dbh = dbh;
+ }
} else {
$icradius_dbh = '';
}
#not needed in 5.004 #srand($$|time);
+sub _cache {
+ my $self = shift;
+ my ( $hashref, $cache ) = @_;
+ if ( $hashref->{'svc_acct_svcnum'} ) {
+ $self->{'_domsvc'} = FS::svc_domain->new( {
+ 'svcnum' => $hashref->{'domsvc'},
+ 'domain' => $hashref->{'svc_acct_domain'},
+ 'catchall' => $hashref->{'svc_acct_catchall'},
+ } );
+ }
+}
+
=head1 NAME
FS::svc_acct - Object methods for svc_acct records
%hash = $record->radius_check;
+ $domain = $record->domain;
+
+ $svc_domain = $record->svc_domain;
+
+ $email = $record->email;
+
+ $seconds_since = $record->seconds_since($timestamp);
+
=head1 DESCRIPTION
An FS::svc_acct object represents an account. FS::svc_acct inherits from
}
}
if ( $icradius_dbh ) {
- my $queue = new FS::queue { 'job' => 'FS::svc_acct::icradius_rc_insert' };
- $error = $queue->insert( $self->username,
- $self->_password,
- $self->radius_check
- );
+
+ my $radcheck_queue =
+ new FS::queue { 'job' => 'FS::svc_acct::icradius_rc_insert' };
+ $error = $radcheck_queue->insert( $self->username,
+ $self->_password,
+ $self->radius_check
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "queueing job (transaction rolled back): $error";
+ }
+
+ my $radreply_queue =
+ new FS::queue { 'job' => 'FS::svc_acct::icradius_rr_insert' };
+ $error = $radreply_queue->insert( $self->username,
+ $self->_password,
+ $self->radius_reply
+ );
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "queueing job (transaction rolled back): $error";
}
+
}
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
1;
}
+sub icradius_rr_insert {
+ my( $username, $password, %radreply ) = @_;
+
+ foreach my $attribute ( keys %radreply ) {
+ my $sth = $icradius_dbh->prepare(
+ "INSERT INTO radreply ( id, UserName, Attribute, Value ) VALUES ( ".
+ join(", ", map { $icradius_dbh->quote($_) } (
+ '',
+ $username,
+ $attribute,
+ $radreply{$attribute},
+ ) ). " )"
+ );
+ $sth->execute or die "can't insert into radreply table: ". $sth->errstr;
+ }
+
+ 1;
+}
+
=item delete
Deletes this account from the database. If there is an error, returns the
foreach my $cust_main_invoice (
qsearch( 'cust_main_invoice', { 'dest' => $self->svcnum } )
) {
- #next unless defined; #wtf is up with qsearch?
- warn $cust_main_invoice;
- next unless defined $cust_main_invoice;
+ unless ( defined($cust_main_invoice) ) {
+ warn "WARNING: something's wrong with qsearch";
+ next;
+ }
my %hash = $cust_main_invoice->hash;
$hash{'dest'} = $self->email;
my $new = new FS::cust_main_invoice \%hash;
}
}
if ( $icradius_dbh ) {
- my $queue = new FS::queue { 'job' => 'FS::svc_acct::icradius_rc_delete' };
- $error = $queue->insert( $self->username );
+
+ my $radcheck_queue =
+ new FS::queue { 'job' => 'FS::svc_acct::icradius_rc_delete' };
+ $error = $radcheck_queue->insert( $self->username );
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "queueing job (transaction rolled back): $error";
}
+
+ my $radreply_queue =
+ new FS::queue { 'job' => 'FS::svc_acct::icradius_rr_delete' };
+ $error = $radreply_queue->insert( $self->username );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "queueing job (transaction rolled back): $error";
+ }
+
}
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
1;
}
+sub icradius_rr_delete {
+ my $username = shift;
+
+ my $sth = $icradius_dbh->prepare(
+ 'DELETE FROM radreply WHERE UserName = ?'
+ );
+ $sth->execute($username)
+ or die "can't delete from radreply table: ". $sth->errstr;
+
+ 1;
+}
+
=item replace OLD_RECORD
Replaces OLD_RECORD with this one in the database. If there is an error,
return "Username in use"
if $old->username ne $new->username &&
- qsearchs( 'svc_acct', { 'username' => $new->username } );
-
- return "Can't change uid!" if $old->uid != $new->uid;
+ qsearchs( 'svc_acct', { 'username' => $new->username,
+ 'domsvc' => $new->domsvc,
+ } );
+ {
+ #no warnings 'numeric'; #alas, a 5.006-ism
+ local($^W) = 0;
+ return "Can't change uid!" if $old->uid != $new->uid;
+ }
return "can't change username using Cyrus"
if $cyrus_server && $old->username ne $new->username;
sub suspend {
my $self = shift;
my %hash = $self->hash;
- unless ( $hash{_password} =~ /^\*SUSPENDED\* / ) {
+ unless ( $hash{_password} =~ /^\*SUSPENDED\* /
+ || $hash{_password} eq '*'
+ ) {
$hash{_password} = '*SUSPENDED* '.$hash{_password};
my $new = new FS::svc_acct ( \%hash );
$new->replace($self);
my $ulen = $usernamemax || $self->dbdef_table->column('username')->length;
if ( $username_uppercase ) {
- $recref->{username} =~ /^([a-z0-9_\-\.]{$usernamemin,$ulen})$/i
- or return "Illegal username";
+ $recref->{username} =~ /^([a-z0-9_\-\.\&]{$usernamemin,$ulen})$/i
+ or return "Illegal username: ". $recref->{username};
+ $recref->{username} = $1;
} else {
- $recref->{username} =~ /^([a-z0-9_\-\.]{$usernamemin,$ulen})$/
- or return "Illegal username";
+ $recref->{username} =~ /^([a-z0-9_\-\.\&]{$usernamemin,$ulen})$/
+ or return "Illegal username: ". $recref->{username};
+ $recref->{username} = $1;
}
- $recref->{username} = $1;
if ( $username_letterfirst ) {
$recref->{username} =~ /^[a-z]/ or return "Illegal username";
if ( $username_noperiod ) {
$recref->{username} =~ /\./ and return "Illegal username";
}
+ unless ( $username_ampersand ) {
+ $recref->{username} =~ /\&/ and return "Illegal username";
+ }
$recref->{popnum} =~ /^(\d*)$/ or return "Illegal popnum: ".$recref->{popnum};
$recref->{popnum} = $1;
# $error = $self->ut_textn('finger');
# return $error if $error;
$self->getfield('finger') =~
- /^([\w \t\!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\*]*)$/
+ /^([\w \t\!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\*\<\>]*)$/
or return "Illegal finger: ". $self->getfield('finger');
$self->setfield('finger', $1);
- $recref->{dir} =~ /^([\/\w\-]*)$/
+ $recref->{dir} =~ /^([\/\w\-\.\&]*)$/
or return "Illegal directory";
$recref->{dir} = $1;
+ return "Illegal directory"
+ if $recref->{dir} =~ /(^|\/)\.+(\/|$)/; #no .. component
+ return "Illegal directory"
+ if $recref->{dir} =~ /\&/ && ! $username_ampersand;
unless ( $recref->{dir} ) {
$recref->{dir} = $dir_prefix . '/';
if ( $dirhash > 0 ) {
unless ( $recref->{_password} );
#if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{4,16})$/ ) {
- if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{$passwordmin,8})$/ ) {
+ if ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([^\t\n]{$passwordmin,$passwordmax})$/ ) {
$recref->{_password} = $1.$3;
#uncomment this to encrypt password immediately upon entry, or run
#bin/crypt_pw in cron to give new users a window during which their
} elsif ( $recref->{_password} eq '!!' ) {
$recref->{_password} = '!!';
} else {
- return "Illegal password";
+ #return "Illegal password";
+ return "Illegal password: ". $recref->{_password};
}
''; #no error
sub radius_reply {
my $self = shift;
- map {
- /^(radius_(.*))$/;
- my($column, $attrib) = ($1, $2);
- #$attrib =~ s/_/\-/g;
- ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) );
- } grep { /^radius_/ && $self->getfield($_) } fields( $self->table );
+ my %reply =
+ map {
+ /^(radius_(.*))$/;
+ my($column, $attrib) = ($1, $2);
+ #$attrib =~ s/_/\-/g;
+ ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) );
+ } grep { /^radius_/ && $self->getfield($_) } fields( $self->table );
+ if ( $self->ip && $self->ip ne '0e0' ) {
+ $reply{'Framed-IP-Address'} = $self->ip;
+ }
+ %reply;
}
=item radius_check
sub domain {
my $self = shift;
if ( $self->domsvc ) {
- my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $self->domsvc } )
+ #$self->svc_domain->domain;
+ my $svc_domain = $self->svc_domain
or die "no svc_domain.svcnum for svc_acct.domsvc ". $self->domsvc;
$svc_domain->domain;
} else {
}
}
+=item svc_domain
+
+Returns the FS::svc_domain record for this account's domain (see
+L<FS::svc_domain>.
+
+=cut
+
+sub svc_domain {
+ my $self = shift;
+ $self->{'_domsvc'}
+ ? $self->{'_domsvc'}
+ : qsearchs( 'svc_domain', { 'svcnum' => $self->domsvc } );
+}
+
+=item cust_svc
+
+Returns the FS::cust_svc record for this account (see L<FS::cust_svc>).
+
+sub cust_svc {
+ my $self = shift;
+ qsearchs( 'cust_svc', { 'svcnum' => $self->svcnum } );
+}
+
=item email
Returns an email address associated with the account.
$self->username. '@'. $self->domain;
}
+=item seconds_since TIMESTAMP
+
+Returns the number of seconds this account has been online since TIMESTAMP.
+See L<FS::session>
+
+TIMESTAMP is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+#note: POD here, implementation in FS::cust_svc
+sub seconds_since {
+ my $self = shift;
+ $self->cust_svc->seconds_since(@_);
+}
+
=item ssh
=cut
=head1 VERSION
-$Id: svc_acct.pm,v 1.47 2001-09-30 20:30:09 ivan Exp $
+$Id: svc_acct.pm,v 1.64 2002-01-29 16:33:15 ivan Exp $
=head1 BUGS
=head1 SEE ALSO
-L<FS::svc_Common>, L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>,
-L<FS::part_svc>, L<FS::cust_pkg>, L<FS::queue>, L<freeside-queued>),
-L<Net::SSH>, L<ssh>, L<FS::svc_acct_pop>,
+L<FS::svc_Common>, edit/part_svc.cgi from an installed web interface,
+export.html from the base documentation, L<FS::Record>, L<FS::Conf>,
+L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, L<FS::queue>,
+L<freeside-queued>), L<Net::SSH>, L<ssh>, L<FS::svc_acct_pop>,
schema.html from the base documentation.
=cut