📄 ch17.htm
字号:
and </FONT><TT><FONT SIZE=4 FACE="Courier">sendmail -t</FONT></TT><FONT SIZE=4>
Flag</FONT></A></H3>
<P>
E-mail gateways are often used in CGI programming because they
are among the few standard UNIX tools that lend themselves to
CGI development. The preferred UNIX options for sending e-mail
composed by a CGI program are <TT><FONT FACE="Courier">mail</FONT></TT>
and <TT><FONT FACE="Courier">sendmail</FONT></TT>. I prefer <TT><FONT FACE="Courier">sendmail</FONT></TT>
because it is the more powerful of the two. <TT><FONT FACE="Courier">sendmail</FONT></TT>
takes its data from <TT><FONT FACE="Courier">stdin</FONT></TT>
and sends the message after it reaches <TT><FONT FACE="Courier">EOF</FONT></TT>
or a line with single <TT><FONT FACE="Courier">.</FONT></TT> on
it.
<P>
One method of utilizing <TT><FONT FACE="Courier">sendmail</FONT></TT>
from Perl would be to write to-be-e-mailed data to a temporary
file and then <TT><FONT FACE="Courier">cat</FONT></TT> it and
pipe the output to <TT><FONT FACE="Courier">sendmail</FONT></TT>,
as follows:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">open(EMAIL,"> tempfile$$.txt");
<BR>
print EMAIL "stuff to be emailed... la la la...\n";
<BR>
close(EMAIL);<BR>
system("cat tempfile$$.txt | /usr/sbin/sendmail $tokens{'email'}");
<BR>
system("rm tempfile$$.txt");</FONT></TT>
</BLOCKQUOTE>
<P>
The <TT><FONT FACE="Courier">$$</FONT></TT> in the name <TT><FONT FACE="Courier">tempfile$$.txt</FONT></TT>
is a special variable in Perl that represents the process identification
number (PID) of the Perl process. I do this so that <TT><FONT FACE="Courier">tempfile</FONT></TT>
isn't accidentally overwritten if two people invoke this CGI program
simultaneously; there are two unique <TT><FONT FACE="Courier">tempfile</FONT></TT>s
with this method. This is a Good Idea that can be used in many
applications. In this particular case, there are better ways to
handle the situation, though.
<P>
The <TT><FONT FACE="Courier">sendmail</FONT></TT> command finds
its destination e-mail address from the associative array element
<TT><FONT FACE="Courier">$tokens{'email'}</FONT></TT>. You can
assume that this variable is obtained from the user putting this
value into his or her form submission. Without any safeguards,
this is a Bad Thing. Consider the malicious hacker who provides
the following "e-mail address" to your Web form:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">noone@nowhere.net ; cd / ; rm -R *</FONT></TT>
</BLOCKQUOTE>
<P>
The semicolons are shell metacharacters that signify the end of
a UNIX command to the shell interpreter. Should any Webmaster
be foolish enough to run their httpd as root, that example would
delete the entire file system. While most Webmasters haven't set
up their systems for this kind of disaster, lesser evils can be
committed if unchecked user input is allowed to reach the shell.
<P>
This problem is avoided in my greenegg.cgi code in two ways. First,
I avoid the intermediate <TT><FONT FACE="Courier">tempfile</FONT></TT>
and instead open a filehandle directly to the pipe that <TT><FONT FACE="Courier">sendmail</FONT></TT>
uses to receive standard input. Second, I use the <TT><FONT FACE="Courier">-t</FONT></TT>
option of <TT><FONT FACE="Courier">sendmail</FONT></TT> that instructs
it to parse standard input to look for an e-mail address rather
than get the address from the command line. With this, no user
input is brought to the attention of the shell at all.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Tip</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
If user input must go to the shell, then you might consider using the following Perl subroutine on it first:</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub shell_proof {<BR>
<BR>
local(@strings);<BR>
@strings = @_;<BR>
<BR>
foreach $string ( @strings ) {<BR>
$string =~<BR>
s/[\001-\011\013-\014\016-\037\041-\052\057\074-\077\133-\136\140\173-
<BR>
377]//g;<BR>
}<BR>
@strings;<BR>
}<BR>
This code will remove shell metacharacters and binary characters
from strings. Its syntax of use is<BR>
@string_list = &shell_safe(@string_list);</FONT></TT>
</BLOCKQUOTE>
<H3><A NAME="UseofprintFHENDENDSyntaxWhe">Use of <TT><FONT SIZE=4 FACE="Courier">print
FH<<END; ... END</FONT></TT><FONT SIZE=4> Syntax When Outputting
Formatted Sections</FONT></A></H3>
<P>
Perl has a strong built-in facility for printing the formatted
output of a large number of consecutive lines of text.
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$things = 'variables';<BR>
print <<END_DESCRIPTOR;<BR>
Now you type<BR>
what you want to<BR>
on as many lines as you'd like and you can even include<BR>
$things which will be interpolated to their values.<BR>
END_DESCRIPTOR</FONT></TT>
</BLOCKQUOTE>
<P>
This syntax is taken from UNIX Bourne shell programming, where
it is very useful when writing batch jobs. I find this feature
of Perl programming to be particularly useful when writing HTML
within Perl.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Note</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Emacs, the ultimate UNIX-based text editor for hacking, has built-in modes for dealing with many different programming languages: C, C++, FORTRAN, Lisp, Prolog, and Perl to name a few. Among other things, these modes will automatically format your code as
you type it. This is useful for discovering certain sorts of errors before you even touch the compiler. Unfortunately, emacs' Perl mode has a tough time figuring out how to format your source following a print <TT><FONT
FACE="Courier"><<END_DESCRIPTOR;</FONT></TT> statement. We can hope that this bug will one day be fixed.
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<H2><A NAME="AVotingBoothWishList"><FONT SIZE=5 COLOR=#FF0000>A
Voting Booth Wish List</FONT></A></H2>
<P>
I think that my main complaint of greenegg.cgi is that it didn't
go far enough. Let's all try closing our eyes and clicking our
heels together three times, thinking about what we'd most want
in a voting booth CGI system...
<UL>
<LI><FONT COLOR=#000000>The voting booth would recognize individual
users, each having a unique e-mail address.</FONT>
<LI><FONT COLOR=#000000>Users could not only vote but re-vote
should they change their minds in the future.</FONT>
<LI><FONT COLOR=#000000>There would be an extensible database
of vote-able objects.</FONT>
<LI><FONT COLOR=#000000>There would be different classes of objects
within this database.</FONT>
<LI><FONT COLOR=#000000>The reports that were generated by the
voting booth were meaningful.</FONT>
</UL>
<P>
Of course, I'm not bringing these points up idly. The next 10
or so pages is the Perl source for vote.cgi, a voting booth program
that demonstrates all these possibilities.
<P>
Figure 17.3 shows the vote.cgi entry page showing the various
options a user would have when reaching it: registering as a new
user, logging in as a current user, and viewing the vote patterns
of all users. I have added my e-mail address into the field that
shows how a new user is registered.
<P>
<A HREF="f17-3.gif" ><B>Figure 17.3:</B> <I>The vote.cgi entry page.</I></A>
<P>
I've really put myself on the line with this example. vote.cgi
gives the whole world an opportunity to tell me what they think
about my taste in music. (Hey, cut me some slack. I happened to
have a tab-delimited ASCII file of my music handy, and I needed
to give myself a voting booth-come-database project, so....)
The Perl source code for vote.cgi is shown in Listing 17.3.
<HR>
<BLOCKQUOTE>
<B>Listing 17.3. vote.cgi-A complex voting booth CGI program.
<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#!/usr/bin/perl<BR>
<BR>
#<BR>
# This program written by Richard Dice of Anadas Software Development
<BR>
# as part of the Sams Net "CGI Programming Unleashed"
book. The author<BR>
# intends this code to be used for instructional purposes and
not for<BR>
# resale or commercial gain.<BR>
#<BR>
# Any questions or comments regarding this program are welcome. You
<BR>
# may contact the author by Internet email: rdice@anadas.com<BR>
#<BR>
<BR>
#<BR>
# minor set-up --> seed the random number generator (used in
issuing<BR>
# passwords) and define the $ref_url, the only URL that this program
<BR>
# will accept FORM-based submissions from. I redirect
standard error<BR>
# so that sendmail won't crash the system if it can't find an
address<BR>
#<BR>
$ref_url = 'http://www.anadas.com/cgiunleashed/voting/exe/vote.cgi';
<BR>
srand;<BR>
open(STDERR,"> /dev/null");<BR>
$admin = 'rdice@anadas.com';<BR>
<BR>
# -- start of my standard GET/POST method handler --<BR>
<BR>
if ( $ENV{REQUEST_METHOD} eq 'POST' ) {<BR>
read(stdin,$input,$ENV{CONTENT_LENGTH});<BR>
} elsif ( $ENV{REQUEST_METHOD} eq 'GET' ) {<BR>
$input = $ENV{QUERY_STRING};<BR>
} else {<BR>
print "Content-type: text/html\n\n";
<BR>
print "This program doesn't support the
<b>$ENV{REQUEST_METHOD}</b>",<BR>
" httpd request method.\n";<BR>
exit 1;<BR>
}<BR>
$input =~ tr/+/ /;<BR>
@fields = split(/\&/,$input);<BR>
$input = '';<BR>
foreach $i (@fields) {<BR>
($field,$data) = split(/=/,$i);<BR>
$field =~ s/%(..)/pack("c",hex($1))/ge;
<BR>
$data =~ s/%(..)/pack("c",hex($1))/ge;
<BR>
$tokens{$field} = $data;<BR>
}<BR>
@fields = (); # delete the @fields array
<BR>
<BR>
# -- end of my standard GET/POST method handler --<BR>
<BR>
#<BR>
# MULTISTATE DETERMINATION<BR>
# ------------------------<BR>
# Switch to one of the possible actions given the value of the
'submit'<BR>
# name <BR>
#<BR>
# * if the submitting URL isn't this URL and yet there is form
input, <BR>
# likely someone is trying to hack the system
<BR>
# * if no form input is encountered, it's the first access to
this page<BR>
# * view historical statistics<BR>
# * allow a registered user to vote<BR>
# * process form following voting<BR>
#<BR>
if ( ($ENV{HTTP_REFERER} ne $ref_url) && defined(%tokens)
) {<BR>
&referer_error;<BR>
}<BR>
&entry_page if !defined($tokens{'submit'});<BR>
&new_user($tokens{'email'}) if $tokens{'submit'} eq 'Register
a New User';<BR>
&view_stats if $tokens{'submit'} eq 'View Historical Statistics';
<BR>
&reg_user if $tokens{'submit'} eq 'Registered Users Proceed';
<BR>
&process_votes if $tokens{'submit'} eq 'Submit these Votes';
<BR>
<BR>
exit 0;<BR>
<BR>
#<BR>
# Some URL other than $ref_url attempted to make a form submission
<BR>
#<BR>
sub refer_error {<BR>
print <<END;<BR>
<HTML><HEAD><TITLE>Refering page error</TITLE></HEAD>
<BR>
<BODY><BR>
<H2>Refering Page Error</H2><BR>
<P><BR>
The page which is being used to access this CGI program is not
permitted<BR>
to invoke the program. Please use this program via:
<BR>
<P><BR>
<A HREF=$ref_page>$ref_page</A><BR>
END<BR>
}<BR>
<BR>
#<BR>
# Entry without form submission : throw up an HTML page which
presents<BR>
# a user with 3 area options. Note that a different
VALUE is associated<BR>
# with each INPUT TYPE=SUBMIT button. The Multistate
switcher in the<BR>
# main of this program looked for that value to determine which
state to<BR>
# employ<BR>
#<BR>
sub entry_page {<BR>
print "Content-type: text/html\n\n";
<BR>
print <<END;<BR>
<HTML><BR>
<HEAD><TITLE>Welcome to Richard's Vote-able Music
Database</TITLE></HEAD><BR>
<BODY><BR>
<H3>Choose one of the following options for interacting
with Richard's <BR>
Vote-able Music Database:</H3><BR>
<HR><BR>
<FORM METHOD=POST ACTION=$ref_url><BR>
<INPUT TYPE=SUBMIT NAME="submit" VALUE="View
Historical Statistics"><BR>
</FORM><BR>
<HR><BR>
<FORM METHOD=POST ACTION=$ref_url><BR>
<TABLE><BR>
<TR><TD VALIGN=TOP ALIGN=LEFT COLSPAN=1><B>Email
Address:</B></TD><BR>
<TD VALIGN=TOP ALIGN=LEFT COLSPAN=1><INPUT TYPE=TEXT
NAME="email"></TD><BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -