#!/usr/bin/perl -w $| = 1; # Phil's Webmail # http://chezphil.org/pwebmail/ # Scroll down about 100 lines for things to configure. # Based originally on webmail version 2.3.14 by Jason Woodward # Comprehensively modified by Phil Endecott # (C) the respective authors # This is version $Name$ # (if there is no version (e.g. V2-2) mentioned in the previous line, # this is probably a snapshot from between "official" releases.) # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # TODO: # Ought to check for and report any errors while deleting messages. # Reply-to-all doesn't work properly; it only includes people who were # cc'd in the original message, not people who were also in the to: # line. # Ought to do something about in-reply-to: and refereces: headers so that # threading works. # Clicking on an email address in a message opens a composition # window, but in the same frame, not in a popup. # The from: address menu doesn't set the corresponding text entry when # used with Internet Explorer (javascript problem?). # The following paragraphs document one of the more cryptic aspects of # the code, in case I forget how it works. Users don't need to worry # about this stuff. # Indicating attachments # In various situations it is necessary to indicate a particular # attachment; for example, when creating an IMG tag in a view of a # message, the SRC attribute gives an action=getattachment URI that # has to indicate which of the message's attachments to return. # "Attachment indices" are used for this. # An attachment index is a comma-separated list of numbers - though # typically it will be a 1-element list. For a flat multipart message # the index is the number of the MIME part containing the attachment, # counting from the start of the message at zero. When the MIME # structure is not flat, such as when a message with attachments has # been forwarded as an attachment, the elements of the attachment # index indicate a path to the attachment part. # To generate attachment indicies, list of part numbers are passed # around, with a number occasionally added, and then joined with # commas. # Forwarding attachments # If you forward a message that has attachments, you can choose # whether or not to forward them. To do this an attachment code is # formed for each attachment, and checkboxes in the message # composition form return a set of these codes indicating which # attachments the user wishes to forward. These codes indicate the # forwarded message's UIDL, the attachment index, the MIME type, and # the filename, all of which are needed for forwarding. The codes are # formed by joining these elements with /s. use lib '.'; use CGI qw(:standard escapeHTML unescapeHTML); use CGI::Carp qw(fatalsToBrowser); use Mail::POP3Client; use MIME::Lite; use Net::SMTP; use MIME::Parser; use URI::Escape; use URI::Find; use Text::Wrap; use Date::Parse; use POSIX qw(strftime); use MIME::Words qw(decode_mimewords); use strict; $Text::Wrap::columns = 80; # Set the following variables to suit your local setup # ---------------------------------------------------- # Note that Perl syntax requires a \ before @ in the all the following # variables. # How to connect to the POP server my $popserver="MY-POP-SERVER"; my $username="MY-USERNAME"; my $password="MY-PASSWORD"; # Full name of user (appears in From: headers and also in the title of # the web pages). my $fullname="Fred Bloggs"; # Identity information used to initialise the From: field in outgoing # messages. # The From: field has a menu listing possible addresses. The following # addresses are always included. my @emailaddresses=("ME\@MY-DOMAIN","OTHER\@ELSEWHERE"); # Anti-spam From: addresses # ------------------------- # (You can skip this section at first if you aren't sure about it.) # You can choose to have a random address in the menu of from # addresses (useful as an anti-spam measure if you have your own # domain.) See the random_addr function for details. This is the # template used to generate the addresses. RAND is replaced with a # random string. my $rand_addr_template="RAND\@MY-DOMAIN"; # You can also choose to have a non-random anti-spam From: address in # replies; when you get a message from someone@xyz.com, you can have # your reply come from an address like "spam_from_xyz@MY-DOMAIN". # This line defines a template used to generate these addresses. # DOMAIN1 is replaced with the first element of the domain of the # original message's from: line, e.g. xyz for xyz.com. my $spam_from_template="spam-from-DOMAIN1\@MY-DOMAIN"; # This regular expression should match all To: or Cc: addresses in an # incoming message that may be refering to this user. Any address matching # this will be added to the menu of From: addresses in a reply. my $my_addrs_re='\@MY_DOMAIN$'; # See the definition of @from_addr_list in the create_common function # for how the From: address menu is composed. If you want to add # extra from address functions, or remove the ones defined here, that # is the place to do it. # End of anti-spam from: address stuff # ------------------------------------ # Default address that emails are BCCed to (so that you have copies of # everything you send). Make it "" if you don't want to get copies. my $bcc_address="ME\@MY-DOMAIN"; # SMTP server to use for outgoing messages my $smtpserver="MY-SMTP-SERVER"; # Name of the machine running the CGI process, as announced to the # SMTP server. my $webserver="MY-DOMAIN"; # Base URI for this CGI script my $scriptbaseuri="http://MY-DOMAIN/webmail.cgi"; # Configuration for the message list page # Order with newest first or oldest first? # 1 = newest first, 0 = oldest first my $NEWESTFIRST=1; # How many messages per page? my $MAXMESSLISTNUM=20; # Enable QWAZERTY keyboard mapping (http://chezphil.org/qwazerty/) my $use_qwazerty=1; # Enable Speller Pages spell checking (http://spellerpages.sourceforge.net) my $use_speller=1; # If you want, rather than changing the values of all these variables # by editting this script, you can set then in an external perl file # that is included at this point. This makes it easier to upgrade to # newer versions of the script, but might marginally slow down # execution. If you don't use an external file, comment out or remove # the following line. eval `cat ./config.pl`; # End of configuration variables. # ------------------------------- $MAIN::POPAUTHMODE = "BEST"; my $parity=0; my $attachment1 = param("attachment1"); my $attachment2 = param("attachment2"); my $attachment3 = param("attachment3"); my $attachment4 = param("attachment4"); my $attachment5 = param("attachment5"); my @attachattachments = param("attachattachments"); my $action = param("action"); my $next_action = param("next_action"); my $uidl = param("uidl"); my $next_uidl = param("next_uidl"); my @deluidls = param("deluidls"); my @tos = param("to"); my $from = param("from"); my $subject = param("subject"); my $replyto = param("replyto"); my $mbody = param("mbody"); my $bcc = param("bcc"); my $cc = param("cc"); my $attachmentindex = param("attachmentindex"); my $lastdisplayed = param("lastdisplayed"); my $showheaders = param("showheaders"); my $listall = param("listall"); my $to = join(", ",@tos); my $header_printed=0; my $page_can_be_cached; if(!defined($action)) { #$action="listmessages"; $action="showframes"; } my $pop; if ($action eq "getattachment") { $pop = open_pop3_connection($username,$password,1); &getattachment(\$pop,$uidl,$attachmentindex); exit 0; } else { if ($action eq "sendmessage") { sendmessage($to,$from,$mbody,$replyto,$subject,$cc,$bcc, $attachment1,$attachment2,$attachment3,$attachment4, $attachment5,@attachattachments); $action="closewindow"; } if ($action eq "showframes") { &showframes(); exit 0; } if ($action eq "closewindow") { &closewindow(); exit 0; } if ($action eq "deletemessage") { $pop = open_pop3_connection($username,$password,1); if($deluidls[0]){ foreach(@deluidls){ &deletemessage(\$pop,$_); } }else{ &deletemessage(\$pop,$uidl); } $pop->Close(); $action=$next_action; $uidl=$next_uidl; } if ($action eq "blank") { &print_header(0); &print_footer(); exit 0; } $pop = open_pop3_connection($username,$password,0); if ($action eq "listmessages") { &listmessages(\$pop,$lastdisplayed,$listall); } elsif ($action eq "readmessage") { &readmessage(\$pop,$uidl,$showheaders); } elsif ($action eq "createmessage") { &createmessage($to); } elsif ($action eq "replymessage") { &replymessage(\$pop,$uidl); } elsif ($action eq "replyallmessage") { &replyallmessage(\$pop,$uidl); } elsif ($action eq "forwardmessage") { &forwardmessage(\$pop,$uidl); } else { &errorpage("ERROR!! NOTHING TO DO"); } &print_footer(); } ### close any open connections if($pop){$pop->Close();} ### clear all params (mod_perl) Delete_all(); ### goodbye 1; # If an error occurs, this MAY get called, if we're lucky, to close # any POP connection. That may save us from having to wait for the # POP server to time out before we can try again. END { if ($pop) { $pop->Close(); } } ### Open a connection to the POP3 server sub open_pop3_connection { my($username,$password,$retry_if_locked) = @_; my $tries = 0; while (1) { my $pop = new Mail::POP3Client(USER => $username, PASSWORD => $password, HOST => $popserver, AUTH_MODE => $MAIN::POPAUTHMODE ); my $servermessage = $pop->Message; if ($servermessage =~ /^\+OK/) { return $pop; } my $errormsg; if ($servermessage =~ m/ERR(.*)/) { $errormsg = $1; } else { $errormsg = "no error message available"; } if ($errormsg =~ m/lock/i) { if ($retry_if_locked) { $tries++; if ($tries<150) { sleep 2; next; } else { $errormsg .= " after $tries attempts"; } } } &errorpage("POP3 connection failed: $errormsg"); } } sub errorpage { my ($msg) = @_; &print_header(0); print qq!
$msg
!; &print_footer(); if($pop){$pop->Close();} exit; } sub createmessage { # Starting point when action=createmessage my ($to) = @_; &create_common($to,"","",""); } sub create_common { # Shared code used to create a message composition window used for # create, reply, reply-all and forward. # Takes initial values for the To: and Subject: header fields, the # message body, and codes representing a message's attachments to be # presented for possible sending. my ($to,$orig_to,$subject,$mbody,@attachment_codes) = @_; $to = escapeHTML($to); $subject = escapeHTML($subject); $mbody = escapeHTML($mbody); &print_header(1,"compose"); print qq! !; } sub listmessages { # Starting point when action=listmessages. my ($pop_ref,$lastdisplayed,$listall) = @_; my ($firstmessage,$lastmessage,@message_index); my @messagesizes = $$pop_ref->ListArray(); my $message_count = $$pop_ref->Count(); my $messlistnum; if ($listall) { $messlistnum=2000; } else { $messlistnum=$MAXMESSLISTNUM; } if ($NEWESTFIRST) { if($lastdisplayed){ $firstmessage = ($lastdisplayed - 1); $lastmessage = 1; if(($lastdisplayed - $messlistnum) > 0){ $lastmessage = ($lastdisplayed - $messlistnum); } }else{ $firstmessage = $message_count; $lastmessage = 1; if($firstmessage > $messlistnum){ $lastmessage = ($firstmessage - $messlistnum) + 1; } } }else { if($lastdisplayed){ $firstmessage = ($lastdisplayed + 1); $lastmessage = $message_count; if(($lastmessage - $lastdisplayed) > $messlistnum){ $lastmessage = ($lastdisplayed + $messlistnum); } }else{ $firstmessage = 1; $lastmessage = $message_count; if($lastmessage > $messlistnum){ $lastmessage = $messlistnum; } } } @message_index = $NEWESTFIRST ? reverse ( $lastmessage .. $firstmessage ) : ( $firstmessage .. $lastmessage ); &print_header(0); print qq@ |; } sub list_message { my ($from,$subject,$dateofmess,$sizeofmess,$uidl) = @_; $from =~ s/\(.*\)//g; if($from eq '' || !$from){ $from = "No From Address"; } $from=escapeHTML($from); $subject=escapeHTML($subject); $dateofmess=escapeHTML($dateofmess); my $uri_escaped_uidl=&my_uri_escape($uidl); my $html_escaped_uidl=escapeHTML($uidl); my $idsubstr=&get_idsubstr($uidl); my $readlink = qq||; print qq!