summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorivan <ivan>2002-04-06 20:37:38 +0000
committerivan <ivan>2002-04-06 20:37:38 +0000
commit8fc4118f1f055195d280b3250b39a3b5fcdf13dc (patch)
tree09f1c079662ae602829cfce585d9396658689206
parent195652229909566ccb3a6ae249d8fa26f25da55a (diff)
big signup server cleanups. uses Storable for network protocol now.
- makes Bugs 384 & 385 easier - closes: Bug#382
-rw-r--r--fs_signup/FS-SignupClient/Makefile.PL1
-rw-r--r--fs_signup/FS-SignupClient/SignupClient.pm58
-rwxr-xr-xfs_signup/FS-SignupClient/cgi/signup.cgi77
-rwxr-xr-xfs_signup/FS-SignupClient/cgi/signup.html5
-rwxr-xr-xfs_signup/FS-SignupClient/fs_signupd99
-rw-r--r--fs_signup/FS-SignupClient/test.pl2
-rwxr-xr-xfs_signup/fs_signup_server138
-rw-r--r--httemplate/docs/install.html1
-rw-r--r--httemplate/docs/signup.html3
9 files changed, 138 insertions, 246 deletions
diff --git a/fs_signup/FS-SignupClient/Makefile.PL b/fs_signup/FS-SignupClient/Makefile.PL
index 859d757c3..208e8507a 100644
--- a/fs_signup/FS-SignupClient/Makefile.PL
+++ b/fs_signup/FS-SignupClient/Makefile.PL
@@ -6,5 +6,6 @@ WriteMakefile(
'VERSION_FROM' => 'SignupClient.pm', # finds $VERSION
'EXE_FILES' => [ 'fs_signupd' ],
'INSTALLSCRIPT' => '/usr/local/sbin',
+ 'INSTALLSITEBIN' => '/usr/local/sbin',
'PERM_RWX' => '750',
);
diff --git a/fs_signup/FS-SignupClient/SignupClient.pm b/fs_signup/FS-SignupClient/SignupClient.pm
index bd917dae7..3933703ae 100644
--- a/fs_signup/FS-SignupClient/SignupClient.pm
+++ b/fs_signup/FS-SignupClient/SignupClient.pm
@@ -6,6 +6,7 @@ use Exporter;
use Socket;
use FileHandle;
use IO::Handle;
+use Storable qw(nstore_fd fd_retrieve);
$VERSION = '0.02';
@@ -58,6 +59,7 @@ FS::SignupClient - Freeside signup client API
'pkgpart' => $pkgpart,
'username' => $username,
'_password' => $password,
+ 'sec_phrase' => $sec_phrase,
'popnum' => $popnum,
} );
@@ -104,51 +106,12 @@ sub signup_info {
print SOCK "signup_info\n";
SOCK->flush;
- chop ( my $n_cust_main_county = <SOCK> );
- my @cust_main_county = map {
- chop ( my $taxnum = <SOCK> );
- chop ( my $state = <SOCK> );
- chop ( my $county = <SOCK> );
- chop ( my $country = <SOCK> );
- {
- 'taxnum' => $taxnum,
- 'state' => $state,
- 'county' => $county,
- 'country' => $country,
- };
- } 1 .. $n_cust_main_county;
-
- chop ( my $n_part_pkg = <SOCK> );
- my @part_pkg = map {
- chop ( my $pkgpart = <SOCK> );
- chop ( my $pkg = <SOCK> );
- {
- 'pkgpart' => $pkgpart,
- 'pkg' => $pkg,
- };
- } 1 .. $n_part_pkg;
-
- chop ( my $n_svc_acct_pop = <SOCK> );
- my @svc_acct_pop = map {
- chop ( my $popnum = <SOCK> );
- chop ( my $city = <SOCK> );
- chop ( my $state = <SOCK> );
- chop ( my $ac = <SOCK> );
- chop ( my $exch = <SOCK> );
- chop ( my $loc = <SOCK> );
- {
- 'popnum' => $popnum,
- 'city' => $city,
- 'state' => $state,
- 'ac' => $ac,
- 'exch' => $exch,
- 'loc' => $loc,
- };
- } 1 .. $n_svc_acct_pop;
-
+ my $init_data = fd_retrieve(\*SOCK);
close SOCK;
- \@cust_main_county, \@part_pkg, \@svc_acct_pop;
+ (map { $init_data->{$_} } qw( cust_main_county part_pkg svc_acct_pop ) ),
+ $init_data;
+
}
=item new_customer HASHREF
@@ -188,6 +151,8 @@ sub new_customer {
my $hashref = shift;
#things that aren't necessary in base class, but are for signup server
+# return "Passwords don't match"
+# if $hashref->{'_password'} ne $hashref->{'_password2'}
return "Empty password" unless $hashref->{'_password'};
return "No POP selected" unless $hashref->{'popnum'};
@@ -195,11 +160,14 @@ sub new_customer {
connect(SOCK, sockaddr_un($fs_signupd_socket)) or die "connect: $!";
print SOCK "new_customer\n";
- print SOCK join("\n", map { $hashref->{$_} } qw(
+ my $signup_data = { map { $_ => $hashref->{$_} } qw(
first last ss company address1 address2 city county state zip country
daytime night fax payby payinfo paydate payname invoicing_list
referral_custnum pkgpart username _password popnum
- ) ), "\n";
+ ) };
+
+ #
+ nstore_fd($signup_data, \*SOCK) or die "can't send customer signup: $!";
SOCK->flush;
chop( my $error = <SOCK> );
diff --git a/fs_signup/FS-SignupClient/cgi/signup.cgi b/fs_signup/FS-SignupClient/cgi/signup.cgi
index b236e6fb2..d44782f9c 100755
--- a/fs_signup/FS-SignupClient/cgi/signup.cgi
+++ b/fs_signup/FS-SignupClient/cgi/signup.cgi
@@ -1,13 +1,13 @@
#!/usr/bin/perl -Tw
#
-# $Id: signup.cgi,v 1.15 2001-09-27 21:37:57 ivan Exp $
+# $Id: signup.cgi,v 1.16 2002-04-06 20:37:38 ivan Exp $
use strict;
use vars qw( @payby $cgi $locales $packages $pops $error
$last $first $ss $company $address1 $address2 $city $state $county
$country $zip $daytime $night $fax $invoicing_list $payby $payinfo
$paydate $payname $referral_custnum
- $pkgpart $username $password $popnum
+ $pkgpart $username $password $password2 $popnum
$ieak_file $ieak_template $cck_file $cck_template
$signup_html $signup_template $success_html $success_template
$ac $exch $loc
@@ -115,37 +115,50 @@ if ( defined $cgi->param('magic') ) {
$invoicing_list = 'POST';
}
- $error = new_customer ( {
- 'last' => $last = $cgi->param('last'),
- 'first' => $first = $cgi->param('first'),
- 'ss' => $ss = $cgi->param('ss'),
- 'company' => $company = $cgi->param('company'),
- 'address1' => $address1 = $cgi->param('address1'),
- 'address2' => $address2 = $cgi->param('address2'),
- 'city' => $city = $cgi->param('city'),
- 'county' => $county,
- 'state' => $state,
- 'zip' => $zip = $cgi->param('zip'),
- 'country' => $country,
- 'daytime' => $daytime = $cgi->param('daytime'),
- 'night' => $night = $cgi->param('night'),
- 'fax' => $fax = $cgi->param('fax'),
- 'payby' => $payby,
- 'payinfo' => $payinfo,
- 'paydate' => $paydate,
- 'payname' => $payname,
- 'invoicing_list' => $invoicing_list,
- 'referral_custnum' => $referral_custnum = $cgi->param('ref'),
- 'pkgpart' => $pkgpart = $cgi->param('pkgpart'),
- 'username' => $username = $cgi->param('username'),
- '_password' => $password = $cgi->param('_password'),
- 'popnum' => $popnum = $cgi->param('popnum'),
- } );
+ $error = '';
+
+ if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
+ $error = "Passwords don't match";
+ $password = '';
+ $password2 = '';
+ } else {
+ $password2 = $cgi->param('_password2');
+
+ $error = new_customer ( {
+ 'last' => $last = $cgi->param('last'),
+ 'first' => $first = $cgi->param('first'),
+ 'ss' => $ss = $cgi->param('ss'),
+ 'company' => $company = $cgi->param('company'),
+ 'address1' => $address1 = $cgi->param('address1'),
+ 'address2' => $address2 = $cgi->param('address2'),
+ 'city' => $city = $cgi->param('city'),
+ 'county' => $county,
+ 'state' => $state,
+ 'zip' => $zip = $cgi->param('zip'),
+ 'country' => $country,
+ 'daytime' => $daytime = $cgi->param('daytime'),
+ 'night' => $night = $cgi->param('night'),
+ 'fax' => $fax = $cgi->param('fax'),
+ 'payby' => $payby,
+ 'payinfo' => $payinfo,
+ 'paydate' => $paydate,
+ 'payname' => $payname,
+ 'invoicing_list' => $invoicing_list,
+ 'referral_custnum' => $referral_custnum = $cgi->param('ref'),
+ 'pkgpart' => $pkgpart = $cgi->param('pkgpart'),
+ 'username' => $username = $cgi->param('username'),
+ '_password' => $password = $cgi->param('_password'),
+ 'popnum' => $popnum = $cgi->param('popnum'),
+ } );
+
+ }
+
if ( $error ) {
print_form();
} else {
print_okay();
}
+
} else {
die "unrecognized magic: ". $cgi->param('magic');
}
@@ -173,6 +186,7 @@ if ( defined $cgi->param('magic') ) {
$pkgpart = '';
$username = '';
$password = '';
+ $password2 = '';
$popnum = '';
$referral_custnum = $cgi->param('ref') || '';
print_form;
@@ -463,10 +477,15 @@ Contact Information
</TR>
<TR>
<TD ALIGN="right">Password</TD>
- <TD><INPUT TYPE="text" NAME="_password" VALUE="<%= $password %>">
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>">
(blank to generate)</TD>
</TR>
<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>">
+ </TD>
+</TR>
+<TR>
<TD ALIGN="right">Access number</TD>
<TD><%= popselector($popnum) %></TD>
</TR>
diff --git a/fs_signup/FS-SignupClient/cgi/signup.html b/fs_signup/FS-SignupClient/cgi/signup.html
index 0f4742d20..909d1fe5b 100755
--- a/fs_signup/FS-SignupClient/cgi/signup.html
+++ b/fs_signup/FS-SignupClient/cgi/signup.html
@@ -130,6 +130,11 @@ Contact Information
(blank to generate)</TD>
</TR>
<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>">
+ </TD>
+</TR>
+<TR>
<TD ALIGN="right">Access number</TD>
<TD><%= popselector($popnum) %></TD>
</TR>
diff --git a/fs_signup/FS-SignupClient/fs_signupd b/fs_signup/FS-SignupClient/fs_signupd
index 8b3cdde53..e764f32b7 100755
--- a/fs_signup/FS-SignupClient/fs_signupd
+++ b/fs_signup/FS-SignupClient/fs_signupd
@@ -3,14 +3,15 @@
# fs_signupd
#
# This is run REMOTELY over ssh by fs_signup_server.
-#
use strict;
use Socket;
+use Storable qw(nstore_fd fd_retrieve);
+use IO::Handle;
use vars qw( $Debug );
-$Debug = 0;
+$Debug = 1;
my($fs_signupd_socket)="/usr/local/freeside/fs_signupd_socket";
@@ -23,50 +24,8 @@ $ENV{'BASH_ENV'} = '';
$|=1;
-warn "[fs_signupd] Reading locales...\n" if $Debug;
-chomp( my $n_cust_main_county = <STDIN> );
-my @cust_main_county = map {
- chomp( my $taxnum = <STDIN> );
- chomp( my $state = <STDIN> );
- chomp( my $county = <STDIN> );
- chomp( my $country = <STDIN> );
- {
- 'taxnum' => $taxnum,
- 'state' => $state,
- 'county' => $county,
- 'country' => $country,
- };
-} ( 1 .. $n_cust_main_county );
-
-warn "[fs_signupd] Reading package definitions...\n" if $Debug;
-chomp( my $n_part_pkg = <STDIN> );
-my @part_pkg = map {
- chomp( my $pkgpart = <STDIN> );
- chomp( my $pkg = <STDIN> );
- {
- 'pkgpart' => $pkgpart,
- 'pkg' => $pkg,
- };
-} ( 1 .. $n_part_pkg );
-
-warn "[fs_signupd] Reading POPs...\n" if $Debug;
-chomp( my $n_svc_acct_pop = <STDIN> );
-my @svc_acct_pop = map {
- chomp( my $popnum = <STDIN> );
- chomp( my $city = <STDIN> );
- chomp( my $state = <STDIN> );
- chomp( my $ac = <STDIN> );
- chomp( my $exch = <STDIN> );
- chomp( my $loc = <STDIN> );
- {
- 'popnum' => $popnum,
- 'city' => $city,
- 'state' => $state,
- 'ac' => $ac,
- 'exch' => $exch,
- 'loc' => $loc,
- };
-} ( 1 .. $n_svc_acct_pop );
+warn "[fs_signupd] Reading init data...\n" if $Debug;
+my $init_data = fd_retrieve(\*STDIN);
warn "[fs_signupd] Creating $fs_signupd_socket\n" if $Debug;
my $uaddr = sockaddr_un($fs_signupd_socket);
@@ -83,56 +42,28 @@ for ( ; $paddr = accept(Client,Server); close Client) {
chop( my $command = <Client> );
if ( $command eq "signup_info" ) {
+
warn "[fs_signupd] sending signup info...\n" if $Debug;
- print Client join("\n", $n_cust_main_county,
- map {
- $_->{taxnum},
- $_->{state},
- $_->{county},
- $_->{country},
- } @cust_main_county
- ), "\n";
-
- print Client join("\n", $n_part_pkg,
- map {
- $_->{pkgpart},
- $_->{pkg},
- } @part_pkg
- ), "\n";
-
- print Client join("\n", $n_svc_acct_pop,
- map {
- $_->{popnum},
- $_->{city},
- $_->{state},
- $_->{ac},
- $_->{exch},
- $_->{loc},
- } @svc_acct_pop
- ), "\n";
+ nstore_fd($init_data, \*Client) or die "can't send init data: $!";
+ Client->flush;
} elsif ( $command eq "new_customer" ) {
+
+ #inefficient...
+
warn "[fs_signupd] reading customer signup...\n" if $Debug;
- my(
- $first, $last, $ss, $company, $address1, $address2, $city, $county,
- $state, $zip, $country, $daytime, $night, $fax, $payby, $payinfo,
- $paydate, $payname, $invoicing_list, $referral_custnum,
- $pkgpart, $username, $password, $popnum,
- ) = map { scalar(<Client>) } ( 1 .. 24 );
+ my $signup_data = fd_retrieve(\*Client);
warn "[fs_signupd] sending customer data to remote server...\n" if $Debug;
- print
- $first, $last, $ss, $company, $address1, $address2, $city, $county,
- $state, $zip, $country, $daytime, $night, $fax, $payby, $payinfo,
- $paydate, $payname, $invoicing_list, $referral_custnum,
- $pkgpart, $username, $password, $popnum,
- ;
+ nstore_fd($signup_data, \*STDOUT) or die "can't send signup data: $!";
+ STDOUT->flush;
warn "[fs_signupd] reading error from remote server...\n" if $Debug;
my $error = <STDIN>;
warn "[fs_signupd] sending error to local client...\n" if $Debug;
print Client $error;
+ Client->flush;
} else {
die "unexpected command from client: $command";
diff --git a/fs_signup/FS-SignupClient/test.pl b/fs_signup/FS-SignupClient/test.pl
index 690f5840e..b6136954d 100644
--- a/fs_signup/FS-SignupClient/test.pl
+++ b/fs_signup/FS-SignupClient/test.pl
@@ -8,7 +8,7 @@
BEGIN { $| = 1; print "1..1\n"; }
END {print "not ok 1\n" unless $loaded;}
-use FS::SignupClient;
+#blah#use FS::SignupClient;
$loaded = 1;
print "ok 1\n";
diff --git a/fs_signup/fs_signup_server b/fs_signup/fs_signup_server
index b0d28be8c..77f7f3056 100755
--- a/fs_signup/fs_signup_server
+++ b/fs_signup/fs_signup_server
@@ -5,6 +5,7 @@
use strict;
use IO::Handle;
+use Storable qw(nstore_fd fd_retrieve);
use Tie::RefHash;
use Net::SSH qw(sshopen2);
use FS::UID qw(adminsuidsetup);
@@ -14,7 +15,7 @@ use FS::cust_main;
use vars qw( $opt $Debug );
-$Debug = 0;
+$Debug = 2;
my @payby = qw(CARD PREPAY);
@@ -25,7 +26,7 @@ my $machine = shift or die &usage;
my $agentnum = shift or die &usage;
my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } ) or die &usage;
-my $pkgpart = $agent->pkgpart_hashref;
+my $pkgpart_href = $agent->pkgpart_hashref;
my $refnum = shift or die &usage;
@@ -36,61 +37,43 @@ my($fs_signupd)="/usr/local/sbin/fs_signupd";
while (1) {
my($reader,$writer)=(new IO::Handle, new IO::Handle);
- $writer->autoflush(1);
+ #seems to be broken - calling ->flush explicitly# $writer->autoflush(1);
warn "[fs_signup_server] Connecting to $machine...\n" if $Debug;
sshopen2($machine,$reader,$writer,$fs_signupd);
- my $data;
-
- warn "[fs_signup_server] Sending locales...\n" if $Debug;
- my @cust_main_county = qsearch('cust_main_county', {} );
- print $writer $data = join("\n",
- ( scalar(@cust_main_county) || die "no tax rates (cust_main_county records)" ),
- map {
- $_->taxnum,
- $_->state,
- $_->county,
- $_->country,
- } @cust_main_county
- ),"\n";
- warn "[fs_signup_server] $data\n" if $Debug > 2;
-
- warn "[fs_signup_server] Sending package definitions...\n" if $Debug;
- my @part_pkg = grep { $_->svcpart('svc_acct') && $pkgpart->{ $_->pkgpart } }
- qsearch( 'part_pkg', { 'disabled' => '' } );
- print $writer $data = join("\n",
- ( scalar(@part_pkg) || die "no usable package definitions, agent $agentnum" ),
- map {
- $_->pkgpart,
- $_->pkg,
- } @part_pkg
- ), "\n";
- warn "[fs_signup_server] $data\n" if $Debug > 2;
-
- warn "[fs_signup_server] Sending POPs...\n" if $Debug;
- my @svc_acct_pop = qsearch ('svc_acct_pop',{} );
- print $writer $data = join("\n",
- ( scalar(@svc_acct_pop) || die "No points of presence (svc_acct_pop records)" ),
- map {
- $_->popnum,
- $_->city,
- $_->state,
- $_->ac,
- $_->exch,
- $_->loc,
- } @svc_acct_pop
- ), "\n";
- warn "[fs_signup_server] $data\n" if $Debug > 2;
+ my $init_data = {
+
+ #'_protocol' => 'signup',
+ #'_version' => '0.1',
+ #'_packet' => 'init'
+
+ 'cust_main_county' =>
+ [ map { $_->hashref } qsearch('cust_main_county', {}) ],
+
+ 'part_pkg' =>
+ [
+ map { $_->hashref }
+ grep { $_->svcpart('svc_acct') && $pkgpart_href->{ $_->pkgpart } }
+ qsearch( 'part_pkg', { 'disabled' => '' } )
+ ],
+
+ 'svc_acct_pop' => [ map { $_->hashref } qsearch ('svc_acct_pop',{} ) ],
+
+ };
+
+ warn "[fs_signup_server] Sending init data...\n" if $Debug;
+ nstore_fd($init_data, $writer) or die "can't send init data: $!";
+ $writer->flush;
warn "[fs_signup_server] Entering main loop...\n" if $Debug;
while (1) {
warn "[fs_signup_server] Reading (waiting for) signup data...\n" if $Debug;
- chop( my(
- $first, $last, $ss, $company, $address1, $address2, $city, $county,
- $state, $zip, $country, $daytime, $night, $fax, $payby, $payinfo,
- $paydate, $payname, $invoicing_list, $referral_custnum,
- $pkgpart, $username, $password, $popnum,
- ) = map { scalar(<$reader>) } ( 1 .. 24 ) );
+ my $signup_data = fd_retrieve($reader);
+
+ if ( $Debug > 1 ) {
+ warn join('',
+ map { " $_ => ". $signup_data->{$_}. "\n" } keys %$signup_data );
+ }
warn "[fs_signup_server] Processing signup...\n" if $Debug;
@@ -99,64 +82,47 @@ while (1) {
#shares some stuff with htdocs/edit/process/cust_main.cgi... take any
# common that are still here and library them.
my $cust_main = new FS::cust_main ( {
- 'custnum' => '',
+ #'custnum' => '',
'agentnum' => $agentnum,
'refnum' => $refnum,
- 'last' => $last,
- 'first' => $first,
- 'ss' => $ss,
- 'company' => $company,
- 'address1' => $address1,
- 'address2' => $address2,
- 'city' => $city,
- 'county' => $county,
- 'state' => $state,
- 'zip' => $zip,
- 'country' => $country,
- 'daytime' => $daytime,
- 'night' => $night,
- 'fax' => $fax,
- 'payby' => $payby,
- 'payinfo' => $payinfo,
- 'paydate' => $paydate,
- 'payname' => $payname,
- 'referral_custnum' => $referral_custnum,
+
+ map { $_ => $signup_data->{$_} } qw(
+ last first ss company address1 address2 city county state zip country
+ daytime night fax payby payinfo paydate payname referral_custnum
+ ),
+
} );
- $error = "Illegal payment type" unless grep { $_ eq $payby } @payby;
+ $error = "Illegal payment type"
+ unless grep { $_ eq $signup_data->{'payby'} } @payby;
- my @invoicing_list = split( /\s*\,\s*/, $invoicing_list );
+ my @invoicing_list = split( /\s*\,\s*/, $signup_data->{'invoicing_list'} );
- my $part_pkg = qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } )
- or $error ||= "WARNING: unknown pkgpart $pkgpart";
+ my $part_pkg =
+ qsearchs( 'part_pkg', { 'pkgpart' => $signup_data->{'pkgpart'} } )
+ or $error ||= "WARNING: unknown pkgpart ". $signup_data->{pkgpart};
my $svcpart = $part_pkg->svcpart unless $error;
# this should wind up in FS::cust_pkg!
my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } );
- my $pkgpart_href = $agent->pkgpart_hashref;
- $error ||= "WARNING: agent $agentnum can't purchase pkgpart $pkgpart"
- unless $pkgpart_href->{ $pkgpart };
+ #my $pkgpart_href = $agent->pkgpart_hashref;
+ $error ||= "WARNING: agent $agentnum can't purchase pkgpart ".
+ $signup_data->{pkgpart}
+ unless $pkgpart_href->{ $signup_data->{pkgpart} };
my $cust_pkg = new FS::cust_pkg ( {
#later#'custnum' => $custnum,
- 'pkgpart' => $pkgpart,
+ 'pkgpart' => $signup_data->{'pkgpart'},
} );
$error ||= $cust_pkg->check;
my $svc_acct = new FS::svc_acct ( {
'svcpart' => $svcpart,
- 'username' => $username,
- '_password' => $password,
- 'popnum' => $popnum,
+ map { $_ => $signup_data->{$_} } qw( username _password popnum ),
} );
my $y = $svc_acct->setdefault; # arguably should be in new method
$error ||= $y unless ref($y);
- #and just in case you were silly
- $svc_acct->svcpart($svcpart);
- $svc_acct->username($username);
- $svc_acct->_password($password);
- $svc_acct->popnum($popnum);
$error ||= $svc_acct->check;
diff --git a/httemplate/docs/install.html b/httemplate/docs/install.html
index 4370d9153..2ad75b15e 100644
--- a/httemplate/docs/install.html
+++ b/httemplate/docs/install.html
@@ -52,6 +52,7 @@ Before installing, you need:
<li><a href="http://search.cpan.org/search?dist=Tie-IxHash">Tie-IxHash</a>
<li><a href="http://search.cpan.org/search?dist=Time-Duration">Time-Duration</a>
<li><a href="http://search.cpan.org/search?dist=HTML-Widgets-SelectLayers">HTML-Widgets-SelectLayers</a>
+ <li><a href="http://search.cpan.org/search?dist=Storable">Storable</a>
</ul>
</ul>
Install the Freeside distribution:
diff --git a/httemplate/docs/signup.html b/httemplate/docs/signup.html
index 262b697bb..e7472158a 100644
--- a/httemplate/docs/signup.html
+++ b/httemplate/docs/signup.html
@@ -9,7 +9,8 @@ webserver. On this machine, install:
<li>A web server, such as <a href="http://www.apache-ssl.org">Apache-SSL</a> or <a href="http://www.apache.org">Apache</a>
<li><a href="ftp://ftp.cs.hut.fi/pub/ssh/">SSH</a>
<li><a href="http://www.perl.com/CPAN/doc/relinfo/INSTALL.html">Perl</a> (at least 5.004_05 for the 5.004 series or 5.005_03 for the 5.005 series. Don't enable experimental features like threads or the PerlIO abstraction layer.)
- <li><a href="http://www.perl.com/CPAN/modules/by-module/Text/">Text::Template</a>
+ <li><a href="http://search.cpan.org/search?dist=Text-Template">Text::Template</a>
+ <li><a href="http://search.cpan.org/search?dist=Storable">Storable</a>
<li><a href="http://www.sisd.com/useragent">HTTP::Headers::UserAgent</a> (version 2.0 or higher; not yet indexed correctly on CPAN)
<li><a href="man/FS/SignupClient.html">FS::SignupClient</a> (copy the fs_signup/FS-SignupClient directory to the external machine, then: perl Makefile.PL; make; make install)