- credit card type pulldown on signup server (closes: Bug#383)
my $dir = $self->dir;
$value =~ /^(.*)$/s;
$value = $1;
my $dir = $self->dir;
$value =~ /^(.*)$/s;
$value = $1;
- unless ( $self->config($file) eq $value ) {
+ unless ( join("\n", @{[ $self->config($file) ]}) eq $value ) {
warn "[FS::Conf] SET $file\n" if $DEBUG;
# warn "$dir" if is_tainted($dir);
# warn "$dir" if is_tainted($file);
warn "[FS::Conf] SET $file\n" if $DEBUG;
# warn "$dir" if is_tainted($dir);
# warn "$dir" if is_tainted($file);
'select_enum' => [ qw(en_US) ],
},
'select_enum' => [ qw(en_US) ],
},
+ {
+ 'key' => 'signup_server-payby',
+ 'section' => '',
+ 'description' => 'Acceptable payment types for the signup server',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [ qw(CARD PREPAY BILL COMP) ],
+ },
+
+
-# $Id: signup.cgi,v 1.17 2002-04-06 21:39:22 ivan Exp $
+# $Id: signup.cgi,v 1.18 2002-04-07 00:00:40 ivan Exp $
use strict;
use vars qw( @payby $cgi $locales $packages $pops $init_data $error
use strict;
use vars qw( @payby $cgi $locales $packages $pops $init_data $error
use subs qw( print_form print_okay expselect signup_default success_default );
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use subs qw( print_form print_okay expselect signup_default success_default );
use CGI;
use CGI::Carp qw(fatalsToBrowser);
+use Text::Template;
+use Business::CreditCard;
use HTTP::Headers::UserAgent 2.00;
use FS::SignupClient 0.03 qw( signup_info new_customer );
use HTTP::Headers::UserAgent 2.00;
use FS::SignupClient 0.03 qw( signup_info new_customer );
#acceptable payment methods
#
#acceptable payment methods
#
}
( $locales, $packages, $pops, $init_data ) = signup_info();
}
( $locales, $packages, $pops, $init_data ) = signup_info();
+@payby = @{$init_data->{'payby'}} if @{$init_data->{'payby'}};
$error = '';
if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
$error = '';
if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
- $error = "Passwords don't match";
+ $error = "Passwords don't match"; #msgcat
$password = '';
$password2 = '';
} else {
$password2 = $cgi->param('_password2');
$password = '';
$password2 = '';
} else {
$password2 = $cgi->param('_password2');
- $error = new_customer ( {
+ if ( $payby eq 'CARD' && $cgi->param('CARD_type')
+ && cardtype($payinfo) ne $cgi->param('CARD_type') ) {
+ $error = 'Not an '. $cgi->param('CARD_type'). '| - is: |'. cardtype($payinfo). '|'; #msgcat
+ }
+
+ $error ||= new_customer ( {
'last' => $last = $cgi->param('last'),
'first' => $first = $cgi->param('first'),
'ss' => $ss = $cgi->param('ss'),
'last' => $last = $cgi->param('last'),
'first' => $first = $cgi->param('first'),
'ss' => $ss = $cgi->param('ss'),
</TD></TR>
<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
</TD></TR>
</TD></TR>
<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
</TD></TR>
-<TR><TD>Billing type</TD></TR></TABLE>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
<TR>
<%=
<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>';
+
- 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font><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="">!,
+ '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="">!,
'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 %paybychecked = (
'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 %paybychecked = (
- 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font><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">!,
+ '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">!,
'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) {
'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) {
- $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
- if ($payby eq $_) {
- $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
- $OUT .= qq!> $payby{$_}</TD>!;
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
</TD></TR>
<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
</TD></TR>
</TD></TR>
<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
</TD></TR>
-<TR><TD>Billing type</TD></TR></TABLE>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
<TR>
<%=
<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>';
+
- 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font><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="">!,
+ '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="">!,
'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 %paybychecked = (
'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 %paybychecked = (
- 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font><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">!,
+ '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">!,
'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) {
'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) {
- $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
- if ($payby eq $_) {
- $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
- $OUT .= qq!> $payby{$_}</TD>!;
+ $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>
<TR>
<TD ALIGN="right">Password</TD>
</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>
(blank to generate)</TD>
</TR>
<TR>
-my @payby = qw(CARD PREPAY);
-
my $user = shift or die &usage;
&adminsuidsetup( $user );
my $conf = new FS::Conf;
my $user = shift or die &usage;
&adminsuidsetup( $user );
my $conf = new FS::Conf;
+#my @payby = qw(CARD PREPAY);
+my @payby = $conf->config('signup_server-payby');
+
my $machine = shift or die &usage;
my $agentnum = shift or die &usage;
my $machine = shift or die &usage;
my $agentnum = shift or die &usage;
'security_phrase' => $conf->exists('security_phrase'),
'security_phrase' => $conf->exists('security_phrase'),
+ 'payby' => [ $conf->config('signup_server-payby') ],
+
};
warn "[fs_signup_server] Sending init data...\n" if $Debug;
};
warn "[fs_signup_server] Sending init data...\n" if $Debug;
}
} elsif ( $type eq 'editlist' || $type eq 'selectmultiple' ) {
if ( scalar(@{[ $cgi->param($i->key. $n) ]}) ) {
}
} elsif ( $type eq 'editlist' || $type eq 'selectmultiple' ) {
if ( scalar(@{[ $cgi->param($i->key. $n) ]}) ) {
- $conf->set($i->key, join("\n", $cgi->param($i->key. $n) ));
+ $conf->set($i->key, join("\n", @{[ $cgi->param($i->key. $n) ]} ));
} else {
$conf->delete($i->key);
}
} else {
$conf->delete($i->key);
}
my $n = 0; %>
<% if ( $type eq '' ) { %>
<tr><td><font color="#ff0000">no type</font></td></tr>
my $n = 0; %>
<% if ( $type eq '' ) { %>
<tr><td><font color="#ff0000">no type</font></td></tr>
- <% } elsif ( $type eq 'textarea' || $type eq 'editlist' ) { %>
+ <% } elsif ( $type eq 'textarea'
+ || $type eq 'editlist'
+ || $type eq 'selectmultiple' ) { %>
<tr><td bgcolor="#ffffff">
<pre>
<%= encode_entities(join("\n", $conf->config($i->key) ) ) %>
<tr><td bgcolor="#ffffff">
<pre>
<%= encode_entities(join("\n", $conf->config($i->key) ) ) %>
<% } elsif ( $type eq 'checkbox' ) { %>
<tr><td bgcolor="#<%= $conf->exists($i->key) ? '00ff00">YES' : 'ff0000">NO' %></td></tr>
<% } elsif ( $type eq 'text' || $type eq 'select' ) { %>
<% } elsif ( $type eq 'checkbox' ) { %>
<tr><td bgcolor="#<%= $conf->exists($i->key) ? '00ff00">YES' : 'ff0000">NO' %></td></tr>
<% } elsif ( $type eq 'text' || $type eq 'select' ) { %>
- <tr><td bgcolor="#ffffff"><%= $conf->exists($i->key) ? $conf->config($i->key) : '' %></td></tr>
+ <tr><td bgcolor="#ffffff"><%= $conf->exists($i->key) ? $conf->config($i->key) : '' %></td></tr>
<% } else { %>
<tr><td>
<font color="#ff0000">unknown type <%= $type %></font>
<% } else { %>
<tr><td>
<font color="#ff0000">unknown type <%= $type %></font>
<input name="<%= $i->key. $n %>" type="checkbox" value="1"<%= $conf->exists($i->key) ? ' CHECKED' : '' %>>
<% } elsif ( $type eq 'text' ) { %>
<input name="<%= $i->key. $n %>" type="<%= $type %>" value="<%= $conf->exists($i->key) ? $conf->config($i->key) : '' %>">
<input name="<%= $i->key. $n %>" type="checkbox" value="1"<%= $conf->exists($i->key) ? ' CHECKED' : '' %>>
<% } elsif ( $type eq 'text' ) { %>
<input name="<%= $i->key. $n %>" type="<%= $type %>" value="<%= $conf->exists($i->key) ? $conf->config($i->key) : '' %>">
- <% } elsif ( $type eq 'select' ) { %>
- <select name="<%= $i->key. $n %>">
+ <% } elsif ( $type eq 'select' || $type eq 'selectmultiple' ) { %>
+ <select name="<%= $i->key. $n %>" <%= $type eq 'selectmultiple' ? 'MULTIPLE' : '' %>>
<% my %saw;
foreach my $value ( "", @{$i->select_enum} ) {
local($^W)=0; next if $saw{$value}++; %>
<% my %saw;
foreach my $value ( "", @{$i->select_enum} ) {
local($^W)=0; next if $saw{$value}++; %>
- <option value="<%= $value %>"<%= $value eq $conf->config($i->key) ? ' SELECTED' : '' %>><%= $value %>
+ <option value="<%= $value %>"<%= $value eq $conf->config($i->key) || ( $type eq 'selectmultiple' && grep { $_ eq $value } $conf->config($i->key) ) ? ' SELECTED' : '' %>><%= $value %>
<% } %>
<% if ( $conf->exists($i->key) && $conf->config($i->key) && ! grep { $conf->config($i->key) eq $_ } @{$i->select_enum}) { %>
<option value=<%= $conf->config($i->key) %> SELECTED><%= conf->config($i->key) %>
<% } %>
<% if ( $conf->exists($i->key) && $conf->config($i->key) && ! grep { $conf->config($i->key) eq $_ } @{$i->select_enum}) { %>
<option value=<%= $conf->config($i->key) %> SELECTED><%= conf->config($i->key) %>
<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://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.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://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://search.cpan.org/search?dist=Business-CreditCard">Business-CreditCard</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)
<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)