summaryrefslogtreecommitdiff
path: root/fs_selfservice
diff options
context:
space:
mode:
Diffstat (limited to 'fs_selfservice')
-rwxr-xr-xfs_selfservice/DEPLOY27
-rw-r--r--fs_selfservice/FS-SelfService/Changes6
-rw-r--r--fs_selfservice/FS-SelfService/MANIFEST6
-rw-r--r--fs_selfservice/FS-SelfService/Makefile.PL19
-rw-r--r--fs_selfservice/FS-SelfService/SelfService.pm1094
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent.cgi457
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html7
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html19
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_login.html22
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_logout.html5
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_main.html37
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_menu.html15
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html19
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_provision.html25
-rw-r--r--fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html19
-rw-r--r--fs_selfservice/FS-SelfService/cgi/cvv2.html25
-rw-r--r--fs_selfservice/FS-SelfService/cgi/cvv2.pngbin0 -> 3854 bytes
-rw-r--r--fs_selfservice/FS-SelfService/cgi/cvv2_amex.pngbin0 -> 4573 bytes
-rw-r--r--fs_selfservice/FS-SelfService/cgi/decline.html5
-rw-r--r--fs_selfservice/FS-SelfService/cgi/delete_svc.html17
-rw-r--r--fs_selfservice/FS-SelfService/cgi/list_customers.html39
-rw-r--r--fs_selfservice/FS-SelfService/cgi/login.html29
-rw-r--r--fs_selfservice/FS-SelfService/cgi/logout.html5
-rw-r--r--fs_selfservice/FS-SelfService/cgi/make_payment.html118
-rw-r--r--fs_selfservice/FS-SelfService/cgi/map.gifbin0 -> 8181 bytes
-rw-r--r--fs_selfservice/FS-SelfService/cgi/myaccount.html45
-rw-r--r--fs_selfservice/FS-SelfService/cgi/myaccount_menu.html89
-rw-r--r--fs_selfservice/FS-SelfService/cgi/order_pkg.html75
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/passwd.cgi60
-rw-r--r--fs_selfservice/FS-SelfService/cgi/passwd.html28
-rw-r--r--fs_selfservice/FS-SelfService/cgi/payment_results.html16
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_svc_acct.html13
-rw-r--r--fs_selfservice/FS-SelfService/cgi/process_svc_external.html15
-rw-r--r--fs_selfservice/FS-SelfService/cgi/promocode.html14
-rw-r--r--fs_selfservice/FS-SelfService/cgi/provision.html10
-rw-r--r--fs_selfservice/FS-SelfService/cgi/provision_list.html87
-rw-r--r--fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html11
-rw-r--r--fs_selfservice/FS-SelfService/cgi/recharge_prepay.html36
-rw-r--r--fs_selfservice/FS-SelfService/cgi/recharge_results.html24
-rw-r--r--fs_selfservice/FS-SelfService/cgi/regcode.html14
-rw-r--r--fs_selfservice/FS-SelfService/cgi/selfservice.cgi305
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-agentselect.html195
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-alternate.html218
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-billaddress.html307
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-freeoption.html262
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup-snarf.html228
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup.cgi333
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/signup.html236
-rw-r--r--fs_selfservice/FS-SelfService/cgi/stateselect.html134
-rw-r--r--fs_selfservice/FS-SelfService/cgi/success.html11
-rw-r--r--fs_selfservice/FS-SelfService/cgi/svc_acct.html55
-rw-r--r--fs_selfservice/FS-SelfService/cgi/view_customer.html29
-rw-r--r--fs_selfservice/FS-SelfService/cgi/view_invoice.html17
-rw-r--r--fs_selfservice/FS-SelfService/freeside-selfservice-clientd271
-rwxr-xr-xfs_selfservice/FS-SelfService/ieak.template40
-rw-r--r--fs_selfservice/FS-SelfService/test.pl17
-rwxr-xr-xfs_selfservice/fs_passwd_test19
57 files changed, 5229 insertions, 0 deletions
diff --git a/fs_selfservice/DEPLOY b/fs_selfservice/DEPLOY
new file mode 100755
index 000000000..63412784f
--- /dev/null
+++ b/fs_selfservice/DEPLOY
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+#this is a quick hack for my dev machine. do not use it.
+# see the "make install-selfservice" and "make update-selfservice" makefile
+# targets to properly install this stuff.
+
+#kill `cat /var/run/freeside-selfservice-server.fs_selfservice.pid`
+
+cd FS-SelfService
+perl Makefile.PL && make && make install
+cd ..
+
+( cd ..; make deploy; cd fs_selfservice )
+
+cp /home/ivan/freeside/fs_selfservice/FS-SelfService/cgi/* /var/www/MyAccount
+chown freeside /var/www/MyAccount/*.cgi
+chmod 755 /var/www/MyAccount/*.cgi
+ln -s /var/www/MyAccount/selfservice.cgi /var/www/MyAccount/index.cgi || true
+
+ #cp /home/ivan/freeside/fs_signup/FS-SignupClient/cgi/* /var/www/signup/
+ ##mv /var/www/signup/signup-snarf.html /var/www/signup/signup.html #!!!!!
+ ##mv /var/www/signup/signup-billaddress.html /var/www/signup/signup.html #!!!!!
+ ##mv /var/www/signup/signup-freeoption.html /var/www/signup/signup.html #!!!!!
+ #chown freeside /var/www/signup/signup.cgi
+ #chmod 755 /var/www/signup/signup.cgi
+ #ln -s /var/www/signup/signup.cgi /var/www/signup/index.cgi || true
+
diff --git a/fs_selfservice/FS-SelfService/Changes b/fs_selfservice/FS-SelfService/Changes
new file mode 100644
index 000000000..b9e26b7dc
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/Changes
@@ -0,0 +1,6 @@
+Revision history for Perl extension FS::SelfService.
+
+0.01 Tue May 28 16:49:41 2002
+ - original version; created by h2xs 1.21 with options
+ -A -X -n FS::SelfService
+
diff --git a/fs_selfservice/FS-SelfService/MANIFEST b/fs_selfservice/FS-SelfService/MANIFEST
new file mode 100644
index 000000000..ebd0d3b1a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/MANIFEST
@@ -0,0 +1,6 @@
+Changes
+Makefile.PL
+MANIFEST
+SelfService.pm
+test.pl
+freeside-selfservice-clientd
diff --git a/fs_selfservice/FS-SelfService/Makefile.PL b/fs_selfservice/FS-SelfService/Makefile.PL
new file mode 100644
index 000000000..0b7fc4606
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/Makefile.PL
@@ -0,0 +1,19 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ 'NAME' => 'FS::SelfService',
+ 'VERSION_FROM' => 'SelfService.pm', # finds $VERSION
+ 'EXE_FILES' => [ 'freeside-selfservice-clientd',
+ #'freeside-selfservice-xmlrpc-server',
+ ],
+ 'INSTALLSCRIPT' => '/usr/local/sbin',
+ 'INSTALLSITEBIN' => '/usr/local/sbin',
+ 'PERM_RWX' => '750',
+ 'PREREQ_PM' => {
+ 'Storable' => 2.09,
+ }, # e.g., Module::Name => 1.1
+ ($] >= 5.005 ? ## Add these new keywords supported since 5.005
+ (ABSTRACT_FROM => 'SelfService.pm', # retrieve abstract from module
+ AUTHOR => 'Ivan Kohler <ivan-freeside-selfservice@420.am>') : ()),
+);
diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm
new file mode 100644
index 000000000..bfce1287b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/SelfService.pm
@@ -0,0 +1,1094 @@
+package FS::SelfService;
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT_OK $dir $socket %autoload $tag);
+use Exporter;
+use Socket;
+use FileHandle;
+#use IO::Handle;
+use IO::Select;
+use Storable 2.09 qw(nstore_fd fd_retrieve);
+
+$VERSION = '0.03';
+
+@ISA = qw( Exporter );
+
+$dir = "/usr/local/freeside";
+$socket = "$dir/selfservice_socket";
+$socket .= '.'.$tag if defined $tag && length($tag);
+
+#maybe should ask ClientAPI for this list
+%autoload = (
+ 'passwd' => 'passwd/passwd',
+ 'chfn' => 'passwd/passwd',
+ 'chsh' => 'passwd/passwd',
+ 'login' => 'MyAccount/login',
+ 'logout' => 'MyAccount/logout',
+ 'customer_info' => 'MyAccount/customer_info',
+ 'edit_info' => 'MyAccount/edit_info', #add to ss cgi!
+ 'invoice' => 'MyAccount/invoice',
+ 'list_invoices' => 'MyAccount/list_invoices', #?
+ 'cancel' => 'MyAccount/cancel', #add to ss cgi!
+ 'payment_info' => 'MyAccount/payment_info',
+ 'process_payment' => 'MyAccount/process_payment',
+ 'process_prepay' => 'MyAccount/process_prepay',
+ 'list_pkgs' => 'MyAccount/list_pkgs', #add to ss cgi!
+ 'order_pkg' => 'MyAccount/order_pkg', #add to ss cgi!
+ 'cancel_pkg' => 'MyAccount/cancel_pkg', #add to ss cgi!
+ 'charge' => 'MyAccount/charge', #?
+ 'part_svc_info' => 'MyAccount/part_svc_info',
+ 'provision_acct' => 'MyAccount/provision_acct',
+ 'provision_external' => 'MyAccount/provision_external',
+ 'unprovision_svc' => 'MyAccount/unprovision_svc',
+ 'signup_info' => 'Signup/signup_info',
+ 'new_customer' => 'Signup/new_customer',
+ 'agent_login' => 'Agent/agent_login',
+ 'agent_logout' => 'Agent/agent_logout',
+ 'agent_info' => 'Agent/agent_info',
+ 'agent_list_customers' => 'Agent/agent_list_customers',
+);
+@EXPORT_OK = ( keys(%autoload), qw( regionselector expselect popselector ) );
+
+$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;
+
+-e $dir or die "FATAL: $dir doesn't exist!";
+-d $dir or die "FATAL: $dir isn't a directory!";
+-r $dir or die "FATAL: Can't read $dir as freeside user!";
+-x $dir or die "FATAL: $dir not searchable (executable) as freeside user!";
+
+foreach my $autoload ( keys %autoload ) {
+
+ my $eval =
+ "sub $autoload { ". '
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ $param = { @_ };
+ }
+
+ $param->{_packet} = \''. $autoload{$autoload}. '\';
+
+ simple_packet($param);
+ }';
+
+ eval $eval;
+ die $@ if $@;
+
+}
+
+sub simple_packet {
+ my $packet = shift;
+ socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
+ connect(SOCK, sockaddr_un($socket)) or die "connect to $socket: $!";
+ nstore_fd($packet, \*SOCK) or die "can't send packet: $!";
+ SOCK->flush;
+
+ #shoudl trap: Magic number checking on storable file failed at blib/lib/Storable.pm (autosplit into blib/lib/auto/Storable/fd_retrieve.al) line 337, at /usr/local/share/perl/5.6.1/FS/SelfService.pm line 71
+
+ #block until there is a message on socket
+# my $w = new IO::Select;
+# $w->add(\*SOCK);
+# my @wait = $w->can_read;
+ my $return = fd_retrieve(\*SOCK) or die "error reading result: $!";
+ die $return->{'_error'} if defined $return->{_error} && $return->{_error};
+
+ $return;
+}
+
+=head1 NAME
+
+FS::SelfService - Freeside self-service API
+
+=head1 SYNOPSIS
+
+ # password and shell account changes
+ use FS::SelfService qw(passwd chfn chsh);
+
+ # "my account" functionality
+ use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
+
+ my $rv = login( { 'username' => $username,
+ 'domain' => $domain,
+ 'password' => $password,
+ }
+ );
+
+ if ( $rv->{'error'} ) {
+ #handle login error...
+ } else {
+ #successful login
+ my $session_id = $rv->{'session_id'};
+ }
+
+ my $customer_info = customer_info( { 'session_id' => $session_id } );
+
+ #payment_info and process_payment are available in 1.5+ only
+ my $payment_info = payment_info( { 'session_id' => $session_id } );
+
+ #!!! process_payment example
+
+ #!!! list_pkgs example
+
+ #!!! order_pkg example
+
+ #!!! cancel_pkg example
+
+ # signup functionality
+ use FS::SelfService qw( signup_info new_customer );
+
+ my $signup_info = signup_info;
+
+ $rv = new_customer( {
+ 'first' => $first,
+ 'last' => $last,
+ 'company' => $company,
+ 'address1' => $address1,
+ 'address2' => $address2,
+ 'city' => $city,
+ 'state' => $state,
+ 'zip' => $zip,
+ 'country' => $country,
+ 'daytime' => $daytime,
+ 'night' => $night,
+ 'fax' => $fax,
+ 'payby' => $payby,
+ 'payinfo' => $payinfo,
+ 'paycvv' => $paycvv,
+ 'paystart_month' => $paystart_month
+ 'paystart_year' => $paystart_year,
+ 'payissue' => $payissue,
+ 'payip' => $payip
+ 'paydate' => $paydate,
+ 'payname' => $payname,
+ 'invoicing_list' => $invoicing_list,
+ 'referral_custnum' => $referral_custnum,
+ 'pkgpart' => $pkgpart,
+ 'username' => $username,
+ '_password' => $password,
+ 'popnum' => $popnum,
+ 'agentnum' => $agentnum,
+ }
+ );
+
+ my $error = $rv->{'error'};
+ if ( $error eq '_decline' ) {
+ print_decline();
+ } elsif ( $error ) {
+ reprint_signup();
+ } else {
+ print_success();
+ }
+
+=head1 DESCRIPTION
+
+Use this API to implement your own client "self-service" module.
+
+If you just want to customize the look of the existing "self-service" module,
+see XXXX instead.
+
+=head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
+
+=over 4
+
+=item passwd
+
+=item chfn
+
+=item chsh
+
+=back
+
+=head1 "MY ACCOUNT" FUNCTIONS
+
+=over 4
+
+=item login HASHREF
+
+Creates a user session. Takes a hash reference as parameter with the
+following keys:
+
+=over 4
+
+=item username
+
+=item domain
+
+=item password
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors.
+
+=item session_id
+
+Session identifier for successful logins
+
+=back
+
+=item customer_info HASHREF
+
+Returns general customer information.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item name
+
+Customer name
+
+=item balance
+
+Balance owed
+
+=item open
+
+Array reference of hash references of open inoices. Each hash reference has
+the following keys: invnum, date, owed
+
+=item small_custview
+
+An HTML fragment containing shipping and billing addresses.
+
+=item The following fields are also returned: first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo payname month year invoicing_list postal_invoicing
+
+=back
+
+=item edit_info HASHREF
+
+Takes a hash reference as parameter with any of the following keys:
+
+first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax payby payinfo paycvv payname month year invoicing_list postal_invoicing
+
+If a field exists, the customer record is updated with the new value of that
+field. If a field does not exist, that field is not changed on the customer
+record.
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors
+
+=item invoice HASHREF
+
+Returns an invoice. Takes a hash reference as parameter with two keys:
+session_id and invnum
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item invnum
+
+Invoice number
+
+=item invoice_text
+
+Invoice text
+
+=back
+
+=item list_invoices HASHREF
+
+Returns a list of all customer invoices. Takes a hash references with a single
+key, session_id.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item invoices
+
+Reference to array of hash references with the following keys:
+
+=over 4
+
+=item invnum
+
+Invoice ID
+
+=item _date
+
+Invoice date, in UNIX epoch time
+
+=back
+
+=back
+
+=item cancel HASHREF
+
+Cancels this customer.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference with a single key, B<error>, which is empty on
+success or an error message on errors.
+
+=item payment_info HASHREF
+
+Returns information that may be useful in displaying a payment page.
+
+Takes a hash reference as parameter with a single key: B<session_id>.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item balance
+
+Balance owed
+
+=item payname
+
+Exact name on credit card (CARD/DCRD)
+
+=item address1
+
+=item address2
+
+=item city
+
+=item state
+
+=item zip
+
+=item payby
+
+Customer's current default payment type.
+
+=item card_type
+
+For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
+
+=item payinfo
+
+For CARD/DCRD payment types, the card number
+
+=item month
+
+For CARD/DCRD payment types, expiration month
+
+=item year
+
+For CARD/DCRD payment types, expiration year
+
+=item cust_main_county
+
+County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>). Note these are not FS::cust_main_county objects, but hash references of columns and values.
+
+=item states
+
+Array reference of all states in the current default country.
+
+=item card_types
+
+Hash reference of card types; keys are card types, values are the exact strings
+passed to the process_payment function
+
+=item paybatch
+
+Unique transaction identifier (prevents multiple charges), passed to the
+process_payment function
+
+=back
+
+=item process_payment HASHREF
+
+Processes a payment and possible change of address or payment type. Takes a
+hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item save
+
+If true, address and card information entered will be saved for subsequent
+transactions.
+
+=item auto
+
+If true, future credit card payments will be done automatically (sets payby to
+CARD). If false, future credit card payments will be done on-demand (sets
+payby to DCRD). This option only has meaning if B<save> is set true.
+
+=item payname
+
+=item address1
+
+=item address2
+
+=item city
+
+=item state
+
+=item zip
+
+=item payinfo
+
+Card number
+
+=item month
+
+Card expiration month
+
+=item year
+
+Card expiration year
+
+=item paybatch
+
+Unique transaction identifier, returned from the payment_info function.
+Prevents multiple charges.
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors
+
+=item list_pkgs
+
+Returns package information for this customer.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference containing customer package information. The hash reference contains the following keys:
+
+=over 4
+
+
+=item cust_pkg HASHREF
+
+Array reference of hash references, each of which has the fields of a cust_pkg
+record (see L<FS::cust_pkg>) as well as the fields below. Note these are not
+the internal FS:: objects, but hash references of columns and values.
+
+=item all fields of part_pkg (XXXpare this down to a secure subset)
+
+=item part_svc - An array of hash references, each of which has the following keys:
+
+=over 4
+
+=item all fields of part_svc (XXXpare this down to a secure subset)
+
+=item avail
+
+=back
+
+=item error
+
+Empty on success, or an error message on errors.
+
+=back
+
+=item order_pkg
+
+Orders a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item pkgpart
+
+=item svcpart
+
+optional svcpart, required only if the package definition does not contain
+one svc_acct service definition with quantity 1 (it may contain others with
+quantity >1)
+
+=item username
+
+=item _password
+
+=item sec_phrase
+
+=item popnum
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors. The special error '_decline' is returned for
+declined transactions.
+
+=item cancel_pkg
+
+Cancels a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item pkgpart
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=back
+
+=head1 SIGNUP FUNCTIONS
+
+=over 4
+
+=item signup_info HASHREF
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id - Optional agent/reseller interface session
+
+=back
+
+Returns a hash reference containing information that may be useful in
+displaying a signup page. The hash reference contains the following keys:
+
+=over 4
+
+=item cust_main_county
+
+County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>). Note these are not FS::cust_main_county objects, but hash references of columns and values.
+
+=item part_pkg
+
+Available packages - array reference of hash references, each of which has the fields of a part_pkg record (see L<FS::part_pkg>). Each hash reference also has an additional 'payby' field containing an array reference of acceptable payment types specific to this package (see below and L<FS::part_pkg/payby>). Note these are not FS::part_pkg objects, but hash references of columns and values. Requires the 'signup_server-default_agentnum' configuration value to be set, or
+an agentnum specified explicitly via reseller interface session_id in the
+options.
+
+=item agent
+
+Array reference of hash references, each of which has the fields of an agent record (see L<FS::agent>). Note these are not FS::agent objects, but hash references of columns and values.
+
+=item agentnum2part_pkg
+
+Hash reference; keys are agentnums, values are array references of available packages for that agent, in the same format as the part_pkg arrayref above.
+
+=item svc_acct_pop
+
+Access numbers - array reference of hash references, each of which has the fields of an svc_acct_pop record (see L<FS::svc_acct_pop>). Note these are not FS::svc_acct_pop objects, but hash references of columns and values.
+
+=item security_phrase
+
+True if the "security_phrase" feature is enabled
+
+=item payby
+
+Array reference of acceptable payment types for signup
+
+=over 4
+
+=item CARD (credit card - automatic)
+
+=item DCRD (credit card - on-demand - version 1.5+ only)
+
+=item CHEK (electronic check - automatic)
+
+=item DCHK (electronic check - on-demand - version 1.5+ only)
+
+=item LECB (Phone bill billing)
+
+=item BILL (billing, not recommended for signups)
+
+=item COMP (free, definately not recommended for signups)
+
+=item PREPAY (special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL)
+
+=back
+
+=item cvv_enabled
+
+True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
+
+=item msgcat
+
+Hash reference of message catalog values, to support error message customization. Currently available keys are: passwords_dont_match, invalid_card, unknown_card_type, and not_a (as in "Not a Discover card"). Values are configured in the web interface under "View/Edit message catalog".
+
+=item statedefault
+
+Default state
+
+=item countrydefault
+
+Default country
+
+=back
+
+=item new_customer HASHREF
+
+Creates a new customer. Takes a hash reference as parameter with the
+following keys:
+
+=over 4
+
+=item first - first name (required)
+
+=item last - last name (required)
+
+=item ss (not typically collected; mostly used for ACH transactions)
+
+=item company
+
+=item address1 (required)
+
+=item address2
+
+=item city (required)
+
+=item county
+
+=item state (required)
+
+=item zip (required)
+
+=item daytime - phone
+
+=item night - phone
+
+=item fax - phone
+
+=item payby - CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
+
+=item payinfo - Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
+
+=item paycvv - Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
+
+=item paydate - Expiration date for CARD/DCRD
+
+=item payname - Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
+
+=item invoicing_list - comma-separated list of email addresses for email invoices. The special value 'POST' is used to designate postal invoicing (it may be specified alone or in addition to email addresses),
+
+=item referral_custnum - referring customer number
+
+=item pkgpart - pkgpart of initial package
+
+=item username
+
+=item _password
+
+=item sec_phrase - security phrase
+
+=item popnum - access number (index, not the literal number)
+
+=item agentnum - agent number
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error Empty on success, or an error message on errors. The special error '_decline' is returned for declined transactions; other error messages should be suitable for display to the user (and are customizable in under Sysadmin | View/Edit message catalog)
+
+=back
+
+=item regionselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item selected_county
+
+=item selected_state
+
+=item selected_country
+
+=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
+
+=item onchange - Specify a javascript subroutine to call on changes
+
+=item default_state
+
+=item default_country
+
+=item locales - An arrayref of hash references specifying regions. Normally you can just pass the value of the I<cust_main_county> field returned by B<signup_info>.
+
+=back
+
+Returns a list consisting of three HTML fragments for county selection,
+state selection and country selection, respectively.
+
+=cut
+
+#false laziness w/FS::cust_main_county (this is currently the "newest" version)
+sub regionselector {
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ $param = { @_ };
+ }
+ $param->{'selected_country'} ||= $param->{'default_country'};
+ $param->{'selected_state'} ||= $param->{'default_state'};
+
+ my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
+
+ my $countyflag = 0;
+
+ my %cust_main_county;
+
+# unless ( @cust_main_county ) { #cache
+ #@cust_main_county = qsearch('cust_main_county', {} );
+ #foreach my $c ( @cust_main_county ) {
+ foreach my $c ( @{ $param->{'locales'} } ) {
+ #$countyflag=1 if $c->county;
+ $countyflag=1 if $c->{county};
+ #push @{$cust_main_county{$c->country}{$c->state}}, $c->county;
+ #$cust_main_county{$c->country}{$c->state}{$c->county} = 1;
+ $cust_main_county{$c->{country}}{$c->{state}}{$c->{county}} = 1;
+ }
+# }
+ $countyflag=1 if $param->{selected_county};
+
+ my $script_html = <<END;
+ <SCRIPT>
+ function opt(what,value,text) {
+ var optionName = new Option(text, value, false, false);
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+ function ${prefix}country_changed(what) {
+ country = what.options[what.selectedIndex].text;
+ for ( var i = what.form.${prefix}state.length; i >= 0; i-- )
+ what.form.${prefix}state.options[i] = null;
+END
+ #what.form.${prefix}state.options[0] = new Option('', '', false, true);
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ my $text = $state || '(n/a)';
+ $script_html .= qq!opt(what.form.${prefix}state, "$state", "$text");\n!;
+ }
+ $script_html .= "}\n";
+ }
+
+ $script_html .= <<END;
+ }
+ function ${prefix}state_changed(what) {
+END
+
+ if ( $countyflag ) {
+ $script_html .= <<END;
+ state = what.options[what.selectedIndex].text;
+ country = what.form.${prefix}country.options[what.form.${prefix}country.selectedIndex].text;
+ for ( var i = what.form.${prefix}county.length; i >= 0; i-- )
+ what.form.${prefix}county.options[i] = null;
+END
+
+ foreach my $country ( sort keys %cust_main_county ) {
+ $script_html .= "\nif ( country == \"$country\" ) {\n";
+ foreach my $state ( sort keys %{$cust_main_county{$country}} ) {
+ $script_html .= "\nif ( state == \"$state\" ) {\n";
+ #foreach my $county ( sort @{$cust_main_county{$country}{$state}} ) {
+ foreach my $county ( sort keys %{$cust_main_county{$country}{$state}} ) {
+ my $text = $county || '(n/a)';
+ $script_html .=
+ qq!opt(what.form.${prefix}county, "$county", "$text");\n!;
+ }
+ $script_html .= "}\n";
+ }
+ $script_html .= "}\n";
+ }
+ }
+
+ $script_html .= <<END;
+ }
+ </SCRIPT>
+END
+
+ my $county_html = $script_html;
+ if ( $countyflag ) {
+ $county_html .= qq!<SELECT NAME="${prefix}county" onChange="$param->{'onchange'}">!;
+ $county_html .= '</SELECT>';
+ } else {
+ $county_html .=
+ qq!<INPUT TYPE="hidden" NAME="${prefix}county" VALUE="$param->{'selected_county'}">!;
+ }
+
+ my $state_html = qq!<SELECT NAME="${prefix}state" !.
+ qq!onChange="${prefix}state_changed(this); $param->{'onchange'}">!;
+ foreach my $state ( sort keys %{ $cust_main_county{$param->{'selected_country'}} } ) {
+ my $text = $state || '(n/a)';
+ my $selected = $state eq $param->{'selected_state'} ? 'SELECTED' : '';
+ $state_html .= "\n<OPTION $selected VALUE=$state>$text</OPTION>"
+ }
+ $state_html .= '</SELECT>';
+
+ $state_html .= '</SELECT>';
+
+ my $country_html = qq!<SELECT NAME="${prefix}country" !.
+ qq!onChange="${prefix}country_changed(this); $param->{'onchange'}">!;
+ my $countrydefault = $param->{default_country} || 'US';
+ foreach my $country (
+ sort { ($b eq $countrydefault) <=> ($a eq $countrydefault) or $a cmp $b }
+ keys %cust_main_county
+ ) {
+ my $selected = $country eq $param->{'selected_country'} ? ' SELECTED' : '';
+ $country_html .= "\n<OPTION$selected>$country</OPTION>"
+ }
+ $country_html .= '</SELECT>';
+
+ ($county_html, $state_html, $country_html);
+
+}
+
+#=item expselect HASHREF | LIST
+#
+#Takes as input a hashref or list of key/value pairs with the following keys:
+#
+#=over 4
+#
+#=item prefix - Specify a unique prefix string if you intend to use the HTML output multiple time son one page.
+#
+#=item date - current date, in yyyy-mm-dd or m-d-yyyy format
+#
+#=back
+
+=item expselect PREFIX [ DATE ]
+
+Takes as input a unique prefix string and the current expiration date, in
+yyyy-mm-dd or m-d-yyyy format
+
+Returns an HTML fragments for expiration date selection.
+
+=cut
+
+sub expselect {
+ #my $param;
+ #if ( ref($_[0]) ) {
+ # $param = shift;
+ #} else {
+ # $param = { @_ };
+ #my $prefix = $param->{'prefix'};
+ #my $prefix = exists($param->{'prefix'}) ? $param->{'prefix'} : '';
+ #my $date = exists($param->{'date'}) ? $param->{'date'} : '';
+ my $prefix = shift;
+ my $date = scalar(@_) ? shift : '';
+
+ my( $m, $y ) = ( 0, 0 );
+ if ( $date =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #PostgreSQL date format
+ ( $m, $y ) = ( $2, $1 );
+ } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
+ ( $m, $y ) = ( $1, $3 );
+ }
+ my $return = qq!<SELECT NAME="$prefix!. qq!_month" SIZE="1">!;
+ for ( 1 .. 12 ) {
+ $return .= "<OPTION";
+ $return .= " SELECTED" if $_ == $m;
+ $return .= ">$_";
+ }
+ $return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
+ my @t = localtime;
+ my $thisYear = $t[5] + 1900;
+ for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. 2037 ) {
+ $return .= "<OPTION";
+ $return .= " SELECTED" if $_ == $y;
+ $return .= ">$_";
+ }
+ $return .= "</SELECT>";
+
+ $return;
+}
+
+=item popselector HASHREF | LIST
+
+Takes as input a hashref or list of key/value pairs with the following keys:
+
+=over 4
+
+=item popnum
+
+=item pops - An arrayref of hash references specifying access numbers. Normally you can just pass the value of the I<svc_acct_pop> field returned by B<signup_info>.
+
+=back
+
+Returns an HTML fragment for access number selection.
+
+=cut
+
+#horrible false laziness with FS/FS/svc_acct_pop.pm::popselector
+sub popselector {
+ my $param;
+ if ( ref($_[0]) ) {
+ $param = shift;
+ } else {
+ $param = { @_ };
+ }
+ my $popnum = $param->{'popnum'};
+ my $pops = $param->{'pops'};
+
+ return '<INPUT TYPE="hidden" NAME="popnum" VALUE="">' unless @$pops;
+ return $pops->[0]{city}. ', '. $pops->[0]{state}.
+ ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}.
+ '<INPUT TYPE="hidden" NAME="popnum" VALUE="'. $pops->[0]{popnum}. '">'
+ if scalar(@$pops) == 1;
+
+ my %pop = ();
+ my %popnum2pop = ();
+ foreach (@$pops) {
+ push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
+ $popnum2pop{$_->{popnum}} = $_;
+ }
+
+ my $text = <<END;
+ <SCRIPT>
+ function opt(what,href,text) {
+ var optionName = new Option(text, href, false, false)
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+END
+
+ my $init_popstate = $param->{'init_popstate'};
+ if ( $init_popstate ) {
+ $text .= '<INPUT TYPE="hidden" NAME="init_popstate" VALUE="'.
+ $init_popstate. '">';
+ } else {
+ $text .= <<END;
+ function acstate_changed(what) {
+ state = what.options[what.selectedIndex].text;
+ what.form.popac.options.length = 0
+ what.form.popac.options[0] = new Option("Area code", "-1", false, true);
+END
+ }
+
+ my @states = $init_popstate ? ( $init_popstate ) : keys %pop;
+ foreach my $state ( sort { $a cmp $b } @states ) {
+ $text .= "\nif ( state == \"$state\" ) {\n" unless $init_popstate;
+
+ foreach my $ac ( sort { $a cmp $b } keys %{ $pop{$state} }) {
+ $text .= "opt(what.form.popac, \"$ac\", \"$ac\");\n";
+ if ($ac eq $param->{'popac'}) {
+ $text .= "what.form.popac.options[what.form.popac.length-1].selected = true;\n";
+ }
+ }
+ $text .= "}\n" unless $init_popstate;
+ }
+ $text .= "popac_changed(what.form.popac)}\n";
+
+ $text .= <<END;
+ function popac_changed(what) {
+ ac = what.options[what.selectedIndex].text;
+ what.form.popnum.options.length = 0;
+ what.form.popnum.options[0] = new Option("City", "-1", false, true);
+
+END
+
+ foreach my $state ( @states ) {
+ foreach my $popac ( keys %{ $pop{$state} } ) {
+ $text .= "\nif ( ac == \"$popac\" ) {\n";
+
+ foreach my $pop ( @{$pop{$state}->{$popac}}) {
+ my $o_popnum = $pop->{popnum};
+ my $poptext = $pop->{city}. ', '. $pop->{state}.
+ ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
+
+ $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n";
+ if ($popnum == $o_popnum) {
+ $text .= "what.form.popnum.options[what.form.popnum.length-1].selected = true;\n";
+ }
+ }
+ $text .= "}\n";
+ }
+ }
+
+
+ $text .= "}\n</SCRIPT>\n";
+
+ $text .=
+ qq!<TABLE CELLPADDING="0"><TR><TD><SELECT NAME="acstate"! .
+ qq!SIZE=1 onChange="acstate_changed(this)"><OPTION VALUE=-1>State!;
+ $text .= "<OPTION" . ($_ eq $param->{'acstate'} ? " SELECTED" : "") .
+ ">$_" foreach sort { $a cmp $b } @states;
+ $text .= '</SELECT>'; #callback? return 3 html pieces? #'</TD>';
+
+ $text .=
+ qq!<SELECT NAME="popac" SIZE=1 onChange="popac_changed(this)">!.
+ qq!<OPTION>Area code</SELECT></TR><TR VALIGN="top">!;
+
+ $text .= qq!<TR><TD><SELECT NAME="popnum" SIZE=1 STYLE="width: 20em"><OPTION>City!;
+
+
+ #comment this block to disable initial list polulation
+ my @initial_select = ();
+ if ( scalar( @$pops ) > 100 ) {
+ push @initial_select, $popnum2pop{$popnum} if $popnum2pop{$popnum};
+ } else {
+ @initial_select = @$pops;
+ }
+ foreach my $pop ( sort { $a->{state} cmp $b->{state} } @initial_select ) {
+ $text .= qq!<OPTION VALUE="!. $pop->{popnum}. '"'.
+ ( ( $popnum && $pop->{popnum} == $popnum ) ? ' SELECTED' : '' ). ">".
+ $pop->{city}. ', '. $pop->{state}.
+ ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc};
+ }
+
+ $text .= qq!</SELECT></TD></TR></TABLE>!;
+
+ $text;
+
+}
+
+=back
+
+=head1 RESELLER FUNCTIONS
+
+Note: Resellers can also use the B<signup_info> and B<new_customer> functions
+with their active session, and the B<customer_info> and B<order_pkg> functions
+with their active session and an additional I<custnum> parameter.
+
+=over 4
+
+=item agent_login
+
+=item agent_info
+
+=item agent_list_customers
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<freeside-selfservice-clientd>, L<freeside-selfservice-server>
+
+=cut
+
+1;
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent.cgi b/fs_selfservice/FS-SelfService/cgi/agent.cgi
new file mode 100644
index 000000000..695d20e4c
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent.cgi
@@ -0,0 +1,457 @@
+#!/usr/bin/perl -Tw
+
+#some false laziness w/selfservice.cgi
+
+use strict;
+use vars qw($DEBUG $me $cgi $session_id $form_max $template_dir);
+use subs qw(do_template);
+use CGI;
+use CGI::Carp qw(fatalsToBrowser);
+use Business::CreditCard;
+use Text::Template;
+#use HTML::Entities;
+use FS::SelfService qw( agent_login agent_logout agent_info
+ agent_list_customers
+ signup_info new_customer
+ customer_info list_pkgs order_pkg
+ part_svc_info provision_acct provision_external
+ unprovision_svc
+ );
+
+$DEBUG = 0;
+$me = 'agent.cgi:';
+
+$template_dir = '.';
+
+$form_max = 255;
+
+warn "$me starting\n" if $DEBUG;
+
+warn "$me initializing CGI\n" if $DEBUG;
+$cgi = new CGI;
+
+unless ( defined $cgi->param('session') ) {
+ warn "$me no session defined, sending login page\n" if $DEBUG;
+ do_template('agent_login',{});
+ exit;
+}
+
+if ( $cgi->param('session') eq 'login' ) {
+
+ warn "$me processing login\n" if $DEBUG;
+
+ $cgi->param('username') =~ /^\s*([a-z0-9_\-\.\&]{0,$form_max})\s*$/i
+ or die "illegal username";
+ my $username = $1;
+
+ $cgi->param('password') =~ /^(.{0,$form_max})$/
+ or die "illegal password";
+ my $password = $1;
+
+ my $rv = agent_login(
+ 'username' => $username,
+ 'password' => $password,
+ );
+ if ( $rv->{error} ) {
+ do_template('agent_login', {
+ 'error' => $rv->{error},
+ 'username' => $username,
+ } );
+ exit;
+ } else {
+ $cgi->param('session' => $rv->{session_id} );
+ $cgi->param('action' => 'agent_main' );
+ }
+}
+
+$session_id = $cgi->param('session');
+
+warn "$me checking action\n" if $DEBUG;
+$cgi->param('action') =~
+ /^(agent_main|signup|process_signup|list_customers|view_customer|agent_provision|provision_svc|process_svc_acct|process_svc_external|delete_svc|agent_order_pkg|process_order_pkg|logout)$/
+ or die "unknown action ". $cgi->param('action');
+my $action = $1;
+
+warn "$me running $action\n" if $DEBUG;
+my $result = eval "&$action();";
+die $@ if $@;
+
+if ( $result->{error} eq "Can't resume session" ) { #ick
+ do_template('agent_login',{});
+ exit;
+}
+
+warn "$me processing template $action\n" if $DEBUG;
+do_template($action, {
+ 'session_id' => $session_id,
+ %{$result}
+});
+warn "$me done processing template $action\n" if $DEBUG;
+
+#--
+
+sub logout {
+ $action = 'agent_logout';
+ agent_logout( 'session_id' => $session_id );
+}
+
+sub agent_main { agent_info( 'session_id' => $session_id ); }
+
+sub signup { signup_info( 'session_id' => $session_id ); }
+
+sub process_signup {
+
+ my $init_data = signup_info( 'session_id' => $session_id );
+ if ( $init_data->{'error'} ) {
+ if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+ do_template('agent_login',{});
+ exit;
+ } else { #?
+ die $init_data->{'error'};
+ }
+ }
+
+ my $error = '';
+
+ #false laziness w/signup.cgi, identical except for agentnum vs session_id
+ my $payby = $cgi->param('payby');
+ if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+ #$payinfo = join('@', map { $cgi->param( $payby. "_payinfo$_" ) } (1,2) );
+ $cgi->param('payinfo' => $cgi->param($payby. '_payinfo1'). '@'.
+ $cgi->param($payby. '_payinfo2')
+ );
+ } else {
+ $cgi->param('payinfo' => $cgi->param( $payby. '_payinfo' ) );
+ }
+ $cgi->param('paydate' => $cgi->param( $payby. '_month' ). '-'.
+ $cgi->param( $payby. '_year' )
+ );
+ $cgi->param('payname' => $cgi->param( $payby. '_payname' ) );
+ $cgi->param('paycvv' => defined $cgi->param( $payby. '_paycvv' )
+ ? $cgi->param( $payby. '_paycvv' )
+ : ''
+ );
+
+ if ( $cgi->param('invoicing_list') ) {
+ $cgi->param('invoicing_list' => $cgi->param('invoicing_list'). ', POST')
+ if $cgi->param('invoicing_list_POST');
+ } else {
+ $cgi->param('invoicing_list' => 'POST' );
+ }
+
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ $error = $init_data->{msgcat}{passwords_dont_match}; #msgcat
+ $cgi->param('_password', '');
+ $cgi->param('_password2', '');
+ }
+
+ if ( $payby =~ /^(CARD|DCRD)$/ && $cgi->param('CARD_type') ) {
+ my $payinfo = $cgi->param('payinfo');
+ $payinfo =~ s/\D//g;
+
+ $payinfo =~ /^(\d{13,16})$/
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ $payinfo = $1;
+ validate($payinfo)
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ cardtype($payinfo) eq $cgi->param('CARD_type')
+ or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
+ }
+
+ unless ( $error ) {
+ my $rv = new_customer ( {
+ 'session_id' => $session_id,
+ map { $_ => scalar($cgi->param($_)) }
+ qw( last first ss company
+ address1 address2 city county state zip country
+ daytime night fax
+
+ ship_last ship_first ship_company
+ ship_address1 ship_address2 ship_city ship_county ship_state
+ ship_zip ship_country
+ ship_daytime ship_night ship_fax
+
+ payby payinfo paycvv paydate payname invoicing_list
+ referral_custnum promo_code reg_code
+ pkgpart username sec_phrase _password popnum refnum
+ ),
+ grep { /^snarf_/ } $cgi->param
+ } );
+ $error = $rv->{'error'};
+ }
+ #eslaf
+
+ if ( $error ) {
+ $action = 'signup';
+ my $r = {
+ $cgi->Vars,
+ %{$init_data},
+ 'error' => $error,
+ };
+ #warn join('\n', map "$_ => $r->{$_}", keys %$r )."\n";
+ $r;
+ } else {
+ $action = 'agent_main';
+ my $agent_info = agent_info( 'session_id' => $session_id );
+ $agent_info->{'message'} = 'Signup sucessful';
+ $agent_info;
+ }
+
+}
+
+sub list_customers {
+
+ my $results =
+ agent_list_customers( 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) }
+ grep defined($cgi->param($_)),
+ qw(prospect active susp cancel),
+ 'search',
+ );
+
+ if ( scalar( @{$results->{'customers'}} ) == 1 ) {
+ $action = 'view_customer';
+ customer_info (
+ 'agent_session_id' => $session_id,
+ 'custnum' => $results->{'customers'}[0]{'custnum'},
+ );
+ } else {
+ $results;
+ }
+
+}
+
+sub view_customer {
+
+ #my $init_data = signup_info( 'session_id' => $session_id );
+ #if ( $init_data->{'error'} ) {
+ # if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+ # do_template('agent_login',{});
+ # exit;
+ # } else { #?
+ # die $init_data->{'error'};
+ # }
+ #}
+ #
+ #my $customer_info =
+ customer_info (
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ );
+ #
+ #return {
+ # ( map { $_ => $init_data->{$_} }
+ # qw( part_pkg security_phrase svc_acct_pop ),
+ # ),
+ # %$customer_info,
+ #};
+}
+
+sub agent_order_pkg {
+
+ my $init_data = signup_info( 'session_id' => $session_id );
+ if ( $init_data->{'error'} ) {
+ if ( $init_data->{'error'} eq "Can't resume session" ) { #ick
+ do_template('agent_login',{});
+ exit;
+ } else { #?
+ die $init_data->{'error'};
+ }
+ }
+
+ my $customer_info = customer_info (
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ );
+
+ return {
+ ( map { $_ => $init_data->{$_} }
+ qw( part_pkg security_phrase svc_acct_pop ),
+ ),
+ %$customer_info,
+ };
+
+}
+
+sub agent_provision {
+ my $result = list_pkgs(
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+ $result;
+}
+
+sub provision_svc {
+
+ my $result = part_svc_info(
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart custnum ),
+ );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+
+ $result->{'svcdb'} =~ /^svc_(.*)$/
+ #or return { 'error' => 'Unknown svcdb '. $result->{'svcdb'} };
+ or die 'Unknown svcdb '. $result->{'svcdb'};
+ $action .= "_$1";
+ $action = "agent_$action";
+
+ $result;
+}
+
+sub process_svc_acct {
+
+ my $result = provision_acct (
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw(
+ custnum pkgnum svcpart username _password _password2 sec_phrase popnum )
+ );
+
+ if ( exists $result->{'error'} && $result->{'error'} ) {
+ #warn "$result $result->{'error'}";
+ $action = 'provision_svc_acct';
+ $action = "agent_$action";
+ return {
+ $cgi->Vars,
+ %{ part_svc_info( 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw(pkgnum svcpart custnum)
+ )
+ },
+ 'error' => $result->{'error'},
+ };
+ } else {
+ #warn "$result $result->{'error'}";
+ $action = 'agent_provision';
+ return {
+ %{agent_provision()},
+ 'message' => $result->{'svc'}. ' setup sucessfully.',
+ };
+ }
+
+}
+
+sub process_svc_external {
+
+ my $result = provision_external (
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( custnum pkgnum svcpart )
+ );
+
+ #warn "$result $result->{'error'}";
+ $action = 'agent_provision';
+ return {
+ %{agent_provision()},
+ 'message' => $result->{'error'}
+ ? '<FONT COLOR="#FF0000">'. $result->{'error'}. '</FONT>'
+ : $result->{'svc'}. ' setup sucessfully'.
+ ': serial number '.
+ sprintf('%010d', $result->{'id'}). '-'. $result->{'title'}
+ };
+
+}
+
+sub delete_svc {
+ my $result = unprovision_svc(
+ 'agent_session_id' => $session_id,
+ 'custnum' => $cgi->param('custnum'),
+ 'svcnum' => $cgi->param('svcnum'),
+ );
+
+ $action = 'agent_provision';
+
+ return {
+ %{agent_provision()},
+ 'message' => $result->{'error'}
+ ? '<FONT COLOR="#FF0000">'. $result->{'error'}. '</FONT>'
+ : $result->{'svc'}. ' removed.'
+ };
+
+}
+
+sub process_order_pkg {
+
+ my $results = '';
+
+ unless ( length($cgi->param('_password')) ) {
+ my $init_data = signup_info( 'session_id' => $session_id );
+ #die $init_data->{'error'} if $init_data->{'error'};
+ $results = { 'error' => $init_data->{msgcat}{empty_password} };
+ }
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ my $init_data = signup_info( 'session_id' => $session_id );
+ $results = { 'error' => $init_data->{msgcat}{passwords_dont_match} };
+ $cgi->param('_password', '');
+ $cgi->param('_password2', '');
+ }
+
+ $results ||= order_pkg (
+ 'agent_session_id' => $session_id,
+ map { $_ => $cgi->param($_) }
+ qw( custnum pkgpart username _password _password2 sec_phrase popnum )
+ );
+
+ if ( $results->{'error'} ) {
+ $action = 'agent_order_pkg';
+ return {
+ $cgi->Vars,
+ %{agent_order_pkg()},
+ #'message' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ 'error' => '<FONT COLOR="#FF0000">'. $results->{'error'}. '</FONT>',
+ };
+ } else {
+ $action = 'view_customer';
+ #$cgi->delete( grep { $_ ne 'custnum' } $cgi->param );
+ return {
+ %{view_customer()},
+ 'message' => 'Package order sucessful.',
+ };
+ }
+
+}
+
+#--
+
+sub do_template {
+ my $name = shift;
+ my $fill_in = shift;
+ #warn join(' / ', map { "$_=>".$fill_in->{$_} } keys %$fill_in). "\n";
+
+ $cgi->delete_all();
+ $fill_in->{'selfurl'} = $cgi->self_url; #OLD
+ $fill_in->{'self_url'} = $cgi->self_url;
+ $fill_in->{'cgi'} = \$cgi;
+
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => "$template_dir/$name.html",
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1, )
+ or die $Text::Template::ERROR;
+
+ local $^W = 0;
+ print $cgi->header( '-expires' => 'now' ),
+ $template->fill_in( PACKAGE => 'FS::SelfService::_agentcgi',
+ HASH => $fill_in
+ );
+}
+
+package FS::SelfService::_agentcgi;
+
+use HTML::Entities;
+use FS::SelfService qw(regionselector expselect popselector);
+
+#false laziness w/selfservice.cgi
+sub include {
+ my $name = shift;
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => "$main::template_dir/$name.html",
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1,
+ )
+ or die $Text::Template::ERROR;
+
+ $template->fill_in( PACKAGE => 'FS::SelfService::_agentcgi',
+ #HASH => $fill_in
+ );
+
+}
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html b/fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html
new file mode 100644
index 000000000..603fc0bd2
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_customer_menu.html
@@ -0,0 +1,7 @@
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+<A HREF="<%= $url %>agent_provision">Setup services</A><BR><BR>
+<A HREF="<%= $url %>agent_order_pkg">Purchase additional package</A><BR><BR>
+
+</TD>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html b/fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html
new file mode 100644
index 000000000..7a2b75071
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_delete_svc.html
@@ -0,0 +1,19 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= $small_custview %>
+<BR>
+<%= if ( $error ) {
+
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT>!;
+} else {
+ $OUT .= "<FONT SIZE=4>$svc removed.</FONT>";
+} %>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_login.html b/fs_selfservice/FS-SelfService/cgi/agent_login.html
new file mode 100644
index 000000000..4b0778ec5
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_login.html
@@ -0,0 +1,22 @@
+<HTML><HEAD><TITLE>Reseller Login</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=5>Reseller Login</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="login">
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+<TR>
+ <TH ALIGN="right">Username </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>">
+ </TD>
+</TR>
+<TR>
+ <TH ALIGN="right">Password </TH>
+ <TD>
+ <INPUT TYPE="password" NAME="password">
+ </TD>
+</TR>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" VALUE="Login">
+</FORM></BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_logout.html b/fs_selfservice/FS-SelfService/cgi/agent_logout.html
new file mode 100644
index 000000000..98094679a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_logout.html
@@ -0,0 +1,5 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+You have been logged out.
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_main.html b/fs_selfservice/FS-SelfService/cgi/agent_main.html
new file mode 100644
index 000000000..9dd338382
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_main.html
@@ -0,0 +1,37 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%= $message
+ ? "<FONT SIZE=\"+2\"><B>$message</B></FONT>"
+ : "Hello $agent!"
+%><BR><BR>
+
+<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">
+<TR><TH BGCOLOR="#cccccc">Customer summary</TH></TR>
+<TR><TD BGCOLOR="#dddddd">
+
+ <B><%= $num_prospect %></B>
+ <%= $num_prospect ? qq!<A HREF="${url}list_customers;prospect=1">! : '' %>prospects</A>
+
+ <BR><FONT COLOR="#00CC00"><B><%= $num_active %></B></FONT>
+ <%= $num_active ? qq!<A HREF="${url}list_customers;active=1">! : '' %>active</A>
+
+ <BR><FONT COLOR="#FF9900"><B><%= $num_susp %></B></FONT>
+ <%= $num_susp ? qq!<A HREF="${url}list_customers;susp=1">! : '' %>suspended</A>
+
+ <BR><FONT COLOR="#FF0000"><B><%= $num_cancel %></B></FONT>
+ <%= $num_cancel ? qq!<A HREF="${url}list_customers;cancel=1">! : '' %>cancelled</A>
+
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
+
+
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_menu.html b/fs_selfservice/FS-SelfService/cgi/agent_menu.html
new file mode 100644
index 000000000..84a295304
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_menu.html
@@ -0,0 +1,15 @@
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+
+<A HREF="<%= $url %>agent_main">Overview</A><BR><BR>
+<A HREF="<%= $url %>signup">New customer<!--/prospect--></A><BR><BR>
+<FORM ACTION="<%= $selfurl %>">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="list_customers">
+<INPUT TYPE="text" NAME="search" SIZE=20><BR>
+<SMALL><I>cust&nbsp;#,&nbsp;last&nbsp;name,&nbsp;or&nbsp;company</I></SMALL><BR>
+<INPUT TYPE="submit" VALUE="Search customers"><BR>
+</FORM>
+<A HREF="<%= $url %>logout">Logout</A><BR><BR>
+
+</TD>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html b/fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html
new file mode 100644
index 000000000..0a665c99e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_order_pkg.html
@@ -0,0 +1,19 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+<%= $small_custview %>
+<BR>
+
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+<%= include('order_pkg') %>
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_provision.html b/fs_selfservice/FS-SelfService/cgi/agent_provision.html
new file mode 100644
index 000000000..8770e2f9e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_provision.html
@@ -0,0 +1,25 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%= $message
+ ? "<FONT SIZE=\"+2\"><B>$message</B></FONT><BR><BR>"
+ : ''
+%>
+
+<%= $small_custview %>
+<BR>
+
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+<%= include('provision_list') %>
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html b/fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html
new file mode 100644
index 000000000..8d299cdc5
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/agent_provision_svc_acct.html
@@ -0,0 +1,19 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;custnum=$custnum;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+<%= $small_custview %>
+<BR>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+<%= include('svc_acct') %>
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2.html b/fs_selfservice/FS-SelfService/cgi/cvv2.html
new file mode 100644
index 000000000..b178c8513
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/cvv2.html
@@ -0,0 +1,25 @@
+<HTML>
+ <HEAD>
+ <TITLE>
+ CVV2 information
+ </TITLE>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+ security code used to reduce credit card fraud.<BR><BR>
+ <TABLE BORDER=0 CELLSPACING=4>
+ <TR>
+ <TH>Visa / MasterCard / Discover</TH>
+ <TH>American Express</TH>
+ </TR>
+ <TR>
+ <TD>
+ <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="cvv2.png">
+ </TD>
+ <TD>
+ <IMG BORDER=0 ALT="American Express" SRC="cvv2_amex.png">
+ </TD>
+ </TABLE>
+ <CENTER><A HREF="javascript:close()">(close window)</A></CENTER>
+ </BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2.png b/fs_selfservice/FS-SelfService/cgi/cvv2.png
new file mode 100644
index 000000000..4610dcbe6
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/cvv2.png
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png b/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png
new file mode 100644
index 000000000..21c36a0ab
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/cvv2_amex.png
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/decline.html b/fs_selfservice/FS-SelfService/cgi/decline.html
new file mode 100644
index 000000000..a37ba3ab6
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/decline.html
@@ -0,0 +1,5 @@
+<HTML><HEAD><TITLE>Processing error</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Processing error</FONT><BR><BR>
+There has been an error processing your account. Please contact customer
+support.
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/delete_svc.html b/fs_selfservice/FS-SelfService/cgi/delete_svc.html
new file mode 100644
index 000000000..8468deb4b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/delete_svc.html
@@ -0,0 +1,17 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT>!;
+} else {
+ $OUT .= "<FONT SIZE=4>$svc removed.</FONT>";
+} %>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/list_customers.html b/fs_selfservice/FS-SelfService/cgi/list_customers.html
new file mode 100644
index 000000000..858e5e9ba
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/list_customers.html
@@ -0,0 +1,39 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%=
+ if ( @customers ) {
+ $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#cccccc" COLSPAN=3>Customers</TH><TD>';
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $customer ( @customers ) {
+ my $td = qq!<TD BGCOLOR="#$col">!;
+ my $a = qq!<A HREF="${url}view_customer;custnum=!.
+ $customer->{'custnum'}. '">';
+ $OUT .=
+ '<TR>'.
+ "$td<FONT COLOR=\"". $customer->{'statuscolor'}. '">'.
+ ucfirst($customer->{'status'}). "</TD>". "$td</TD>".
+ "$td$a". $customer->{'name'}. "</A></TD>".
+ '</TR>';
+ #"$td</TD>".
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+ $OUT .= '</TABLE>';
+ } else {
+ $OUT .= 'No customers.<BR><BR>';
+ }
+%>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/login.html b/fs_selfservice/FS-SelfService/cgi/login.html
new file mode 100644
index 000000000..5607de783
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/login.html
@@ -0,0 +1,29 @@
+<HTML><HEAD><TITLE>Login</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=5>Login</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="login">
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+<TR>
+ <TH ALIGN="right">Username </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>">
+ </TD>
+</TR>
+<TR>
+ <TH ALIGN="right">Domain </TH>
+ <TD>
+ <INPUT TYPE="text" NAME="domain" VALUE="<%= $domain %>">
+ </TD>
+</TR>
+<!--<INPUT TYPE="hidden" NAME="domain" VALUE="myisp.com">-->
+<TR>
+ <TH ALIGN="right">Password </TH>
+ <TD>
+ <INPUT TYPE="password" NAME="password">
+ </TD>
+</TR>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" VALUE="Login">
+</FORM></BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/logout.html b/fs_selfservice/FS-SelfService/cgi/logout.html
new file mode 100644
index 000000000..0e774e9eb
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/logout.html
@@ -0,0 +1,5 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+You have been logged out.
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/make_payment.html b/fs_selfservice/FS-SelfService/cgi/make_payment.html
new file mode 100644
index 000000000..1bbbe90b2
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/make_payment.html
@@ -0,0 +1,118 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Make a payment</FONT><BR><BR>
+<FORM NAME="OneTrueForm" METHOD="POST" ACTION="<%=$selfurl%>" onSubmit="document.OneTrueForm.process.disabled=true">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%=$session_id%>">
+<INPUT TYPE="hidden" NAME="action" VALUE="payment_results">
+<TABLE BGCOLOR="#cccccc">
+<TR>
+ <TD ALIGN="right">Amount&nbsp;Due</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<%=sprintf("%.2f",$balance)%>
+ </TD></TR></TABLE>
+ </TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Payment&nbsp;amount</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<INPUT TYPE="text" NAME="amount" SIZE=8 VALUE="<%=sprintf("%.2f",$balance)%>">
+ </TD></TR></TABLE>
+ </TD>
+</TR><TR>
+ <TD ALIGN="right">Card&nbsp;type</TD>
+ <TD>
+ <SELECT NAME="card_type"><OPTION></OPTION>
+ <%= foreach ( keys %card_types ) {
+ $selected = $card_type eq $card_types{$_} ? ' SELECTED' : '';
+ $OUT .= qq(<OPTION$selected VALUE="). $card_types{$_}. qq(">$_\n);
+ } %>
+ </SELECT>
+ </TD>
+</TD><TR>
+ <TD ALIGN="right">Card&nbsp;number</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="payinfo" SIZE=20 MAXLENGTH=19 VALUE="<%=$payinfo%>"> </TD>
+ <TD>Exp.</TD>
+ <TD>
+ <SELECT NAME="month">
+ <%= for ( ( map "0$_", 1 .. 9 ), 10 .. 12 ) {
+ $OUT .= '<OPTION'. ($_ == $month ? ' SELECTED' : ''). ">$_\n";
+ } %>
+ </SELECT>
+ </TD>
+ <TD> / </TD>
+ <TD>
+ <SELECT NAME="year">
+ <%= my @a = localtime; for ( $a[5]+1900 .. $a[5]+1915 ) {
+ $OUT .= '<OPTION'. ($_ == $year ? ' SELECTED' : ''). ">$_\n";
+ } %>
+ </SELECT>
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+</TR><TR>
+ <TD ALIGN="right">Exact&nbsp;name&nbsp;on&nbsp;card</TD>
+ <TD><INPUT TYPE="text" SIZE=32 MAXLENGTH=80 NAME="payname" VALUE="<%=$payname%>"></TD>
+</TR><TR>
+ <TD ALIGN="right">Card&nbsp;billing&nbsp;address</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address1" VALUE="<%=$address1%>">
+ </TD>
+</TR><TR>
+ <TD ALIGN="right">Address&nbsp;line&nbsp;2</TD>
+ <TD>
+ <INPUT TYPE="text" SIZE=40 MAXLENGTH=80 NAME="address2" VALUE="<%=$address2%>">
+ </TD>
+</TR><TR>
+ <TD ALIGN="right">City</TD>
+ <TD>
+ <TABLE>
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="city" SIZE="12" MAXLENGTH=80 VALUE="<%=$city%>">
+ </TD>
+ <TD>State</TD>
+ <TD>
+ <SELECT NAME="state">
+ <%= for ( @states ) {
+ $OUT .= '<OPTION'. ($_ eq $state ? ' SELECTED' : '' ). ">$_\n";
+ } %>
+ </SELECT>
+ </TD>
+ <TD>Zip</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="zip" SIZE=11 MAXLENGTH=10 VALUE="<%=$zip%>">
+ </TD>
+ </TR>
+ </TABLE>
+ </TD>
+</TR><TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox" CHECKED NAME="save" VALUE="1">
+ Remember this information
+ </TD>
+</TR><TR>
+ <TD COLSPAN=2>
+ <INPUT TYPE="checkbox"<%= $payby eq 'CARD' ? ' CHECKED' : '' %> NAME="auto" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.save.checked=true; }">
+ Charge future payments to this card automatically
+ </TD>
+</TR>
+</TABLE>
+<BR>
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="<%=$paybatch%>">
+<INPUT TYPE="submit" NAME="process" VALUE="Process payment"> <!-- onClick="this.disabled=true"> -->
+</FORM>
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/map.gif b/fs_selfservice/FS-SelfService/cgi/map.gif
new file mode 100644
index 000000000..ef884d8f9
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/map.gif
Binary files differ
diff --git a/fs_selfservice/FS-SelfService/cgi/myaccount.html b/fs_selfservice/FS-SelfService/cgi/myaccount.html
new file mode 100644
index 000000000..546ca1112
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/myaccount.html
@@ -0,0 +1,45 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+Hello <%= $name %>!<BR><BR>
+<%= $small_custview %>
+<BR>
+<%= if ( $balance > 0 ) {
+ $OUT .= qq! <B><A HREF="${url}make_payment">Make a payment</A></B><BR><BR>!;
+} %>
+<%=
+ if ( @open_invoices ) {
+ $OUT .= '<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#eeeeee">'.
+ '<TR><TH BGCOLOR="#ff6666" COLSPAN=5>Open Invoices</TH></TR>';
+ my $link = qq!<A HREF="<%= $url %>myaccount!;
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $invoice ( @open_invoices ) {
+ my $td = qq!<TD BGCOLOR="#$col">!;
+ my $a=qq!<A HREF="${url}view_invoice;invnum=!. $invoice->{'invnum'}. '">';
+ $OUT .=
+ "<TR>$td${a}Invoice #". $invoice->{'invnum'}. "</A></TD>$td</TD>".
+ "$td$a". $invoice->{'date'}. "</A></TD>$td</TD>".
+ qq!<TD BGCOLOR="#$col" ALIGN="right">$a\$!. $invoice->{'owed'}.
+ '</A></TD>'.
+ '</TR>';
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+ $OUT .= '</TABLE>';
+ } else {
+ $OUT .= 'You have no outstanding invoices.<BR><BR>';
+ }
+%>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
+
+
diff --git a/fs_selfservice/FS-SelfService/cgi/myaccount_menu.html b/fs_selfservice/FS-SelfService/cgi/myaccount_menu.html
new file mode 100644
index 000000000..f2e5e998e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/myaccount_menu.html
@@ -0,0 +1,89 @@
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0><TR>
+<TD VALIGN="top" HEIGHT=384 BGCOLOR="#dddddd">
+
+<TABLE CELLSPACING=0 BORDER=0>
+
+<%=
+
+my @menu = (
+{ title=>' ' },
+{ title=>'Overview', url=>'myaccount', size=>'+1', },
+{ title=>' ' },
+
+{ title=>'Purchase', size=>'+1', },
+ { title=>'Purchase additional package*', url=>'order', 'indent'=>2 },
+);
+
+if ( 1 ) { #XXXFIXME "enable selfservice prepay features" flag or something, eventually per-pkg or something really fancy
+
+ push @menu, (
+ { title=>'Recharge my account with a credit card',
+ url=>'make_payment', indent=>2 },
+ { title=>'Recharge my account with a prepaid card',
+ url=>'recharge_prepay', indent=>2 },
+ );
+
+}
+
+push @menu, (
+
+{ title=>' ' },
+
+{ title=>'Setup my services', url=>'provision', size=>'+1', },
+
+{ title=>' ' },
+
+{ title=>'Change my information', size=>'+1', },
+ { title=>'Change payment information*', url=>'change_bill', indent=>2 },
+ { title=>'Change service address*', url=>'change_ship', indent=>2 },
+ { title=>'Change password(s)*', url=>'hmmmFIXME', indent=>2 },
+
+{ title=>' ' },
+
+{ title=>'Logout', url=>'logout', size=>'+1', },
+
+);
+
+foreach my $item ( @menu ) {
+
+ $OUT .= '<TR><TD';
+ if ( exists $item->{'url'} && $action eq $item->{'url'} ) {
+ $OUT .= ' BGCOLOR="#eeeeee" '.
+ ' STYLE="border-top: 1px solid black;'.
+ ' border-left: 1px solid black;'.
+ ' border-bottom: 1px solid black"';
+ } else {
+ $OUT .= ' STYLE="border-right: 1px solid black"';
+ }
+ $OUT.='>';
+
+ $OUT .= '<FONT SIZE="'. $item->{'size'}. '">'
+ if exists $item->{'size'};
+
+ $OUT .= '&nbsp;' x $item->{'indent'}
+ if exists $item->{'indent'};
+
+ $OUT .= '<A HREF="'. $url. $item->{'url'}. '">'
+ if exists $item->{'url'} && $action ne $item->{'url'};
+
+ $item->{'title'} =~ s/ /&nbsp;/g;
+ $OUT .= $item->{'title'};
+
+ $OUT .= '</FONT>'
+ if exists $item->{'size'};
+
+ $OUT .= '</A>'
+ if exists $item->{'url'} && $action ne $item->{'url'};
+
+ $OUT .= '</TD></TR>';
+
+}
+
+%>
+
+</TABLE>
+
+<A HREF="passwd.html">(tempFIXME) Change&nbsp;password(s)</A><BR><BR>
+*&nbsp;coming&nbsp;soon
+</TD>
diff --git a/fs_selfservice/FS-SelfService/cgi/order_pkg.html b/fs_selfservice/FS-SelfService/cgi/order_pkg.html
new file mode 100644
index 000000000..9cdd4cd6c
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/order_pkg.html
@@ -0,0 +1,75 @@
+<SCRIPT TYPE="text/javascript">
+function enable_order_pkg () {
+ if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) {
+ document.OrderPkgForm.submit.disabled = false;
+ } else {
+ document.OrderPkgForm.submit.disabled = true;
+ }
+}
+</SCRIPT>
+<FONT SIZE=4>Purchase additional package</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">$error</FONT><BR><BR>!;
+} ''; %>
+<FORM NAME="OrderPkgForm" ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_order_pkg">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
+<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart" onChange="enable_order_pkg()">
+ <OPTION VALUE="">
+
+ <%=
+ foreach my $part_pkg ( @part_pkg ) {
+ $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $part_pkg->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $_password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $_password2 %>"></TD>
+</TR>
+<%=
+ if ( $security_phrase ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( @svc_acct_pop ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector( 'popnum' => $popnum,
+ 'pops' => \@svc_acct_pop,
+ 'init_popstate' => $init_popstate,
+ 'popac' => $popac,
+ 'acstate' => $acstate,
+ ).
+ '</TD></TR>';
+ } else {
+ $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+ }
+%>
+</TABLE>
+<INPUT NAME="submit" TYPE="submit" VALUE="Purchase" disabled>
+</FORM>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/passwd.cgi b/fs_selfservice/FS-SelfService/cgi/passwd.cgi
new file mode 100755
index 000000000..d77876e37
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/passwd.cgi
@@ -0,0 +1,60 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use Getopt::Std;
+use FS::SelfService qw(passwd);
+use CGI;
+use CGI::Carp qw(fatalsToBrowser);
+
+my $freeside_uid = scalar(getpwnam('freeside'));
+
+$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'} = '';
+
+die "passwd.cgi isn't running as freeside user\n" if $> != $freeside_uid;
+
+my $cgi = new CGI;
+
+$cgi->param('username') =~ /^([^\n]{0,255}$)/ or die "Illegal username";
+my $me = $1;
+
+$cgi->param('domain') =~ /^([^\n]{0,255}$)/ or die "Illegal domain";
+my $domain = $1;
+
+$cgi->param('old_password') =~ /^([^\n]{0,255}$)/ or die "Illegal old_password";
+my $old_password = $1;
+
+$cgi->param('new_password') =~ /^([^\n]{0,255}$)/ or die "Illegal new_password";
+my $new_password = $1;
+
+die "New passwords don't match"
+ unless $new_password eq $cgi->param('new_password2');
+
+my $rv = passwd(
+ 'username' => $me,
+ 'domain' => $domain,
+ 'old_password' => $old_password,
+ 'new_password' => $new_password,
+);
+
+my $error = $rv->{error};
+
+if ($error) {
+ die $error;
+} else {
+ print $cgi->header(), <<END;
+<html>
+ <head>
+ <title>Password changed</title>
+ </head>
+ <body bgcolor="#e8e8e8">
+ <h3>Password changed</h3>
+<br>Your password has been changed.
+ </body>
+</html>
+END
+}
diff --git a/fs_selfservice/FS-SelfService/cgi/passwd.html b/fs_selfservice/FS-SelfService/cgi/passwd.html
new file mode 100644
index 000000000..459c96aa8
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/passwd.html
@@ -0,0 +1,28 @@
+<html>
+ <head>
+ <title>Change password</title>
+ </head>
+ <body bgcolor="#e8e8e8">
+ <h3>Change password</h3>
+ <form action="passwd.cgi" method="post">
+ <table bgcolor="#cccccc" border=0 cellspacing=2>
+ <tr><th align="right">Username</th>
+ <td><input type="text" name="username" size="18"></td>
+ </tr>
+ <tr><th align="right">Domain</th>
+ <td><input type="text" name="domain" size="18"></td>
+ </tr>
+ <tr><th align="right">Current password</th>
+ <td><input type="password" name="old_password" size="18"></td>
+ </tr>
+ <tr><th align="right">New password</th>
+ <td><input type="password" name="new_password" size="18"></td>
+ </tr>
+ <tr><th align="right">Re-enter new password</th>
+ <td><input type="password" name="new_password2" size="18"></td>
+ </tr>
+ </table>
+ <br><input type="submit" value="Change password">
+ </body>
+</html>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/payment_results.html b/fs_selfservice/FS-SelfService/cgi/payment_results.html
new file mode 100644
index 000000000..de6c54dae
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/payment_results.html
@@ -0,0 +1,16 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Payment results</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error processing your payment: $error</FONT>!;
+} else {
+ $OUT .= 'Your payment was processed sucessfully. Thank you.';
+} %>
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/process_svc_acct.html b/fs_selfservice/FS-SelfService/cgi/process_svc_acct.html
new file mode 100644
index 000000000..200a80dc9
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_svc_acct.html
@@ -0,0 +1,13 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4><%= $svc %> setup sucessfully.</FONT>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/process_svc_external.html b/fs_selfservice/FS-SelfService/cgi/process_svc_external.html
new file mode 100644
index 000000000..2328fa10f
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/process_svc_external.html
@@ -0,0 +1,15 @@
+<HTML><HEAD><TITLE><%= $error ? 'MyAccount' : sprintf("Your serial number is %010d-$title", $id) %></TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE=4><%= $svc %> setup sucessfully.</FONT>
+
+<BR><BR>Your serial number is <%= sprintf("%010d-$title", $id) %>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/promocode.html b/fs_selfservice/FS-SelfService/cgi/promocode.html
new file mode 100644
index 000000000..f8ee7f6eb
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/promocode.html
@@ -0,0 +1,14 @@
+<HTML><HEAD><TITLE>ISP Signup</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup - promotional code</FONT><BR><BR>
+<SCRIPT>
+function gotoURL(object) {
+ window.location.href = 'signup.cgi?promo_code=' + object.promo_code.value;
+}
+</SCRIPT>
+<FORM>
+Enter promotional code <INPUT TYPE="text" NAME="promo_code">
+<INPUT type="submit" VALUE="Signup" onClick="gotoURL(this.form)">
+
+</FORM>
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/provision.html b/fs_selfservice/FS-SelfService/cgi/provision.html
new file mode 100644
index 000000000..d31e6070b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/provision.html
@@ -0,0 +1,10 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= include('provision_list') %>
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/provision_list.html b/fs_selfservice/FS-SelfService/cgi/provision_list.html
new file mode 100644
index 000000000..0f68dfe3c
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/provision_list.html
@@ -0,0 +1,87 @@
+<FONT SIZE=4>Setup services</FONT><BR><BR>
+
+<SCRIPT>
+function areyousure(href, message) {
+ if (confirm(message) == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
+<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#ffffff">
+
+<%= foreach my $pkg (
+ grep { scalar(@{$_->{part_svc}})
+ || scalar(@{$_->{cust_svc}})
+ } @cust_pkg
+ ) {
+
+ $OUT .= #'<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#ffffff">'.
+ '<TR><TH BGCOLOR="#6666ff" COLSPAN=3>'.
+ $pkg->{'pkg'}.
+ '</TH></TR>';
+
+ my $col1 = "ffffff";
+ my $col2 = "dddddd";
+ my $col = $col1;
+
+ foreach my $cust_svc ( @{ $pkg->{cust_svc} } ) {
+ my $td = qq!<TD BGCOLOR="#$col"!;
+
+ $OUT .= '<TR>'.
+ "$td ALIGN=right>". $cust_svc->{label}[0]. ': </TD>'.
+ "$td><B>". $cust_svc->{label}[1]. '</B>';
+ $OUT .= '<BR><I>password: '. encode_entities($cust_svc->{_password}). '</I>'
+ if exists($cust_svc->{_password});
+ $OUT .= '</TD>'.
+ "$td><FONT SIZE=-1>";
+
+ #if ( $cust_svc->{label}[2] eq 'svc_acct' ) {
+ # $OUT .= qq!(<A HREF="${url}changepw;svcnum=$cust_svc->{'svcnum'}">!.
+ # 'change&nbsp;pw) ';
+ #}
+
+ unless ( $cust_svc->{'svcnum'} == $svcnum ) {
+ $OUT .= qq!(<A HREF="javascript:areyousure('${url}delete_svc;svcnum=$cust_svc->{svcnum}', 'This will permanently delete the $cust_svc->{label}[1] $cust_svc->{label}[0]. Are you sure?')">!.
+ 'delete</A>)';
+
+ }
+ $OUT .= '</FONT></TD></TR>';
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+
+ $OUT .= '<TR><TD COLSPAN=3 BGCOLOR="#000000"></TD></TR>'
+ if scalar(@{$pkg->{part_svc}}) && scalar(@{$pkg->{cust_svc}});
+
+ $col = $col1;
+
+ foreach my $part_svc ( @{ $pkg->{part_svc} } ) {
+
+ my $td = qq!<TD BGCOLOR="#$col"!;
+
+ my $link;
+
+ if ( $part_svc->{'svcdb'} eq 'svc_external'
+ #&& $conf->exists('svc_external-skip_manual')
+ ) {
+ $link = "${url}process_svc_external;".
+ "pkgnum=$pkg->{'pkgnum'};".
+ "svcpart=$part_svc->{'svcpart'}";
+ } else {
+ $link = "${url}provision_svc;".
+ "pkgnum=$pkg->{'pkgnum'};".
+ "svcpart=$part_svc->{'svcpart'}";
+ }
+
+ $OUT .= "<TR>$td COLSPAN=3 ALIGN=center>".
+ qq!<A HREF="$link">!. 'Setup '. $part_svc->{'svc'}. '</A> '.
+ '('. $part_svc->{'num_avail'}. ' available)'.
+ '</TD></TR>';
+ $col = $col eq $col1 ? $col2 : $col1;
+ }
+
+ #$OUT .= '</TABLE><BR>';
+ $OUT .= '<TR><TD BGCOLOR="#eeeeee" COLSPAN=3>&nbsp;</TD></TR>';
+
+} %>
+
+</TABLE>
diff --git a/fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html b/fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html
new file mode 100644
index 000000000..50540742a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/provision_svc_acct.html
@@ -0,0 +1,11 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<%= include('svc_acct') %>
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/recharge_prepay.html b/fs_selfservice/FS-SelfService/cgi/recharge_prepay.html
new file mode 100644
index 000000000..f8584597a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/recharge_prepay.html
@@ -0,0 +1,36 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Recharge with prepaid card</FONT><BR><BR>
+<FORM NAME="OneTrueForm" METHOD="POST" ACTION="<%=$selfurl%>" onSubmit="document.OneTrueForm.process.disabled=true">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%=$session_id%>">
+<INPUT TYPE="hidden" NAME="action" VALUE="recharge_results">
+<TABLE BGCOLOR="#cccccc">
+<!--
+<TR>
+ <TD ALIGN="right">Amount&nbsp;Due</TD>
+ <TD>
+ <TABLE><TR><TD BGCOLOR="#ffffff">
+ $<%=sprintf("%.2f",$balance)%>
+ </TD></TR></TABLE>
+ </TD>
+</TR>
+-->
+<TR>
+ <TD ALIGN="right">Prepaid&nbsp;card&nbsp;number</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="prepaid_cardnum" SIZE=20 MAXLENGTH=19 VALUE="<%=$prepaid_cardnum%>">
+ </TD>
+</TR>
+</TABLE>
+<BR>
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="<%=$paybatch%>">
+<INPUT TYPE="submit" NAME="process" VALUE="Recharge"> <!-- onClick="this.disabled=true"> -->
+</FORM>
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/recharge_results.html b/fs_selfservice/FS-SelfService/cgi/recharge_results.html
new file mode 100644
index 000000000..ec3ea2c7a
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/recharge_results.html
@@ -0,0 +1,24 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+<FONT SIZE=4>Recharge results</FONT><BR><BR>
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error processing your prepaid card: $error</FONT>!;
+} else {
+ $OUT .= 'Prepaid card recharge sucessful!<BR><BR>';
+
+ $OUT .= '$'. sprintf('%.2f', $amount). ' added to your account.<BR><BR>'
+ if $amount;
+
+ $OUT .= $duration. ' added to your account.<BR><BR>'
+ if $seconds;
+
+ $OUT .= 'Thank you.';
+} %>
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
diff --git a/fs_selfservice/FS-SelfService/cgi/regcode.html b/fs_selfservice/FS-SelfService/cgi/regcode.html
new file mode 100644
index 000000000..e639b9b53
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/regcode.html
@@ -0,0 +1,14 @@
+<HTML><HEAD><TITLE>ISP Signup</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup - registration code</FONT><BR><BR>
+<SCRIPT>
+function gotoURL(object) {
+ window.location.href = 'signup.cgi?reg_code=' + object.reg_code.value;
+}
+</SCRIPT>
+<FORM>
+Enter registration code <INPUT TYPE="text" NAME="reg_code">
+<INPUT type="submit" VALUE="Signup" onClick="gotoURL(this.form)">
+
+</FORM>
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi
new file mode 100644
index 000000000..034a684c6
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi
@@ -0,0 +1,305 @@
+#!/usr/bin/perl -Tw
+
+use strict;
+use vars qw($cgi $session_id $form_max $template_dir);
+use subs qw(do_template);
+use CGI;
+use CGI::Carp qw(fatalsToBrowser);
+use Text::Template;
+use HTML::Entities;
+use FS::SelfService qw( login customer_info invoice
+ payment_info process_payment
+ process_prepay
+ list_pkgs
+ part_svc_info provision_acct provision_external
+ unprovision_svc
+ );
+
+$template_dir = '.';
+
+$form_max = 255;
+
+$cgi = new CGI;
+
+unless ( defined $cgi->param('session') ) {
+ do_template('login',{});
+ exit;
+}
+
+if ( $cgi->param('session') eq 'login' ) {
+
+ $cgi->param('username') =~ /^\s*([a-z0-9_\-\.\&]{0,$form_max})\s*$/i
+ or die "illegal username";
+ my $username = $1;
+
+ $cgi->param('domain') =~ /^\s*([\w\-\.]{0,$form_max})\s*$/
+ or die "illegal domain";
+ my $domain = $1;
+
+ $cgi->param('password') =~ /^(.{0,$form_max})$/
+ or die "illegal password";
+ my $password = $1;
+
+ my $rv = login(
+ 'username' => $username,
+ 'domain' => $domain,
+ 'password' => $password,
+ );
+ if ( $rv->{error} ) {
+ do_template('login', {
+ 'error' => $rv->{error},
+ 'username' => $username,
+ 'domain' => $domain,
+ } );
+ exit;
+ } else {
+ $cgi->param('session' => $rv->{session_id} );
+ $cgi->param('action' => 'myaccount' );
+ }
+}
+
+$session_id = $cgi->param('session');
+
+#order|pw_list XXX ???
+$cgi->param('action') =~
+ /^(myaccount|view_invoice|make_payment|payment_results|recharge_prepay|recharge_results|logout|change_bill|change_ship|provision|provision_svc|process_svc_acct|process_svc_external|delete_svc)$/
+ or die "unknown action ". $cgi->param('action');
+my $action = $1;
+
+my $result = eval "&$action();";
+die $@ if $@;
+
+if ( $result->{error} eq "Can't resume session" ) { #ick
+ do_template('login',{});
+ exit;
+}
+
+#warn $result->{'open_invoices'};
+#warn scalar(@{$result->{'open_invoices'}});
+
+warn "processing template $action\n";
+do_template($action, {
+ 'session_id' => $session_id,
+ 'action' => $action, #so the menu knows what tab we're on...
+ %{$result}
+});
+
+#--
+
+sub myaccount { customer_info( 'session_id' => $session_id ); }
+
+sub view_invoice {
+
+ $cgi->param('invnum') =~ /^(\d+)$/ or die "illegal invnum";
+ my $invnum = $1;
+
+ invoice( 'session_id' => $session_id,
+ 'invnum' => $invnum,
+ );
+
+}
+
+sub make_payment {
+ payment_info( 'session_id' => $session_id );
+}
+
+sub payment_results {
+
+ use Business::CreditCard;
+
+ $cgi->param('amount') =~ /^\s*(\d+(\.\d{2})?)\s*$/
+ or die "illegal amount"; #!!!
+ my $amount = $1;
+
+ my $payinfo = $cgi->param('payinfo');
+ $payinfo =~ s/\D//g;
+ $payinfo =~ /^(\d{13,16})$/
+ #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ or die "illegal card"; #!!!
+ $payinfo = $1;
+ validate($payinfo)
+ #or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ or die "invalid card"; #!!!
+ cardtype($payinfo) eq $cgi->param('card_type')
+ #or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
+ or die "not a ". $cgi->param('card_type');
+
+ $cgi->param('month') =~ /^(\d{2})$/ or die "illegal month";
+ my $month = $1;
+ $cgi->param('year') =~ /^(\d{4})$/ or die "illegal year";
+ my $year = $1;
+
+ $cgi->param('payname') =~ /^(.{0,80})$/ or die "illegal payname";
+ my $payname = $1;
+
+ $cgi->param('address1') =~ /^(.{0,80})$/ or die "illegal address1";
+ my $address1 = $1;
+
+ $cgi->param('address2') =~ /^(.{0,80})$/ or die "illegal address2";
+ my $address2 = $1;
+
+ $cgi->param('city') =~ /^(.{0,80})$/ or die "illegal city";
+ my $city = $1;
+
+ $cgi->param('state') =~ /^(.{2})$/ or die "illegal state";
+ my $state = $1;
+
+ $cgi->param('zip') =~ /^(.{0,10})$/ or die "illegal zip";
+ my $zip = $1;
+
+ my $save = 0;
+ $save = 1 if $cgi->param('save');
+
+ my $auto = 0;
+ $auto = 1 if $cgi->param('auto');
+
+ $cgi->param('paybatch') =~ /^([\w\-\.]+)$/ or die "illegal paybatch";
+ my $paybatch = $1;
+
+ process_payment(
+ 'session_id' => $session_id,
+ 'amount' => $amount,
+ 'payinfo' => $payinfo,
+ 'month' => $month,
+ 'year' => $year,
+ 'payname' => $payname,
+ 'address1' => $address1,
+ 'address2' => $address2,
+ 'city' => $city,
+ 'state' => $state,
+ 'zip' => $zip,
+ 'save' => $save,
+ 'auto' => $auto,
+ 'paybatch' => $paybatch,
+ );
+
+}
+
+sub recharge_prepay {
+ customer_info( 'session_id' => $session_id );
+}
+
+sub recharge_results {
+
+ my $prepaid_cardnum = $cgi->param('prepaid_cardnum');
+ $prepaid_cardnum =~ s/\W//g;
+ $prepaid_cardnum =~ /^(\w*)$/ or die "illegal prepaid card number";
+ $prepaid_cardnum = $1;
+
+ process_prepay ( 'session_id' => $session_id,
+ 'prepaid_cardnum' => $prepaid_cardnum,
+ );
+}
+
+sub logout {
+ FS::SelfService::logout( 'session_id' => $session_id );
+}
+
+sub provision {
+ my $result = list_pkgs( 'session_id' => $session_id );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+ $result;
+}
+
+sub provision_svc {
+
+ my $result = part_svc_info(
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart ),
+ );
+ die $result->{'error'} if exists $result->{'error'} && $result->{'error'};
+
+ $result->{'svcdb'} =~ /^svc_(.*)$/
+ #or return { 'error' => 'Unknown svcdb '. $result->{'svcdb'} };
+ or die 'Unknown svcdb '. $result->{'svcdb'};
+ $action .= "_$1";
+
+ $result;
+}
+
+sub process_svc_acct {
+
+ my $result = provision_acct (
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw(
+ pkgnum svcpart username _password _password2 sec_phrase popnum )
+ );
+
+ if ( exists $result->{'error'} && $result->{'error'} ) {
+ #warn "$result $result->{'error'}";
+ $action = 'provision_svc_acct';
+ return {
+ $cgi->Vars,
+ %{ part_svc_info( 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
+ )
+ },
+ 'error' => $result->{'error'},
+ };
+ } else {
+ #warn "$result $result->{'error'}";
+ return $result;
+ }
+
+}
+
+sub process_svc_external {
+ provision_external (
+ 'session_id' => $session_id,
+ map { $_ => $cgi->param($_) } qw( pkgnum svcpart )
+ );
+}
+
+sub delete_svc {
+ unprovision_svc(
+ 'session_id' => $session_id,
+ 'svcnum' => $cgi->param('svcnum'),
+ );
+}
+
+#--
+
+sub do_template {
+ my $name = shift;
+ my $fill_in = shift;
+
+ $cgi->delete_all();
+ $fill_in->{'selfurl'} = $cgi->self_url;
+ $fill_in->{'cgi'} = \$cgi;
+
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => "$template_dir/$name.html",
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1, )
+ or die $Text::Template::ERROR;
+
+ print $cgi->header( '-expires' => 'now' ),
+ $template->fill_in( PACKAGE => 'FS::SelfService::_selfservicecgi',
+ HASH => $fill_in
+ );
+}
+
+#*FS::SelfService::_selfservicecgi::include = \&Text::Template::fill_in_file;
+
+package FS::SelfService::_selfservicecgi;
+
+#use FS::SelfService qw(regionselector expselect popselector);
+use HTML::Entities;
+use FS::SelfService qw(popselector);
+
+#false laziness w/agent.cgi
+sub include {
+ my $name = shift;
+ my $template = new Text::Template( TYPE => 'FILE',
+ SOURCE => "$main::template_dir/$name.html",
+ DELIMITERS => [ '<%=', '%>' ],
+ UNTAINT => 1,
+ )
+ or die $Text::Template::ERROR;
+
+ $template->fill_in( PACKAGE => 'FS::SelfService::_selfservicecgi',
+ #HASH => $fill_in
+ );
+
+}
+
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-agentselect.html b/fs_selfservice/FS-SelfService/cgi/signup-agentselect.html
new file mode 100755
index 000000000..7851c5601
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-agentselect.html
@@ -0,0 +1,195 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Agent <SELECT NAME="agentnum">
+<%=
+ warn $init_data;
+ warn $init_data->{'agent'};
+ foreach my $agent ( @{$init_data->{'agent'}} ) {
+ $OUT .= '<OPTION VALUE="'. $agent->{'agentnum'}. '"';
+ $OUT .= ' SELECTED' if $agent->{'agentnum'} eq $agentnum;
+ $OUT .= '>'. $agent->{'agent'};
+ }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">(none)
+
+ <%=
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $package->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup" >
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-alternate.html b/fs_selfservice/FS-SelfService/cgi/signup-alternate.html
new file mode 100755
index 000000000..490cefa5e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-alternate.html
@@ -0,0 +1,218 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="dummy">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="3">
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD><SELECT NAME="state" SIZE="1">
+
+ <%=
+ foreach ( @{$locales} ) {
+ my $value = $_->{'state'};
+ $value .= ' ('. $_->{'county'}. ')' if $_->{'county'};
+ $value .= ' / '. $_->{'country'};
+
+ $OUT .= qq(<OPTION VALUE="$value");
+ $OUT .= ' SELECTED' if ( $state eq $_->{'state'}
+ && $county eq $_->{'county'}
+ && $country eq $_->{'country'}
+ );
+ $OUT .= ">$value</OPTION>";
+ }
+ %>
+
+ </SELECT></TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+
+<BR><BR>
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Username</TH>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Password</TH>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Re-enter Password</TH>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+
+<%= if ( $init_data->{'security_phrase'} ) {
+ <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+
+<%= if ( scalar(@$pops) ) {
+ '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ popselector($popnum);
+ }
+%>
+
+</TABLE><font color="#ff0000">*</font> required fields
+
+<BR><BR>First package
+
+ <%= use Tie::IxHash;
+ my %pkgpart2payby = map { $_->{pkgpart} => $_->{payby}[0] } @{$packages};
+ tie my %options, 'Tie::IxHash',
+ '' => '(none)',
+ map { $_->{pkgpart} => $_->{pkg} }
+ sort { $a->{recur} <=> $b->{recur} }
+ @{$packages}
+ ;
+
+ use HTML::Widgets::SelectLayers 0.02;
+ my @form_text = qw( magic ref ss agentnum
+ last first company address1 address2
+ city zip daytime night fax
+ username _password _password2 sec_phrase );
+ my @form_select = qw( state ); #county country
+ if ( scalar(@$pops) == 0 or scalar(@$pops) == 1 ) {
+ push @form_text, 'popnum',
+ } else {
+ push @form_select, 'popnum',
+ }
+ my $widget = new HTML::Widgets::SelectLayers(
+ options => \%options,
+ selected_layer => $pkgpart,
+ form_name => 'dummy',
+ form_action => $self_url,
+ form_text => \@form_text,
+ form_select => \@form_select,
+ layer_callback => sub {
+ my $layer = shift;
+ my $html = qq( <INPUT TYPE="hidden" NAME="pkgpart" VALUE="$layer">);
+
+ if ( $pkgpart2payby{$layer} eq 'BILL' ) {
+ $html .= <<ENDOUT;
+<INPUT TYPE="hidden" NAME="payby" VALUE="BILL">
+<INPUT TYPE="hidden" NAME="invoicing_list_POST" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_payinfo" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_month" VALUE="12">
+<INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">
+<INPUT TYPE="hidden" NAME="BILL_payname" VALUE="">
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+ENDOUT
+ } elsif ( $pkgpart2payby{$layer} eq 'CARD' ) {
+ my $postal_checked = '';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $postal_checked = 'CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+
+ $invoicing_list= join(', ', grep { $_ ne 'POST' } @invoicing_list );
+
+ my $expselect = expselect("CARD", $paydate);
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected =
+ $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .=
+ qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ $html .= <<ENDOUT;
+<INPUT TYPE="hidden" NAME="payby" VALUE="CARD">
+<BR><BR>Billing information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0>
+<INPUT TYPE="hidden" NAME="invoicing_list_POST" VALUE="">
+<TR>
+ <TD ALIGN="right">Email statement to </TD>
+ <TD><INPUT TYPE="text" NAME="invoicing_list" VALUE="$invoicing_list"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Credit card type</TH>
+ <TD>$cardselect</TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Card number</TH>
+ <TD><INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>*</font>Exp</TH>
+ <TD>$expselect</TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Name on card</TH>
+ <TD><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname"></TD>
+</TR>
+</TABLE>
+<font color="#ff0000">*</font> required fields
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+ENDOUT
+ } else {
+ $html = <<ENDOUT;
+<BR>Please select a package.<BR>
+ENDOUT
+
+ }
+
+ $html;
+
+ },
+ );
+
+ $widget->html;
+
+
+ %>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-billaddress.html b/fs_selfservice/FS-SelfService/cgi/signup-billaddress.html
new file mode 100755
index 000000000..3cf9d2505
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-billaddress.html
@@ -0,0 +1,307 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+ $OUT .= '<OPTION VALUE="">' unless $refnum;
+ foreach my $part_referral ( @{$init_data->{'part_referral'}} ) {
+ $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+ $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+ $OUT .= '>'. $part_referral->{'referral'};
+ }
+%>
+</SELECT><BR><BR>
+Billing Address (where credit card statement is sent)
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Exact name on card<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>" onChange="changed(this)">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>" onChange="changed(this)"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country, '', 'changed(this)' );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12 onChange="changed(this)"></TD>
+</TR>
+</TABLE>
+
+<SCRIPT>
+function changed(what) {
+ what.form.same.checked = false;
+}
+function samechanged(what) {
+ if ( what.checked ) {
+
+ <%= foreach (qw(
+ last first company address1 address2 city zip daytime night fax
+ )) {
+ $OUT .= "what.form.ship_$_.value = what.form.$_.value;\n";
+ }
+ %>
+
+ what.form.ship_country.selectedIndex = what.form.country.selectedIndex;
+ ship_country_changed(what.form.ship_country);
+ what.form.ship_state.selectedIndex = what.form.state.selectedIndex;
+ ship_state_changed(what.form.ship_state);
+ what.form.ship_county.selectedIndex = what.form.county.selectedIndex;
+ }
+}
+</SCRIPT>
+
+<BR><BR>
+Service Address
+(<INPUT TYPE="checkbox" NAME="same" VALUE="Y" onClick="samechanged(this)" <%= $same eq 'Y' ? 'CHECKED' : '' %>>same as billing address)<BR>
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_last" VALUE="<%= $ship_last %>" onChange="changed(this)">,
+ <INPUT TYPE="text" NAME="ship_first" VALUE="<%= $ship_first %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_company" SIZE=70 VALUE="<%= $ship_company %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_address1" SIZE=70 VALUE="<%= $ship_address1 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_address2" SIZE=70 VALUE="<%= $ship_address2 %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="ship_city" VALUE="<%= $ship_city %>" onChange="changed(this)"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($ship_county_html, $ship_state_html, $ship_country_html) =
+ regionselector( $ship_county,
+ $ship_state,
+ $ship_country,
+ 'ship_',
+ 'changed(this)',
+ );
+
+ "$ship_county_html $ship_state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="ship_zip" SIZE=10 VALUE="<%= $ship_zip %>" onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $ship_country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_daytime" VALUE="<%= $ship_daytime %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_night" VALUE="<%= $ship_night %>" SIZE=18 onChange="changed(this)"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="ship_fax" VALUE="<%= $ship_fax %>" SIZE=12 onChange="changed(this)"></TD>
+</TR>
+</TABLE>
+
+<font color="#ff0000">*</font> required fields<BR>
+
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate), #. qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<INPUT TYPE="hidden" NAME="promo_code" VALUE="<%= $cgi->param('promo_code') %>">
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart">
+
+ <%=
+ $OUT .= '<OPTION VALUE="">(none)' unless scalar(@$packages) == 1;
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED'
+ if ( $pkgpart && $package->{'pkgpart'} == $pkgpart )
+ || scalar(@$packages) == 1;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-freeoption.html b/fs_selfservice/FS-SelfService/cgi/signup-freeoption.html
new file mode 100755
index 000000000..40ad03c0b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-freeoption.html
@@ -0,0 +1,262 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+ $OUT .= '<OPTION VALUE="">' unless $refnum;
+ foreach my $part_referral ( @{$init_data->{'part_referral'}} ) {
+ $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+ $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+ $OUT .= '>'. $part_referral->{'referral'};
+ }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>
+<%=
+ my $first_payby = $packages->[0]{'payby'}[0];
+ unless ( grep { scalar( @{$_->{'payby'}} ) > 1
+ || $_->{'payby'}->[0] ne $first_payby
+ } @$packages
+ ) {
+ @payby = ( $first_payby );
+ }
+
+ unless ( scalar(@payby) == 1 && $payby[0] eq 'BILL' ) {
+
+ $OUT .= ' Billing information
+ <TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+ <TR><TD>
+ <INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+
+ my @invoicing_list = split(', ', $invoicing_list );
+
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+
+ $OUT .= '> Postal mail invoice
+ </TD></TR>
+ <TR><TD>Email invoice
+ <INPUT TYPE="text" NAME="invoicing_list" VALUE="'
+ .join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ).
+ '"></TD></TR>';
+
+ $OUT .= '<TR><TD>Billing type</TD></TR>'
+ if scalar(@payby) > 1;
+
+ $OUT .= '</TABLE>';
+
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="invoicing_list" VALUE="">
+ <INPUT TYPE="hidden" NAME="invoicing_list_POST" VALUE="">';
+ }
+
+%>
+
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => <<'END',
+<INPUT TYPE="hidden" NAME="BILL_payinfo" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_month" VALUE="12">
+<INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">
+<INPUT TYPE="hidden" NAME="BILL_payname" VALUE="">
+END
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => <<'END',
+<INPUT TYPE="hidden" NAME="BILL_payinfo" VALUE="">
+<INPUT TYPE="hidden" NAME="BILL_month" VALUE="12">
+<INPUT TYPE="hidden" NAME="BILL_year" VALUE="2037">
+<INPUT TYPE="hidden" NAME="BILL_payname" VALUE="">
+END
+
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE>
+<%= unless ( scalar(@payby) == 1 && $payby[0] eq 'BILL' ) {
+ $OUT .= '<font color="#ff0000">*</font> required fields for each billing type';
+ }
+ '';
+%>
+<BR><BR>First package
+<INPUT TYPE="hidden" NAME="promo_code" VALUE="<%= $cgi->param('promo_code') %>"><TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart">
+
+ <%=
+ $OUT .= '<OPTION VALUE="">(none)' unless scalar(@$packages) == 1;
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED'
+ if ( $pkgpart && $package->{'pkgpart'} == $pkgpart )
+ || scalar(@$packages) == 1;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup-snarf.html b/fs_selfservice/FS-SelfService/cgi/signup-snarf.html
new file mode 100755
index 000000000..d167efbf9
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup-snarf.html
@@ -0,0 +1,228 @@
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">(none)
+
+ <%=
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $package->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR>Enter up to ten external accounts from which to retrieve email
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="left">Mail server</TH>
+ <TH ALIGN="left">Username</TH>
+ <TH ALIGN="left">Password</TH>
+</TR>
+<%=
+ for my $num ( 1..10 ) {
+ no strict 'vars';
+ $OUT .= qq!<TR><TD><INPUT TYPE="text" NAME="snarf_machine$num" VALUE="${"snarf_machine$num"}"></TD>!.
+ qq!<INPUT TYPE="hidden" NAME="snarf_protocol$num" VALUE="pop3">!.
+ qq!<TD><INPUT TYPE="text" NAME="snarf_username$num" VALUE="${"snarf_username$num"}"></TD>!.
+ qq!<TD><INPUT TYPE="password" NAME="snarf_password$num" VALUE="${"snarf_password$num"}"></TD>!.
+ qq!</TR>!;
+ }
+%>
+</TABLE>
+
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/signup.cgi b/fs_selfservice/FS-SelfService/cgi/signup.cgi
new file mode 100755
index 000000000..d2ad0d64b
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup.cgi
@@ -0,0 +1,333 @@
+#!/usr/bin/perl -T
+#!/usr/bin/perl -Tw
+#
+# $Id: signup.cgi,v 1.2 2005-03-12 14:35:12 ivan Exp $
+
+use strict;
+use vars qw( @payby $cgi $init_data
+ $self_url $error $agentnum
+
+ $ieak_file $ieak_template
+ $signup_html $signup_template
+ $success_html $success_template
+ $decline_html $decline_template
+ );
+
+use subs qw( print_form print_okay print_decline
+ success_default decline_default
+ );
+use CGI;
+#use CGI::Carp qw(fatalsToBrowser);
+use Text::Template;
+use Business::CreditCard;
+use HTTP::BrowserDetect;
+use FS::SelfService qw( signup_info new_customer );
+
+#acceptable payment methods
+#
+#@payby = qw( CARD BILL COMP );
+#@payby = qw( CARD BILL );
+#@payby = qw( CARD );
+@payby = qw( CARD PREPAY );
+
+$ieak_file = '/usr/local/freeside/ieak.template';
+$signup_html = -e 'signup.html'
+ ? 'signup.html'
+ : '/usr/local/freeside/signup.html';
+$success_html = -e 'success.html'
+ ? 'success.html'
+ : '/usr/local/freeside/success.html';
+$decline_html = -e 'decline.html'
+ ? 'decline.html'
+ : '/usr/local/freeside/decline.html';
+
+
+if ( -e $ieak_file ) {
+ my $ieak_txt = Text::Template::_load_text($ieak_file)
+ or die $Text::Template::ERROR;
+ $ieak_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $ieak_txt = $1;
+ $ieak_txt =~ s/\r//g; # don't double \r on old templates
+ $ieak_txt =~ s/\n/\r\n/g;
+ $ieak_template = new Text::Template ( TYPE => 'STRING', SOURCE => $ieak_txt )
+ or die $Text::Template::ERROR;
+} else {
+ $ieak_template = '';
+}
+
+$agentnum = '';
+if ( -e $signup_html ) {
+ my $signup_txt = Text::Template::_load_text($signup_html)
+ or die $Text::Template::ERROR;
+ $signup_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $signup_txt = $1;
+ $signup_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => $signup_txt,
+ DELIMITERS => [ '<%=', '%>' ]
+ )
+ or die $Text::Template::ERROR;
+ if ( $signup_txt =~
+ /<\s*INPUT TYPE="?hidden"?\s+NAME="?agentnum"?\s+VALUE="?(\d+)"?\s*>/si
+ ) {
+ $agentnum = $1;
+ }
+} else {
+ #too much maintenance hassle to keep in this file
+ die "can't find ./signup.html or /usr/local/freeside/signup.html";
+ #$signup_template = new Text::Template ( TYPE => 'STRING',
+ # SOURCE => &signup_default,
+ # DELIMITERS => [ '<%=', '%>' ]
+ # )
+ # or die $Text::Template::ERROR;
+}
+
+if ( -e $success_html ) {
+ my $success_txt = Text::Template::_load_text($success_html)
+ or die $Text::Template::ERROR;
+ $success_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $success_txt = $1;
+ $success_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => $success_txt,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+} else {
+ $success_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => &success_default,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+}
+
+if ( -e $decline_html ) {
+ my $decline_txt = Text::Template::_load_text($decline_html)
+ or die $Text::Template::ERROR;
+ $decline_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
+ $decline_txt = $1;
+ $decline_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => $decline_txt,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+} else {
+ $decline_template = new Text::Template ( TYPE => 'STRING',
+ SOURCE => &decline_default,
+ DELIMITERS => [ '<%=', '%>' ],
+ )
+ or die $Text::Template::ERROR;
+}
+
+$cgi = new CGI;
+
+$init_data = signup_info( 'agentnum' => $agentnum,
+ 'promo_code' => scalar($cgi->param('promo_code')),
+ 'reg_code' => uc(scalar($cgi->param('reg_code'))),
+ );
+
+if ( ( defined($cgi->param('magic')) && $cgi->param('magic') eq 'process' )
+ || ( defined($cgi->param('action')) && $cgi->param('action') eq 'process_signup' )
+ ) {
+
+ $error = '';
+
+ $cgi->param('agentnum', $agentnum) if $agentnum;
+ $cgi->param('reg_code', uc(scalar($cgi->param('reg_code'))) );
+
+ #false laziness w/agent.cgi, identical except for agentnum
+ my $payby = $cgi->param('payby');
+ if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) {
+ #$payinfo = join('@', map { $cgi->param( $payby. "_payinfo$_" ) } (1,2) );
+ $cgi->param('payinfo' => $cgi->param($payby. '_payinfo1'). '@'.
+ $cgi->param($payby. '_payinfo2')
+ );
+ } else {
+ $cgi->param('payinfo' => $cgi->param( $payby. '_payinfo' ) );
+ }
+ $cgi->param('paydate' => $cgi->param( $payby. '_month' ). '-'.
+ $cgi->param( $payby. '_year' )
+ );
+ $cgi->param('payname' => $cgi->param( $payby. '_payname' ) );
+ $cgi->param('paycvv' => defined $cgi->param( $payby. '_paycvv' )
+ ? $cgi->param( $payby. '_paycvv' )
+ : ''
+ );
+
+ if ( $cgi->param('invoicing_list') ) {
+ $cgi->param('invoicing_list' => $cgi->param('invoicing_list'). ', POST')
+ if $cgi->param('invoicing_list_POST');
+ } else {
+ $cgi->param('invoicing_list' => 'POST' );
+ }
+
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ $error = $init_data->{msgcat}{passwords_dont_match}; #msgcat
+ $cgi->param('_password', '');
+ $cgi->param('_password2', '');
+ }
+
+ if ( $payby =~ /^(CARD|DCRD)$/ && $cgi->param('CARD_type') ) {
+ my $payinfo = $cgi->param('payinfo');
+ $payinfo =~ s/\D//g;
+
+ $payinfo =~ /^(\d{13,16})$/
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ $payinfo = $1;
+ validate($payinfo)
+ or $error ||= $init_data->{msgcat}{invalid_card}; #. $self->payinfo;
+ cardtype($payinfo) eq $cgi->param('CARD_type')
+ or $error ||= $init_data->{msgcat}{not_a}. $cgi->param('CARD_type');
+ }
+
+ unless ( $error ) {
+ my $rv = new_customer( {
+ map { $_ => scalar($cgi->param($_)) }
+ qw( last first ss company
+ address1 address2 city county state zip country
+ daytime night fax
+
+ ship_last ship_first ship_company
+ ship_address1 ship_address2 ship_city ship_county ship_state
+ ship_zip ship_country
+ ship_daytime ship_night ship_fax
+
+ payby payinfo paycvv paydate payname invoicing_list
+ referral_custnum promo_code reg_code
+ pkgpart username sec_phrase _password popnum refnum
+ agentnum
+ ),
+ grep { /^snarf_/ } $cgi->param
+ } );
+ $error = $rv->{'error'};
+ }
+ #eslaf
+
+ if ( $error eq '_decline' ) {
+ print_decline();
+ } elsif ( $error ) {
+ #fudge the snarf info
+ no strict 'refs';
+ ${$_} = $cgi->param($_) foreach grep { /^snarf_/ } $cgi->param;
+ print_form();
+ } else {
+ print_okay(
+ 'pkgpart' => scalar($cgi->param('pkgpart')),
+ );
+ }
+
+} else {
+ $error = '';
+ print_form;
+}
+
+sub print_form {
+
+ $error = "Error: $error" if $error;
+
+ my $r = {
+ $cgi->Vars,
+ %{$init_data},
+ 'error' => $error,
+ };
+
+ $r->{referral_custnum} = $r->{'ref'};
+ #$cgi->delete('ref');
+ #$cgi->delete('init_popstate');
+ $r->{self_url} = $cgi->self_url;
+
+ print $cgi->header( '-expires' => 'now' ),
+ $signup_template->fill_in( PACKAGE => 'FS::SelfService::_signupcgi',
+ HASH => $r
+ );
+}
+
+sub print_decline {
+ print $cgi->header( '-expires' => 'now' ),
+ $decline_template->fill_in();
+}
+
+sub print_okay {
+ my %param = @_;
+ my $user_agent = new HTTP::BrowserDetect $ENV{HTTP_USER_AGENT};
+
+ $cgi->param('username') =~ /^(.+)$/
+ or die "fatal: invalid username got past FS::SelfService::new_customer";
+ my $username = $1;
+ $cgi->param('_password') =~ /^(.+)$/
+ or die "fatal: invalid password got past FS::SelfService::new_customer";
+ my $password = $1;
+ ( $cgi->param('first'). ' '. $cgi->param('last') ) =~ /^(.*)$/
+ or die "fatal: invalid email_name got past FS::SelfService::new_customer";
+ my $email_name = $1; #global for template
+
+ #my %pop = ();
+ my %popnum2pop = ();
+ foreach ( @{ $init_data->{'svc_acct_pop'} } ) {
+ #push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_;
+ $popnum2pop{$_->{popnum}} = $_;
+ }
+
+ my( $ac, $exch, $loc);
+ my $pop = $popnum2pop{$cgi->param('popnum')};
+ #or die "fatal: invalid popnum got past FS::SelfService::new_customer";
+ if ( $pop ) {
+ ( $ac, $exch, $loc ) = ( $pop->{'ac'}, $pop->{'exch'}, $pop->{'loc'} );
+ } else {
+ ( $ac, $exch, $loc ) = ( '', '', ''); #presumably you're not using them.
+ }
+
+ #global for template
+ my $pkg = ( grep { $_->{'pkgpart'} eq $param{'pkgpart'} }
+ @{ $init_data->{'part_pkg'} }
+ )[0]->{'pkg'};
+
+ if ( $ieak_template && $user_agent->windows && $user_agent->ie ) {
+ #send an IEAK config
+ print $cgi->header('application/x-Internet-signup'),
+ $ieak_template->fill_in();
+ } else { #send a simple confirmation
+ print $cgi->header( '-expires' => 'now' ),
+ $success_template->fill_in( HASH => {
+ username => $username,
+ password => $password,
+ _password => $password,
+ email_name => $email_name,
+ ac => $ac,
+ exch => $exch,
+ loc => $loc,
+ pkg => $pkg,
+ });
+ }
+}
+
+sub success_default { #html to use if you don't specify a success file
+ <<'END';
+<HTML><HEAD><TITLE>Signup successful</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Signup successful</FONT><BR><BR>
+Thanks for signing up!
+<BR><BR>
+Signup information for <%= $email_name %>:
+<BR><BR>
+Username: <%= $username %><BR>
+Password: <%= $password %><BR>
+Access number: (<%= $ac %>) / <%= $exch %> - <%= $local %><BR>
+Package: <%= $pkg %><BR>
+</BODY></HTML>
+END
+}
+
+sub decline_default { #html to use if there is a decline
+ <<'END';
+<HTML><HEAD><TITLE>Processing error</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Processing error</FONT><BR><BR>
+There has been an error processing your account. Please contact customer
+support.
+</BODY></HTML>
+END
+}
+
+# subs for the templates...
+
+package FS::SelfService::_signupcgi;
+use HTML::Entities;
+use FS::SelfService qw(regionselector expselect popselector);
+
diff --git a/fs_selfservice/FS-SelfService/cgi/signup.html b/fs_selfservice/FS-SelfService/cgi/signup.html
new file mode 100755
index 000000000..2ab07b37e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/signup.html
@@ -0,0 +1,236 @@
+<HTML><HEAD><TITLE><%= $agent || 'ISP' %> Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7><%= $agent || 'ISP' %> Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM NAME="OneTrueForm" ACTION="<%= $self_url %>" METHOD=POST onSubmit="document.OneTrueForm.signup.disabled=true">
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_signup">
+<INPUT TYPE="hidden" NAME="referral_custnum" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+ $OUT .= '<OPTION VALUE="">' unless $refnum;
+ foreach my $part_referral ( @part_referral ) {
+ $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+ $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+ $OUT .= '>'. $part_referral->{'referral'};
+ }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">&nbsp;</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( {
+ selected_county => $county,
+ selected_state => $state,
+ selected_country => $country,
+ default_state => $statedefault,
+ default_country => $countrydefault,
+ locales => \@cust_main_county,
+ } );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $CARD_type eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $cvv_enabled ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $cvv_enabled ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2&nbsp;(<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>)&nbsp;<INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<INPUT TYPE="hidden" NAME="promo_code" VALUE="<%= $promo_code %>">
+<INPUT TYPE="hidden" NAME="reg_code" VALUE="<%= $reg_code %>">
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart">
+
+ <%=
+ $OUT .= '<OPTION VALUE="">(none)' unless scalar(@part_pkg ) ==1;
+ foreach my $part_pkg ( @part_pkg ) {
+ $OUT .= '<OPTION VALUE="'. $part_pkg->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $part_pkg->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $part_pkg->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $_password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $_password2 %>"></TD>
+</TR>
+<%=
+ if ( $security_phrase ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( @svc_acct_pop ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector( 'popnum' => $popnum,
+ 'pops' => \@svc_acct_pop,
+ 'init_popstate' => $init_popstate,
+ 'popac' => $popac,
+ 'acstate' => $acstate,
+ ).
+ '</TD></TR>';
+ } else {
+ $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" NAME="signup" VALUE="Signup">
+</FORM></BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/stateselect.html b/fs_selfservice/FS-SelfService/cgi/stateselect.html
new file mode 100644
index 000000000..ba55bff74
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/stateselect.html
@@ -0,0 +1,134 @@
+<HTML><HEAD><TITLE>ISP Signup</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup - state selection</FONT><BR><BR>
+<SCRIPT>
+function gotoURL(object) {
+ window.location.href = object.options[object.selectedIndex].value;
+}
+</SCRIPT>
+<FORM>
+Select your state from the map or dropdown:
+<MAP NAME=usmap>
+<area shape=poly COORDS="264,157,286,155,292,193,276,195,270,199,264,157" href="signup.cgi?init_popstate=AL">
+<area shape=poly COORDS="28,197,46,185,72,199,72,241,88,243,102,261,92,263,70,241,42,243,28,257,12,259,34,243,20,233,16,223,34,215,22,207,30,205,28,197" href="../states/Alaska.html">
+<area shape=poly COORDS="70,137,106,137,100,189,84,187,60,173,70,133,70,137,70,137" href="signup.cgi?init_popstate=AZ">
+<area shape=poly COORDS="250,153,242,179,220,177,218,171,216,145,252,143,250,155,250,153" href="signup.cgi?init_popstate=AR">
+<area shape=poly COORDS="10,79,38,81,30,109,62,151,56,173,40,169,20,145,4,101,10,75,26,79,10,79,10,79" href="signup.cgi?init_popstate=CA">
+<area shape=poly COORDS="108,103,158,107,154,141,104,137,110,101,128,103,108,103" href="signup.cgi?init_popstate=CO">
+<area shape=poly COORDS="374,107,405,105,405,123,372,125,374,107" href="signup.cgi?init_popstate=CT">
+<area shape=poly COORDS="370,143,402,145,405,157,362,157,370,143" href="signup.cgi?init_popstate=DE">
+<area shape=poly COORDS="275,193,325,187,327,197,341,219,341,233,335,237,317,215,315,205,307,195,293,203,275,193" href="signup.cgi?init_popstate=FL">
+<area shape=poly COORDS="297,153,283,155,297,191,321,189,321,169,297,153" href="signup.cgi?init_popstate=GA">
+<area shape=poly COORDS="98,233,142,263,156,251,162,239,164,229,136,231,94,221,100,235,98,233" href="signup.cgi?init_popstate=HI">
+<area shape=poly COORDS="68,21,76,21,72,35,80,47,80,55,84,65,100,69,94,93,56,83,66,51,70,19,68,21" href="signup.cgi?init_popstate=ID">
+<area shape=poly COORDS="242,91,258,89,266,123,256,139,234,109,248,87,242,91" href="signup.cgi?init_popstate=IL">
+<area shape=poly COORDS="261,95,265,123,265,131,285,117,277,91,261,95" href="signup.cgi?init_popstate=IN">
+<area shape=poly COORDS="198,87,206,111,232,109,240,99,240,91,232,79,198,87" href="signup.cgi?init_popstate=IA">
+<area shape=poly COORDS="158,111,158,135,214,139,214,127,208,113,158,111" href="signup.cgi?init_popstate=KS">
+<area shape=poly COORDS="263,133,275,129,289,115,303,121,307,129,299,135,251,141,269,131,263,133" href="signup.cgi?init_popstate=KY">
+<area shape=poly COORDS="222,179,246,179,244,197,258,193,262,213,226,209,224,177,222,179" href="signup.cgi?init_popstate=LA">
+<area shape=poly COORDS="363,37,373,59,373,47,387,31,377,9,365,15,363,37" href="signup.cgi?init_popstate=ME">
+<area shape=poly COORDS="376,159,405,159,405,175,374,177,376,159" href="signup.cgi?init_popstate=MD">
+<area shape=poly COORDS="378,74,380,88,404,88,404,72,378,74" href="signup.cgi?init_popstate=MA">
+<area shape=poly COORDS="265,73,269,83,265,93,293,91,295,71,281,53,271,53,267,69,265,73,265,73" href="signup.cgi?init_popstate=MI">
+<area shape=poly COORDS="194,31,222,33,242,35,224,51,222,63,222,73,234,79,196,85,194,31" href="signup.cgi?init_popstate=MN">
+<area shape=poly COORDS="265,159,271,199,257,201,259,195,241,197,251,155,265,159" href="signup.cgi?init_popstate=MS">
+<area shape=poly COORDS="206,113,234,111,256,139,248,147,214,145,208,111,206,113" href="signup.cgi?init_popstate=MO">
+<area shape=poly COORDS="78,23,148,31,146,67,84,63,78,35,80,19,78,23" href="signup.cgi?init_popstate=MT">
+<area shape=poly COORDS="146,85,148,103,158,105,164,109,206,109,198,85,144,87,146,85" href="signup.cgi?init_popstate=NE">
+<area shape=poly COORDS="40,83,76,87,64,151,32,109,40,83,40,83" href="signup.cgi?init_popstate=NV">
+<area shape=poly COORDS="298,11,330,9,330,25,298,25,298,11" href="signup.cgi?init_popstate=NH">
+<area shape=poly COORDS="372,127,404,125,405,141,368,139,376,125,372,127" href="signup.cgi?init_popstate=NJ">
+<area shape=poly COORDS="106,137,100,191,122,187,148,187,150,139,106,137,106,137" href="signup.cgi?init_popstate=NM">
+<area shape=poly COORDS="313,79,331,63,337,45,349,45,359,65,357,79,345,65,315,77,313,79,313,79" href="signup.cgi?init_popstate=NY">
+<area shape=poly COORDS="309,137,295,151,319,149,337,153,357,131,351,129,309,137,309,137" href="signup.cgi?init_popstate=NC">
+<area shape=poly COORDS="146,31,148,57,198,57,190,31,146,31,146,31" href="signup.cgi?init_popstate=ND">
+<area shape=poly COORDS="281,93,285,113,299,121,311,101,309,85,299,93,281,93,281,93" href="signup.cgi?init_popstate=OH">
+<area shape=poly COORDS="148,145,174,145,174,163,218,171,216,143,150,139,150,145,156,143,148,145,148,145" href="signup.cgi?init_popstate=OK">
+<area shape=poly COORDS="20,41,8,73,16,77,22,77,28,77,36,79,42,81,48,83,56,83,66,49,20,41,20,41" href="signup.cgi?init_popstate=OR">
+<area shape=poly COORDS="309,83,345,71,351,93,313,105,309,83,309,83" href="signup.cgi?init_popstate=PA">
+<area shape=poly COORDS="376,93,405,93,405,107,376,105,376,93" href="signup.cgi?init_popstate=RI">
+<area shape=poly COORDS="301,155,321,149,337,155,325,175,301,157,301,155,301,155" href="signup.cgi?init_popstate=SC">
+<area shape=poly COORDS="146,59,198,61,198,83,146,83,148,57,146,59,146,59" href="signup.cgi?init_popstate=SD">
+<area shape=poly COORDS="255,145,251,157,297,153,311,133,255,145,255,145" href="signup.cgi?init_popstate=TN">
+<area shape=poly COORDS="150,145,172,145,174,167,198,173,218,173,228,207,204,221,198,231,202,247,180,241,154,207,146,219,120,189,154,189,152,145,150,145,150,145" href="signup.cgi?init_popstate=TX">
+<area shape=poly COORDS="78,89,96,91,96,103,110,103,106,135,70,133,78,89,78,89" href="signup.cgi?init_popstate=UT">
+<area shape=poly COORDS="298,29,332,29,332,47,294,45,298,29" href="signup.cgi?init_popstate=VT">
+<area shape=poly COORDS="307,127,297,137,351,127,349,113,341,111,341,105,329,107,315,131,307,127,307,127" href="signup.cgi?init_popstate=VA">
+<area shape=poly COORDS="32,13,68,19,64,47,20,39,20,13,30,19,32,13,32,13" href="signup.cgi?init_popstate=WA">
+<area shape=poly COORDS="303,119,313,129,329,103,311,105,299,121,313,127,303,119,303,119" href="signup.cgi?init_popstate=WV">
+<area shape=poly COORDS="228,51,256,55,254,89,238,89,234,77,224,71,230,49,236,53,228,51,228,51" href="signup.cgi?init_popstate=WI">
+<area shape=poly COORDS="146,71,144,103,96,99,102,63,148,69,146,71,146,71" href="signup.cgi?init_popstate=WY">
+</MAP>
+<IMG SRC="map.gif" usemap=#usmap WIDTH=405 HEIGHT=270 border=0><BR>
+<SELECT NAME="init_popstate" onChange="gotoURL(this.form.init_popstate)">
+<OPTION VALUE="stateselect.html"></OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AL">Alabama</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AK">Alaska</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=AS">American Samoa</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=AZ">Arizona</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AR">Arkansas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=CA">California</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=CO">Colorado</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=CT">Connecticut</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=DE">Delaware</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=DC">District of Columbia</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=FM">Federated States of Micronesia</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=FL">Florida</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=GA">Georgia</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=GU">Guam</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=HI">Hawaii</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=ID">Idaho</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=IL">Illinois</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=IN">Indiana</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=IA">Iowa</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=KS">Kansas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=KY">Kentucky</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=LA">Louisiana</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=ME">Maine</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=MH">Marshall Islands</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=MD">Maryland</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MA">Massachusetts</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MI">Michigan</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MN">Minnesota</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MS">Mississippi</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MO">Missouri</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=MT">Montana</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NE">Nebraska</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NV">Nevada</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NH">New Hampshire</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NJ">New Jersey</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NM">New Mexico</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NY">New York</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=NC">North Carolina</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=ND">North Dakota</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=MP">Northern Mariana Islands</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=OH">Ohio</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=OK">Oklahoma</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=OR">Oregon</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=PW">Palau</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=PA">Pennsylvania</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=PR">Puerto Rico</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=RI">Rhode Island</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=SC">South Carolina</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=SD">South Dakota</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=TN">Tennessee</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=TX">Texas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=UT">Utah</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=VT">Vermont</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=VI">Virgin Islands</OPTION>-->
+<OPTION VALUE="signup.cgi?init_popstate=VA">Virginia</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WA">Washington</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WV">West Virginia</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WI">Wisconsin</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=WY">Wyoming</OPTION>
+<!--<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Africa</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AA">Armed Forces Americas</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Canada</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Europe</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AE">Armed Forces Middle East</OPTION>
+<OPTION VALUE="signup.cgi?init_popstate=AP">Armed Forces Pacific</OPTION>
+-->
+</SELECT>
+</FORM>
+</BODY>
+</HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/success.html b/fs_selfservice/FS-SelfService/cgi/success.html
new file mode 100644
index 000000000..397cc6c30
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/success.html
@@ -0,0 +1,11 @@
+<HTML><HEAD><TITLE>Signup successful</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>Signup successful</FONT><BR><BR>
+Thanks for signing up!
+<BR><BR>
+Signup information for <%= $email_name %>:
+<BR><BR>
+Username: <%= $username %><BR>
+Password: <%= $password %><BR>
+Access number: (<%= $ac %>) / <%= $exch %> - <%= $local %><BR>
+Package: <%= $pkg %><BR>
+</BODY></HTML>
diff --git a/fs_selfservice/FS-SelfService/cgi/svc_acct.html b/fs_selfservice/FS-SelfService/cgi/svc_acct.html
new file mode 100644
index 000000000..abed8786e
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/svc_acct.html
@@ -0,0 +1,55 @@
+<FONT SIZE=4>Setup <%= $svc %></FONT><BR><BR>
+
+<%= if ( $error ) {
+ $OUT .= qq!<FONT SIZE="+1" COLOR="#ff0000">Error setting up $svc: $error!.
+ '</FONT><BR><BR>';
+} ''; %>
+<FORM ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="session" VALUE="<%= $session_id %>">
+<INPUT TYPE="hidden" NAME="action" VALUE="process_svc_acct">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $svcpart %>">
+<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=2 BGCOLOR="#cccccc">
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $_password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $_password2 %>"></TD>
+</TR>
+<%=
+ if ( $security_phrase ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( @svc_acct_pop ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector( 'popnum' => $popnum,
+ 'pops' => \@svc_acct_pop,
+ 'init_popstate' => $init_popstate,
+ 'popac' => $popac,
+ 'acstate' => $acstate,
+ ).
+ '</TD></TR>';
+ } else {
+ $OUT .= popselector(popnum=>$popnum, pops=>\@svc_acct_pop);
+ }
+%>
+</TABLE>
+<INPUT TYPE="submit" VALUE="Setup">
+</FORM>
diff --git a/fs_selfservice/FS-SelfService/cgi/view_customer.html b/fs_selfservice/FS-SelfService/cgi/view_customer.html
new file mode 100644
index 000000000..11e4432d0
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/view_customer.html
@@ -0,0 +1,29 @@
+<HTML><HEAD><TITLE>Reseller</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>Reseller</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_menu') %>
+<TD VALIGN="top">
+
+<%= $message
+ ? "<FONT SIZE=\"+2\"><B>$message</B></FONT><BR><BR>"
+ : ''
+%>
+
+<%= $small_custview %>
+
+<BR>
+
+<TABLE BORDER=0 CELLPADDING=4><TR>
+<%= include('agent_customer_menu') %>
+<TD VALIGN="top">
+
+</TD></TR></TABLE>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
+
+
diff --git a/fs_selfservice/FS-SelfService/cgi/view_invoice.html b/fs_selfservice/FS-SelfService/cgi/view_invoice.html
new file mode 100644
index 000000000..72d061980
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/view_invoice.html
@@ -0,0 +1,17 @@
+<HTML><HEAD><TITLE>MyAccount</TITLE></HEAD>
+<BODY BGCOLOR="#eeeeee"><FONT SIZE=5>MyAccount</FONT><BR><BR>
+<%= $url = "$selfurl?session=$session_id;action="; ''; %>
+<%= include('myaccount_menu') %>
+<TD VALIGN="top">
+
+<FONT SIZE="-1"><PRE>
+<%= $invoice_text %>
+</FONT></PRE>
+
+</TD></TR></TABLE>
+<HR>
+<FONT SIZE="-2">powered by <a href="http://www.sisd.com/freeside">freeside</a></FONT>
+</BODY></HTML>
+
+
+
diff --git a/fs_selfservice/FS-SelfService/freeside-selfservice-clientd b/fs_selfservice/FS-SelfService/freeside-selfservice-clientd
new file mode 100644
index 000000000..ededfa6e9
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/freeside-selfservice-clientd
@@ -0,0 +1,271 @@
+#!/usr/bin/perl -w
+#
+# freeside-selfservice-clientd
+#
+# This is run REMOTELY over ssh by freeside-selfservice-server
+
+use strict;
+use subs qw(spawn logmsg lock_write unlock_write);
+use Fcntl qw(:flock);
+use POSIX qw(:sys_wait_h);
+use Socket;
+use Storable 2.09 qw(nstore_fd fd_retrieve);
+use IO::Handle qw(_IONBF);
+use IO::Select;
+use IO::File;
+
+#STDOUT->setbuf('');
+
+my $tag = scalar(@ARGV) ? '.'.shift : '';
+
+use vars qw( $Debug );
+$Debug = 2; #2 will turn on child logging
+ #3 will log packet contents,#including passwords
+ #4 will log receipts of all packets from server including
+ # keepalives (big!)
+
+my $socket = "/usr/local/freeside/selfservice_socket$tag";
+my $pid_file = "$socket.pid";
+
+my $log_file = "/usr/local/freeside/selfservice$tag.log";
+
+my $lock_file = "/usr/local/freeside/selfservice$tag.writelock";
+
+#my $me = '[client]';
+
+$|=1;
+
+$SIG{__WARN__} = \&_logmsg;
+
+#read data to be cached or something
+#warn "$me Reading init data\n" if $Debug;
+#my $signup_init =
+
+warn "Creating $lock_file\n" if $Debug;
+open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
+close LOCKFILE;
+
+warn "Creating $socket\n" if $Debug;
+my $uaddr = sockaddr_un($socket);
+my $proto = getprotobyname('tcp');
+socket(Server,PF_UNIX,SOCK_STREAM,0) or die "socket: $!";
+unlink($socket);
+bind(Server, $uaddr) or die "bind: $!";
+listen(Server,SOMAXCONN) or die "listen: $!";
+
+if ( -e $pid_file ) {
+ open(PIDFILE,"<$pid_file");
+ my $old_pid = <PIDFILE>;
+ close PIDFILE;
+ $old_pid =~ /^(\d+)$/;
+ kill 'TERM', $1;
+}
+open(PIDFILE,">$pid_file");
+print PIDFILE "$$\n";
+close PIDFILE;
+
+#my $waitedpid;
+#sub REAPER { $waitedpid = wait; $SIG{CHLD} = \&REAPER; }
+#$SIG{CHLD} = \&REAPER;
+
+warn "enabling keep alives\n" if $Debug;
+nstore_fd( { _packet => '_enable_keepalive' } , \*STDOUT );
+
+warn "entering main loop\n" if $Debug;
+
+my %kids;
+
+my $s = new IO::Select;
+$s->add(\*STDIN);
+$s->add(\*Server);
+
+#for ( $waitedpid = 0;
+# accept(Client,Server) || $waitedpid;
+# $waitedpid = 0, close Client)
+#{
+# next if $waitedpid;
+
+#$SIG{PIPE} = sub { warn "SIGPIPE received" };
+#$SIG{CHLD} = sub { warn "SIGCHLD received" };
+
+#sub REAPER { warn "SIGCHLD received"; my $pid = wait; $SIG{CHLD} = \&REAPER; }
+#sub REAPER { my $pid = wait; $SIG{CHLD} = \&REAPER; }
+#sub REAPER { my $pid = wait; delete $kids{$pid}; $SIG{CHLD} = \&REAPER; }
+#$SIG{CHLD} = \&REAPER;
+
+my $undisp = 0;
+while (1) {
+
+ &reap_kids;
+
+ warn "waiting for connection\n" if $Debug && !$undisp;
+
+ #my @handles = $s->can_read();
+ my @handles = $s->can_read(5);
+ $undisp = !scalar(@handles);
+ foreach my $handle ( @handles ) {
+
+ if ( $handle == \*STDIN ) {
+
+ warn "receiving packet from server\n" if $Debug > 3;
+
+ my $packet = fd_retrieve(\*STDIN);
+ my $token = $packet->{'_token'};
+
+ if ( $token eq '_keepalive' ) {
+ $undisp = 1;
+ next;
+ }
+
+ warn "received packet from server with token $token\n".
+ ( $Debug > 2
+ ? join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
+ : '' )
+ if $Debug;
+
+ if ( exists($kids{$token}) ) {
+ warn "sending return packet to $token via $kids{$token}\n"
+ if $Debug;
+ nstore_fd($packet, $kids{$token});
+ warn "flushing to $token\n" if $Debug;
+ until ( $kids{$token}->flush ) {
+ warn "WARNING: error flushing: $!";
+ sleep 1;
+ }
+ #no close or delete here - will block waiting for child
+ warn "done with $token\n" if $Debug;
+ } else {
+ warn "WARNING: unknown token $token, discarding message";
+ }
+
+ } elsif ( $handle == \*Server ) {
+
+ until ( accept(Client, Server) ) {
+ warn "WARNING: accept failed: $!";
+ next;
+ }
+
+ warn "received local connection; forking\n" if $Debug;
+
+ spawn sub { #child
+ warn "[child-$$] reading packet from local client" if $Debug > 1;
+ my $packet = fd_retrieve(\*Client);
+ warn "[child-$$] packet received:\n".
+ join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
+ if $Debug > 2;
+ my $command = $packet->{'command'};
+ #handle some commands weirdly?
+ $packet->{_token}=$$;
+
+ warn "[child-$$] locking write stream\n" if $Debug > 1;
+ lock_write;
+
+ warn "[child-$$] sending packet to remote server\n" if $Debug > 1;
+ nstore_fd($packet, \*STDOUT) or die "FATAL: can't send response: $!";
+
+ warn "[child-$$] flushing write stream\n" if $Debug > 1;
+ STDOUT->flush or die "FATAL: can't flush: $!";
+
+ warn "[child-$$] releasing write lock\n" if $Debug > 1;
+ unlock_write;
+
+ warn "[child-$$] closing write stream\n" if $Debug > 1;
+ close STDOUT or die "FATAL: can't close write stream: $!"; #??!
+
+ warn "[child-$$] waiting for response from parent\n" if $Debug > 1;
+ my $w = new IO::Select;
+ $w->add(\*STDIN);
+ until ( $w->can_read ) {
+ warn "[child-$$] WARNING: interrupted select: $!\n";
+ }
+ my $rv = fd_retrieve(\*STDIN);
+
+ #close STDIN;
+
+ warn "[child-$$] sending response to local client" if $Debug > 1;
+ nstore_fd($rv, \*Client);
+ Client->flush or die "FATAL: can't flush to local client: $!";
+ close Client or die "FATAL: can't close connection to local client: $!";
+
+ warn "[child-$$] child exiting" if $Debug > 1;
+ exit;
+
+ }; #eo child
+
+ #close Client;
+
+ } else {
+ die "wtf? $handle";
+ }
+
+ }
+
+}
+
+sub reap_kids {
+ #warn "reaping kids\n";
+ foreach my $pid ( keys %kids ) {
+ my $kid = waitpid($pid, WNOHANG);
+ if ( $kid > 0 ) {
+ close $kids{$kid};
+ delete $kids{$kid};
+ }
+ }
+ #warn "done reaping\n";
+}
+
+sub spawn {
+ my $coderef = shift;
+
+ unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') {
+ use Carp;
+ confess "usage: spawn CODEREF";
+ }
+
+ my $pid;
+ #if (!defined($pid = fork)) {
+ my $kid = new IO::Handle;
+ if (!defined($pid = open($kid, '|-'))) {
+ warn "WARNING: cannot fork: $!";
+ return;
+ } elsif ($pid) {
+ warn "begat $pid" if $Debug;
+ $kids{$pid} = $kid;
+ #$kids{$pid}->autoflush;
+ return; # I'm the parent
+ }
+ # else I'm the child -- go spawn
+
+# open(STDIN, "<&Client") || die "can't dup client to stdin";
+# open(STDOUT, ">&Client") || die "can't dup client to stdout";
+# open(STDERR, ">&STDOUT") || die "can't dup stdout to stderr";
+ exit &$coderef();
+}
+
+sub _logmsg {
+ chomp( my $msg = shift );
+ my $log = new IO::File ">>$log_file";
+ die "can't open $log_file: $!" unless defined($log);
+ flock($log, LOCK_EX);
+ seek($log, 0, 2);
+ print $log "[client] [". scalar(localtime). "] [$$] $msg\n";
+ flock($log, LOCK_UN);
+ close $log;
+}
+
+sub lock_write {
+ #broken on freebsd?
+ #flock(STDOUT, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+
+ #open a new one for each kid to get a unique lock
+ open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
+
+ flock(LOCKFILE, LOCK_EX) or die "FATAL: can't lock $lock_file: $!";
+}
+
+sub unlock_write {
+ #broken on freebsd?
+ #flock(STDOUT, LOCK_UN) or die "FATAL: can't release write lock: $!";
+
+ flock(LOCKFILE, LOCK_UN) or die "FATAL: can't unlock $lock_file: $!";
+}
diff --git a/fs_selfservice/FS-SelfService/ieak.template b/fs_selfservice/FS-SelfService/ieak.template
new file mode 100755
index 000000000..52edaa951
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/ieak.template
@@ -0,0 +1,40 @@
+[Entry]
+Entry_Name = The Internet
+[Phone]
+Dial_As_Is=no
+Phone_Number = { $exch. $loc }
+Area_Code = { $ac }
+Country_Code = 1
+Country_Id = 1
+[Server]
+Type = PPP
+SW_Compress = Yes
+PW_Encrypt = Yes
+Negotiate_TCP/IP = Yes
+Disable_LCP = No
+[TCP/IP]
+Specify_IP_Address = No
+Specity_Server_Address = No
+IP_Header_Compress = Yes
+Gateway_On_Remote = Yes
+[User]
+Name = { $username }
+Password = { $password }
+Display_Password = Yes
+[Internet_Mail]
+Email_Name = { $email_name }
+Email_Address = { $username }\@domain.tld
+POP_Server = mail.domain.tld
+POP_Server_Port_Number = 110
+POP_Login_Name = { $username }
+POP_Login_Password = { $password }
+SMTP_Server = mail.domain.tld
+SMTP_Server_Port_Number = 25
+Install_Mail = 1
+[Internet_News]
+NNTP_Server = news.domain.tld
+NNTP_Server_Port_Number = 119
+Logon_Required = No
+Install_News = 1
+[Branding]
+Window_Title = The Internet
diff --git a/fs_selfservice/FS-SelfService/test.pl b/fs_selfservice/FS-SelfService/test.pl
new file mode 100644
index 000000000..7468ea471
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/test.pl
@@ -0,0 +1,17 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+#########################
+
+# change 'tests => 1' to 'tests => last_test_to_print';
+
+use Test;
+BEGIN { plan tests => 1 };
+use FS::SelfService;
+ok(1); # If we made it this far, we're ok.
+
+#########################
+
+# Insert your test code below, the Test module is use()ed here so read
+# its man page ( perldoc Test ) for help writing this test script.
+
diff --git a/fs_selfservice/fs_passwd_test b/fs_selfservice/fs_passwd_test
new file mode 100755
index 000000000..4f8b8a888
--- /dev/null
+++ b/fs_selfservice/fs_passwd_test
@@ -0,0 +1,19 @@
+#!/usr/bin/perl -w
+
+use strict;
+use FS::SelfService qw(passwd);
+
+my $rv = passwd(
+ 'username' => 'ivan',
+ 'old_password' => 'heyhoo',
+ 'new_password' => 'haloo',
+);
+my $error = $rv->{error};
+
+if ( $error eq 'Incorrect password.' ) {
+ exit;
+} else {
+ die $error if $error;
+ die "no error";
+}
+