#!/usr/bin/perl use Socket; sub OutputError; sub OutputLoginPage; sub OutputHeader; sub OutputFooter; sub LoginToPOPServer; sub QueryServer; sub GetMessageInfo; sub HTMLize; sub OutputMessageInfo; sub OutputMessage; sub DeleteMessage; sub OutputMessageForm; sub SendMessage; sub ReadConfig; sub InterpolateVariables; sub HideString; sub UnhideString; sub OSSettings; sub ReadParse; ############################################################################ # This script is used to generate many different individual web pages, and # invoked separately at several different points in the process. These # possible points are: # # 1) Initially: query string is null. This script generates a login page. # 2) Login: the login page calls this script to do the mail check. # 3) Reply: this script is invoked to reply to a message (or a forward). # 4) Send: this script is invoked to send a new message. # # This section of the program is used to determine what behavior this # individual invocation of the script is to be responsible for, based # upon the query string. ############################################################################ # Do anything that is OS-specific. OSSettings(); # Set this to TRUE if your Perl interpreter supports "alarm". As of this # Writing, NT Perl does not. If this is not set, Mail will not be able # to timeout when a server hangs. The OSSettings() routine will attempt # to set this variable, but you can override it here if you want. # $UseAlarm = TRUE; # Retrieve some important variables to make them easier to use. ReadParse(); $login = UnhideString($in{"LOGIN"}); $hidden_login = HideString($login); $password = UnhideString($in{"PASSWORD"}); $hidden_password = HideString($password); $server = UnhideString($in{"SERVER"}); $hidden_server = HideString($server); $card_number = 1; # Read the configuration file. ReadConfig(); # Find a URL to this script so that Mail can call itself. $Me = $ENV{SCRIPT_NAME}; unless($Me =~ /^\//){ $Me = "/$Me"; } # Make sure it's rooted. # First check for orders from a form, from a previous instance of mailman. if($in{"MAILMAN_LOGIN"}) { QueryServer(); exit(0); } # If this is the result of a message display subform. elsif($in{"MAILMAN_DISPLAY"}) { OutputMessage($in{"ID"}); exit(0); } # If this is the result of a message delete subform. elsif($in{"MAILMAN_DELETE"}) { DeleteMessage($in{"ID"}); exit(0); } # If this is the result of a new message subform. elsif($in{"MAILMAN_NEW"}) { OutputMessageForm("NEW",0,0); exit(0); } # If this is the result of a reply subform. elsif($in{"MAILMAN_REPLY"}) { OutputMessageForm($in{"ID"},0,0); exit(0); } # If this is the result of a reply all subform. elsif($in{"MAILMAN_REPLYALL"}) { OutputMessageForm($in{"ID"},TRUE,0); exit(0); } # If this is the result of a forward subform. elsif($in{"MAILMAN_FORWARD"}) { OutputMessageForm($in{"ID"},0,TRUE); exit(0); } # If this is the result of a forward subform. elsif($in{"MAILMAN_SEND"}) { SendMessage(); exit(0); } # This is a new login. else { # If not all info is present, the login page must be presented # instead of simply querying the server. if($login && $password && $server) { QueryServer(); exit(0); } # If there is not a query string, and no configuration file, # or if there is no password in the configuration file, # then display the login page. OutputLoginPage(); exit(0); } # A general-purpose routine used to produce error message pages. sub OutputError { my($error_message) = @_; # If there is no parameter, then this routine was called # by the system as a timeout, so use the preset timeout message if($error_message eq "ALRM") { if($UseAlarm eq TRUE){ alarm(0); } $error_message = "$timeout_message"; # Finish the session gracefully. print SOCKET "QUIT\n"; close SOCKET; } # Produce the standard header. OutputHeader("MailMan Error: $error_message"); # Print the actual error. print "$error_message
"; # Produce the standard footer. OutputFooter(); # Quit the program. exit(1); } # Routine used to produce a user login page. sub OutputLoginPage { # Produce the standard header. OutputHeader("MailMan Login"); # Interpolate variables into the template. $HTMLLoginInterpolated = InterpolateVariables($HTMLLogin, ''=>$login, ''=>$password, ''=>$server); # Print the template. print $HTMLLoginInterpolated; # Product the standard footer. OutputFooter(); } # This produces the HTML declaration for the server, plus a standard # header from a file, or a default header if there is no file found. sub OutputHeader { my($title,$extra) = @_; # Only do this once. if($OutputHeaderUsed++) { return; } # No buffering on the output. $|=1; # Instruct everybody downstream not to cache this document. # print "Pragma: no-cache\n"; # print "Expires: Fri, 01 Jan 1983 00:00:00 GMT\n"; # Declare that this is HTML code. print "Content-Type: text/vnd.wap.wml\n\n"; # Spit out any extra headers that might be wanted. print "\n$extra\n"; # Print the template. print InterpolateVariables($HTMLHeader, '',$title); } # Just like OutputHeader, but at the bottom of the page. sub OutputFooter { # Only do this once. if($OutputFooterUsed++) { return; } print $HTMLFooter; } # This routine deals with the details of loggin in to a POP server. sub LoginToPOPServer { # Make sure that there is enough information to proceed. unless($login) { OutputError("No login provided, cannot proceed."); } unless($password) { OutputError("No password provided, cannot proceed."); } unless($server) { OutputError("No server provided, cannot proceed."); } # Set up a timeout for this operation. if($UseAlarm eq TRUE) { $timeout_message = "Connection to server timed out."; $SIG{'ALRM'} = \&OutputError; alarm(180); } # Attempt to open a socket to the POP3 server. $protocol = getprotobyname('tcp'); socket(SOCKET,PF_INET,SOCK_STREAM,$protocol); $remote_ip = gethostbyname($server); $remote_sock = pack('Sna4x8', AF_INET, 110, $remote_ip); unless(connect(SOCKET, $remote_sock)) { OutputError("Could not connect to server \"$server\""); } # No buffering on the socket. select(SOCKET); $|=1; select(STDOUT); # Set up the next timeout. $timeout_message = "The server connected, but will not respond."; if($UseAlarm eq TRUE){ alarm(180); } # Check to make sure that it looks like a POP3 server, # according to RFC 1725. unless( =~ /\+OK/) { OutputError("The server does not respond appropriately."); } # Reset the timeout clock. $timeout_message = "The server timed out during login."; if($UseAlarm eq TRUE){ alarm(180); } # Send the user name. print SOCKET "USER $login\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } print SOCKET "PASS $password\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } # Get a list from the server. print SOCKET "LIST\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } $MessageNumber = 0; while( =~ /(\d+) (\d+)/) { $MessageSize[$1] = $2; $MessageNumber ++; } } # This routine does the real work of connecting to the specified server. sub QueryServer { # Do the server login with the details provided. LoginToPOPServer(); # Produce the standard header. OutputHeader("Mailman: $login\@$server", ""); # Produce some output for starters. print InterpolateVariables($HTMLMessagesHeader, ''=>$MessageNumber, ''=>$login, ''=>$server, ''=>$hidden_login, ''=>$hidden_password, ''=>$hidden_server); # Reset the timeout clock. $timeout_message = "The server timed out during message listing."; if($UseAlarm eq TRUE){ alarm(180); } # Get a list from the server. print SOCKET "LIST\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } $MessageNumber = 0; while( =~ /(\d+) (\d+)/) { $MessageSize[$1] = $2; $MessageNumber ++; } # Cycle through the message list again, producing output. for($index=0;$index<$MessageNumber;$index++) { GetMessageInfo($index+1); $card_number++; print qq!
Next

!; OutputMessageInfo($index+1); } # Finish the session gracefully. print SOCKET "QUIT\n"; # Produce the messages footer. print $HTMLMessagesFooter; # Produce the standard footer. OutputFooter(); } sub GetMessageInfo { my($message) = @_; # Reset the timeout clock. $timeout_message = "The server timed out fetching a header."; if($UseAlarm eq TRUE){ alarm(10); } # Get a list from the server. print SOCKET "TOP $message 0\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } $To = ""; $Cc = ""; $From = ""; $Date = ""; $Subject = ""; $ReplyTo = ""; $ID = ""; while() { if(/^\.\r$/){ last; } if(/^To\: (.+)\r$/) { $To = $1; } if(/^[cC][cC]\: (.+)\r$/) { $Cc = $1; } if(/^From\: (.+)\r$/) { $From = $1; } if(/^Date\: (.+)\r$/) { $Date = $1; } if(/^Subject: (.+)\r$/) { $Subject = $1; } if(/^Reply-To: (.+)\r$/) { $ReplyTo = $1; } $ID .= $_; } # Finish creating message info field, which is a # fixed-length hash string. # Shorten the ID without throwing away any data. while(length($ID)>20) { $ID = (substr($ID,0,20) ^ substr($ID,20)); } $ID = pack("u*",$ID); $ID =~ s/(\W)/sprintf("%%%x", ord($1))/eg; # Clean up. unless($To){ $To = "Unknown";} unless($From){ $From = "Unknown";} unless($Date){ $Date = "Unknown";} unless($Subject){ $Subject = "Unspecified";} unless($ID){ $ID = "0";} # Next/Prev $PreviousID[$message+1] = $ID; $NextID[$message-1] = $ID; # Make these HTML-safe. $HTMLTo = HTMLize($To); $HTMLCc = HTMLize($Cc); $HTMLFrom = HTMLize($From); $HTMLSubject = HTMLize($Subject); } # This routine takes a string and makes it safe for display in HTML # by escaping out "<", ">", and "&"; sub HTMLize { my($string) = @_; # Detect email addresses. # (Escape the produced "<" and ">" characters to protect them.) $string =~ s/([^\s\<]+\@[^\s\r\,\;\>]+)/\%lta href\=\"mailto\:$1\"\%gt$1\%lt\/a\%gt/g; $string =~ s/\&/\&/g; $string =~ s/\/\>/g; # Un-escape the "<" and ">" characters from the email URLs. $string =~ s/\%lt/\/g; # URLs. $string =~ s/(http\:\S+)\s/\$1\<\/a\>/g; return $string; } sub OutputMessageInfo { my($message) = @_; # Once a source and date are known, print them. print InterpolateVariables($HTMLMessageInfo, ''=>$ID, ''=>$HTMLSubject, ''=>$MessageSize[$message], ''=>$HTMLFrom, ''=>$Date, ''=>$MessageNumber, ''=>$login, ''=>$server, ''=>$hidden_login, ''=>$hidden_password, ''=>$hidden_server); } sub OutputMessage { my($message_id) = @_; # Do the server login with the details provided. LoginToPOPServer(); # Produce the standard header. OutputHeader('MailMan Message', ""); # Reset the timeout clock. $timeout_message = "The server timed out during message listing."; if($UseAlarm eq TRUE){ alarm(180); } # Get a list from the server. print SOCKET "LIST\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } $MessageNumber = 0; while( =~ /(\d+) (\d+)/) { $MessageSize[$1] = $2; $MessageNumber ++; } # Cycle through the message list, finding headers. $MessageFound = 0; for($index=0;$index<$MessageNumber;$index++) { GetMessageInfo($index+1); # Check to see if the right message has been found. #if($ID eq $message_id || URI::Escape->uri_unescape($ID) eq $message_id) { if(unescapeURL($ID) eq unescapeURL($message_id)) { $message_id = $ID; $MessageFound = TRUE; last; } } # Get the next message header to find out what the ID is, for # a "Next" button. if($index+1<$MessageNumber && $MessageFound) { GetMessageInfo($index+2); # Re-fetch the current header. GetMessageInfo($index+1); } # Insert a CC field if there are Cc components. if($Cc) { $HTMLCCRow = "CC: $HTMLCc
\n"; } # Prepare prev/next buttons. $prevbutton = $nextbutton = ""; if($PreviousID[$index+1]) { $prevbutton = qq||; } if($NextID[$index+1]) { $nextbutton = qq||; } # If a message was found, spit it out. if($MessageFound) { print InterpolateVariables($HTMLMessageHeader, ''=>$ID, ''=>$HTMLTo, ''=>$HTMLFrom, ''=>$Date, ''=>$HTMLSubject, ''=>$HTMLCCRow, ''=>$prevbutton, ''=>$nextbutton, ''=>$hidden_login, ''=>$hidden_password, ''=>$hidden_server); # Reset the timeout clock. $timeout_message = "The server timed out retrieving a message."; if($UseAlarm eq TRUE){ alarm(180); } # Fetch the actual message. $pop_index = $index+1; print SOCKET "RETR $pop_index\n"; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } $PastHeader=0; while() { if(/^\.\r$/){ last; } if($PastHeader) { # Make HTML-safe. $safe_string = HTMLize($_); # Wrap big lines, but not unless they really need it. $safe_string =~ s/(.{1,86})\s+/$1\n/g; # The CR/LF vs LF thing. $safe_string =~ s/\r//g; print $safe_string; } if(/^\s$/){ $PastHeader=TRUE; } } } else { OutputError("Could not find the specified message."); } # Finish the session gracefully. print SOCKET "QUIT\n"; # print $HTMLMessageFooter; print InterpolateVariables($HTMLMessageFooter, ''=>$ID, ''=>$HTMLTo, ''=>$HTMLFrom, ''=>$Date, ''=>$HTMLSubject, ''=>$HTMLCCRow, ''=>$prevbutton, ''=>$nextbutton, ''=>$hidden_login, ''=>$hidden_password, ''=>$hidden_server); # Produce the standard footer. OutputFooter(); } sub DeleteMessage { my($message_id) = @_; # Do the server login with the details provided. LoginToPOPServer(); # Reset the timeout clock. $timeout_message = "The server timed out during message listing."; if($UseAlarm eq TRUE){ alarm(180); } # Get a list from the server. print SOCKET "LIST\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } $MessageNumber = 0; while( =~ /(\d+) (\d+)/) { $MessageSize[$1] = $2; $MessageNumber ++; } # Cycle through the message list, finding headers. $MessageFound = 0; for($index=0;$index<$MessageNumber;$index++) { GetMessageInfo($index+1); # Check to see if the right message has been found. if($ID eq $message_id) { $MessageFound = TRUE; last; } } # Reset the timeout clock. $timeout_message = "The server timed out deleting a message."; if($UseAlarm eq TRUE){ alarm(180); } # If a message was found, spit it out. if($MessageFound) { $pop_index = $index+1; print SOCKET "DELE $pop_index\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } } # Finish the session gracefully. print SOCKET "QUIT\n"; # Go back to the beginning and produce some output. QueryServer(); } sub OutputMessageForm { my($message_id,$reply_all,$forward) = @_; # Produce the standard header. OutputHeader('MailMan Message Form'); if($message_id ne "NEW") { # Do the server login with the details provided. LoginToPOPServer(); # Reset the timeout clock. $timeout_message = "The server timed out during message listing."; if($UseAlarm eq TRUE){ alarm(180); } # Get a list from the server. print SOCKET "LIST\n"; $return = ; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } $MessageNumber = 0; while( =~ /(\d+) (\d+)/) { $MessageSize[$1] = $2; $MessageNumber ++; } # Cycle through the message list, finding headers. $MessageFound = 0; for($index=0;$index<$MessageNumber;$index++) { GetMessageInfo($index+1); # Check to see if the right message has been found. if($ID eq $message_id) { $MessageFound = TRUE; last; } } # Set the header lines appropriately. $OriginalTo = $To; $OriginalSubject = $Subject; if($ReplyTo) { $To = $ReplyTo; } else { $To = $From; } if($reply_all) { $To .= ", $OriginalTo"; # Also anybody that got CC'd originally. if($Cc){ $To .= ", $Cc"; } } if($forward) { unless($Subject =~ /^[Ff]wd\:/){ $Subject = "Fwd: $Subject"; } $To = ""; } else { unless($Subject =~ /^[Rr]e\:/){ $Subject = "Re: $Subject"; } } } # Make sure that there is a name to display. unless($name){ $name = "$login\@$server"; } # Clean up the "To", "From" and "Subject" lines to make them safe. $To =~ s/\"//g; $Subject =~ s/\"//g; $name =~ s/\"//g; # If a message was found, spit it out. if($MessageFound || ($message_id eq "NEW")) { print InterpolateVariables($HTMLSendMessageHeader, ''=>$hidden_login, ''=>$hidden_password, ''=>$hidden_server, ''=>$To, ''=>$cc, ''=>$name, ''=>$Subject, ''=>$outgoing); if($message_id ne "NEW") { # Reset the timeout clock. $timeout_message = "The server timed out retrieving a message."; if($UseAlarm eq TRUE){ alarm(180); } # Fetch the actual message. $pop_index = $index+1; print SOCKET "RETR $pop_index\n"; unless($return =~ /\+OK/) { OutputError("Server Error: \"$return\""); } # Insert the user's signature if one was specified (forwards). if($forward) { print $signature; print InterpolateVariables($HTMLForwardHeader, ''=>$From, ''=>$OriginalTo, ''=>$Date, ''=>$OriginalSubject); } $PastHeader=0; while() { if(/^\.\s$/){ last; } if($PastHeader) { # The CR/LF vs LF thing. s/\r//g; print "> $_"; } if(/^\s$/){ $PastHeader=TRUE; } } } # Insert the user's signature if one was specified (replies). if(!$forward) { print "\n$signature"; } print InterpolateVariables($HTMLSendMessageFooter, ''=>$ID); } else { OutputError("Could not find the specified message."); } # Finish the session gracefully. if($message_id ne "NEW"){ print SOCKET "QUIT\n"; } # Produce the standard footer. OutputFooter(); } sub SendMessage { # Find the SMTP server name. $server = $in{"OUTGOING"}; unless($server) { OutputError("No server provided, cannot proceed."); } # Attempt to open a socket to the SMTP server. $protocol = getprotobyname('tcp'); socket(SOCKET,PF_INET,SOCK_STREAM,$protocol); $remote_ip = gethostbyname($server); $remote_sock = pack('Sna4x8', AF_INET, 25, $remote_ip); unless(connect(SOCKET, $remote_sock)) { OutputError("Could not connect to server."); } # No buffering on the socket. select(SOCKET); $|=1; select(STDOUT); # Set up the next timeout. $timeout_message = "The server connected, but will not respond."; if($UseAlarm eq TRUE){ alarm(180); } # Check to make sure that it looks like an SMTP server, # according to RFC 821. $return = ; unless($return =~ /^220.+/) { OutputError("The server does not respond appropriately."); } # Get the whole reply, in case it's multi-line. while($return =~ /^\d\d\d\-/) { $return = ; } # Send a greeting, using the client's machine name. # print SOCKET "HELO $ENV{REMOTE_HOST}\r\n"; print SOCKET "HELO $server\r\n"; $return = ; unless($return =~ /^250.+/) { OutputError("Server Error: \"$return\""); } # Get the whole reply, in case it's multi-line. while($return =~ /^\d\d\d\-/) { $return = ; } # Reset the timeout clock. $timeout_message = "The server timed out while accepting a message."; if($UseAlarm eq TRUE){ alarm(180); } # Send the source name. print SOCKET "MAIL FROM: <>\r\n"; $return = ; unless($return =~ /^250.+/) { OutputError("Server Error: \"$return\""); } # Get the whole reply, in case it's multi-line. while($return =~ /^\d\d\d\-/) { $return = ; } # Send each recipient. $in{"TO"} =~ s/\;/\,/g; # Allow ";"s to divide addresses. $all_recipients = $in{"TO"}; if($in{"CC"}){ $all_recipients .= qq|,$in{"CC"}|; } @Recipients = split(/[\,]/,$all_recipients); while($recipient = shift(@Recipients)) { if($recipient =~ /([^\s<]+@[^\s\r,>]+)/) { $recipient = '<' . $1 . '>'; print SOCKET "RCPT TO: $recipient\r\n"; $return = ; unless($return =~ /^250.+/) { OutputError("Server Error: \"$return\""); } # Get the whole reply, in case it's multi-line. while($return =~ /^\d\d\d\-/) { $return = ; } } } print SOCKET "DATA\n"; $return = ; unless($return =~ /^354.+/) { OutputError("Server Error: \"$return\""); } # Get the whole reply, in case it's multi-line. while($return =~ /^\d\d\d\-/) { $return = ; } # Build an RFC 822 date. @days = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); @months = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime(time); $wday = $days[$wday]; $mon = $months[$mon]; $hour = sprintf("%2.2d",$hour); $min = sprintf("%2.2d",$min); $sec = sprintf("%2.2d",$sec); $datestr = "$wday, $mday $mon $year $hour:$min:$sec +0000"; # Build the message header. $Message = qq|To: $in{"TO"}\r\n|; if($in{"CC"}){ $Message .= qq|Cc: $in{"CC"}\r\n|; } $Message .= qq|From: $in{"FROM"}\r\n|; $Message .= qq|Subject: $in{"SUBJECT"}\r\n|; $Message .= qq|Date: $datestr\r\n|; $Message .= "X-Mailer: Endymion MailMan v1.1\r\n"; $Message .= "X-Mailer-Info: http://www.endymion.com/portfolio/software/scripts/mailman.htm\r\n"; $Message .= "\r\n"; # The message itself. $Message .= $in{"TEXT"}; # Clean up after DOS, if necessary. # $Message =~ s/\r\n?/\n/g; print SOCKET $Message; # Terminate the message. print SOCKET "\r\n.\r\n"; $return = ; unless($return =~ /^250.+/) { OutputError("Server Error: \"$return\""); } # Get the whole reply, in case it's multi-line. while($return =~ /^\d\d\d\-/) { $return = ; } # Finish the connection. print SOCKET "QUIT\r\n"; # Close connection. close SOCKET; # Produce a response message. OutputHeader('MailMan Message Sent', ""); print InterpolateVariables($HTMLSendAck, ''=>$hidden_login, ''=>$hidden_password, ''=>$hidden_server); OutputFooter(); # End gracefully. exit(0); } ############################################################################ # Password Obfuscation: # Since some browsers and proxy servers may not pay the proper attention # to 'Don't Cache' directives (MSIE comes to mind) and since this script # may not always be run through SSL, an effort is made here to obscure # passwords. This is obviously very weak as encryption goes, but it's # better than simply transmitting the passwords in cleartext. If you # are concerned about using stronger encryption for protecting passwords, # then you can easily drop your better encryption package in by modifying # these two functions. The only requirement for replacement functions # is that UnhideString must be able to recognize cleartext and return # it unmodified. This implementation of UnhideString does this by looking # for a signature tag of "%%%%" at the beginning and end of a cipher string. ############################################################################ # Build a key which will be unique for the host/server pair, but # Will always be the same for that pair. You specify your own length. sub ConnectionKey { my($desired_length) = @_; $FirstPart = crypt($ENV{SERVER_NAME},42); $SecondPart = crypt($ENV{REMOTE_HOST},69); # Trim the key into the proper length and return it. $key = $FirstPart ^ $SecondPart; if(length($key)==$desired_length) { return($key); } elsif(length($key)>$desired_length) { return(substr($key,0,$desired_length)); } else { while(length($key)<$desired_length) { $key = "$key$key"; } return(substr($key,0,$desired_length)); } } sub ReadConfig { # If there is a configuration file, use it for login information. if(open(CONFIG,") { if(/^LOGIN\=(.*)$/) { $login = $1 unless $login; $hidden_login = HideString($login); } elsif(/^PASSWORD\=(.*)$/) { $password = $1 unless $password; $hidden_password = HideString($password); } elsif(/^SERVER\=(.*)$/) { $server = $1 unless $server; $hidden_server = HideString($server); } elsif(/^OUTGOING\=(.*)$/) { $outgoing = $1; } elsif(/^NAME\=(.*)$/) { $name = $1; } elsif(/^CC\=(.*)$/) { $cc = $1; } elsif(/^SIGNATURE_BEGIN/) { SignatureLoop: while() { if(/^SIGNATURE_END$/){ last SignatureLoop; } else { $signature .= $_; } } } elsif(/^HEADER_BEGIN/) { $HTMLHeader = ""; header_loop: while() { if(/^HEADER_END/){ last header_loop; } else { $HTMLHeader .= $_; } } } elsif(/^FOOTER_BEGIN/) { $HTMLFooter = ""; footer_loop: while() { if(/^FOOTER_END/){ last footer_loop; } else { $HTMLFooter .= $_; } } } elsif(/^LOGIN_BEGIN/) { $HTMLLogin = ""; login_loop: while() { if(/^LOGIN_END/){ last login_loop; } else { $HTMLLogin .= $_; } } } elsif(/^MESSAGES_HEADER_BEGIN/) { $HTMLMessagesHeader = ""; messages_header_loop: while() { if(/^MESSAGES_HEADER_END/){ last messages_header_loop; } else { $HTMLMessagesHeader .= $_; } } } elsif(/^MESSAGES_FOOTER_BEGIN/) { $HTMLMessagesFooter = ""; messages_footer_loop: while() { if(/^MESSAGES_FOOTER_END/){ last messages_footer_loop; } else { $HTMLMessagesFooter .= $_; } } } elsif(/^MESSAGE_INFO_BEGIN/) { $HTMLMessageInfo = ""; message_info_loop: while() { if(/^MESSAGE_INFO_END/){ last message_info_loop; } else { $HTMLMessageInfo .= $_; } } } elsif(/^MESSAGE_HEADER_BEGIN/) { $HTMLMessageHeader = ""; message_header_loop: while() { if(/^MESSAGE_HEADER_END/){ last message_header_loop; } else { $HTMLMessageHeader .= $_; } } } elsif(/^MESSAGE_FOOTER_BEGIN/) { $HTMLMessageFooter = ""; message_footer_loop: while() { if(/^MESSAGE_FOOTER_END/){ last message_footer_loop; } else { $HTMLMessageFooter .= $_; } } } elsif(/^SEND_MESSAGE_HEADER_BEGIN/) { $HTMLSendMessageHeader = ""; send_message_header_loop: while() { if(/^SEND_MESSAGE_HEADER_END/){ last send_message_header_loop; } else { $HTMLSendMessageHeader .= $_; } } } elsif(/^SEND_MESSAGE_FOOTER_BEGIN/) { $HTMLSendMessageFooter = ""; send_message_footer_loop: while() { if(/^SEND_MESSAGE_FOOTER_END/){ last send_message_footer_loop; } else { $HTMLSendMessageFooter .= $_; } } } elsif(/^FORWARD_HEADER_BEGIN/) { $HTMLForwardHeader = ""; forward_header_loop: while() { if(/^FORWARD_HEADER_END/){ last forward_header_loop; } else { $HTMLForwardHeader .= $_; } } } elsif(/^SEND_ACK_BEGIN/) { $HTMLSendAck = ""; send_ack_loop: while() { if(/^SEND_ACK_END/){ last send_ack_loop; } else { $HTMLSendAck .= $_; } } } } close(CONFIG); } # Fill in defaults for any HTML templates that are not present $HTMLHeader = <<'EndOfHeader' unless $HTMLHeader; <PAGE_TITLE> EndOfHeader $HTMLFooter = <<'EndOfFooter' unless $HTMLFooter; EndOfFooter $HTMLLogin = <<'EndOfLogin' unless $HTMLLogin;

MailMan

Welcome to MailMan! For more information about what this program is, and what it does, see the MailMan page at Endymion Corporation. To check your mail now, login here:
Login:
Password:
Server:

EndOfLogin $HTMLMessagesHeader = <<'EndOfMessagesHeader' unless $HTMLMessagesHeader; @
|

This account has messages:

EndOfMessagesHeader $HTMLMessagesFooter = <<'EndOfMessagesFooter' unless $HTMLMessagesFooter;
EndOfMessagesFooter $HTMLMessageInfo = <<'EndOfMessageInfo' unless $HTMLMessageInfo; Subject:
From:
Date:
Size:
-----------------------

EndOfMessageInfo $HTMLMessageHeader = <<'EndOfMessageHeader' unless $HTMLMessageHeader;
|

| | |

Subject:
From:
To:

Date:
EndOfMessageHeader $HTMLMessageFooter = <<'EndOfMessageFooter' unless $HTMLMessageFooter; EndOfMessageFooter $HTMLSendMessageHeader = <<'EndOfSendMessageHeader' unless $HTMLSendMessageHeader; To:
CC:
From:
Subject:
SMTP Server:


| EndOfSendMessageFooter $HTMLForwardHeader = <<'EndOfForwardHeader' unless $HTMLForwardHeader; Forwarded Message Follows: From: To: Date: Subject: ----- EndOfForwardHeader $HTMLSendAck = <<'EndOfSendAck' unless $HTMLSendAck;

Your message has been sent.

Note: Don't reload this page. If you do, your message may be sent again, depending upon your browser's form reloading behavior.
EndOfSendAck } # This routine is used to interpolate variables into the HTML # templates specified in a configuration file. sub InterpolateVariables { my($string,@variables) = @_; my($name,$interpolant); # Process each pair in the input list. while($name=shift(@variables)) { $interpolant = shift(@variables); # Do the substitution. $string =~ s/$name/$interpolant/g; } # This should *always* be interpolated. $string =~ s//$Me/g; return $string; } # Takes , returns . sub HideString { my($cleartext) = @_; $key = ConnectionKey(length($cleartext)); $ciphertext = $cleartext ^ $key; $ciphertext = pack("u*",$ciphertext); chop($ciphertext); $ciphertext =~ s/(\W)/sprintf("%%%x", ord($1))/eg; return "%%%%$ciphertext%%%%"; } # Takes , returns . sub UnhideString { my($ciphertext) = @_; if($ciphertext =~ /\%\%\%\%(.+)\%\%\%\%/) { $ciphertext = $1; } else { return $ciphertext; } $ciphertext =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $ciphertext = unpack("u*",$ciphertext); $key = ConnectionKey(length($ciphertext)); $cleartext = $ciphertext ^ $key; return $cleartext; } # Do anything that is OS-specific. This is an attempt to make # porting easier for people that don't actually read my code. sub OSSettings { # Detect the OS. eval '$os = `uname`'; $os = 'Unix' if $os; # if you just need generic OS $os = 'Mac' if defined( $MacPerl'Version ); $os = 'NT' if ($] =~ /NT/) && ! $os; $os = 'Dos' unless $os; if($os eq 'Unix') { $UseAlarm = TRUE; } elsif($os eq 'Mac') { # Is this right? Does MacPerl do alarms? $UseAlarm = FALSE; } elsif($os eq 'NT') { $UseAlarm = FALSE; } elsif($os eq 'Dos') { $UseAlarm = FALSE; } } # This routine is originally from cgi-lib.pl, by S.E.Brenner@bioc.cam.ac.uk # Copyright notice from cgi-lib.pl: ############################################################ # Copyright 1994 Steven E. Brenner # Unpublished work. # Permission granted to use and modify this library so long as the # copyright above is maintained, modifications are documented, and # credit is given for any use of the library. # # Thanks are due to many people for reporting bugs and suggestions # especially Meng Weng Wong, Maki Watanabe, Bo Frese Rasmussen, # Andrew Dalke, Mark-Jason Dominus and Dave Dittrich. # # For more information, see: # http://www.bio.cam.ac.uk/web/form.html # http://www.seas.upenn.edu/~mengwong/forms/ ############################################################ sub ReadParse { local (*in) = @_ if @_; local ($i, $key, $val); # Read in text read(STDIN,$in,$ENV{'CONTENT_LENGTH'}); @in = split(/&/,$in); foreach $i (0 .. $#in) { # Convert plus's to spaces $in[$i] =~ s/\+/ /g; # Split into key and value. ($key, $val) = split(/=/,$in[$i],2); # splits on the first =. # Convert %XX from hex numbers to alphanumeric $key =~ s/%(..)/pack("c",hex($1))/ge; $val =~ s/%(..)/pack("c",hex($1))/ge; # Check for special conditions (MailMan only!) if($key =~ /SHOW:(\S+)$/) { $in{'MAILMAN_DISPLAY'} = 'Show'; $in{'ID'} = $1; } if($key =~ /DELETE:(\S+)$/) { $in{'MAILMAN_DELETE'} = 'Delete'; $in{'ID'} = $1; } if($key =~ /REPLY:(\S+)$/) { $in{'MAILMAN_REPLY'} = 'Reply'; $in{'ID'} = $1; } if($key =~ /REPLYALL:(\S+)$/) { $in{'MAILMAN_REPLYALL'} = 'Reply All'; $in{'ID'} = $1; } if($key =~ /FORWARD:(\S+)$/) { $in{'MAILMAN_FORWARD'} = 'Forward'; $in{'ID'} = $1; } # Associate key and value $in{$key} .= "\0" if (defined($in{$key})); # \0 is the multiple separator $in{$key} .= $val; } return length($in); } sub unescapeURL { # Note from RFC1630: "Sequences which start with a percent sign # but are not followed by two hexadecimal characters are reserved # for future extension" my $str = shift; if (@_ && wantarray) { # not executed for the common case of a single argument my @str = @_; # need to copy return map { s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg } $str, @str; } $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; $str; }