From fba31d6a0954ccfbb1d491ec018f0513d2a4ee2a Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 18 Oct 2001 15:04:54 +0000 Subject: [PATCH] preliminary self administration --- fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm | 541 ++++++++++++++++ fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi | 698 +++++++++++++++++++++ fs_selfadmin/FS-MailAdminServer/fs_mailadmind | 366 +++++++++++ fs_selfadmin/fs_mailadmin_server | 642 +++++++++++++++++++ 4 files changed, 2247 insertions(+) create mode 100755 fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm create mode 100755 fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi create mode 100755 fs_selfadmin/FS-MailAdminServer/fs_mailadmind create mode 100755 fs_selfadmin/fs_mailadmin_server diff --git a/fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm b/fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm new file mode 100755 index 000000000..46cde4c0d --- /dev/null +++ b/fs_selfadmin/FS-MailAdminServer/MailAdminClient.pm @@ -0,0 +1,541 @@ +package FS::MailAdminClient; + +use strict; +use vars qw($VERSION @ISA @EXPORT_OK $fs_mailadmind_socket); +use Exporter; +use Socket; +use FileHandle; +use IO::Handle; + +$VERSION = '0.01'; + +@ISA = qw( Exporter ); +@EXPORT_OK = qw( signup_info authenticate list_packages list_mailboxes delete_mailbox password_mailbox add_mailbox list_forwards list_pkg_forwards delete_forward add_forward new_customer ); + +$fs_mailadmind_socket = "/usr/local/freeside/fs_mailadmind_socket"; + +$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin'; +$ENV{'SHELL'} = '/bin/sh'; +$ENV{'IFS'} = " \t\n"; +$ENV{'CDPATH'} = ''; +$ENV{'ENV'} = ''; +$ENV{'BASH_ENV'} = ''; + +my $freeside_uid = scalar(getpwnam('freeside')); +die "not running as the freeside user\n" if $> != $freeside_uid; + +=head1 NAME + +FS::MailAdminClient - Freeside mail administration client API + +=head1 SYNOPSIS + + use FS::MailAdminClient qw( signup_info list_mailboxes new_customer ); + + ( $locales, $packages, $pops ) = signup_info; + + ( $accounts ) = list_mailboxes; + + $error = new_customer ( { + 'first' => $first, + 'last' => $last, + 'ss' => $ss, + 'comapny' => $company, + 'address1' => $address1, + 'address2' => $address2, + 'city' => $city, + 'county' => $county, + 'state' => $state, + 'zip' => $zip, + 'country' => $country, + 'daytime' => $daytime, + 'night' => $night, + 'fax' => $fax, + 'payby' => $payby, + 'payinfo' => $payinfo, + 'paydate' => $paydate, + 'payname' => $payname, + 'invoicing_list' => $invoicing_list, + 'pkgpart' => $pkgpart, + 'username' => $username, + '_password' => $password, + 'popnum' => $popnum, + } ); + +=head1 DESCRIPTION + +This module provides an API for a remote mail administration server. + +It needs to be run as the freeside user. Because of this, the program which +calls these subroutines should be written very carefully. + +=head1 SUBROUTINES + +=over 4 + +=item signup_info + +Returns three array references of hash references. + +The first set of hash references is of allowable locales. Each hash reference +has the following keys: + taxnum + state + county + country + +The second set of hash references is of allowable packages. Each hash +reference has the following keys: + pkgpart + pkg + +The third set of hash references is of allowable POPs (Points Of Presence). +Each hash reference has the following keys: + popnum + city + state + ac + exch + +=cut + +sub signup_info { + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "signup_info\n"; + SOCK->flush; + + chop ( my $n_cust_main_county = ); + my @cust_main_county = map { + chop ( my $taxnum = ); + chop ( my $state = ); + chop ( my $county = ); + chop ( my $country = ); + { + 'taxnum' => $taxnum, + 'state' => $state, + 'county' => $county, + 'country' => $country, + }; + } 1 .. $n_cust_main_county; + + chop ( my $n_part_pkg = ); + my @part_pkg = map { + chop ( my $pkgpart = ); + chop ( my $pkg = ); + { + 'pkgpart' => $pkgpart, + 'pkg' => $pkg, + }; + } 1 .. $n_part_pkg; + + chop ( my $n_svc_acct_pop = ); + my @svc_acct_pop = map { + chop ( my $popnum = ); + chop ( my $city = ); + chop ( my $state = ); + chop ( my $ac = ); + chop ( my $exch = ); + chop ( my $loc = ); + { + 'popnum' => $popnum, + 'city' => $city, + 'state' => $state, + 'ac' => $ac, + 'exch' => $exch, + 'loc' => $loc, + }; + } 1 .. $n_svc_acct_pop; + + close SOCK; + + \@cust_main_county, \@part_pkg, \@svc_acct_pop; +} + +=item authenticate + +Authentictes against a service on the remote Freeside system. Requires a hash +reference as a parameter with the following keys: + authuser + _password + +Returns a scalar error message of the form "authuser OK|FAILED" or an error +message. + +=cut + +sub authenticate { + my $hashref = shift; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "authenticate", "\n"; + SOCK->flush; + + print SOCK join("\n", map { $hashref->{$_} } qw( + authuser _password + ) ), "\n"; + SOCK->flush; + + chop( my $error = ); + close SOCK; + + $error; +} + +=item list_packages + +Returns one array reference of hash references. + +The set of hash references is of existing packages. Each hash reference +has the following keys: + pkgnum + domain + account + +=cut + +sub list_packages { + my $user = shift; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "list_packages\n", $user, "\n"; + SOCK->flush; + + chop ( my $n_packages = ); + my @packages = map { + chop ( my $pkgnum = ); + chop ( my $domain = ); + chop ( my $account = ); + { + 'pkgnum' => $pkgnum, + 'domain' => $domain, + 'account' => $account, + }; + } 1 .. $n_packages; + + close SOCK; + + \@packages; +} + +=item list_mailboxes + +Returns one array references of hash references. + +The set of hash references is of existing accounts. Each hash reference +has the following keys: + svcnum + username + _password + +=cut + +sub list_mailboxes { + my ($user, $package) = @_; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "list_mailboxes\n", $user, "\n", $package, "\n"; + SOCK->flush; + + chop ( my $n_svc_acct = ); + my @svc_acct = map { + chop ( my $svcnum = ); + chop ( my $username = ); + chop ( my $_password = ); + { + 'svcnum' => $svcnum, + 'username' => $username, + '_password' => $_password, + }; + } 1 .. $n_svc_acct; + + close SOCK; + + \@svc_acct; +} + +=item delete_mailbox + +Deletes a mailbox service from the remote Freeside system. Requires a hash +reference as a paramater with the following keys: + authuser + account + +Returns a scalar error message, or the empty string for success. + +=cut + +sub delete_mailbox { + my $hashref = shift; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "delete_mailbox", "\n"; + SOCK->flush; + + print SOCK join("\n", map { $hashref->{$_} } qw( + authuser account + ) ), "\n"; + SOCK->flush; + + chop( my $error = ); + close SOCK; + + $error; +} + +=item password_mailbox + +Changes the password for a mailbox service on the remote Freeside system. + Requires a hash reference as a paramater with the following keys: + authuser + account + _password + +Returns a scalar error message, or the empty string for success. + +=cut + +sub password_mailbox { + my $hashref = shift; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "password_mailbox", "\n"; + SOCK->flush; + + print SOCK join("\n", map { $hashref->{$_} } qw( + authuser account _password + ) ), "\n"; + SOCK->flush; + + chop( my $error = ); + close SOCK; + + $error; +} + +=item add_mailbox + +Creates a mailbox service on the remote Freeside system. Requires a hash +reference as a parameter with the following keys: + authuser + package + account + _password + +Returns a scalar error message, or the empty string for success. + +=cut + +sub add_mailbox { + my $hashref = shift; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "add_mailbox", "\n"; + SOCK->flush; + + print SOCK join("\n", map { $hashref->{$_} } qw( + authuser package account _password + ) ), "\n"; + SOCK->flush; + + chop( my $error = ); + close SOCK; + + $error; +} + +=item list_forwards + +Returns one array references of hash references. + +The set of hash references is of existing forwards. Each hash reference +has the following keys: + svcnum + dest + +=cut + +sub list_forwards { + my ($user, $service) = @_; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "list_forwards\n", $user, "\n", $service, "\n"; + SOCK->flush; + + chop ( my $n_svc_forward = ); + my @svc_forward = map { + chop ( my $svcnum = ); + chop ( my $dest = ); + { + 'svcnum' => $svcnum, + 'dest' => $dest, + }; + } 1 .. $n_svc_forward; + + close SOCK; + + \@svc_forward; +} + +=item list_pkg_forwards + +Returns one array references of hash references. + +The set of hash references is of existing forwards. Each hash reference +has the following keys: + svcnum + srcsvc + dest + +=cut + +sub list_pkg_forwards { + my ($user, $package) = @_; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "list_pkg_forwards\n", $user, "\n", $package, "\n"; + SOCK->flush; + + chop ( my $n_svc_forward = ); + my @svc_forward = map { + chop ( my $svcnum = ); + chop ( my $srcsvc = ); + chop ( my $dest = ); + { + 'svcnum' => $svcnum, + 'srcsvc' => $srcsvc, + 'dest' => $dest, + }; + } 1 .. $n_svc_forward; + + close SOCK; + + \@svc_forward; +} + +=item delete_forward + +Deletes a forward service from the remote Freeside system. Requires a hash +reference as a paramater with the following keys: + authuser + svcnum + +Returns a scalar error message, or the empty string for success. + +=cut + +sub delete_forward { + my $hashref = shift; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "delete_forward", "\n"; + SOCK->flush; + + print SOCK join("\n", map { $hashref->{$_} } qw( + authuser svcnum + ) ), "\n"; + SOCK->flush; + + chop( my $error = ); + close SOCK; + + $error; +} + +=item add_forward + +Creates a forward service on the remote Freeside system. Requires a hash +reference as a parameter with the following keys: + authuser + package + source + dest + +Returns a scalar error message, or the empty string for success. + +=cut + +sub add_forward { + my $hashref = shift; + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "add_forward", "\n"; + SOCK->flush; + + print SOCK join("\n", map { $hashref->{$_} } qw( + authuser package source dest + ) ), "\n"; + SOCK->flush; + + chop( my $error = ); + close SOCK; + + $error; +} + +=item new_customer HASHREF + +Adds a customer to the remote Freeside system. Requires a hash reference as +a paramater with the following keys: + first + last + ss + comapny + address1 + address2 + city + county + state + zip + country + daytime + night + fax + payby + payinfo + paydate + payname + invoicing_list + pkgpart + username + _password + popnum + +Returns a scalar error message, or the empty string for success. + +=cut + +sub new_customer { + my $hashref = shift; + + socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; + connect(SOCK, sockaddr_un($fs_mailadmind_socket)) or die "connect: $!"; + print SOCK "new_customer\n"; + + print SOCK join("\n", map { $hashref->{$_} } qw( + first last ss company address1 address2 city county state zip country + daytime night fax payby payinfo paydate payname invoicing_list + pkgpart username _password popnum + ) ), "\n"; + SOCK->flush; + + chop( my $error = ); + $error; +} + +=back + +=head1 VERSION + +$Id: MailAdminClient.pm,v 1.1 2001-10-18 15:04:54 jeff Exp $ + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L + +=cut + +1; + diff --git a/fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi b/fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi new file mode 100755 index 000000000..82002169b --- /dev/null +++ b/fs_selfadmin/FS-MailAdminServer/cgi/mailadmin.cgi @@ -0,0 +1,698 @@ +#!/usr/bin/perl +######################################################################## +# # +# mailadmin.cgi NCI2000 # +# Jeff Finucane # +# 26 April 2001 # +# # +######################################################################## + +use DBI; +use strict; +use CGI; +use FS::TestAdminClient qw(authenticate list_packages list_mailboxes delete_mailbox password_mailbox add_mailbox list_forwards list_pkg_forwards delete_forward add_forward); + +my $sessionfile = '/usr/local/apache/htdocs/mailadmin/adminsess'; # session file +my $tmpdir = '/usr/local/apache/htdocs/mailadmin/tmp'; # Location to store temp files +my $cookiedomain = ".your.dom"; # domain if THIS server, should prepend with a '.' +my $cookieexpire = '+12h'; # expire the cookie session after this much idle time +my $sessexpire = 43200; # expire session after this long of no use (in seconds) + +my $body = ""; + +#### Should not have to change anything under this line #### +my $printmainpage = 1; +my $i = 0; +my $printheader = 1; +my $query = new CGI; +my $cgi = $query->url(); +my $now = getdatetime(); +my $current_package = 0; +my $current_account = 0; +my $current_domname = ""; + +# if they are trying to login we wont check the session yet +if ($query->param('login') eq '' && $query->param('action') ne 'login') { + checksession(); + printheader(); +} + +if ($query->param('login') ne '') { + + my $username = $query->param('username'); + my $password = $query->param('password'); + + if (!checkuserpass($username, $password)) { + printheader(); + error('not_admin'); + } + + my @alpha = ('A'..'Z', 'a'..'z', 0..9); + my $sessid = ''; + for (my $i = 0; $i < 10; $i++) { + $sessid .= @alpha[rand(@alpha)]; + } + + my $cookie1 = $query->cookie(-name=>'username', + -value=>$username, + -expires=>$cookieexpire, + -domain=>$cookiedomain); + + my $cookie2 = $query->cookie(-name=>'ma_sessionid', + -value=>$sessid, + -expires=>$cookieexpire, + -domain=>$cookiedomain); + + my $now = time(); + open(NEWSESS, ">>$sessionfile") || error('open'); + print NEWSESS "$username $sessid $now 0 0\n"; + close(NEWSESS); + + print $query->header(-COOKIE=>[$cookie1, $cookie2]); + + $printmainpage = 1; + +} elsif ($query->param('action') eq 'blankframe') { + + print "$body\n"; + $printmainpage = 0; + +} elsif ($query->param('action') eq 'list_packages') { + + my $username = $query->cookie(-name=>'username'); # session checked + my $list = list_packages($username); + print "$body\n"; + print "
\n"; + print "\n"; + foreach my $package ( @{$list} ) { + print ""; + print "\n"; + print "\n"; + print ""; + } + print "

Package Number

Description

$package->{'pkgnum'}

$package->{'domain'}

{'pkgnum'}&account=$package->{'account'}&domname=$package->{'domain'}\" target=\"rightmainframe\">select
\n"; + print "\n"; + $printmainpage=0; + +} elsif ($query->param('action') eq 'list_mailboxes') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $list = list_mailboxes($username, $current_package); + my $forwardlist = list_pkg_forwards($username, $current_package); + print "$body\n"; + print "
\n"; + print "\n"; + foreach my $account ( @{$list} ) { + print ""; + print "\n"; + print "\n"; + print ""; + +# my $forwardlist = list_forwards($username, $account->{'svcnum'}); +# foreach my $forward ( @{$forwardlist} ) { +# my $label = qq!=> ! . $forward->{'dest'}; +# print "\n"; +# } + foreach my $forward ( @{$forwardlist} ) { + if ($forward->{'srcsvc'} == $account->{'svcnum'}) { + my $label = qq!=> ! . $forward->{'dest'}; + print "\n"; + } + } + + } + print "

Username

Password

$account->{'username'}

$account->{'_password'}

{'svcnum'}&mailbox=$account->{'username'}\" target=\"rightmainframe\">change

$label

$label

\n"; + print "\n"; + $printmainpage=0; + +} elsif ($query->param('action') eq 'select') { + + my $username = $query->cookie(-name=>'username'); # session checked + $current_package = $query->param('package'); + $current_account = $query->param('account'); + $current_domname = $query->param('domname'); + set_package(); + print "$body\n"; + print "
\n"; + print "
\n"; + print "

Selected package $current_package\n"; + print "

\n"; + print "
\n"; + print "\n"; + $printmainpage=0; + +} elsif ($query->param('action') eq 'change') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $account = $query->param('account'); + my $mailbox = $query->param('mailbox'); + my $list = list_forwards($username, $account); + print "$body\n"; + print "
\n"; + print "
\n"; + print "\n"; + print "\n"; + print "\n"; + foreach my $forward ( @{$list} ) { + my $label = qq!=> ! . $forward->{'dest'}; +# print "\n"; + print "\n"; + } + print "\n"; + print "

Username

$mailbox

$label

$label

{'svcnum'}&mailbox=$mailbox&dest=$forward->{'dest'}\" target=\"rightmainframe\">remove

Password

\n"; + print "\n"; + print "\n"; + print "\n"; + print "
\n"; + print "
\n"; + print "
\n"; + print "

You may delete this user and all mailforwarding by pressing Delete This User.\n"; + print "

To set or change the password for this user, type the new password in the box next to Password and press Change The Password.\n"; + print "

If you would like to have mail destined for this user forwarded to another email address then press the Add Forwarding button.\n"; + print "\n"; + $printmainpage=0; + +} elsif ($query->param('deleteaccount') ne '') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $account = $query->param('account'); + my $mailbox = $query->param('mailbox'); + print "$body\n"; + print "

\n"; + print "

Are you certain you want to delete user $mailbox?\n"; + print "

\n"; + print "\n"; + print "\n"; + $printmainpage=0; + +} elsif ($query->param('deleteaccounty') ne '') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $account = $query->param('account'); + + if ( my $error = delete_mailbox ( { + 'authuser' => $username, + 'account' => $account, + } ) ) { + print "$body\n"; + print "

$error\n"; + print "\n"; + + } else { + print "$body\n"; + print "

Deleted\n"; + print "\n"; + } + + $printmainpage=0; + +} elsif ($query->param('changepassword') ne '') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $account = $query->param('account'); + my $_password = $query->param('_password'); + + if ( my $error = password_mailbox ( { + 'authuser' => $username, + 'account' => $account, + '_password' => $_password, + } ) ) { + print "$body\n"; + print "

$error\n"; + print "\n"; + + } else { + print "$body\n"; + print "

Changed\n"; + print "\n"; + } + + $printmainpage=0; + +} elsif ($query->param('action') eq 'newmailbox') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + print "$body\n"; + print "\n"; + print "

\n"; + print "\n"; + print "\n"; + print "

Username

@ " . $current_domname . "

Password

\n"; + print "\n"; + print "
\n"; + print "
\n"; + print "
\n"; + print "

Use this screen to add a new mailbox user. If the domain name of the email address (the part after the @ sign) is not what you expect then you may need to use List Packages to select the package with the correct domain.\n"; + print "

Enter the first portion of the email address in the box adjacent to Username and enter the password for that user in the space next to Password. Then press the button labeled Add The User.\n"; + print "

If you do not want to add a new user at this time then select a choice from the menu at the left, such as List Mailboxes.\n"; + print "\n"; + $printmainpage=0; + +} elsif ($query->param('addmailbox') ne '') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $account = $query->param('account'); + my $_password = $query->param('_password'); + + if ( my $error = add_mailbox ( { + 'authuser' => $username, + 'package' => $current_package, + 'account' => $account, + '_password' => $_password, + } ) ) { + print "$body\n"; + print "

$error\n"; + print "\n"; + + } else { + print "$body\n"; + print "

Created\n"; + print "\n"; + } + + $printmainpage=0; + +} elsif ($query->param('action') eq 'deleteforward') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $svcnum = $query->param('service'); + my $mailbox = $query->param('mailbox'); + my $dest = $query->param('dest'); + print "$body\n"; + print "

\n"; + print "

Are you certain you want to remove the forwarding from $mailbox to $dest?\n"; + print "

\n"; + print "\n"; + print "\n"; + $printmainpage=0; + +} elsif ($query->param('deleteforwardy') ne '') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $service = $query->param('service'); + + if ( my $error = delete_forward ( { + 'authuser' => $username, + 'svcnum' => $service, + } ) ) { + print "$body\n"; + print "

$error\n"; + print "\n"; + + } else { + print "$body\n"; + print "

Forwarding Removed\n"; + print "\n"; + } + + $printmainpage=0; + +} elsif ($query->param('addforward') ne '') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $account = $query->param('account'); + my $mailbox = $query->param('mailbox'); + + print "$body\n"; + print "\n"; + print "

\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "
Forward mail from

$mailbox:

to

Destination:

\n"; + print "\n"; + print "
\n"; + print "
\n"; + print "
\n"; + print "

If you would like mail originally destined for the above address to be forwarded to a different email address then type that email address in the box next to Destination: and press the Add the Forwarding button.\n"; + print "

If you do not want to add mail forwarding then select a choice from the menu at the left, such as List Accounts.\n"; + + $printmainpage=0; + +} elsif ($query->param('addforwarddst') ne '') { + + my $username = $query->cookie(-name=>'username'); # session checked + select_package($username) unless $current_package; + my $account = $query->param('account'); + my $dest = $query->param('dest'); + + if ( my $error = add_forward ( { + 'authuser' => $username, + 'package' => $current_package, + 'source' => $account, + 'dest' => $dest, + } ) ) { + print "$body\n"; + print "

$error\n"; + print "\n"; + + } else { + print "$body\n"; + print "

Forwarding Created\n"; + print "\n"; + } + + $printmainpage=0; + +} elsif ($query->param('action') eq 'navframe') { + + print "\n"; + print "

NCI2000 MAIL ADMIN Web Interface

\n"; + + print "
Choose Action:

\n"; + print "
\n"; + print "
    \n"; + print "
\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "
  • Log Off
  • List Packages
  • List Accounts
  • Add Account
  • \n"; + + print "


    \n"; + print "\n"; + + $printmainpage = 0; + +} elsif ($query->param('action') eq 'rightmainframe') { + + print "$body\n"; + print "


    \n"; + print "<----- Please choose function on the left menu\n"; + print "

    \n"; + print "

    Choose Log Off when you are finished. This helps prevent unauthorized access to your accounts.\n"; + print "

    Use List Packages when you administer multiple packages. When you have multiple domains at NCI2000 you are likely to have multiple packages. Use of List Packages is not necessary if administer only one package.\n"; + print "

    Use List Accounts to view your current arrangement of mailboxes. From this list you my choose to make changes to existing mailboxes or delete mailboxes. If you would like to modify the forwarding associated with a mailbox then choose it from this list.\n"; + print "

    Use Add Account when you would like an additional mailbox. After you have added the mailbox you may choose to make additional changes from the list provided by List Accounts.\n"; + print "\n"; + + $printmainpage = 0; + +} + + +if ($query->param('action') eq 'login') { + + printheader(); + printlogin(); + +} elsif ($query->param('action') eq 'logout') { + + destroysession(); + printheader(); + printlogin(); + +} elsif ($printmainpage) { + + + print "NCI2000 MAIL ADMIN Web Interface\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + + +} + +sub getdatetime { + my $today = localtime(time()); + my ($day,$mon,$dayofmon,$time,$year) = split(/\s+/,$today); + my @datemonths = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"); + + my $numidx = "01"; + my ($nummon); + foreach my $mons (@datemonths) { + if ($mon eq $mons) { + $nummon = $numidx; + } + $numidx++; + } + + return "$year-$nummon-$dayofmon $time"; + +} + +sub error { + + my $error = shift; + my $arg1 = shift; + + printheader(); + + if ($error eq 'not_admin') { + print "Error!\n"; + print "$body\n"; + print "

    Error!

    \n"; + print "Unauthorized attempt to access mail administration.\n"; + print "
    Please login again if you think this is an error.\n"; + print "
    \n"; + print "\n"; + } elsif ($error eq 'exists') { + print "Error!\n"; + print "$body\n"; + print "

    Error!

    \n"; + print "The user you are trying to enter already exists. Please go back and enter a different username\n"; + print "\n"; + } elsif ($error eq 'ingroup') { + print "Error!\n"; + print "$body\n"; + print "

    Error!

    \n"; + print "This user is already in the group $arg1. Please go back and deselect group $arg1 from the list.\n"; + print "
    \n"; + print "\n"; + } elsif ($error eq 'sess_expired') { + print "$body\n"; + print "
    Your session has expired.
    \n"; + print "

    Please login again HERE
    \n"; + print "\n"; + } elsif ($error eq 'open') { + print "$body\n"; + print "
    Unable to open or rename file.
    \n"; + print "

    If this continues, please contact your administrator
    \n"; + print "\n"; + } + + + exit; + +} + + +#print a html header if not printed yet +sub printheader { + + if ($printheader) { + print "Content-Type: text/html\n\n"; + $printheader = 0; + } + +} + + +#verify user can access administration +sub checksession { + + my $username = $query->cookie(-name=>'username'); + my $sessionid = $query->cookie(-name=>'ma_sessionid'); + + if ($sessionid eq '') { + printheader(); + if ($query->param()) { + error('sess_expired'); + } else { + printlogin(); + exit; + } + } + + my $now = time(); + my $founduser = 0; + open(SESSFILE, "$sessionfile") || error('open'); + error('open') if -l "$tmpdir/adminsess.$$"; + open(NEWSESS, ">$tmpdir/adminsess.$$") || error('open'); + while () { + chomp(); + my ($user, $sess, $time, $pkgnum, $svcdomain, $domname) = split(/\s+/); + next if $now - $sessexpire > $time; + if ($username eq $user && !$founduser) { + if ($sess eq $sessionid) { + $founduser = 1; + print NEWSESS "$user $sess $now $pkgnum $svcdomain $domname\n"; + $current_package=$pkgnum; + $current_account=$svcdomain; + $current_domname=$domname; + next; + } + } + print NEWSESS "$user $sess $time $pkgnum $svcdomain $domname\n"; + } + close(SESSFILE); + close(NEWSESS); + system("mv $tmpdir/adminsess.$$ $sessionfile"); + error('sess_expired') unless $founduser; + + my $cookie1 = $query->cookie(-name=>'username', + -value=>$username, + -expires=>$cookieexpire, + -domain=>$cookiedomain); + + my $cookie2 = $query->cookie(-name=>'ma_sessionid', + -value=>$sessionid, + -expires=>$cookieexpire, + -domain=>$cookiedomain); + + print $query->header(-COOKIE=>[$cookie1, $cookie2]); + + $printheader = 0; + + return 0; + +} + +sub destroysession { + + my $username = $query->cookie(-name=>'username'); + my $sessionid = $query->cookie(-name=>'ma_sessionid'); + + if ($sessionid eq '') { + printheader(); + if ($query->param()) { + error('sess_expired'); + } else { + printlogin(); + exit; + } + } + + my $now = time(); + my $founduser = 0; + open(SESSFILE, "$sessionfile") || error('open'); + error('open') if -l "$tmpdir/adminsess.$$"; + open(NEWSESS, ">$tmpdir/adminsess.$$") || error('open'); + while () { + chomp(); + my ($user, $sess, $time, $pkgnum, $svcdomain, $domname) = split(/\s+/); + next if $now - $sessexpire > $time; + if ($username eq $user && !$founduser) { + if ($sess eq $sessionid) { + $founduser = 1; + next; + } + } + print NEWSESS "$user $sess $time $pkgnum $svcdomain $domname\n"; + } + close(SESSFILE); + close(NEWSESS); + system("mv $tmpdir/adminsess.$$ $sessionfile"); + error('sess_expired') unless $founduser; + + $printheader = 0; + + return 0; + +} + +# checks the username and pass against the database +sub checkuserpass { + + my $username = shift; + my $password = shift; + + my $error = authenticate ( { + 'authuser' => $username, + '_password' => $password, + } ); + + if ($error eq "$username OK") { + return 1; + }else{ + return 0; + } + +} + +#printlogin prints a login page +sub printlogin { + + print "$body\n"; + print "
    Please login to access MAIL ADMIN
    \n"; + print "
    \n"; + print "
    Email Address:   \n"; + print "
    Email Password: \n"; + print "
    \n"; + print "
    \n"; + print "\n"; +} + + +#select_package chooses a administrable package if more than one exists +sub select_package { + my $user = shift; + my $packages = list_packages($user); + if (scalar(@{$packages}) eq 1) { + $current_package = @{$packages}[0]->{'pkgnum'}; + set_package(); + } + if (scalar(@{$packages}) > 1) { +# print $query->redirect("$cgi\?action=list_packages"); + print "

    No package selected. You must first select a package.\n"; + exit; + } +} + +sub set_package { + + my $username = $query->cookie(-name=>'username'); + my $sessionid = $query->cookie(-name=>'ma_sessionid'); + + if ($sessionid eq '') { + printheader(); + if ($query->param()) { + error('sess_expired'); + } else { + printlogin(); + exit; + } + } + + my $now = time(); + my $founduser = 0; + open(SESSFILE, "$sessionfile") || error('open'); + error('open') if -l "$tmpdir/adminsess.$$"; + open(NEWSESS, ">$tmpdir/adminsess.$$") || error('open'); + while () { + chomp(); + my ($user, $sess, $time, $pkgnum, $svcdomain, $domname) = split(/\s+/); + next if $now - $sessexpire > $time; + if ($username eq $user && !$founduser) { + if ($sess eq $sessionid) { + $founduser = 1; + print NEWSESS "$user $sess $time $current_package $current_account $current_domname\n"; + next; + } + } + print NEWSESS "$user $sess $time $pkgnum $svcdomain $domname\n"; + } + close(SESSFILE); + close(NEWSESS); + system("mv $tmpdir/adminsess.$$ $sessionfile"); + error('sess_expired') unless $founduser; + + $printheader = 0; + + return 0; + +} + diff --git a/fs_selfadmin/FS-MailAdminServer/fs_mailadmind b/fs_selfadmin/FS-MailAdminServer/fs_mailadmind new file mode 100755 index 000000000..746d7822e --- /dev/null +++ b/fs_selfadmin/FS-MailAdminServer/fs_mailadmind @@ -0,0 +1,366 @@ +#!/usr/bin/perl -Tw + +eval 'exec /usr/bin/perl -Tw -S $0 ${1+"$@"}' + if 0; # not running under some shell +# +# fs_mailadmind +# +# This is run REMOTELY over ssh by fs_mailadmin_server. +# + +use strict; +use Socket; + +use vars qw( $Debug ); + +$Debug = 0; + +my($fs_mailadmind_socket)="/usr/local/freeside/fs_mailadmind_socket"; + +$ENV{'PATH'} ='/usr/local/bin:/usr/bin:/usr/ucb:/bin'; +$ENV{'SHELL'} = '/bin/sh'; +$ENV{'IFS'} = " \t\n"; +$ENV{'CDPATH'} = ''; +$ENV{'ENV'} = ''; +$ENV{'BASH_ENV'} = ''; + +$|=1; + +warn "[fs_mailadmind] Reading locales...\n" if $Debug; +chomp( my $n_cust_main_county = ); +my @cust_main_county = map { + chomp( my $taxnum = ); + chomp( my $state = ); + chomp( my $county = ); + chomp( my $country = ); + { + 'taxnum' => $taxnum, + 'state' => $state, + 'county' => $county, + 'country' => $country, + }; +} ( 1 .. $n_cust_main_county ); + +warn "[fs_mailadmind] Reading package definitions...\n" if $Debug; +chomp( my $n_part_pkg = ); +my @part_pkg = map { + chomp( my $pkgpart = ); + chomp( my $pkg = ); + { + 'pkgpart' => $pkgpart, + 'pkg' => $pkg, + }; +} ( 1 .. $n_part_pkg ); + +warn "[fs_mailadmind] Reading POPs...\n" if $Debug; +chomp( my $n_svc_acct_pop = ); +my @svc_acct_pop = map { + chomp( my $popnum = ); + chomp( my $city = ); + chomp( my $state = ); + chomp( my $ac = ); + chomp( my $exch = ); + chomp( my $loc = ); + { + 'popnum' => $popnum, + 'city' => $city, + 'state' => $state, + 'ac' => $ac, + 'exch' => $exch, + 'loc' => $loc, + }; +} ( 1 .. $n_svc_acct_pop ); + +warn "[fs_mailadmind] Creating $fs_mailadmind_socket\n" if $Debug; +my $uaddr = sockaddr_un($fs_mailadmind_socket); +my $proto = getprotobyname('tcp'); +socket(Server,PF_UNIX,SOCK_STREAM,0) or die "socket: $!"; +unlink($fs_mailadmind_socket); +bind(Server, $uaddr) or die "bind: $!"; +listen(Server,SOMAXCONN) or die "listen: $!"; + +warn "[fs_mailadmind] Entering main loop...\n" if $Debug; +my $paddr; +for ( ; $paddr = accept(Client,Server); close Client) { + + chop( my $command = ); + + if ( $command eq "signup_info" ) { + warn "[fs_mailadmind] sending signup info...\n" if $Debug; + print Client join("\n", $n_cust_main_county, + map { + $_->{taxnum}, + $_->{state}, + $_->{county}, + $_->{country}, + } @cust_main_county + ), "\n"; + + print Client join("\n", $n_part_pkg, + map { + $_->{pkgpart}, + $_->{pkg}, + } @part_pkg + ), "\n"; + + print Client join("\n", $n_svc_acct_pop, + map { + $_->{popnum}, + $_->{city}, + $_->{state}, + $_->{ac}, + $_->{exch}, + $_->{loc}, + } @svc_acct_pop + ), "\n"; + + } elsif ( $command eq "new_customer" ) { + warn "[fs_mailadmind] reading customer signup...\n" if $Debug; + my( + $first, $last, $ss, $company, $address1, $address2, $city, $county, + $state, $zip, $country, $daytime, $night, $fax, $payby, $payinfo, + $paydate, $payname, $invoicing_list, $pkgpart, $username, $password, + $popnum, + ) = map { scalar() } ( 1 .. 23 ); + + warn "[fs_mailadmind] sending customer data to remote server...\n" if $Debug; + print + $first, $last, $ss, $company, $address1, $address2, $city, $county, + $state, $zip, $country, $daytime, $night, $fax, $payby, $payinfo, + $paydate, $payname, $invoicing_list, $pkgpart, $username, $password, + $popnum, + ; + + warn "[fs_mailadmind] reading error from remote server...\n" if $Debug; + my $error = ; + + warn "[fs_mailadmind] sending error to local client...\n" if $Debug; + print Client $error; + + } elsif ( $command eq "authenticate" ) { + warn "[fs_mailadmind] reading user information to auth...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading authentication material...\n" if $Debug; + chop( my $password = ); + warn "[fs_mailadmind] sending information to remote server...\n" if $Debug; + print "authenticate\n", $user, "\n", $password, "\n"; + + warn "[fs_mailadmind] reading error from remote server...\n" if $Debug; + my $error = ; + + warn "[fs_mailadmind] sending error to local client...\n" if $Debug; + print Client $error; + + } elsif ( $command eq "list_packages" ) { + warn "[fs_mailadmind] reading user information to list_packages...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug; + print "list_packages\n", $user, "\n"; + + warn "[fs_mailadmind] reading data from remote server...\n" if $Debug; + chomp( my $n_packages = ); + my @packages = map { + chomp( my $pkgnum = ); + chomp( my $domain = ); + chomp( my $account = ); + { + 'pkgnum' => $pkgnum, + 'domain' => $domain, + 'account' => $account, + }; + } ( 1 .. $n_packages ); + + warn "[fs_mailadmind] sending data to local client...\n" if $Debug; + + print Client join("\n", $n_packages, + map { + $_->{pkgnum}, + $_->{domain}, + $_->{account}, + } @packages + ), "\n"; + + } elsif ( $command eq "list_mailboxes" ) { + warn "[fs_mailadmind] reading user information to list_mailboxes...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading package number to list_mailboxes...\n" if $Debug; + chop( my $package = ); + warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug; + print "list_mailboxes\n", $user, "\n", $package, "\n"; + + warn "[fs_mailadmind] reading data from remote server...\n" if $Debug; + chomp( my $n_svc_acct = ); + my @svc_acct = map { + chomp( my $svcnum = ); + chomp( my $username = ); + chomp( my $_password = ); + { + 'svcnum' => $svcnum, + 'username' => $username, + '_password' => $_password, + }; + } ( 1 .. $n_svc_acct ); + + warn "[fs_mailadmind] sending data to local client...\n" if $Debug; + + print Client join("\n", $n_svc_acct, + map { + $_->{svcnum}, + $_->{username}, + $_->{_password}, + } @svc_acct + ), "\n"; + + } elsif ( $command eq "delete_mailbox" ) { + warn "[fs_mailadmind] reading user information to auth...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading account information to delete...\n" if $Debug; + chop( my $account = ); + warn "[fs_mailadmind] sending information to remote server...\n" if $Debug; + print "delete_mailbox\n", $user, "\n", $account, "\n"; + + warn "[fs_mailadmind] reading error from remote server...\n" if $Debug; + my $error = ; + + warn "[fs_mailadmind] sending error to local client...\n" if $Debug; + print Client $error; + + } elsif ( $command eq "password_mailbox" ) { + warn "[fs_mailadmind] reading user information to auth...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading account information to password...\n" if $Debug; + my( + $account, $_password, + ) = map { scalar() } ( 1 .. 2 ); + + warn "[fs_mailadmind] sending password data to remote server...\n" if $Debug; + print "password_mailbox", "\n"; + print + $user, "\n", $account, $_password, + ; + + warn "[fs_mailadmind] reading error from remote server...\n" if $Debug; + my $error = ; + + warn "[fs_mailadmind] sending error to local client...\n" if $Debug; + print Client $error; + + } elsif ( $command eq "add_mailbox" ) { + warn "[fs_mailadmind] reading user information to auth...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading account information to create...\n" if $Debug; + my( + $package, $account, $_password, + ) = map { scalar() } ( 1 .. 3 ); + + warn "[fs_mailadmind] sending service data to remote server...\n" if $Debug; + print "add_mailbox", "\n"; + print + $user, "\n", $package, $account, $_password, + ; + + warn "[fs_mailadmind] reading error from remote server...\n" if $Debug; + my $error = ; + + warn "[fs_mailadmind] sending error to local client...\n" if $Debug; + print Client $error; + + } elsif ( $command eq "add_forward" ) { + warn "[fs_mailadmind] reading user information to auth...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading forward information to create...\n" if $Debug; + my( + $package, $source, $dest, + ) = map { scalar() } ( 1 .. 3 ); + + warn "[fs_mailadmind] sending service data to remote server...\n" if $Debug; + print "add_forward", "\n"; + print + $user, "\n", $package, $source, $dest, + ; + + warn "[fs_mailadmind] reading error from remote server...\n" if $Debug; + my $error = ; + + warn "[fs_mailadmind] sending error to local client...\n" if $Debug; + print Client $error; + + } elsif ( $command eq "delete_forward" ) { + warn "[fs_mailadmind] reading user information to auth...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading forward information to delete...\n" if $Debug; + chop( my $service = ); + warn "[fs_mailadmind] sending information to remote server...\n" if $Debug; + print "delete_forward\n", $user, "\n", $service, "\n"; + + warn "[fs_mailadmind] reading error from remote server...\n" if $Debug; + my $error = ; + + warn "[fs_mailadmind] sending error to local client...\n" if $Debug; + print Client $error; + + } elsif ( $command eq "list_forwards" ) { + warn "[fs_mailadmind] reading user information to list_forwards...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading service number to list_forwards...\n" if $Debug; + chop( my $service = ); + warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug; + print "list_forwards\n", $user, "\n", $service, "\n"; + + warn "[fs_mailadmind] reading data from remote server...\n" if $Debug; + chomp( my $n_svc_forward = ); + my @svc_forward = map { + chomp( my $svcnum = ); + chomp( my $dest = ); + { + 'svcnum' => $svcnum, + 'dest' => $dest, + }; + } ( 1 .. $n_svc_forward ); + + warn "[fs_mailadmind] sending data to local client...\n" if $Debug; + + print Client join("\n", $n_svc_forward, + map { + $_->{svcnum}, + $_->{dest}, + } @svc_forward + ), "\n"; + + } elsif ( $command eq "list_pkg_forwards" ) { + warn "[fs_mailadmind] reading user information to list_pkg_forwards...\n" if $Debug; + chop( my $user = ); + warn "[fs_mailadmind] reading service number to list_forwards...\n" if $Debug; + chop( my $package = ); + warn "[fs_mailadmind] sending user information to remote server...\n" if $Debug; + print "list_pkg_forwards\n", $user, "\n", $package, "\n"; + + warn "[fs_mailadmind] reading data from remote server...\n" if $Debug; + chomp( my $n_svc_forward = ); + my @svc_forward = map { + chomp( my $svcnum = ); + chomp( my $srcsvc = ); + chomp( my $dest = ); + { + 'svcnum' => $svcnum, + 'srcsvc' => $srcsvc, + 'dest' => $dest, + }; + } ( 1 .. $n_svc_forward ); + + warn "[fs_mailadmind] sending data to local client...\n" if $Debug; + + print Client join("\n", $n_svc_forward, + map { + $_->{svcnum}, + $_->{srcsvc}, + $_->{dest}, + } @svc_forward + ), "\n"; + + } else { + die "unexpected command from client: $command"; + } + +} + diff --git a/fs_selfadmin/fs_mailadmin_server b/fs_selfadmin/fs_mailadmin_server new file mode 100755 index 000000000..1bcc42e5d --- /dev/null +++ b/fs_selfadmin/fs_mailadmin_server @@ -0,0 +1,642 @@ +#!/usr/bin/perl -Tw +# +# fs_mailadmin_server +# + +use strict; +use IO::Handle; +use FS::SSH qw(sshopen2); +use FS::UID qw(adminsuidsetup); +use FS::Conf; +use FS::Record qw( qsearch qsearchs ); +use FS::cust_main_county; +use FS::cust_main; +use FS::svc_acct_admin; + +use vars qw( $opt $Debug $conf $default_domain ); + +$Debug = 1; + +my @payby = qw(CARD PREPAY); + +my $user = shift or die &usage; +&adminsuidsetup( $user ); + +$conf = new FS::Conf; +$default_domain = $conf->config('domain'); + +my $machine = shift or die &usage; + +my $agentnum = shift or die &usage; +my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } ) or die &usage; +my $pkgpart = $agent->pkgpart_hashref; + +my $refnum = shift or die &usage; + +#causing trouble for some folks +#$SIG{CHLD} = sub { wait() }; + +my($fs_mailadmind)=$conf->config('fs_mailadmind'); + +while (1) { + my($reader,$writer)=(new IO::Handle, new IO::Handle); + $writer->autoflush(1); + warn "[fs_mailadmin_server] Connecting to $machine...\n" if $Debug; + sshopen2($machine,$reader,$writer,$fs_mailadmind); + + my $data; + + warn "[fs_mailadmin_server] Sending locales...\n" if $Debug; + my @cust_main_county = qsearch('cust_main_county', {} ); + print $writer $data = join("\n", + ( scalar(@cust_main_county) || die "no tax rates (cust_main_county records)" ), + map { + $_->taxnum, + $_->state, + $_->county, + $_->country, + } @cust_main_county + ),"\n"; + warn "[fs_mailadmin_server] $data\n" if $Debug > 2; + + warn "[fs_mailadmin_server] Sending package definitions...\n" if $Debug; + my @part_pkg = grep { $_->svcpart('svc_acct') && $pkgpart->{ $_->pkgpart } } + qsearch( 'part_pkg', {} ); + print $writer $data = join("\n", + ( scalar(@part_pkg) || die "no usable package definitions, agent $agentnum" ), + map { + $_->pkgpart, + $_->pkg, + } @part_pkg + ), "\n"; + warn "[fs_mailadmin_server] $data\n" if $Debug > 2; + + warn "[fs_mailadmin_server] Sending POPs...\n" if $Debug; + my @svc_acct_pop = qsearch ('svc_acct_pop',{} ); + print $writer $data = join("\n", + ( scalar(@svc_acct_pop) || die "No points of presence (svc_acct_pop records)" ), + map { + $_->popnum, + $_->city, + $_->state, + $_->ac, + $_->exch, + $_->loc, + } @svc_acct_pop + ), "\n"; + warn "[fs_mailadmin_server] $data\n" if $Debug > 2; + + warn "[fs_mailadmin_server] Entering main loop...\n" if $Debug; +COMMAND: while (1) { + warn "[fs_mailadmin_server] Reading (waiting for) command...\n" if $Debug; + chop( my($command, $user) = map { scalar(<$reader>) } ( 1 .. 2 ) ); + my $domain = $default_domain; + $user =~ /^([\w\.\-]+)\@(([\w\-]+\.)+\w+)$/; + ($user, $domain) = ($1, $2); + + if ($command eq 'authenticate'){ + warn "[fs_mailadmin_server] Processing authenticate command for $user \n" if $Debug; + chop( my($password) = map { scalar(<$reader>) } ( 1 .. 1 ) ); + + my $error = ''; + + my @svc_domain = qsearchs('svc_domain', { 'domain' => $domain }); + + if (scalar(@svc_domain) != 1) { + warn "Nonexistant or duplicate service account for \"$domain\""; + next COMMAND; + } + + my @svc_acct = qsearchs('svc_acct', { 'username' => $user, + 'domsvc' => $svc_domain[0]->svcnum }); + if (scalar(@svc_acct) != 1) { + die "Nonexistant or duplicate service account for \"$user\""; + next COMMAND; + } + + if ($svc_acct[0]->_password eq $password) { + $error = "$user\@$domain OK"; + }else{ + $error = "$user\@$domain FAILED"; + } + warn "[fs_mailadmin_server] Sending results...\n" if $Debug; + print $writer $error, "\n"; + } + elsif ($command eq 'list_packages'){ + warn "[fs_mailadmin_server] Processing list_packages command for $user \n" if $Debug; + + my $error = ''; + + my @packages = eval {find_administrable_packages( $user, $domain )}; + warn "$@" if $@; + + my %packages; + my %accounts; + + foreach my $package (@packages) { + $packages{my $pkgnum = $package->getfield('pkgnum')} = $default_domain; + $accounts{$pkgnum} = 0; + my @services = qsearch('cust_svc', { 'pkgnum' => $pkgnum }); + foreach my $service (@services) { + if ($service->getfield('svcpart') eq '4'){ + my $account=qsearchs('svc_domain', { 'svcnum' => $service->getfield('svcnum') }); + $packages{$pkgnum}=$account->getfield('domain'); + $accounts{$pkgnum}=$account->getfield('svcnum'); + } + } + } + + print $writer $data = join("\n", + ( scalar(keys(%packages)) ), + map { + $_, + $packages{$_}, + $accounts{$_}, + } keys(%packages) + ), "\n"; + warn "[fs_mailadmin_server] $data\n" if $Debug > 2; + + }elsif ($command eq 'list_mailboxes'){ + + warn "[fs_mailadmin_server] Processing list_mailboxes command for $user" if $Debug; + chop( my($pkgnum) = map { scalar(<$reader>) } ( 1 .. 1 ) ); + warn "package $pkgnum \n" if $Debug; + + my $error = ''; + + my @packages = eval {find_administrable_packages( $user, $domain )}; + warn "$@" if $@; + + my @accounts; + + foreach my $package (@packages) { + next unless ($pkgnum eq $package->getfield('pkgnum')); + my @services = qsearch('cust_svc', { 'pkgnum' => $package->getfield('pkgnum') }); + foreach my $service (@services) { + if ($service->getfield('svcpart') eq '2'){ + my $account=qsearchs('svc_acct', { 'svcnum' => $service->getfield('svcnum') }); +# $accounts[$#accounts+1]=$account->getfield('username'); + $accounts[$#accounts+1]=$account; + } + } + } + + print $writer $data = join("\n", +# ( scalar(@accounts) || die "No accounts (svc_acct records)" ), + ( scalar(@accounts) ), + map { + $_->svcnum, +# $_->username, + $_->email, +# $_->_password, + '*****', + } @accounts + ), "\n"; + warn "[fs_mailadmin_server] $data\n" if $Debug > 2; + + + } elsif ($command eq 'delete_mailbox'){ + warn "[fs_mailadmin_server] Processing delete_mailbox command for $user " if $Debug; + chop( my($account) = map { scalar(<$reader>) } ( 1 .. 1 ) ); + warn "account $account \n" if $Debug; + + my $error = ''; + + my @packages = eval { find_administrable_packages($user, $domain) }; + warn "$@" if $@; + $error ||= "$@" if $@; + + my @svc_acct = qsearchs('svc_acct', { 'svcnum' => $account }) unless $error; + if (scalar(@svc_acct) != 1) { $error ||= 'Nonexistant or duplicate service account for user.' }; + if (! $error && check_administrator(\@packages, $svc_acct[0])){ +# not sure about the next three lines... do we delete? or return error + foreach my $svc_forward (qsearch('svc_forward', { 'dstsvc' => $svc_acct[0]->getfield('svcnum') })) { + $error ||= $svc_forward->delete; + } + foreach my $svc_forward (qsearch('svc_forward', { 'srcsvc' => $svc_acct[0]->getfield('svcnum') })) { + $error ||= $svc_forward->delete; + } + $error ||= $svc_acct[0]->delete; + } else { + $error ||= "Illegal attempt to remove service"; + } + + + warn "[fs_mailadmin_server] Sending results...\n" if $Debug; + print $writer $error, "\n"; + + } elsif ($command eq 'password_mailbox'){ + warn "[fs_mailadmin_server] Processing password_mailbox command for $user " if $Debug; + chop( my($account, $_password) = map { scalar(<$reader>) } ( 1 .. 2 ) ); + warn "account $account with password $_password \n" if $Debug; + + my $error = ''; + + my @packages = eval { find_administrable_packages($user, $domain) }; + warn "$@" if $@; + $error ||= "$@" if $@; + + my @svc_acct = qsearchs('svc_acct', { 'svcnum' => $account }) unless $error; + if (scalar(@svc_acct) != 1) { $error ||= 'Nonexistant or duplicate service account.' }; + + if (! $error && check_administrator(\@packages, $svc_acct[0])){ + my $new = new FS::svc_acct ({$svc_acct[0]->hash}); + $new->setfield('_password' => $_password); + $error ||= $new->replace($svc_acct[0]); + } else { + $error ||= "Illegal attempt to change password"; + } + + + warn "[fs_mailadmin_server] Sending results...\n" if $Debug; + print $writer $error, "\n"; + + } elsif ($command eq 'add_mailbox'){ + warn "[fs_mailadmin_server] Processing add_mailbox command for $user " if $Debug; + chop( my($target_package, $account, $_password) = map { scalar(<$reader>) } ( 1 .. 3 ) ); + warn "in package $target_package account $account with password $_password \n" if $Debug; + + my $found_package; + my $domainsvc=0; + my $svcpart=2; # this is 'email box' + my $svcpartsm=3; # this is 'domain alias' + my $error = ''; + my $found = 0; + + my @packages = eval { find_administrable_packages($user, $domain) }; + warn "$@" if $@; + $error ||= "$@" if $@; + + foreach my $package (@packages) { + if ($package->getfield('pkgnum') eq $target_package) { + $found = 1; + $found_package=$package; + my @services = qsearch('cust_svc', { 'pkgnum' => $target_package }); + foreach my $service (@services) { + if ($service->getfield('svcpart') eq '4'){ + my @svc_domain=qsearchs('svc_domain', { 'svcnum' => $service->getfield('svcnum') }); + if (scalar(@svc_domain) eq 1) { + $domainsvc=$svc_domain[0]->getfield('svcnum'); + } + } + } + last; + } + } + warn "User $user does not have administration rights to package $target_package\n" unless $found; + $error ||= "User $user does not have administration rights to package $target_package\n" unless $found; + + my $part_pkg = qsearchs('part_pkg',{'pkgpart'=>$found_package->getfield('pkgpart')}); + + #list of services this pkgpart includes (although at the moment we only care + # about $svcpart + my $pkg_svc; + my %pkg_svc = (); + foreach $pkg_svc ( qsearch('pkg_svc',{'pkgpart'=> $found_package->pkgpart }) ) { + $pkg_svc{$pkg_svc->svcpart} = $pkg_svc->quantity if $pkg_svc->quantity; + } + + my @services = qsearch('cust_svc', {'pkgnum' => $found_package->getfield('pkgnum'), + 'svcpart' => $svcpart, + }); + + if (scalar(@services) >= $pkg_svc{$svcpart}) { + $error="Maximum allowed already reached."; + } + + my $svc_acct = new FS::svc_acct ( { + 'pkgnum' => $found_package->pkgnum, + 'svcpart' => $svcpart, + 'username' => $account, + 'domsvc' => $domainsvc, + '_password' => $_password, + } ); + + my $y = $svc_acct->setdefault; # arguably should be in new method + $error ||= $y unless ref($y); + #and just in case you were silly + $svc_acct->pkgnum($found_package->pkgnum); + $svc_acct->svcpart($svcpart); + $svc_acct->username($account); + $svc_acct->domsvc($domainsvc); + $svc_acct->_password($_password); + + $error ||= $svc_acct->check; + + if ( ! $error ) { #in this case, $cust_pkg should always + #be definied, but.... + $error ||= $svc_acct->insert; + warn "WARNING: $error on pre-checked svc_acct record!" if $error; + } + + warn "[fs_mailadmin_server] Sending results...\n" if $Debug; + print $writer $error, "\n"; + + }elsif ($command eq 'list_forwards'){ + + warn "[fs_mailadmin_server] Processing list_forwards command for $user" if $Debug; + chop( my($svcnum) = map { scalar(<$reader>) } ( 1 .. 1 ) ); + warn "service $svcnum \n" if $Debug; + + my $error = ''; + + my @packages = eval {find_administrable_packages( $user, $domain )}; + warn "$@" if $@; + + my @forwards; + + foreach my $package (@packages) { +# next unless ($pkgnum eq $package->getfield('pkgnum')); + my @services = qsearch('cust_svc', { 'pkgnum' => $package->getfield('pkgnum') }); + foreach my $service (@services) { + if ($service->getfield('svcpart') eq '10'){ + my $forward=qsearchs('svc_forward', { 'svcnum' => $service->getfield('svcnum') }); + $forwards[$#forwards+1]=$forward if ($forward->getfield('srcsvc') == $svcnum); + } + } + } + + print $writer $data = join("\n", + ( scalar(@forwards) ), + map { + $_->svcnum, + ($_->dstsvc ? qsearchs('svc_acct', {'svcnum' => $_->dstsvc})->email : $_->dst), + } @forwards + ), "\n"; + warn "[fs_mailadmin_server] $data\n" if $Debug > 2; + + + }elsif ($command eq 'list_pkg_forwards'){ + + warn "[fs_mailadmin_server] Processing list_pkg_forwards command for $user" if $Debug; + chop( my($pkgnum) = map { scalar(<$reader>) } ( 1 .. 1 ) ); + warn "package $pkgnum \n" if $Debug; + + my $error = ''; + + my @packages = eval {find_administrable_packages( $user, $domain )}; + warn "$@" if $@; + + my @forwards; + + foreach my $package (@packages) { + next unless ($pkgnum eq $package->getfield('pkgnum')); + my @services = qsearch('cust_svc', { 'pkgnum' => $package->getfield('pkgnum') }); + foreach my $service (@services) { + if ($service->getfield('svcpart') eq '10'){ + my $forward=qsearchs('svc_forward', { 'svcnum' => $service->getfield('svcnum') }); + $forwards[$#forwards+1]=$forward; + } + } + } + + print $writer $data = join("\n", + ( scalar(@forwards) ), + map { + $_->svcnum, + $_->srcsvc, + ($_->dstsvc ? qsearchs('svc_acct', {'svcnum' => $_->dstsvc})->email : $_->dst), + } @forwards + ), "\n"; + warn "[fs_mailadmin_server] $data\n" if $Debug > 2; + + + } elsif ($command eq 'delete_forward'){ + warn "[fs_mailadmin_server] Processing delete_forward command for $user " if $Debug; + chop( my($forward) = map { scalar(<$reader>) } ( 1 .. 1 ) ); + warn "forward $forward \n" if $Debug; + + my $error = ''; + + my @packages = eval { find_administrable_packages($user, $domain) }; + warn "$@" if $@; + $error ||= "$@" if $@; + + my @svc_forward = qsearchs('svc_forward', { 'svcnum' => $forward }) unless $error; + if (scalar(@svc_forward) != 1) { $error ||= 'Nonexistant or duplicate service account for user.' }; + if (! $error && check_administrator(\@packages, $svc_forward[0])){ +# not sure about the next three lines... do we delete? or return error + $error ||= $svc_forward[0]->delete; + } else { + $error ||= "Illegal attempt to remove service"; + } + + + warn "[fs_mailadmin_server] Sending results...\n" if $Debug; + print $writer $error, "\n"; + + } elsif ($command eq 'add_forward'){ + warn "[fs_mailadmin_server] Processing add_forward command for $user " if $Debug; + chop( my($target_package, $source, $dest) = map { scalar(<$reader>) } ( 1 .. 3 ) ); + warn "in package $target_package source $source with destination $dest \n" if $Debug; + + my $found_package; + my $domainsvc=0; + my $svcpart=10; # this is 'forward service' + my $error = ''; + my $found = 0; + + my @packages = eval { find_administrable_packages($user, $domain) }; + warn "$@" if $@; + $error ||= "$@" if $@; + + foreach my $package (@packages) { + if ($package->getfield('pkgnum') eq $target_package) { + $found = 1; + $found_package=$package; + last; + } + } + warn "User $user does not have administration rights to package $target_package\n" unless $found; + $error ||= "User $user does not have administration rights to package $target_package\n" unless $found; + + my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $source }); + warn "Forwarding source $source does not exist.\n" unless $svc_acct; + $error ||= "Forwarding source $source does not exist.\n" unless $svc_acct; + + my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $source }); + warn "Forwarding source $source not attached to any account.\n" unless $cust_svc; + $error ||= "Forwarding source $source not attached to any account.\n" unless $cust_svc; + + if ( ! $error ) { + warn "Forwarding source $source is not in package $target_package\n" + unless ($cust_svc->getfield('pkgnum') == $target_package); + $error ||= "Forwarding source $source is not in package $target_package\n" + unless ($cust_svc->getfield('pkgnum') == $target_package); + } + + my $part_pkg = qsearchs('part_pkg',{'pkgpart'=>$found_package->getfield('pkgpart')}); + + #list of services this pkgpart includes (although at the moment we only care + # about $svcpart + my $pkg_svc; + my %pkg_svc = (); + foreach $pkg_svc ( qsearch('pkg_svc',{'pkgpart'=> $found_package->pkgpart }) ) { + $pkg_svc{$pkg_svc->svcpart} = $pkg_svc->quantity if $pkg_svc->quantity; + } + + my @services = qsearch('cust_svc', {'pkgnum' => $found_package->getfield('pkgnum'), + 'svcpart' => $svcpart, + }); + + if (scalar(@services) >= $pkg_svc{$svcpart}) { + $error="Maximum allowed already reached."; + } + + my $svc_forward = new FS::svc_forward ( { + 'pkgnum' => $found_package->pkgnum, + 'svcpart' => $svcpart, + 'srcsvc' => $source, + 'dstsvc' => 0, + 'dst' => $dest, + } ); + + my $y = $svc_forward->setdefault; # arguably should be in new method + $error ||= $y unless ref($y); + #and just in case you were silly + $svc_forward->pkgnum($found_package->pkgnum); + $svc_forward->svcpart($svcpart); + $svc_forward->srcsvc($source); + $svc_forward->dstsvc(0); + $svc_forward->dst($dest); + + $error ||= $svc_forward->check; + + if ( ! $error ) { #in this case, $cust_pkg should always + #be definied, but.... + $error ||= $svc_forward->insert; + warn "WARNING: $error on pre-checked svc_forward record!" if $error; + } + + warn "[fs_mailadmin_server] Sending results...\n" if $Debug; + print $writer $error, "\n"; + + } else { + warn "[fs_mailadmin_server] Bad command: $command \n" if $Debug; + print $writer "Bad command \n"; + } + } + close $writer; + close $reader; + warn "connection to $machine lost! waiting 60 seconds...\n"; + sleep 60; + warn "reconnecting...\n"; +} + +sub usage { + die "Usage:\n\n fs_mailadmin_server user machine agentnum refnum\n"; +} + +#sub find_administrable_packages { +# my $user = shift; +# +# my $error = ''; +# +# my @svc_acct = qsearchs('svc_acct', { 'username' => $user }); +# if (scalar(@svc_acct) != 1) { +# die "Nonexistant or duplicate service account for \"$user\""; +# } +# +# my @cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct[0]->getfield('svcnum') }); +# if (scalar(@cust_svc) != 1 ) { +# die "Nonexistant or duplicate customer service for \"$user\""; +# } +# +# my @cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc[0]->getfield('pkgnum') }); +# if (scalar(@cust_pkg) != 1) { +# die "Nonexistant or duplicate customer package for \"$user\""; +# } +# +# my @cust_main = qsearchs('cust_main', { 'custnum' => $cust_pkg[0]->getfield('custnum') }); +# if (scalar(@cust_main) != 1 ) { +# die "Nonexistant or duplicate customer for \"$user\""; +# } +# +# my @packages = $cust_main[0]->ncancelled_pkgs; +#} + +sub find_administrable_packages { + my $user = shift; + my $domain = shift; + + my @packages; + my $error = ''; + + my @svc_domain = qsearchs('svc_domain', { 'domain' => $domain }); + + if (scalar(@svc_domain) != 1) { + die "Nonexistant or duplicate service account for \"$domain\""; + } + + my @svc_acct = qsearchs('svc_acct', { 'username' => $user, + 'domsvc' => $svc_domain[0]->svcnum }); + if (scalar(@svc_acct) != 1) { + die "Nonexistant or duplicate service account for \"$user\""; + } + + my @svc_acct_admin = qsearch('svc_acct_admin', {'adminsvc' => $svc_acct[0]->getfield('svcnum') }); + die "Nonexistant or duplicate customer service for \"$user\"" unless scalar(@svc_acct_admin); + + foreach my $svc_acct_admin (@svc_acct_admin) { + my @cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct_admin->getfield('svcnum') }); + if (scalar(@cust_svc) != 1 ) { + die "Nonexistant or duplicate customer service for admin \"$svc_acct_admin->getfield('svcnum')\""; + } + + my @cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc[0]->getfield('pkgnum') }); + if (scalar(@cust_pkg) != 1) { + die "Nonexistant or duplicate customer package for admin \"$user\""; + } + + push @packages, $cust_pkg[0] unless $cust_pkg[0]->getfield('cancel'); + + } + (@packages); +} + +sub check_administrator { + my ($allowed_packages_aref, $svc_acct_ref) = @_; + + my $error = ''; + my $found = 0; + + { + my @cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_acct_ref->getfield('svcnum') }); + if (scalar(@cust_svc) != 1 ) { + warn "Nonexistant or duplicate customer service for \"$svc_acct_ref->getfield('username')\""; + last; + } + + my @cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_svc[0]->getfield('pkgnum') }); + if (scalar(@cust_pkg) != 1) { + warn "Nonexistant or duplicate customer package for \"$svc_acct_ref->getfield('username')\""; + last; + } + + foreach my $package (@$allowed_packages_aref) { + if ($package->getfield('pkgnum') eq $cust_pkg[0]->getfield('pkgnum')) { + $found = 1; + last; + } + } + } + + $found; +} + +sub check_add { + my ($allowed_packages_aref, $target_package) = @_; + + my $error = ''; + my $found = 0; + + foreach my $package (@$allowed_packages_aref) { + if ($package->getfield('pkgnum') eq $target_package) { + $found = 1; + last; + } + } + + $found; +} + -- 2.11.0