Viewing file: index.cgi (115.98 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#!/usr/bin/perl
use strict; no strict 'vars'; $usrname = ( getpwuid($<) )[0]; push (@INC, "/home/$usrname/.webmail"); use CGI qw(:standard); use CGI::Carp qw(fatalsToBrowser); use Fcntl qw(:DEFAULT :flock); use FileHandle; use MD5; BEGIN { @AnyDBM_File::ISA = qw(DB_File GDBM_File) } use AnyDBM_File;
if($usrname eq "nobody"){ print "Content-Type: text/html\n\n"; print "<b>You must have suEXEC enabled on your account for this program to function.</b><br><p><br>Enabeling suEXEC is simple, you need to log onto <a href=http://admin.he.net/><b>admin.he.net</b></a> with your account username and password, Then go to the section titled \"Edit Virtual Host Options\" select \"suEXEC\" click change and by the <b>next day</b> it will be enabled."; exit 0; }
CGI::nph(); # Treat script as a non-parsed-header script
$ENV{PATH} = ""; # no PATH should be needed umask(0007);
require "/usr/local/webmail/webmail.conf";
my $version = "1.25";
# Atrribute name to array index mapping for the new DB routines. ($_OFFSET, $_FROM, $_TO, $_DATE, $_SUBJECT, $_CONTENT_TYPE, $_STATUS, $_SIZE) = (0,1,2,3,4,5,6,7);
# %month is used in the getheaders() sub to convert localtime() dates to # a better format for readability and sorting. my %month = qw(Jan 1 Feb 2 Mar 3 Apr 4 May 5 Jun 6 Jul 7 Aug 8 Sep 9 Oct 10 Nov 11 Dec 12);
my %timezones = qw(ACDT +1030 ACST +0930 ADT -0300 AEDT +1100 AEST +1000 AHDT -0900 AHST -1000 AST -0400 AT -0200 AWDT +0900 AWST +0800 AZST +0400 BAT +0300 BDST +0200 BET -1100 BST -0300 BT +0300 BZT2 -0300 CADT +1030 CAST +0930 CAT -1000 CCT +0800 CDT -0500 CED +0200 CET +0100 CST -0600 EAST +1000 EDT -0400 EED +0300 EET +0200 EEST +0300 EST -0500 FST +0200 FWT +0100 GMT +0000 GST +1000 HDT -0900 HST -1000 IDLE +1200 IDLW -1200 IST +0530 IT +0330 JST +0900 JT +0700 MDT -0600 MED +0200 MET +0100 MEST +0200 MEWT +0100 MST -0700 MT +0800 NDT -0230 NFT -0330 NT -1100 NST +0630 NZ +1100 NZST +1200 NZDT +1300 NZT +1200 PDT -0700 PST -0800 ROK +0900 SAD +1000 SAST +0900 SAT +0900 SDT +1000 SST +0200 SWT +0100 USZ3 +0400 USZ4 +0500 USZ5 +0600 USZ6 +0700 UT +0000 UTC +0000 UZ10 +1100 WAT -0100 WET +0000 WST +0800 YDT -0800 YST -0900 ZP4 +0400 ZP5 +0500 ZP6 +0600);
my $thissession = param("sessionid") || ''; my $user = $thissession || ''; $user =~ s/\-session\-0.*$//; # Grab userid from sessionid ($user =~ /^(.+)$/) && ($user = $1); # untaint $user...
my $setcookie = undef;
my ($login, $pass, $uid, $gid, $homedir); if ($user) { if (($homedirspools eq 'yes') || ($homedirfolders eq 'yes')) { ($login, $pass, $uid, $gid, $homedir) = (getpwnam($user))[0,1,2,3,7] or webmailerror("User $user doesn't exist!"); $gid = getgrnam('users'); } }
my %prefs = %{&readprefs}; my %style = %{&readstyle};
my $lang = $prefs{'language'} || $defaultlanguage; ($lang =~ /^(..)$/) && ($lang = $1); require "$masterdir/lang/$lang"; $lang_charset ||= 'iso-8859-1';
my $numberofheaders = $prefs{'numberofmessages'} || $numberofheaders;
my $firstmessage; if (param("firstmessage")) { $firstmessage = param("firstmessage"); } else { $firstmessage = 1; }
my $sort; if (param("sort")) { $sort = param("sort"); } else { $sort = $prefs{"sort"} || 'date'; }
my $hitquota = 0;
my $folderdir; if ( $homedirfolders eq 'yes') { $folderdir = "$homedir/mail"; } else { $folderdir = "$userprefsdir/$user"; }
my $folder; my @validfolders; if ($user) { my $isvalid = 0; @validfolders = @{&getfolders()}; if (param("folder")) { $folder = param("folder"); foreach my $checkfolder (@validfolders) { if ($folder eq $checkfolder) { $isvalid = 1; last; } } ($folder = 'INBOX') unless ( $isvalid ); } else { $folder = "INBOX"; } }
my $printfolder = $lang_folders{$folder} || $folder || ''; my $escapedfolder = urlescape($folder);
$sessiontimeout = $sessiontimeout/60/24; # convert to format expected by -M
# once we print the header, we don't want to do it again if there's an error my $headerprinted = 0; my $total_size = 0; my $savedattsize; my $validsession = 0;
########################## MAIN ############################## if (param()) { # an action has been chosen my $action = param("action"); if ($action =~ /^(\w+)$/) { $action = $1; if ($action eq "login") { login(); } elsif ($action eq "displayheaders") { displayheaders(); } elsif ($action eq "readmessage") { readmessage(); } elsif ($action eq "emptytrash") { emptytrash(); } elsif ($action eq "viewattachment") { viewattachment(); } elsif ($action eq "composemessage") { composemessage(); } elsif ($action eq "sendmessage") { sendmessage(); } elsif ($action eq "movemessage") { movemessage(); } elsif ($action eq "logout") { logout(); } else { webmailerror("Action $lang_err{'has_illegal_chars'}"); } } else { webmailerror("Action $lang_err{'has_illegal_chars'}"); } } else { # no action has been taken, display login page printheader(), my $html=''; my $temphtml; open (LOGIN, "$templatesdir/$lang/login.template") or webmailerror("$lang_err{'couldnt_open'} login.template!"); while (<LOGIN>) { $html .= $_; } close (LOGIN);
$html = applystyle($html);
$temphtml = startform(-action=>$scripturl, -name=>'login'); $temphtml .= hidden("action","login"); $html =~ s/\@\@\@STARTFORM\@\@\@/$temphtml/; $temphtml = textfield(-name=>'userid', -default=>'', -size=>'10', -override=>'1'); $html =~ s/\@\@\@USERIDFIELD\@\@\@/$temphtml/; $temphtml = password_field(-name=>'password', -default=>'', -size=>'10', -override=>'1'); $html =~ s/\@\@\@PASSWORDFIELD\@\@\@/$temphtml/; $temphtml = submit("$lang_text{'login'}"); $html =~ s/\@\@\@LOGINBUTTON\@\@\@/$temphtml/; $temphtml = reset("$lang_text{'clear'}"); $html =~ s/\@\@\@CLEARBUTTON\@\@\@/$temphtml/; $temphtml = end_form(); $html =~ s/\@\@\@ENDFORM\@\@\@/$temphtml/; print $html; } ###################### END MAIN ##############################
####################### LOGIN ######################## sub login { my $userid = param("userid") || ''; $userid = lc( $userid ); my $password = param("password") || ''; $userid =~ /^(.*)$/; # accept any characters for userid/pass auth info $userid = $1; $password =~ /^(.*)$/; $password = $1;
# Checklogin() is modularized so that it's easily replaceable with other # auth methods. if ($userid eq 'root') { writelog("ATTEMPTED ROOT LOGIN"); webmailerror ("$lang_err{'norootlogin'}"); }
if ( ( -x "$masterdir/checklogin.pl" ) && (eval { open (CHECKLOGIN,"| $masterdir/checklogin.pl"); print CHECKLOGIN @domainnames,"\n"; print CHECKLOGIN "$userid\n"; print CHECKLOGIN "$password\n"; close (CHECKLOGIN); } ) ) { $thissession = $userid . "-session-" . rand(); # name the sessionid $user = $userid; writelog("login - $thissession");
cleanupoldsessions(); # Deletes sessionids that have expired
$setcookie = crypt(rand(),'NM'); open (SESSION, '>' . $webmaildir . $thissession) or # create sessionid webmailerror("$lang_err{'couldnt_open'} $thissession!"); print SESSION "$setcookie"; close (SESSION);
if (($homedirspools eq 'yes') || ($homedirfolders eq 'yes')) { ($login, $pass, $uid, $gid, $homedir) = (getpwnam($user))[0,1,2,3,7] or webmailerror("User $user doesn't exist!"); $gid = getgrnam('users'); }
if ( $homedirfolders eq 'yes') { $folderdir = "$homedir/mail"; unless ( -d $folderdir ) { mkdir ($folderdir, oct(700)) or webmailerror($lang_err{'cant_create_dir'}); chown ($uid, $gid, $folderdir); } } else { $folderdir = "$userprefsdir/$user"; }
if ( -d "$userprefsdir$user" ) { %prefs = %{&readprefs}; %style = %{&readstyle}; $lang = $prefs{'language'} || $defaultlanguage; ($lang =~ /^(..)$/) && ($lang = $1); require "$masterdir/lang/$lang"; $lang_charset ||= 'iso-8859-1'; $sort = $prefs{"sort"} || 'date'; $numberofheaders = $prefs{'numberofmessages'} || $numberofheaders; my $isvalid = 0; @validfolders = @{&getfolders()}; $folder = "INBOX"; displayheaders(); } else { firsttimeuser(); } } else { # Password is INCORRECT my $html = ''; writelog("invalid login attempt for username=$userid"); printheader(); open (INCORRECT, "$templatesdir/$lang/passwordincorrect.template") or webmailerror("$lang_err{'couldnt_open'} passwordincorrect.template!"); while (<INCORRECT>) { $html .= $_; } close (INCORRECT);
$html = applystyle($html); print $html; printfooter(); exit 0; } } #################### END LOGIN #####################
#################### LOGOUT ######################## sub logout { webmailerror("Session ID $lang_err{'has_illegal_chars'}") unless (($thissession =~ /^(.+?\-\d?\.\d+)$/) && ($thissession = $1)); $thissession =~ s/\///g; # just in case someone gets tricky ... unlink "$webmaildir$thissession";
writelog("logout - $thissession");
print "Location: $scripturl\n\n"; } ################## END LOGOUT ######################
################## GETFOLDERS #################### sub getfolders { my $totalfoldersize = 0; my @folders; my $filename; opendir (FOLDERDIR, "$folderdir") or webmailerror("$lang_err{'couldnt_open'} $folderdir!"); while (defined($filename = readdir(FOLDERDIR))) { next if ( ($filename =~ /\.db$/) || ($filename =~ /\.dir$/) || ($filename =~ /\.pag$/) ); # Skip DBM files if ( $homedirfolders eq 'yes' ) { unless ( ($filename eq 'saved-messages') || ($filename eq 'sent-mail') || ($filename eq 'webmail-trash') || ($filename eq '.') || ($filename eq '..') ) { push (@folders, $filename); $totalfoldersize += ( -s "$folderdir/$filename" ); } } else { if ($filename =~ /^(.+)\.folder$/) { push (@folders, $1); $totalfoldersize += ( -s "$folderdir/$filename" ); } } } closedir (FOLDERDIR) or webmailerror("$lang_err{'couldnt_close'} $folderdir!"); @folders = sort(@folders); if ( $homedirfolders eq 'yes' ) { unshift(@folders, 'INBOX', # Make sure these folders top the list 'saved-messages', 'sent-mail', 'webmail-trash' ); $totalfoldersize += ( -s "$folderdir/sent-mail" ) || 0; $totalfoldersize += ( -s "$folderdir/saved-messages" ) || 0; $totalfoldersize += ( -s "$folderdir/webmail-trash" ) || 0; } else { unshift(@folders, 'INBOX', 'SAVED', 'SENT', 'TRASH' ); $totalfoldersize += ( -s "$folderdir/SAVED" ) || 0; $totalfoldersize += ( -s "$folderdir/TRASH" ) || 0; $totalfoldersize += ( -s "$folderdir/SENT" ) || 0; } if ($folderquota) { ($hitquota = 1) if ($totalfoldersize >= ($folderquota * 1024)); }
return \@folders; } ################ END GETFOLDERS ##################
################ CLEANUPOLDSESSIONS ################## sub cleanupoldsessions { my $sessionid; opendir (WEBMAILDIR, "$webmaildir") or webmailerror("$lang_err{'couldnt_open'} $webmaildir!"); while (defined($sessionid = readdir(WEBMAILDIR))) { if ($sessionid =~ /^(\w+\-session\-0\.\d*.*)$/) { $sessionid = $1; if ( -M "$webmaildir/$sessionid" > $sessiontimeout ) { writelog("session cleanup - $sessionid"); unlink "$webmaildir/$sessionid"; } } } closedir (WEBMAILDIR); } ############## END CLEANUPOLDSESSIONS ################
############## VERIFYSESSION ######################## sub verifysession { if ($validsession == 1) { return 1; } if ( -M "$webmaildir/$thissession" > $sessiontimeout || !(-e "$webmaildir/$thissession")) { my $html = ''; printheader(); open (TIMEOUT, "$templatesdir/$lang/sessiontimeout.template") or webmailerror("$lang_err{'couldnt_open'} sessiontimeout.template!"); while (<TIMEOUT>) { $html .= $_; } close (TIMEOUT);
$html = applystyle($html);
print $html;
printfooter(); writelog("timed-out session access attempt - $thissession"); exit 0; } if ( -e "$webmaildir/$thissession" ) { open (SESSION, "$webmaildir/$thissession"); my $cookie = <SESSION>; close (SESSION); chomp $cookie; unless ( cookie("sessionid") eq $cookie) { writelog("attempt to hijack session $thissession!"); webmailerror("$lang_err{'inv_sessid'}"); } }
webmailerror("Session ID $lang_err{'has_illegal_chars'}") unless (($thissession =~ /^([\w\.\-]+)$/) && ($thissession = $1)); open (SESSION, '>' . $webmaildir . $thissession) or webmailerror("$lang_err{'couldnt_open'} $thissession!"); print SESSION cookie("sessionid"); close (SESSION); $validsession = 1; return 1; } ############# END VERIFYSESSION #####################
################ DISPLAYHEADERS ##################### sub displayheaders { verifysession() unless $setcookie; printheader();
my ($bgcolor, $status, $message_size); my $newmessages = 0; my $escapedmessageid; # Used when creating link from subject line my @headers = @{&getheaders($user)}; my $numheaders = $#headers + 1 || 1; my $page_total = $numheaders/$numberofheaders || 1; $page_total = int($page_total) + 1 if ($page_total != int($page_total));
if (defined(param("custompage"))) { my $pagenumber = param("custompage"); $pagenumber = 1 if ($pagenumber < 1); $pagenumber = $page_total if ($pagenumber > $page_total); $firstmessage = (($pagenumber-1)*$numberofheaders) + 1; }
### Perform verification of $firstmessage, make sure it's within bounds if ($firstmessage > ($#headers + 1)) { $firstmessage = $#headers - ($numberofheaders - 1); } if ($firstmessage < 1) { $firstmessage = 1; } my $lastmessage = $firstmessage + $numberofheaders - 1; if ($lastmessage > ($#headers + 1)) { $lastmessage = ($#headers + 1); }
foreach my $messnum (0 .. $#headers) { unless (${$headers[$messnum]}{status} =~ /r/i) { $newmessages++; } }
my $base_url = "$scripturl?sessionid=$thissession&sort=$sort&folder=$escapedfolder";
my $page_nb; if ($#headers > 0) { $page_nb = ($firstmessage) * (($#headers + 1) / $numberofheaders) / ($#headers + 1); ($page_nb = int($page_nb) + 1) if ($page_nb != int($page_nb)); } else { $page_nb = 1; }
if ($total_size > 1048575){ $total_size = int(($total_size/1048576)+0.5) . " MB"; } elsif ($total_size > 1023) { $total_size = int(($total_size/1024)+0.5) . " KB"; } else { $total_size = $total_size . " B"; }
my $html = ''; my $temphtml; open (VIEWFOLDER, "$templatesdir/$lang/viewfolder.template") or webmailerror("$lang_err{'couldnt_open'} viewfolder.template!"); while (<VIEWFOLDER>) { $html .= $_; } close (VIEWFOLDER);
$html = applystyle($html);
$temphtml = end_form(); $html =~ s/\@\@\@ENDFORM\@\@\@/$temphtml/g;
$temphtml = startform(-action=>$scripturl, -name=>'FolderForm'); $temphtml .= hidden(-name=>'sessionid', -value=>$thissession, -override=>'1'); $temphtml .= hidden(-name=>'sort', -value=>$sort, -override=>'1'); $temphtml .= hidden(-name=>'action', -value=>'displayheaders', -override=>'1'); $temphtml .= hidden(-name=>'firstmessage', -value=>$firstmessage, -override=>'1'); $html =~ s/\@\@\@STARTFOLDERFORM\@\@\@/$temphtml/;
$temphtml = popup_menu(-name=>'folder', -"values"=>\@validfolders, -default=>$folder, -labels=>\%lang_folders, -onChange=>'JavaScript:document.FolderForm.submit();', -override=>'1'); $html =~ s/\@\@\@FOLDERPOPUP\@\@\@/$temphtml/;
if (defined($headers[0])) { $temphtml = ($firstmessage) . " - " . ($lastmessage) . " $lang_text{'of'} " . ($#headers + 1) . " $lang_text{'messages'} "; if ($newmessages) { $temphtml .= "($newmessages $lang_text{'unread'})"; } $temphtml .= " - $total_size"; } else { $temphtml = $lang_text{'nomessages'}; }
if ($hitquota) { $temphtml .= " [ $lang_text{'quota_hit'} ]"; }
$html =~ s/\@\@\@NUMBEROFMESSAGES\@\@\@/$temphtml/g;
$temphtml = "<a href=\"$base_url&action=composemessage&firstmessage=$firstmessage\">$lang_text{'composenew'}</a> <b>|</b> "; $temphtml .= "<a href=\"$base_url&action=displayheaders&firstmessage=$firstmessage\">$lang_text{'refresh'}</a> <b>|</b> "; $temphtml .= "<a href=\"$prefsurl?sessionid=$thissession&sort=$sort&folder=$escapedfolder&firstmessage=$firstmessage\">$lang_text{'userprefs'} <b>|</b> </a> "; $temphtml .= "<a href=\"$prefsurl?action=editaddresses&sessionid=$thissession&sort=$sort&folder=$escapedfolder&firstmessage=$firstmessage\">$lang_text{'addressbook'}</a> <b>|</b> "; $temphtml .= "<a href=\"$prefsurl?action=editfolders&sessionid=$thissession&sort=$sort&folder=$escapedfolder&firstmessage=$firstmessage\">$lang_text{'folders'}</a> <b>|</b> "; $temphtml .= "<a href=\"$base_url&action=emptytrash&firstmessage=$firstmessage\">$lang_text{'emptytrash'}</a> <b>|</b> "; $temphtml .= "<a href=\"$base_url&action=logout&firstmessage=$firstmessage\">$lang_text{'logout'}</a>";
$html =~ s/\@\@\@MENUBARLINKS\@\@\@/$temphtml/g;
$temphtml = start_form(-action=>$scripturl); $temphtml .= hidden(-name=>'action', -default=>'displayheaders', -override=>'1'); $temphtml .= hidden(-name=>'sessionid', -default=>$thissession, -override=>'1'); $temphtml .= hidden(-name=>'folder', -default=> $folder || 'INBOX', -override=>'1'); $temphtml .= hidden(-name=>'sort', -default=>$sort, -override=>'1');
$html =~ s/\@\@\@STARTPAGEFORM\@\@\@/$temphtml/g; if ($firstmessage != 1) { $temphtml = "<a href=\"$base_url&action=displayheaders&firstmessage=1\">"; $temphtml .= "<img src=\"$image_url/first.gif\" align=\"absmiddle\" border=\"0\" alt=\"<<\"></a>"; } else { $temphtml = "<img src=\"$image_url/first-grey.gif\" align=\"absmiddle\" border=\"0\" alt=\"\">"; }
if (($firstmessage - $numberofheaders) >= 1) { $temphtml .= "<a href=\"$base_url&action=displayheaders&firstmessage=" . ($firstmessage - $numberofheaders) . "\">"; $temphtml .= "<img src=\"$image_url/left.gif\" align=\"absmiddle\" border=\"0\" alt=\"<\"></a>"; } else { $temphtml .= "<img src=\"$image_url/left-grey.gif\" align=\"absmiddle\" border=\"0\" alt=\"\">"; }
$temphtml .= "[$lang_text{'page'} " . textfield(-name=>'custompage', -default=>$page_nb, -size=>'2', -override=>'1') . " $lang_text{'of'} " . $page_total . ']';
if (($firstmessage + $numberofheaders) <= ($#headers + 1)) { $temphtml .= "<a href=\"$base_url&action=displayheaders&firstmessage=" . ($firstmessage + $numberofheaders) . "\">"; $temphtml .= "<img src=\"$image_url/right.gif\" align=\"absmiddle\" border=\"0\" alt=\">\"></a>"; } else { $temphtml .= "<img src=\"$image_url/right-grey.gif\" align=\"absmiddle\" border=\"0\" alt=\"\">"; }
if (($firstmessage + $numberofheaders) <= ($#headers +1) ) { $temphtml .= "<a href=\"$base_url&action=displayheaders&custompage=" . "$page_total\">"; $temphtml .= "<img src=\"$image_url/last.gif\" align=\"absmiddle\" border=\"0\" alt=\">>\"></a>"; } else { $temphtml .= "<img src=\"$image_url/last-grey.gif\" align=\"absmiddle\" border=\"0\" alt=\"\">"; }
$html =~ s/\@\@\@PAGECONTROL\@\@\@/$temphtml/g;
$temphtml = start_form(-action=>$scripturl, -onSubmit=>"return confirm($lang_text{'moveconfirm'})", -name=>'moveform'); my @movefolders; foreach my $checkfolder (@validfolders) { unless ( ($checkfolder eq 'INBOX') || ($checkfolder eq $folder) ) { push (@movefolders, $checkfolder); } } $temphtml .= hidden(-name=>'action', -default=>'movemessage', -override=>'1'); $temphtml .= hidden(-name=>'sessionid', -default=>$thissession, -override=>'1'); $temphtml .= hidden(-name=>'firstmessage', -default=>$firstmessage, -override=>'1'); $temphtml .= hidden(-name=>'sort', -default=>$sort, -override=>'1'); $temphtml .= hidden(-name=>'folder', -default=>$folder, -override=>'1'); $html =~ s/\@\@\@STARTMOVEFORM\@\@\@/$temphtml/g; if ( $homedirfolders eq 'yes' ) { $temphtml = popup_menu(-name=>'destination', -"values"=>\@movefolders, -default=>'webmail-trash', -labels=>\%lang_folders, -override=>'1'); } else { $temphtml = popup_menu(-name=>'destination', -"values"=>\@movefolders, -default=>'TRASH', -labels=>\%lang_folders, -override=>'1'); } $temphtml .= submit("$lang_text{'move'}");
$html =~ s/\@\@\@MOVECONTROLS\@\@\@/$temphtml/g;
$temphtml = "<a href=\"$scripturl?action=displayheaders&firstmessage=". ($firstmessage)."&sessionid=$thissession&folder=$escapedfolder&sort="; if ($sort eq "date") { $temphtml .= "date_rev\">$lang_text{'date'} <IMG SRC=\"$image_url/up.gif\" border=\"0\" alt=\"^\"></a>"; } elsif ($sort eq "date_rev") { $temphtml .= "date\">$lang_text{'date'} <IMG SRC=\"$image_url/down.gif\" border=\"0\" alt=\"v\"></a>"; } else { $temphtml .= "date\">$lang_text{'date'}</a>"; }
$html =~ s/\@\@\@DATE\@\@\@/$temphtml/g; $temphtml = "<a href=\"$scripturl?action=displayheaders&firstmessage=". ($firstmessage)."&sessionid=$thissession&folder=$escapedfolder&sort=";
if ( ($folder eq 'SENT') || ($folder eq 'sent-mail') ) { if ($sort eq "sender") { $temphtml .= "sender_rev\">$lang_text{'recipient'} <IMG SRC=\"$image_url/down.gif\" border=\"0\" alt=\"v\"></a>"; } elsif ($sort eq "sender_rev") { $temphtml .= "sender\">$lang_text{'recipient'} <IMG SRC=\"$image_url/up.gif\" border=\"0\" alt=\"^\"></a>"; } else { $temphtml .= "sender\">$lang_text{'recipient'}</a>"; } } else { if ($sort eq "sender") { $temphtml .= "sender_rev\">$lang_text{'sender'} <IMG SRC=\"$image_url/down.gif\" border=\"0\" alt=\"v\"></a>"; } elsif ($sort eq "sender_rev") { $temphtml .= "sender\">$lang_text{'sender'} <IMG SRC=\"$image_url/up.gif\" border=\"0\" alt=\"^\"></a>"; } else { $temphtml .= "sender\">$lang_text{'sender'}</a>"; } }
$html =~ s/\@\@\@SENDER\@\@\@/$temphtml/g;
$temphtml = "<a href=\"$scripturl?action=displayheaders&firstmessage=". ($firstmessage)."&sessionid=$thissession&folder=$escapedfolder&sort=";
if ($sort eq "subject") { $temphtml .= "subject_rev\">$lang_text{'subject'} <IMG SRC=\"$image_url/down.gif\" border=\"0\" alt=\"v\"></a>"; } elsif ($sort eq "subject_rev") { $temphtml .= "subject\">$lang_text{'subject'} <IMG SRC=\"$image_url/up.gif\" border=\"0\" alt=\"^\"></a>"; } else { $temphtml .= "subject\">$lang_text{'subject'}</a>"; }
$html =~ s/\@\@\@SUBJECT\@\@\@/$temphtml/g;
$temphtml = "<a href=\"$scripturl?action=displayheaders&firstmessage=". ($firstmessage)."&sessionid=$thissession&folder=$escapedfolder&sort=";
if ($sort eq "size") { $temphtml .= "size_rev\">$lang_text{'size'} <IMG SRC=\"$image_url/up.gif\" border=\"0\" alt=\"^\"></a>"; } elsif ($sort eq "size_rev") { $temphtml .= "size\">$lang_text{'size'} <IMG SRC=\"$image_url/down.gif\" border=\"0\" alt=\"v\"></a>"; } else { $temphtml .= "size\">$lang_text{'size'}</a>"; }
$html =~ s/\@\@\@SIZE\@\@\@/$temphtml/g;
$temphtml = ''; my ($boldon, $boldoff); # Used to control whether text is bold for new mails my $email; # will store the e-mail in case no from: name my $messagelink; # will hold the href to each message my $addlink; # Will hold link to add sender to address book foreach my $messnum (($firstmessage - 1) .. ($lastmessage - 1)) { ### Stop when we're out of messages! last if !(defined($headers[$messnum]));
${$headers[$messnum]}{subject} =~ s/&/&/g; ${$headers[$messnum]}{subject} =~ s/\"/"/g; ${$headers[$messnum]}{subject} =~ s/</</g; ${$headers[$messnum]}{subject} =~ s/>/>/g; if (${$headers[$messnum]}{from} =~ /\"/) { (${$headers[$messnum]}{from}, $email) = (split(/\"/, ${$headers[$messnum]}{from}))[1,2]; $email =~ s/[(\s*<)|(>\s*)]//g; } elsif (${$headers[$messnum]}{from} =~ /</) { (${$headers[$messnum]}{from}, $email) = (split(/</, ${$headers[$messnum]}{from}))[0,1]; ${$headers[$messnum]}{from} =~ s/\s+$//; $email =~ s/>//; } else { $email = ${$headers[$messnum]}{from}; } unless (${$headers[$messnum]}{from} =~ /[^\s]/) { ${$headers[$messnum]}{from} = $email; } $addlink = "<a href=\"$prefsurl?action=addaddress&realname=".urlescape(${$headers[$messnum]}{from})."&email=".urlescape($email)."&firstmessage=$firstmessage&sessionid=$thissession&folder=$escapedfolder&sort=$sort\"><IMG SRC=\"$image_url/addaddress.gif\" border=\"0\" alt=\"+\" align=\"absmiddle\"></a>"; ${$headers[$messnum]}{from} =~ s/&/&/g; ${$headers[$messnum]}{from} =~ s/\"/"/g; ${$headers[$messnum]}{from} =~ s/</</g; ${$headers[$messnum]}{from} =~ s/>/>/g;
### Make sure there's SOMETHING clickable for subject line unless (${$headers[$messnum]}{subject} =~ /[^\s]/) { ${$headers[$messnum]}{subject} = "N/A"; }
$escapedmessageid = urlescape(${$headers[$messnum]}{message_id});
if ( $messnum % 2 ) { $bgcolor = $style{"tablerow_light"}; } else { $bgcolor = $style{"tablerow_dark"}; }
$message_size = ${$headers[$messnum]}{messagesize}; ### Round message size and change to an appropriate unit for display if ($message_size > 1048575){ $message_size = int(($message_size/1048576)+0.5) . "MB"; } elsif ($message_size > 1023) { $message_size = int(($message_size/1024)+0.5) . "KB"; }
$status = "<B>".($messnum+1)."</B> "; ### Choose status icons based on Status: line and type of encoding if ( ${$headers[$messnum]}{status} =~ /r/i ) { $boldon = ''; $boldoff = ''; } else { $status .= "<img src=\"$image_url/new.gif\" align=\"absmiddle\" alt=\"$lang_text{'unread'}\">"; $boldon = "<B>"; $boldoff = "</B>"; }
if ( (${$headers[$messnum]}{content_type} ne 'N/A') && !(${$headers[$messnum]}{content_type} =~ /^text/i) ) { $status .= "<img src=\"$image_url/attach.gif\" align=\"absmiddle\" alt=\"$lang_text{'attachment'}\">"; } $messagelink = "<a href=\"$scripturl?action=readmessage&firstmessage=". ($firstmessage)."&sessionid=$thissession&status=". ${$headers[$messnum]}{status}."&folder=$escapedfolder&sort=$sort&headers=". ($prefs{"headers"} || 'simple'). "&message_id=". $escapedmessageid ."\">";
$temphtml .= "<tr><td valign=\"middle\" width=\"50\" bgcolor=$bgcolor>$status </td>". "<td valign=\"middle\" width=\"150\" bgcolor=$bgcolor>$boldon". ${$headers[$messnum]}{date}."$boldoff</td>". "<td valign=\"middle\" width=\"150\" bgcolor=$bgcolor>$boldon". $messagelink . ${$headers[$messnum]}{from}."</a>$addlink $boldoff</td>". "<td valign=\"middle\" width=\"350\" bgcolor=$bgcolor>$boldon". $messagelink . ${$headers[$messnum]}{subject}."</a>$boldoff</td>". "<td valign=\"middle\" width=\"40\" bgcolor=$bgcolor>$boldon". $message_size . "$boldoff</td>". "<td align=\"center\" valign=\"middle\" width=\"50\" bgcolor=$bgcolor>". checkbox(-name=>'message_ids', -value=>${$headers[$messnum]}{message_id}, -label=>''). '</td></tr>'; } $html =~ s/\@\@\@HEADERS\@\@\@/$temphtml/;
print $html;
unless (defined($headers[0])) { print '<p align="center"><b>',$lang_text{'nomessages'},'</b></p>'; }
printfooter(); } ############### END DISPLAYHEADERS ##################
################# READMESSAGE #################### sub readmessage { verifysession(); printheader(); my $messageid = param("message_id"); my $escapedmessageid = urlescape($messageid); my %message = %{&getmessage($user,$messageid)}; my $headers = param("headers") || 'simple'; if (%message) { my $html = ''; my $temphtml; open (READMESSAGE, "$templatesdir/$lang/readmessage.template") or webmailerror("$lang_err{'couldnt_open'} readmessage.template!"); while (<READMESSAGE>) { $html .= $_; } close (READMESSAGE);
$html = applystyle($html);
### these will hold web-ified headers my ($from, $replyto, $to, $cc, $subject, $body); $from = $message{from} || ''; $from =~ s/&/&/g; $from =~ s/\"/"/g; $from =~ s/</</g; $from =~ s/>/>/g; $replyto = $message{replyto} || ''; $replyto =~ s/&/&/g; $replyto =~ s/\"/"/g; $replyto =~ s/</</g; $replyto =~ s/>/>/g; $to = $message{to} || ''; $to =~ s/&/&/g; $to =~ s/\"/"/g; $to =~ s/</</g; $to =~ s/>/>/g; $cc = $message{cc} || ''; $cc =~ s/&/&/g; $cc =~ s/\"/"/g; $cc =~ s/</</g; $cc =~ s/>/>/g; $subject = $message{subject} || ''; $subject =~ s/&/&/g; $subject =~ s/\"/"/g; $subject =~ s/</</g; $subject =~ s/>/>/g; ### Handle mail programs that send the body of a message quoted-printable if ( ($message{contenttype} =~ /^text/i) && ($message{encoding} =~ /^quoted-printable/i) ) { $message{"body"} = decode_qp($message{"body"}); ### OR base64 :) } elsif ( ($message{contenttype} =~ /^text/i) && ($message{encoding} =~ /^base64/i) ) { $message{"body"} = decode_base64($message{"body"}); }
$body = $message{"body"} || ''; $body =~ s/&/&/g; $body =~ s/\"/"/g; $body =~ s/</</g; $body =~ s/>/>/g; $body =~ s/\n/<BR>\n/g; $body =~ s/ {2}/ /g; $body =~ s/\t/ /g;
my $base_url = "$scripturl?sessionid=$thissession&firstmessage=" . ($firstmessage) . "&sort=$sort&folder=$escapedfolder&message_id=$escapedmessageid"; my $base_url_noid = "$scripturl?sessionid=$thissession&firstmessage=" . ($firstmessage) . "&sort=$sort&folder=$escapedfolder";
##### Set up the message to go to after move. my $messageaftermove; if (defined($message{"next"})) { $messageaftermove = $message{"next"}; } elsif (defined($message{"prev"})) { $messageaftermove = $message{"prev"}; }
$html =~ s/\@\@\@MESSAGENUMBER\@\@\@/$message{"number"}/g;
$temphtml = "<a href=\"$base_url&action=displayheaders\">$lang_text{'backto'} $printfolder</a> <b>|</b> "; $temphtml .= "<a href=\"$base_url&action=composemessage&composetype=reply\">$lang_text{'reply'}</a> <b>|</b> " . "<a href=\"$base_url&action=composemessage&composetype=replyall\">$lang_text{'replyall'}</a> <b>|</b> " . "<a href=\"$base_url&action=composemessage&composetype=forward\">$lang_text{'forward'}</a> <b>|</b> " . "<a href=\"$base_url&action=logout\">$lang_text{'logout'}</a>"; $html =~ s/\@\@\@MENUBARLINKS\@\@\@/$temphtml/g;
if (defined($message{"prev"})) { $temphtml = "<a href=\"$base_url_noid&action=readmessage&message_id=$message{'prev'}\"><img src=\"$image_url/left.gif\" align=\"absmiddle\" border=\"0\" alt=\"<<\"></a>"; } else { $temphtml = "<img src=\"$image_url/left-grey.gif\" align=\"absmiddle\" border=\"0\" alt=\"\">"; }
$temphtml .= " " . $message{"number"} . " ";
if (defined($message{"next"})) { $temphtml .= "<a href=\"$base_url_noid&action=readmessage&message_id=$message{'next'}\"><img src=\"$image_url/right.gif\" align=\"absmiddle\" border=\"0\" alt=\">>\"></a>"; } else { $temphtml .= "<img src=\"$image_url/right-grey.gif\" align=\"absmiddle\" border=\"0\" alt=\"\">"; }
$html =~ s/\@\@\@MESSAGECONTROL\@\@\@/$temphtml/g;
$temphtml = start_form(-action=>$scripturl, -onSubmit=>"return confirm($lang_text{'moveconfirm'})", -name=>'moveform'); my @movefolders; foreach my $checkfolder (@validfolders) { unless ( ($checkfolder eq 'INBOX') || ($checkfolder eq $folder) ) { push (@movefolders, $checkfolder); } } $temphtml .= hidden(-name=>'action', -default=>'movemessage', -override=>'1'); $temphtml .= hidden(-name=>'sessionid', -default=>$thissession, -override=>'1'); $temphtml .= hidden(-name=>'firstmessage', -default=>$firstmessage, -override=>'1'); $temphtml .= hidden(-name=>'sort', -default=>$sort, -override=>'1'); $temphtml .= hidden(-name=>'folder', -default=>$folder, -override=>'1'); $temphtml .= hidden(-name=>'message_ids', -default=>$messageid, -override=>'1'); if ($messageaftermove) { $temphtml .= hidden(-name=>'messageaftermove', -default=>'1', -override=>'1'); $temphtml .= hidden(-name=>'message_id', -default=>$messageaftermove, -override=>'1'); } $html =~ s/\@\@\@STARTMOVEFORM\@\@\@/$temphtml/g; if ( $homedirfolders eq 'yes' ) { $temphtml = popup_menu(-name=>'destination', -"values"=>\@movefolders, -labels=>\%lang_folders, -default=>'webmail-trash', -override=>'1'); } else { $temphtml = popup_menu(-name=>'destination', -"values"=>\@movefolders, -labels=>\%lang_folders, -default=>'TRASH', -override=>'1'); } $temphtml .= submit("$lang_text{'move'}");
$html =~ s/\@\@\@MOVECONTROLS\@\@\@/$temphtml/g;
if ($headers eq "all") { $message{"header"} = decode_mimewords($message{"header"}); $message{"header"} =~ s/&/&/g; $message{"header"} =~ s/\"/"/g; $message{"header"} =~ s/</</g; $message{"header"} =~ s/>/>/g; $message{"header"} =~ s/\n/<BR>\n/g; $message{"header"} =~ s/ {2}/ /g; $message{"header"} =~ s/\t/ /g; $message{"header"} =~ s/\n([-\w]+?:)/\n<B>$1<\/B>/g; $temphtml = $message{"header"}; } else { $temphtml = "<B>$lang_text{'date'}:</B> $message{date}<BR>\n"; $temphtml .= "<B>$lang_text{'from'}:</B> $from<BR>\n"; if ($replyto) { $temphtml .= "<B>$lang_text{'replyto'}:</B> $replyto<BR>\n"; } if ($to) { $temphtml .= "<B>$lang_text{'to'}:</B> $to<BR>\n"; } if ($cc) { $temphtml .= "<B>$lang_text{'cc'}:</B> $cc<BR>\n"; } if ($subject) { $temphtml .= "<B>$lang_text{'subject'}:</B> $subject\n"; } }
$html =~ s/\@\@\@HEADERS\@\@\@/$temphtml/g;
if ($headers eq "all") { $temphtml = "<a href=\"$base_url&action=readmessage&message_id=$escapedmessageid&headers=simple\">$lang_text{'simplehead'}</a>"; } else { $temphtml = "<a href=\"$base_url&action=readmessage&message_id=$escapedmessageid&headers=all\">$lang_text{'allhead'}</a>"; } $html =~ s/\@\@\@HEADERSTOGGLE\@\@\@/$temphtml/g;
foreach (qw(http https ftp nntp news gopher telnet)) { $body =~ s/($_:\/\/[\w\.\-]+?\/?[^\s<>]*[\w\/])([\b|\n| ]*)/<A HREF=\"$1\" TARGET=\"_blank\">$1<\/A>$2/gs; } $body =~ s/([\b|\n| ]+)(www\.[-\w\.]+\.[-\w]{2,3})([\b|\n| ]*)/$1<a href=\"http:\/\/$2\" TARGET=\"_blank\">$2<\/a>$3/gs;
$temphtml = $body;
# Handle the messages generated if sendmail is set up to send MIME error reports if ($message{contenttype} =~ /^multipart\/report/i) { foreach my $attnumber (0 .. $#{$message{attachment}}) { if (defined(${$message{attachment}[$attnumber]}{contents})) { ${$message{attachment}[$attnumber]}{contents} =~ s/&/&/g; ${$message{attachment}[$attnumber]}{contents} =~ s/\"/"/g; ${$message{attachment}[$attnumber]}{contents} =~ s/</</g; ${$message{attachment}[$attnumber]}{contents} =~ s/>/>/g; ${$message{attachment}[$attnumber]}{contents} =~ s/\n/<BR>\n/g; ${$message{attachment}[$attnumber]}{contents} =~ s/ {2}/ /g; ${$message{attachment}[$attnumber]}{contents} =~ s/\t/ /g; $temphtml .= ${$message{attachment}[$attnumber]}{contents} . hr(); } } } elsif ( ($message{contenttype} ne 'N/A') && !($message{contenttype} =~ /^text/i )) { foreach my $attnumber (0 .. $#{$message{attachment}}) { next unless (defined(%{$message{attachment}[$attnumber]})); if (($attnumber == 0) && (${$message{attachment}[$attnumber]}{contenttype} =~ /^text\/plain/i)) { if (${$message{attachment}[$attnumber]}{encoding} =~ /^quoted-printable/i) { ${$message{attachment}[$attnumber]}{contents} = decode_qp(${$message{attachment}[$attnumber]}{contents}); } elsif (${$message{attachment}[$attnumber]}{encoding} =~ /^base64/i) { ${$message{attachment}[$attnumber]}{contents} = decode_base64(${$message{attachment}[$attnumber]}{contents}); } $temphtml .= hr(); ${$message{attachment}[$attnumber]}{contents} =~ s/&/&/g; ${$message{attachment}[$attnumber]}{contents} =~ s/\"/"/g; ${$message{attachment}[$attnumber]}{contents} =~ s/</</g; ${$message{attachment}[$attnumber]}{contents} =~ s/>/>/g; ${$message{attachment}[$attnumber]}{contents} =~ s/\n/<BR>\n/g; ${$message{attachment}[$attnumber]}{contents} =~ s/ {2}/ /g; ${$message{attachment}[$attnumber]}{contents} =~ s/\t/ /g; foreach (qw(http https ftp nntp news gopher telnet)) { ${$message{attachment}[$attnumber]}{contents} =~ s/($_:\/\/[\w\.\-]+?\/?[^\s<>]*[\w\/])([\b|\n| ]*)/<A HREF=\"$1\" TARGET=\"_blank\">$1<\/A>$2/gs; } ${$message{attachment}[$attnumber]}{contents} =~ s/([\b|\n| ]+)(www\.[-\w\.]+\.[-\w]{2,3})([\b|\n| ]*)/$1<a href=\"http:\/\/$2\" TARGET=\"_blank\">$2<\/a>$3/gs; $temphtml .= ${$message{attachment}[$attnumber]}{contents} . "<BR><BR>"; } else { my $escapedfilename = urlescape(${$message{attachment}[$attnumber]}{filename}); if (${$message{attachment}[$attnumber]}{filename} =~ /\.(jpg|jpeg|gif|png)$/i) { $temphtml .= "<table border=\"0\" align=\"center\" cellpadding=\"2\"> <tr><td valign=\"middle\" bgcolor=" . $style{"attachment_dark"} . " align=\"center\">$lang_text{'attachment'} $attnumber: ${$message{attachment}[$attnumber]}{filename} </td></tr><td valign=\"middle\" bgcolor=" . $style{"attachment_light"} . " align=\"center\">"; $temphtml .= "<IMG BORDER=\"0\" SRC=\"$scripturl/$escapedfilename?action=viewattachment&sessionid=$thissession&message_id=$escapedmessageid&folder=$escapedfolder&attachment_number=$attnumber\">"; $temphtml .= "</td></tr></table>";
} else {
$temphtml .= "<table border=\"0\" align=\"center\" cellpadding=\"2\"><tr><td colspan=\"2\" valign=\"middle\" bgcolor=" . $style{"attachment_dark"} . " align=\"center\">$lang_text{'attachment'} $attnumber</td></tr><td valign=\"middle\" bgcolor=" . $style{"attachment_light"} . " align=\"center\">"; $temphtml .= "$lang_text{'type'}: " . ${$message{attachment}[$attnumber]}{contenttype} . "<BR>"; $temphtml .= "$lang_text{'filename'}: " . ${$message{attachment}[$attnumber]}{filename} . "<BR>"; $temphtml .= "$lang_text{'encoding'}: " . ${$message{attachment}[$attnumber]}{encoding}; $temphtml .= '</td><td valign="middle" bgcolor=' . $style{"attachment_light"} . ' align="center">'; $temphtml .= "<a href=\"$scripturl/$escapedfilename?action=viewattachment&sessionid=$thissession&message_id=$escapedmessageid&folder=$escapedfolder&attachment_number=$attnumber\">$lang_text{'download'}</a>"; $temphtml .= '</td></tr></table>'; } } } }
$html =~ s/\@\@\@BODY\@\@\@/$temphtml/g; print $html;
} else { $messageid =~ s/&/&/g; $messageid =~ s/\"/"/g; $messageid =~ s/>/>/g; $messageid =~ s/</</g;
print "What the heck? Message $messageid seems to be gone!"; } printfooter();
### A child fork is spawned to perform the status update unless ($message{status} =~ /r/i) { $| = 1; # Flush output buffers $SIG{CHLD} = sub { wait }; # Quick (and dirty) way to cope with zombies if ( fork() == 0 ) { # We're the child if fork doesn't have a value close (STDOUT); close (STDIN); updatestatus($user,$messageid,'R'); exit; } } } ############### END READMESSAGE ##################
############### COMPOSEMESSAGE ################### sub composemessage { no strict 'refs'; verifysession(); my $html = ''; my $temphtml; my @attlist; open (COMPOSEMESSAGE, "$templatesdir/$lang/composemessage.template") or webmailerror("$lang_err{'couldnt_open'} composemessage.template!"); while (<COMPOSEMESSAGE>) { $html .= $_; } close (COMPOSEMESSAGE);
$html = applystyle($html); if (defined(param($lang_text{'add'}))) { @attlist = @{&getattlist()}; my $attachment = param("attachment"); my $attname = $attachment; my $attcontents = ''; if ($attachment) { if ( ($attlimit) && ( ( $savedattsize + (-s $attachment) ) > ($attlimit * 1048576) ) ) { webmailerror ("$lang_err{'att_overlimit'} $attlimit MB!"); } my $content_type; ### Convert :: back to the ' like it should be. $attname =~ s/::/'/g; ### Trim the path info from the filename $attname =~ s/^.*\\//; $attname =~ s/^.*\///; $attname =~ s/^.*://; $/ = undef; # Force a single input to grab the whole spool $attcontents = <$attachment>; $/ = "\n"; $attcontents = encode_base64($attcontents); $savedattsize += length($attcontents); if (defined(uploadInfo($attachment))) { $content_type = ${uploadInfo($attachment)}{'Content-Type'} || 'application/octet-stream'; } else { $content_type = 'application/octet-stream'; } my $attnum = ($#attlist +1) || 0; open (ATTFILE, ">$webmaildir$thissession-att$attnum"); print ATTFILE "Content-Type: ", $content_type,";\n"; print ATTFILE "\tname=\"$attname\"\nContent-Transfer-Encoding: base64\n\n"; print ATTFILE "$attcontents"; close ATTFILE; $attname =~ s/&/&/g; $attname =~ s/\"/"/g; $attname =~ s/</</g; $attname =~ s/>/>/g; $attname =~ s/^(.*)$/<em>$1<\/em>/; push (@attlist, $attname); } } elsif ( !(defined(param($lang_text{'add'}))) ) { deleteattachments(); }
my $messageid = param("message_id"); my %message; my $attnumber; my $from; if ($prefs{"fromname"}) { $from = $prefs{"fromname"} . "@" . $prefs{domainname}; } else { $from = $thissession; $from =~ s/\-session\-0.*$/\@$prefs{domainname}/; # create from: address } if ($prefs{"realname"}) { my $realname = $prefs{"realname"}; $from =~ s/^(.+)$/$realname <$1>/; } my $escapedfrom = $from; $escapedfrom =~ s/&/&/g; $escapedfrom =~ s/\"/"/g; $escapedfrom =~ s/</</g; $escapedfrom =~ s/>/>/g; my $to = ''; my $cc = ''; my $bcc = ''; my $subject = ''; my $body = ''; my $composetype = param("composetype");
if ($composetype) { $to = param("to") || ''; $cc = param("cc") || ''; $bcc = param("bcc") || ''; $subject = param("subject") || ''; $body = param("body") || '';
if (($composetype eq "reply") || ($composetype eq "replyall") || ($composetype eq "forward") ) { %message = %{&getmessage($user,$messageid)};
### Handle mail programs that send the body of a message quoted-printable if ( ($message{contenttype} =~ /^text/i) && ($message{encoding} =~ /^quoted-printable/i) ) { $message{"body"} = decode_qp($message{"body"}); } $body = $message{"body"} || ''; ### If the first attachment is text, assume it's the body of a message ### in multi-part format if (($message{contenttype} =~ /^multipart/i) && (defined(${$message{attachment}[0]}{contenttype})) && (${$message{attachment}[0]}{contenttype} =~ /^text\/plain/i)) { if (${$message{attachment}[0]}{encoding} =~ /^quoted-printable/i) { ${$message{attachment}[0]}{contents} = decode_qp(${$message{attachment}[0]}{contents}); } elsif (${$message{attachment}[$attnumber]}{encoding} =~ /^base64/i) { ${$message{attachment}[$attnumber]}{contents} = decode_base64(${$message{attachment}[$attnumber]}{contents}); } $body .= "\n" . ${$message{attachment}[0]}{contents}; shift @{$message{attachment}}; } # Handle the messages generated if sendmail is set up to send MIME error reports if ($message{contenttype} =~ /^multipart\/report/i) { foreach my $attnumber (0 .. $#{$message{attachment}}) { if (defined(${$message{attachment}[$attnumber]}{contents})) { $body .= ${$message{attachment}[$attnumber]}{contents}; shift @{$message{attachment}}; } } } }
if (($composetype eq "reply") || ($composetype eq "replyall")) { $subject = $message{"subject"} || ''; $subject = "Re: " . $subject unless ($subject =~ /^re:/i); if (defined($message{"replyto"})) { $to = $message{"replyto"} || ''; } else { $to = $message{"from"} || ''; } if ($composetype eq "replyall") { $to .= "," . $message{"to"} if (defined($message{"to"})); $to .= "," . $message{"cc"} if (defined($message{"cc"})); }
$body = "\n" . $body; $body =~ s/\n/\n\> /g; $body = "\n\n" . $body; }
if ($composetype eq "forward") { if (defined(${$message{attachment}[0]}{header})) { foreach my $attnumber (0 .. $#{$message{attachment}}) { ($attnumber =~ /^(\d+)$/) && ($attnumber = $1); # untaint it open (ATTFILE, ">$webmaildir$thissession-att$attnumber"); print ATTFILE ${$message{attachment}[$attnumber]}{header}, "\n\n", ${$message{attachment}[$attnumber]}{contents}; close ATTFILE; } @attlist = @{&getattlist()}; } $subject = $message{"subject"} || ''; $subject = "Fw: " . $subject unless ($subject =~ /^fw:/i); $body = "\n\n------------- Forwarded message follows -------------\n\n$body"; }
} if ( (defined($prefs{"signature"})) && ($composetype ne 'continue') ) { $body .= "\n\n".$prefs{"signature"}; } printheader(); $temphtml = "<a href=\"$scripturl?action=displayheaders&sessionid=$thissession&folder=$escapedfolder&sort=$sort&firstmessage=$firstmessage\">$lang_text{'backto'} $printfolder</a>"; $html =~ s/\@\@\@BACKTOFOLDER\@\@\@/$temphtml/g;
$temphtml = start_multipart_form(-name=>'composeform'); $temphtml .= hidden(-name=>'action', -default=>'sendmessage', -override=>'1'); $temphtml .= hidden(-name=>'sessionid', -default=>$thissession, -override=>'1'); $temphtml .= hidden(-name=>'composetype', -default=>'continue', -override=>'1'); $temphtml .= hidden(-name=>'sort', -default=>$sort, -override=>'1'); $temphtml .= hidden(-name=>'firstmessage', -default=>$firstmessage, -override=>'1'); $temphtml .= hidden(-name=>'folder', -default=>$folder, -override=>'1'); $html =~ s/\@\@\@STARTCOMPOSEFORM\@\@\@/$temphtml/g;
$html =~ s/\@\@\@ESCAPEDFROM\@\@\@/$escapedfrom/g;
$temphtml = textfield(-name=>'to', -default=>$to, -size=>'70', -override=>'1'); $html =~ s/\@\@\@TOFIELD\@\@\@/$temphtml/g;
$temphtml = textfield(-name=>'cc', -default=>$cc, -size=>'70', -override=>'1'); $html =~ s/\@\@\@CCFIELD\@\@\@/$temphtml/g; $temphtml = textfield(-name=>'bcc', -default=>$bcc, -size=>'70', -override=>'1'); $html =~ s/\@\@\@BCCFIELD\@\@\@/$temphtml/g; $temphtml = textfield(-name=>'replyto', -default=>$prefs{"replyto"} || '', -size=>'70', -override=>'1'); $html =~ s/\@\@\@REPLYTOFIELD\@\@\@/$temphtml/g; $temphtml = ''; foreach my $filename (@attlist) { $temphtml .= "$filename<BR>"; } if ( $savedattsize ) { $temphtml .= "<em>" . int($savedattsize/1024) . "KB"; if ( $attlimit ) { $temphtml .= " $lang_text{'of'} $attlimit MB"; } $temphtml .= "</em><BR>"; } $temphtml .= filefield(-name=>'attachment', -default=>'', -size=>'60', -override=>'1', -tabindex=>'-1'); $temphtml .= submit(-name=>"$lang_text{'add'}", -value=>"$lang_text{'add'}", -tabindex=>'-1' ); $html =~ s/\@\@\@ATTACHMENTFIELD\@\@\@/$temphtml/g;
$temphtml = textfield(-name=>'subject', -default=>$subject, -size=>'70', -override=>'1'); $html =~ s/\@\@\@SUBJECTFIELD\@\@\@/$temphtml/g;
$temphtml = textarea(-name=>'body', -default=>$body, -rows=>'15', -columns=>'72', -wrap=>'hard', -override=>'1'); $html =~ s/\@\@\@BODYAREA\@\@\@/$temphtml/g;
$temphtml = submit("$lang_text{'send'}"); $html =~ s/\@\@\@SENDBUTTON\@\@\@/$temphtml/g;
$temphtml = end_form(); $html =~ s/\@\@\@ENDFORM\@\@\@/$temphtml/g;
$temphtml = start_form(-action=>$scripturl); if (param("message_id")) { $temphtml .= hidden(-name=>'action', -default=>'readmessage', -override=>'1'); $temphtml .= hidden(-name=>'firstmessage', -default=>$firstmessage, -override=>'1'); $temphtml .= hidden(-name=>'sort', -default=>$sort, -override=>'1'); $temphtml .= hidden(-name=>'folder', -default=>$folder, -override=>'1'); $temphtml .= hidden(-name=>'headers', -default=>$prefs{"headers"} || 'simple', -override=>'1'); $temphtml .= hidden(-name=>'sessionid', -default=>$thissession, -override=>'1'); $temphtml .= hidden(-name=>'message_id', -default=>param("message_id"), -override=>'1'); } else { $temphtml .= hidden(-name=>'action', -default=>'displayheaders', -override=>'1'); $temphtml .= hidden(-name=>'firstmessage', -default=>$firstmessage, -override=>'1'); $temphtml .= hidden(-name=>'sort', -default=>$sort, -override=>'1'); $temphtml .= hidden(-name=>'folder', -default=>$folder, -override=>'1'); $temphtml .= hidden(-name=>'sessionid', -default=>$thissession, -override=>'1'); } $html =~ s/\@\@\@STARTCANCELFORM\@\@\@/$temphtml/g;
$temphtml = submit("$lang_text{'cancel'}"); $html =~ s/\@\@\@CANCELBUTTON\@\@\@/$temphtml/g;
$html =~ s/\@\@\@SESSIONID\@\@\@/$thissession/g;
print $html;
printfooter(); } ############# END COMPOSEMESSAGE #################
############### SENDMESSAGE ###################### sub sendmessage { no strict 'refs'; verifysession(); if (defined(param($lang_text{'add'}))) { composemessage(); } else { ### Add a header that will allow SENT folder to function correctly my $localtime = scalar(localtime); my $messagecontents = "From $user $localtime\n"; my $date = localtime(); my @datearray = split(/\s+/, $date); $date = "$datearray[0], $datearray[2] $datearray[1] $datearray[4] $datearray[3] $timeoffset"; my $from; my $realname = $prefs{"realname"} || ''; if($prefs{"fromname"}) { # Create from: address for when "fromname" is defined $from = $prefs{"fromname"} . "@" . $prefs{domainname}; } else { # Create from: address for when "fromname" is not defined $from = $thissession; $from =~ s/\-session\-0.*$/\@$prefs{domainname}/; } $from =~ s/[\&|;|\`|\'|\\|\""|\*|\?|~|<|>|^|\(|\)|\[|\]|\{|\}|\$|\n|\r]/ /g; # Get rid of shell escape attempts $realname =~ s/[\&|;|\`|\'|\\|\""|\*|\?|~|<|>|^|\(|\)|\[|\]|\{|\}|\$|\n|\r]/ /g; # Get rid of shell escape attempts ($realname =~ /^(.+)$/) && ($realname = '"'.$1.'"'); ($from =~ /^(.+)$/) && ($from = $1);
my $savedatts = ''; # Will buffer saved attachments my $boundary = "----=WEBMAIL_ATT_" . rand(); my $to = param("to"); my $cc = param("cc"); my $bcc = param("bcc"); my $subject = param("subject"); my $body = param("body"); $body =~ s/\r//g; # strip ^M characters from message. How annoying! my $attachment = param("attachment"); if ( $attachment ) { getattlist(); if ( ($attlimit) && ( ( $savedattsize + (-s $attachment) ) > ($attlimit * 1048576) ) ) { webmailerror ("$lang_err{'att_overlimit'} $attlimit MB!"); } } my $attname = $attachment; ### Convert :: back to the ' like it should be. $attname =~ s/::/'/g; ### Trim the path info from the filename $attname =~ s/^.*\\//; $attname =~ s/^.*\///; $attname =~ s/^.*://;
#open (SENDMAIL, "|" . $sendmail . " -oem -oi -F '$realname' -f '$from' -t 1>&2") or open (SENDMAIL, "|" . $sendmail . " -oi -t 1>&2") or webmailerror("$lang_err{'couldnt_open'} $sendmail!"); print SENDMAIL "From: $realname <$from>\n"; $messagecontents .= "From: $realname <$from>\n"; print SENDMAIL "To: $to\n"; $messagecontents .= "To: $to\n"; if ($cc) { print SENDMAIL "CC: $cc\n"; $messagecontents .= "CC: $cc\n"; } if ($bcc) { print SENDMAIL "Bcc: $bcc\n"; $messagecontents .= "Bcc: $bcc\n"; } if ($prefs{"replyto"}) { print SENDMAIL "Reply-To: ",$prefs{"replyto"},"\n"; $messagecontents .= "Reply-To: ".$prefs{"replyto"}."\n"; } print SENDMAIL "Subject: $subject\n"; $messagecontents .= "Subject: $subject\n"; print SENDMAIL "X-Mailer: WebMail $version\n"; $messagecontents .= "X-Mailer: WebMail $version\n"; print SENDMAIL "X-IPAddress: $ENV{REMOTE_ADDR}\n"; $messagecontents .= "X-IPAddress: $ENV{REMOTE_ADDR}\n"; my $messageid="<WebMail-saved-".rand().">"; $messagecontents .= "Message-Id: $messageid\nDate: ". "$date\nStatus: R\n"; print SENDMAIL "MIME-Version: 1.0\n"; $messagecontents .= "MIME-Version: 1.0\n";
opendir (WEBMAILDIR, "$webmaildir") or webmailerror("$lang_err{'couldnt_open'} $webmaildir!"); while (defined(my $currentfile = readdir(WEBMAILDIR))) { if ($currentfile =~ /^($thissession-att\d+)$/) { $currentfile = $1; open (ATTFILE, "$webmaildir$currentfile"); $/ = undef; # Force a single input to grab the whole spool $savedatts .= "\n--$boundary\n" . <ATTFILE>; $/ = "\n"; close (ATTFILE); } } closedir (WEBMAILDIR);
my $contenttype = ''; if ($attachment || $savedatts) { $contenttype = 'multipart/mixed;'; print SENDMAIL "Content-Type: multipart/mixed;\n"; print SENDMAIL "\tboundary=\"$boundary\"\n\n"; print SENDMAIL "This is a multi-part message in MIME format.\n\n"; print SENDMAIL "--$boundary\n"; print SENDMAIL "Content-Type: text/plain; charset=$lang_charset\n\n"; print SENDMAIL $body; $body =~ s/^From />From /gm; print SENDMAIL "\n$savedatts\n";
$messagecontents .= "Content-Type: multipart/mixed;\n". "\tboundary=\"$boundary\"\n\nThis is a multi-part message in MIME format.\n\n". "--$boundary\nContent-Type: text/plain; charset=$lang_charset\n\n$body\n$savedatts\n"; if ($attachment) { my $attcontents = ''; $/ = undef; # Force a single input to grab the whole spool $attcontents = <$attachment>; $/ = "\n"; $attcontents = encode_base64($attcontents); my $content_type; if (defined(uploadInfo($attachment))) { $content_type = ${uploadInfo($attachment)}{'Content-Type'} || 'application/octet-stream'; } else { $content_type = 'application/octet-stream'; } print SENDMAIL "--$boundary\nContent-Type: ", $content_type,";\n"; print SENDMAIL "\tname=\"$attname\"\nContent-Transfer-Encoding: base64\n\n"; print SENDMAIL "$attcontents\n"; $messagecontents .= "--$boundary\nContent-Type: $content_type;\n". "\tname=\"$attname\"\nContent-Transfer-Encoding: base64\n\n". "$attcontents\n"; } print SENDMAIL "--$boundary--"; $messagecontents .= "--$boundary--\n\n"; } else { print SENDMAIL "Content-Type: text/plain; charset=$lang_charset\n\n$body\n"; $body =~ s/^From />From /gm; $messagecontents .= "Content-Type: text/plain; charset=$lang_charset\n\n$body\n\n"; }
close(SENDMAIL) or senderror(); deleteattachments(); my $sentfolder; if ( $homedirfolders eq 'yes' ) { $sentfolder = 'sent-mail'; } else { $sentfolder = 'SENT'; } unless ($hitquota) { if ( ($homedirfolders eq 'yes') && ($> == 0) ) { $) = $gid; $> = $uid; } open (SENT, ">>$folderdir/$sentfolder") or webmailerror("$lang_err{'couldnt_open'} $sentfolder!"); unless (filelock("$folderdir/$sentfolder", LOCK_EX)) { #webmailerror("$lang_err{'couldnt_lock'} $sentfolder!"); }
my @attr; # Will hold message attributes to be written to DBM
$attr[$_OFFSET]=tell(SENT); $attr[$_FROM]=$from; $attr[$_TO]=$to; $attr[$_DATE]="$month{$datearray[1]}/$datearray[2]/$datearray[4] $datearray[3] $timeoffset"; $attr[$_SUBJECT]=$subject; $attr[$_CONTENT_TYPE]=$contenttype; $attr[$_STATUS]='R'; $attr[$_SIZE]=length($messagecontents)+length("\n");
print SENT "$messagecontents\n"; close (SENT) or webmailerror ("$lang_err{'couldnt_close'} $sentfolder!");
filelock("$folderdir/$sentfolder", LOCK_UN); } displayheaders(); } }
sub senderror { my $html = ''; my $temphtml; open (SENDERROR, "$templatesdir/$lang/senderror.template") or webmailerror("$lang_err{'couldnt_open'} senderror.template!"); while (<SENDERROR>) { $html .= $_; } close (SENDERROR);
$html = applystyle($html);
printheader();
print $html;
printfooter(); exit 0; } ############## END SENDMESSAGE ###################
################ VIEWATTACHMENT ################## sub viewattachment { verifysession(); my %message = %{&getmessage($user,param("message_id"))}; if (%message) { my $attnumber = param("attachment_number"); my $attachment; if (${$message{attachment}[$attnumber]}{encoding} =~ /^base64$/i) { $attachment = decode_base64(${$message{attachment}[$attnumber]}{contents}); } elsif (${$message{attachment}[$attnumber]}{encoding} =~ /^quoted-printable$/i) { $attachment = decode_qp(${$message{attachment}[$attnumber]}{contents}); } else { ## Guessing it's 7-bit, at least sending SOMETHING back! :) $attachment = ${$message{attachment}[$attnumber]}{contents}; } my $length = length($attachment); print "Content-Length: $length\n"; print "Content-Transfer-Coding: binary\n"; print "Connection: close\n"; print "Content-Type: ${$message{attachment}[$attnumber]}{contenttype}; name=\"${$message{attachment}[$attnumber]}{filename}\"\n"; unless (${$message{attachment}[$attnumber]}{contenttype} =~ /^text/i) { print "Content-Disposition: attachment; filename=\"${$message{attachment}[$attnumber]}{filename}\"\n"; } print "\n"; print $attachment; } else { printheader(); print "What the heck? Message seems to be gone!"; printfooter(); } } ################### END VIEWATTACHMENT ##################
################## GETHEADERS ####################### sub getheaders { my $username = shift; my @message = (); my @datearray; my $currentheader; my $spoolfile; my $headerdb = "$userprefsdir$user/$folder";
if ($folder eq 'INBOX') { if ($homedirspools eq "yes") { $spoolfile = "$homedir/$homedirspoolname"; } elsif ($hashedmailspools eq "yes") { $username =~ /^(.)(.)/; my $firstchar = $1; my $secondchar = $2; $spoolfile = "$mailspooldir/$firstchar/$secondchar/$username"; } else { $spoolfile = "$mailspooldir/$username"; } } elsif ( ($folder eq 'SAVED') || ($folder eq 'SENT') || ($folder eq 'TRASH') || ( $homedirfolders eq 'yes' ) ) { $spoolfile = "$folderdir/$folder"; } else { $spoolfile = "$folderdir/$folder.folder"; }
if ( ( ($homedirfolders eq 'yes') || ($homedirspools eq 'yes') ) && ($> == 0) ) { $) = $gid; $> = $uid; }
### We've got to make sure not to try and lock a default folder that hasn't ### been auto-created yet! return \@message unless ( -e $spoolfile ); unless ( filelock($spoolfile, LOCK_SH) ) { if ( ! -d $spoolfile ) { webmailerror("$lang_err{'couldnt_lock'} $spoolfile!"); } } update_headerdb($headerdb, $spoolfile); filelock($spoolfile, LOCK_UN);
open (SPOOL, $spoolfile) or return \@message;
my $messagenumber = -1;
filelock("$headerdb.db", LOCK_SH); dbmopen (%HDB, "$headerdb.db", 0660);
my ($db_message_id, $db_offset, $db_from, $db_to, $db_date, $db_subject, $db_content_type, $db_status, $db_messagesize, $db_message_attr);
while ( ($db_message_id, $db_message_attr) = each(%HDB) ) { next if ( $db_message_id eq 'METAINFO' || $db_message_id eq "" );
### Create a unique memory address, these things will end up refs in an ### array, can't have them passing out of scope now, can we? my %header;
$header{'message_id'} = $db_message_id; ( $header{'offset'}, $header{'from'}, $header{'to'}, $header{'date'}, $header{'subject'}, $header{'content_type'}, $header{'status'}, $header{'messagesize'} ) = split(/@@@/, $db_message_attr);
if ( ($folder eq 'SENT') || ($folder eq 'sent-mail') ) { ### We aren't interested in the sender in this case, but the recipient ### Handling it this way avoids having a separate sort sub for To:. $header{from} = (split(/,/, $header{to}))[0]; if ( ($header{from} =~ tr/\"/\"/) == 1 ) { ### Handle real name fields in the "Last, First" format, not ### going to get more involved than this, doesn't go past two ### commas enough to merit a loop construct $header{from} .= ',' . (split(/,/, $header{to}))[1]; } }
$total_size += $header{'messagesize'}; $messagenumber++;
$message[$messagenumber] = \%header; } dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN);
if ( $sort eq 'date' ) { @message = sort by_date @message; } elsif ( $sort eq 'date_rev' ) { @message = reverse(sort by_date @message); } elsif ( $sort eq 'sender' ) { @message = sort by_sender @message; } elsif ( $sort eq 'sender_rev' ) { @message = reverse(sort by_sender @message); } elsif ( $sort eq 'size' ) { @message = sort by_size @message; } elsif ( $sort eq 'size_rev' ) { @message = reverse(sort by_size @message); } elsif ( $sort eq 'subject' ) { @message = sort by_subject @message; } else { @message = reverse(sort by_subject @message); }
return \@message; }
sub by_date { (my $datea, my $timea, my $offseta) = split(/\s/, ${$a}{date}); (my $dateb, my $timeb, my $offsetb) = split(/\s/, ${$b}{date});
$offseta = $timezones{$offseta} unless ($offseta =~ /[\+|\-]/); $offsetb = $timezones{$offsetb} unless ($offsetb =~ /[\+|\-]/);
my @datearraya = split(/\//, $datea); my @timearraya = split(/:/, $timea); my @datearrayb = split(/\//, $dateb); my @timearrayb = split(/:/, $timeb); ### No, this doesn't take into account the day change near midnight, but ### It's close enough. :) $timearraya[0] -= $offseta / 100; $timearrayb[0] -= $offsetb / 100;
$datearrayb[2] <=> $datearraya[2] or $datearrayb[0] <=> $datearraya[0] or $datearrayb[1] <=> $datearraya[1] or $timearrayb[0] <=> $timearraya[0] or $timearrayb[1] <=> $timearraya[1] or $timearrayb[2] <=> $timearraya[2] or ${$a}{message_id} cmp ${$b}{message_id}; }
sub by_sender { my $sendera = ${$a}{from}; my $senderb = ${$b}{from};
lc($sendera) cmp lc($senderb) or ${$a}{message_id} cmp ${$b}{message_id}; }
sub by_subject { lc(${$a}{subject}) cmp lc(${$b}{subject}) or ${$a}{message_id} cmp ${$b}{message_id}; }
sub by_size { ${$b}{messagesize} <=> ${$a}{messagesize} or ${$a}{message_id} cmp ${$b}{message_id}; } #################### END GETHEADERS #######################
#################### GETMESSAGE ########################### sub getmessage { my $username = shift; my $messageid = shift; my $spoolfile; my $headerdb = "$userprefsdir$user/$folder"; my $spoolhandle = FileHandle->new();
my %message = (); my @attachment = ();
my ($currentheader, $currentbody, $currentfrom, $currentdate, $currentsubject, $currentid, $currenttype, $currentto, $currentcc, $currentreplyto, $currentencoding, $currentstatus);
if ($folder eq 'INBOX') { if ($homedirspools eq "yes") { $spoolfile = "$homedir/$homedirspoolname"; } elsif ($hashedmailspools eq "yes") { $username =~ /^(.)(.)/; my $firstchar = $1; my $secondchar = $2; $spoolfile = "$mailspooldir/$firstchar/$secondchar/$username"; } else { $spoolfile = "$mailspooldir/$username"; } } elsif ( ($folder eq 'SAVED') || ($folder eq 'SENT') || ($folder eq 'TRASH') || ($homedirfolders eq 'yes') ) { $spoolfile = "$folderdir/$folder"; } else { $spoolfile = "$folderdir/$folder.folder"; }
if ( ( ($homedirfolders eq 'yes') || ($homedirspools eq 'yes') ) && ($> == 0) ) { $) = $gid; $> = $uid; }
open ($spoolhandle, $spoolfile) or return \%message; unless (flock($spoolhandle, LOCK_SH)) { webmailerror("$lang_err{'couldnt_lock'} $spoolfile!"); }
update_headerdb($headerdb, $spoolfile);
($currentheader, $currentbody) = getmessageblock($messageid, $headerdb, $spoolhandle, 'split'); close ($spoolhandle);
if ($currentheader ne '') { my @message_attr = headerdb_get_message_attr($messageid, $headerdb); $currentcc = $currentreplyto = $currentencoding = 'N/A'; ($currentfrom, $currentdate, $currentsubject, $currenttype, $currentstatus) = @message_attr[1,3..6]; my $lastline = 'NONE'; foreach (split(/\n/, $currentheader)) { if (/^\s/) { if ($lastline eq 'TO') { $currentto .= $_ } elsif ($lastline eq 'REPLYTO') { $currentreplyto .= $_ } elsif ($lastline eq 'ENCODING') { $currentencoding .= $_ } elsif ($lastline eq 'CC') { $currentcc .= $_ } } elsif (/^to:\s+(.+)$/ig) { $currentto = $1; $lastline = 'TO'; } elsif (/^reply-to:\s+(.+)$/ig) { $currentreplyto = $1; $lastline = 'REPLYTO'; } elsif (/^cc:\s+(.+)$/ig) { $currentcc = $1; $lastline = 'CC'; } elsif (/^content-transfer-encoding:\s+(.+)$/ig) { $currentencoding = $1; $lastline = 'ENCODING'; } else { $lastline = 'NONE'; } }
if ($currenttype =~ /^multipart/i) { my @attachments; my ($attheader, $attcontents); my $boundary = $currenttype; $boundary =~ s/.*boundary="?([^"]+)"?.*$/$1/si; chomp($boundary); ($currentbody, @attachments) = split(/\-\-\Q$boundary\E\n*/,$currentbody); my $attnum = -1; # So that the first increment gives 0 my $outerattnum = -1; foreach my $bound (0 .. $#attachments) { $outerattnum++; $attnum++; my %temphash; ($attheader, $attcontents) = split(/\n\n/, $attachments[$outerattnum], 2); my $lastline='NONE'; my ($attcontenttype, $attdisposition, $attfilename, $attencoding); foreach (split(/\n/, $attheader)) { if (/^\s/) { if ($lastline eq 'TYPE') { $attcontenttype .= $_ } elsif ($lastline eq 'DISP') { $attdisposition .= $_ } } elsif (/^content-type:\s+(.+)$/ig) { $attcontenttype = $1; $lastline = 'TYPE'; } elsif (/^content-transfer-encoding:\s+(.+)$/ig) { $attencoding = $1; $lastline = 'NONE'; } elsif (/^content-disposition:\s+(.+)$/ig) { $attdisposition = $1; $lastline = 'DISP'; } else { $lastline = 'NONE'; } }
# Handle Outlook's funky "multipart inside multipart" encapsulation. Leave # it to MS to do something weird. TODO: Possibly implement recursion for # deeper levels of multipart messages, to reduce code size? Would have to # avoid being exploited by someone sucking up all memory by deliberately # sending messages with hundreds-of-levels deep attachments
if ($attfilename || $attcontenttype) { if ($attcontenttype =~ /^multipart/i) { my $boundary = $attcontenttype; $boundary =~ s/.*boundary="?([^"]+)"?.*$/$1/si; chomp($boundary); ($attcontents, my @attachments) = split(/\-\-\Q$boundary\E\n*/,$attcontents); $attnum--; # avoid incrementing $attnum first time foreach my $bound (0 .. $#attachments) { last if ($attachments[$attnum + 1] =~ /^\-\-\n/); $attnum++; my %temphash; my ($attheader, $attcontents) = split(/\n\n/, $attachments[$attnum], 2); my $lastline='NONE'; my ($attcontenttype, $attdisposition, $attfilename, $attencoding); foreach (split(/\n/, $attheader)) { if (/^\s/) { if ($lastline eq 'TYPE') { $attcontenttype .= $_ } elsif ($lastline eq 'DISP') { $attdisposition .= $_ } } elsif (/^content-type:\s+(.+)$/ig) { $attcontenttype = $1; $lastline = 'TYPE'; } elsif (/^content-transfer-encoding:\s+(.+)$/ig) { $attencoding = $1; $lastline = 'NONE'; } elsif (/^content-disposition:\s+(.+)$/ig) { $attdisposition = $1; $lastline = 'DISP'; } else { $lastline = 'NONE'; } } if ($attfilename || $attcontenttype) { $attfilename = $attcontenttype; $attcontenttype =~ s/^(.+);.*/$1/g; unless ($attfilename =~ s/^.+name="?([^"]+)"?.*$/$1/ig) { $attfilename = $attdisposition || ''; unless ($attfilename =~ s/^.+filename="?([^"]+)"?.*$/$1/ig) { $attfilename = "Unknown.html"; } } $attfilename = decode_mimewords($attfilename); $attheader = decode_mimewords($attheader); $temphash{filename} = $attfilename; $temphash{contenttype} = $attcontenttype || 'text/plain'; $temphash{encoding} = $attencoding; $temphash{header} = $attheader; $temphash{contents} = $attcontents; $attachment[$attnum] = \%temphash; } } } else { $attfilename = $attcontenttype; $attcontenttype =~ s/^(.+);.*/$1/g; unless ($attfilename =~ s/^.+name="?([^"]+)"?.*$/$1/ig) { $attfilename = $attdisposition || ''; unless ($attfilename =~ s/^.+filename="?([^"]+)"?.*$/$1/ig) { $attfilename = "Unknown.html"; } } $attfilename = decode_mimewords($attfilename); $attheader = decode_mimewords($attheader); $temphash{filename} = $attfilename; $temphash{contenttype} = $attcontenttype || 'text/plain'; $temphash{encoding} = $attencoding; $temphash{header} = $attheader; $temphash{contents} = $attcontents; $attachment[$attnum] = \%temphash; } } } } elsif ( ($currenttype ne 'N/A') && !($currenttype =~ /^text/i) ) { my ($attfilename, $attcontenttype); my %temphash; $attcontenttype = $attfilename = $currenttype; $attcontenttype =~ s/^(.+);.*/$1/g; unless ($attfilename =~ s/^.+name="?([^"]+)"?.*$/$1/ig) { $attfilename = "Unknown.html"; } $attfilename = decode_mimewords($attfilename); $attheader = decode_mimewords($attheader); $temphash{filename} = $attfilename; $temphash{contenttype} = $attcontenttype || 'text/plain'; $temphash{encoding} = $currentencoding; $temphash{header} = $attheader; $temphash{contents} = $currentbody; $currentbody = " "; $attachment[0] = \%temphash; }
$currentfrom = decode_mimewords($currentfrom); $currentto = decode_mimewords($currentto); $currentcc = decode_mimewords($currentcc); $currentreplyto = decode_mimewords($currentreplyto); $currentsubject = decode_mimewords($currentsubject);
$message{"header"} = $currentheader; $message{"body"} = $currentbody; $message{from} = $currentfrom; $message{replyto} = $currentreplyto unless ($currentreplyto eq "N/A"); $message{to} = $currentto unless ($currentto eq "N/A"); $message{cc} = $currentcc unless ($currentcc eq "N/A"); $message{date} = $currentdate; $message{subject} = $currentsubject; $message{status} = $currentstatus; $message{messageid} = $currentid; $message{contenttype} = $currenttype; $message{encoding} = $currentencoding; $message{attachment} = \@attachment;
# Determine message's number and previous and next # message IDs. my @headers = @{&getheaders($username)}; foreach my $messagenumber (0..$#headers) { if (${$headers[$messagenumber]}{message_id} eq $messageid) { $message{"prev"} = ${$headers[$messagenumber-1]}{message_id} if ($messagenumber > 0); $message{"next"} = ${$headers[$messagenumber+1]}{message_id} if ($messagenumber < $#headers); $message{"number"} = $messagenumber+1; last; } } return \%message; } else { return \%message; } } #################### END GETMESSAGE #######################
################# GETMESSAGEBLOCK ######################## sub getmessageblock { my ($messageid, $headerdb, $spoolhandle, $mode)=@_; my (@attr, $buff, $oldoffset);
$oldoffset=tell($spoolhandle);
@attr = headerdb_get_message_attr($messageid, $headerdb); if ($#attr > 0) { seek($spoolhandle, $attr[$_OFFSET], 0); read($spoolhandle, $buff, $attr[$_SIZE]); }
seek($spoolhandle, $oldoffset, 0); if ($mode eq 'split') { my ($header, $body) = split(/\n\r*\n/, $buff, 2); undef($buff); if ( $header =~ /^content-type:\s+message\/rfc822/mi ) { return(split(/\n\r*\n/, $body, 2)); } else { return($header, $body); } } else { return($buff); } } ############ END GETMESSAGEBLOCK ########################
#################### SHIFTBLOCK #################### sub shiftblock { my ($fh, $start, $size, $movement)=@_; my ($oldoffset, $movestart, $left, $buff);
$oldoffset=tell($fh); $left=$size; if ( $movement >0 ) { while ($left>0) { if ($left>=65536) { $movestart=$start+$left-65536; seek($fh, $movestart, 0); read($fh, $buff, 65536); seek($fh, $movestart+$movement, 0); print $fh "$buff"; $left=$left-65536; } else { $movestart=$start; seek($fh, $movestart, 0); read($fh, $buff, $left); seek($fh, $movestart+$movement, 0); print $fh "$buff"; $left=0; } }
} else { # $movement <0 while ($left>0) { if ($left>=65536) { $movestart=$start+$size-$left; seek($fh, $movestart, 0); read($fh, $buff, 65536); seek($fh, $movestart+$movement, 0); print $fh "$buff"; $left=$left-65536; } else { $movestart=$start+$size-$left; seek($fh, $movestart, 0); read($fh, $buff, $left); seek($fh, $movestart+$movement, 0); print $fh "$buff"; $left=0; } } } seek($fh, $oldoffset, 0); } #################### END SHIFTBLOCK ####################
#################### UPDATESTATUS ######################### sub updatestatus { my $username = shift; my $messageid = shift; my $status = shift; my ($currmessage, $currentheader, $currentbody); my $currentmessageid; my $spool = ''; my $spoolfile; my $headerdb = "$userprefsdir$user/$folder"; my $spoolhandle = FileHandle->new();
if ($folder eq 'INBOX') { if ($homedirspools eq "yes") { $spoolfile = "$homedir/$homedirspoolname"; } elsif ($hashedmailspools eq "yes") { $username =~ /^(.)(.)/; my $firstchar = $1; my $secondchar = $2; $spoolfile = "$mailspooldir/$firstchar/$secondchar/$username"; } else { $spoolfile = "$mailspooldir/$username"; } } elsif ( ($folder eq 'SAVED') || ($folder eq 'SENT') || ($folder eq 'TRASH') || ($homedirfolders eq 'yes') ) { $spoolfile = "$folderdir/$folder"; } else { $spoolfile = "$folderdir/$folder.folder"; }
if ( ( ($homedirfolders eq 'yes') || ($homedirspools eq 'yes') ) && ($> == 0) ) { $) = $gid; $> = $uid; }
unless (filelock($spoolfile, LOCK_EX)) { webmailerror("$lang_err{'couldnt_lock'} $spoolfile!"); }
update_headerdb($headerdb, $spoolfile);
open ($spoolhandle, "+<$spoolfile") or webmailerror("$lang_err{'couldnt_open'} $spoolfile!");
my @messageids = get_messageids_sorted_by_offset($headerdb); my $movement; my @attr; my $i; # The de-facto standard counter variable ;) for ($i = 0; $i <= $#messageids; $i++) { if ($messageids[$i] eq $messageid) { @attr = headerdb_get_message_attr($messageid, $headerdb);
my $messagestart = $attr[$_OFFSET]; my $messagesize = $attr[$_SIZE]; my $messageend = $messagestart + $messagesize; my $foldersize; my ($messagenewsize, $messagenewstatus);
seek ($spoolhandle, $messagestart, 0) or webmailerror("$lang_err{'couldnt_seek'} $spoolfile!"); read($spoolhandle, $currmessage, $messagesize);
($currentheader, $currentbody) = split(/\n\r*\n/,$currmessage, 2);
if ($currentheader =~ s/^status:\s+(.*?)$/Status: $status$1/im) { $messagenewstatus="$status$1"; } else { $currentheader .= "\nStatus: $status"; $messagenewstatus="$status"; } $currentheader="From $currentheader" unless ($currentheader =~ /^From /);
$messagenewsize=length($currentheader)+length("\n\n")+length($currentbody); $movement=$messagenewsize-$messagesize;
my @s=stat($spoolhandle); $foldersize=$s[7]; shiftblock($spoolhandle, $messageend, $foldersize-$messageend, $movement);
seek ($spoolhandle, $messagestart, 0) or webmailerror("$lang_err{'couldnt_seek'} $spoolfile!"); print $spoolhandle $currentheader, "\n\n", $currentbody;
seek($spoolhandle, $foldersize+$movement, 0); truncate($spoolhandle, tell($spoolhandle));
### set attributes in headerdb for this status changed message $attr[$_SIZE]=$messagenewsize; $attr[$_STATUS]=$messagenewstatus; headerdb_set_message_attr($messageid, $headerdb, @attr);
last; } } $i++; ### set attributes in headerdb for messages after the above status changed message for (;$i<=$#messageids; $i++) { @attr = headerdb_get_message_attr($messageids[$i], $headerdb); $attr[$_OFFSET]+=$movement; headerdb_set_message_attr($messageids[$i], $headerdb, @attr); } close ($spoolhandle) or webmailerror("$lang_err{'couldnt_close'} $spoolfile!");
headerdb_set_metainfo($headerdb, $spoolfile);
filelock("$spoolfile", LOCK_UN); } ################### END UPDATESTATUS ######################
#################### MOVEMESSAGE ######################## sub movemessage { verifysession(); my $destination = param("destination"); ($destination =~ /(.+)/) && ($destination = $1); my $destnoext = $destination; # Will be used to update headerdb my @messageids = param("message_ids"); my $messageids = join("\n", @messageids); my $spoolfile; my $headerdb = "$userprefsdir$user/$folder"; my $spoolhandle=FileHandle->new(); my $moved=0;
webmailerror ("$lang_err{'shouldnt_move_here'}") if (($folder eq $destination) || ($destination eq 'INBOX'));
unless ( ($destination eq 'TRASH') || ($destination eq 'SAVED') || ($destination eq 'SENT') || ($destination eq 'sent-mail') || ($destination eq 'saved-messages') || ($destination eq 'webmail-trash') ) { $destination .= ".folder" unless ( $homedirfolders eq 'yes' ); webmailerror("$lang_err{'destination_folder'} $destination $lang_err{'doesnt_exist'}") unless ( -f "$folderdir/$destination" ); }
if ($hitquota) { unless ( ($destination eq 'TRASH') || ($destination eq 'webmail-trash') ){ webmailerror("$lang_err{'folder_hitquota'}"); } }
if ($folder eq 'INBOX') { if ($homedirspools eq "yes") { $spoolfile = "$homedir/$homedirspoolname"; } elsif ($hashedmailspools eq "yes") { $user =~ /^(.)(.)/; my $firstchar = $1; my $secondchar = $2; $spoolfile = "$mailspooldir/$firstchar/$secondchar/$user"; } else { $spoolfile = "$mailspooldir/$user"; } } elsif ( ($folder eq 'SAVED') || ($folder eq 'SENT') || ($folder eq 'TRASH') || ($homedirfolders eq 'yes') ) { $spoolfile = "$folderdir/$folder"; } else { $spoolfile = "$folderdir/$folder.folder"; }
if ( ( ($homedirfolders eq 'yes') || ($homedirspools eq 'yes') ) && ($> == 0) ) { $) = $gid; $> = $uid; }
unless (filelock($spoolfile, LOCK_EX)) { webmailerror("$lang_err{'couldnt_lock'} $spoolfile!"); }
update_headerdb($headerdb, $spoolfile); open ($spoolhandle, "+<$spoolfile") or webmailerror("$lang_err{'couldnt_open'} $spoolfile!");
my @allmessageids = get_messageids_sorted_by_offset($headerdb); my ($blockstart, $blockend, $writepointer); my ($currmessage, $messagestart, $messagesize, @attr); my $i;
$blockstart = $blockend = $writepointer = 0;
for ($i = 0; $i <= $#allmessageids; $i++) {
if ($messageids =~ /^\Q$allmessageids[$i]\E$/m) { # We need to move it $moved++; @attr = headerdb_get_message_attr($allmessageids[$i], $headerdb); $messagestart = $attr[$_OFFSET]; $messagesize = $attr[$_SIZE];
shiftblock($spoolhandle, $blockstart, $blockend - $blockstart, $writepointer-$blockstart); $writepointer = $writepointer + ($blockend - $blockstart); $blockstart = $blockend = $messagestart + $messagesize;
$currmessage = getmessageblock($allmessageids[$i], $headerdb, $spoolhandle); unless ($hitquota) { open (DEST, ">>$folderdir/$destination") or webmailerror ("$lang_err{'couldnt_open'} $destination!"); unless (filelock("$folderdir/$destination", LOCK_EX)) { webmailerror("$lang_err{'couldnt_lock'} $destination!"); }
update_headerdb("$userprefsdir$user/$destnoext", "$folderdir/$destination"); #$attr[$_OFFSET]=tell(DEST); #tell doesn't work the same in append mode any more.
$attr[$_OFFSET]= -s "$folderdir/$destination"; if ($currmessage =~ /^From /) { $attr[$_SIZE] = length($currmessage); print DEST "$currmessage"; } else { $attr[$_SIZE] = length("From ") + length($currmessage); print DEST "From ", $currmessage; }
close (DEST) or webmailerror("$lang_err{'couldnt_close'} $destination!");
headerdb_set_message_attr($allmessageids[$i], "$userprefsdir$user/$destnoext", @attr); headerdb_set_metainfo("$userprefsdir$user/$destnoext","$folderdir/$destination");
filelock("$folderdir/$destination", LOCK_UN); }
headerdb_delete_message($allmessageids[$i], $headerdb);
} else { # msg to be kept in same folder @attr=headerdb_get_message_attr($allmessageids[$i], $headerdb); $messagestart=$attr[$_OFFSET]; $messagesize=$attr[$_SIZE]; $blockend=$messagestart+$messagesize;
my $movement=$writepointer-$blockstart; if ($movement<0) { $attr[$_OFFSET]+=$movement; headerdb_set_message_attr($allmessageids[$i], $headerdb, @attr); } } }
if ($moved>0) { shiftblock($spoolhandle, $blockstart, $blockend - $blockstart, $writepointer-$blockstart); seek($spoolhandle, $writepointer + ($blockend - $blockstart), 0); truncate($spoolhandle, tell($spoolhandle)); $messageids =~ s/\n/, /g; writelog("moved messages to $destination - ids=$messageids"); headerdb_set_metainfo($headerdb, $spoolfile); } close ($spoolhandle) or webmailerror("$lang_err{'couldnt_close'} $spoolfile!"); filelock($spoolfile, LOCK_UN);
if (param("messageaftermove")) { readmessage(); } else { displayheaders(); } } #################### END MOVEMESSAGE #######################
#################### EMPTYTRASH ######################## sub emptytrash { verifysession();
my $trashfile; if ( $homedirfolders eq 'yes' ) { $trashfile = "$folderdir/webmail-trash"; } else { $trashfile = "$folderdir/TRASH"; } if ( ($homedirfolders eq 'yes') && ($> == 0) ) { $) = $gid; $> = $uid; } open (TRASH, ">$trashfile") or webmailerror ("$lang_err{'couldnt_open'} $trashfile!"); close (TRASH) or webmailerror("$lang_err{'couldnt_close'} $trashfile!"); writelog("trash emptied"); displayheaders(); } #################### END EMPTYTRASH #######################
###################### ENCODING/DECODING ############################## # Most of this code is blatantly snatched from parts of the MIME-Tools # Perl modules, by the talented Eryq, and particularly code contributed to # them by Gisle Aas
sub encode_base64 ($;$) { my $unencoded = $_[0]; my $res = ""; my $eol = $_[1]; $eol = "\n" unless defined $eol; while ($unencoded =~ /(.{1,45})/gs) { $res .= substr(pack('u', $1), 1); chop($res); } $res =~ tr|` -_|AA-Za-z0-9+/|; # `# help emacs # fix padding at the end my $padding = (3 - length($_[0]) % 3) % 3; $res =~ s/.{$padding}$/'=' x $padding/e if $padding; # break encoded string into lines of no more than 76 characters each if (length $eol) { $res =~ s/(.{1,76})/$1$eol/g; } $res; }
sub decode_base64 ($) { local($^W) = 0; # unpack("u",...) gives bogus warning in 5.00[123]
my $str = shift; my $res = "";
$str =~ tr|A-Za-z0-9+=/||cd; # remove non-base64 chars $str =~ s/=+$//; # remove padding $str =~ tr|A-Za-z0-9+/| -_|; # convert to uuencoded format while ($str =~ /(.{1,60})/gs) { my $len = chr(32 + length($1)*3/4); # compute length byte $res .= unpack("u", $len . $1 ); # uudecode } $res; }
sub decode_qp ($) { my $res = shift; $res =~ s/[ \t]+?(\r?\n)/$1/g; # rule #3 (trailing space must be deleted) $res =~ s/=\r?\n//g; # rule #5 (soft line breaks) $res =~ s/=([\da-fA-F]{2})/pack("C", hex($1))/ge; $res; }
sub decode_mimewords { my $encstr = shift; my %params = @_; my @tokens; $@ = ''; # error-return
# Collapse boundaries between adjacent encoded words: $encstr =~ s{(\?\=)[\r\n \t]*(\=\?)}{$1$2}gs; pos($encstr) = 0; ### print STDOUT "ENC = [", $encstr, "]\n";
# Decode: my ($charset, $encoding, $enc, $dec); while (1) { last if (pos($encstr) >= length($encstr)); my $pos = pos($encstr); # save it
# Case 1: are we looking at "=?..?..?="? if ($encstr =~ m{\G # from where we left off.. =\?([^?]*) # "=?" + charset + \?([bq]) # "?" + encoding + \?([^?]+) # "?" + data maybe with spcs + \?= # "?=" }xgi) { ($charset, $encoding, $enc) = ($1, lc($2), $3); $dec = (($encoding eq 'q') ? _decode_Q($enc) : _decode_B($enc)); push @tokens, [$dec, $charset]; next; }
# Case 2: are we looking at a bad "=?..." prefix? # We need this to detect problems for case 3, which stops at "=?": pos($encstr) = $pos; # reset the pointer. if ($encstr =~ m{\G=\?}xg) { $@ .= qq|unterminated "=?..?..?=" in "$encstr" (pos $pos)\n|; push @tokens, ['=?']; next; }
# Case 3: are we looking at ordinary text? pos($encstr) = $pos; # reset the pointer. if ($encstr =~ m{\G # from where we left off... ([\x00-\xFF]*? # shortest possible string, \n*) # followed by 0 or more NLs, (?=(\Z|=\?)) # terminated by "=?" or EOS }xg) { length($1) or die "MIME::Words: internal logic err: empty token\n"; push @tokens, [$1]; next; }
# Case 4: bug! die "MIME::Words: unexpected case:\n($encstr) pos $pos\n\t". "Please alert developer.\n"; } return (wantarray ? @tokens : join('',map {$_->[0]} @tokens)); }
sub _decode_Q { my $str = shift; $str =~ s/=([\da-fA-F]{2})/pack("C", hex($1))/ge; # RFC-1522, Q rule 1 $str =~ s/_/\x20/g; # RFC-1522, Q rule 2 $str; }
sub _decode_B { my $str = shift; decode_base64($str); }
################### END ENCODING/DECODING ##########################
##################### GETATTLIST ############################### sub getattlist { my $currentfile; my @attlist; $savedattsize = 0; opendir (WEBMAILDIR, "$webmaildir") or webmailerror("$lang_err{'couldnt_open'} $webmaildir!"); while (defined($currentfile = readdir(WEBMAILDIR))) { if ($currentfile =~ /^($thissession-att\d+)$/) { $currentfile = $1; $savedattsize += ( -s "$webmaildir$currentfile" ); open (ATTFILE, "$webmaildir$currentfile"); while (defined(my $line = <ATTFILE>)) { if ($line =~ s/^.+name="?([^"]+)"?.*$/$1/i) { $line =~ s/&/&/g; $line =~ s/\"/"/g; $line =~ s/</</g; $line =~ s/>/>/g; $line =~ s/^(.*)$/<em>$1<\/em>/; push (@attlist, $line); last; } } close (ATTFILE); } } closedir (WEBMAILDIR); return \@attlist; } ##################### END GETATTLIST ###########################
##################### DELETEATTACHMENTS ############################ sub deleteattachments { my $currentfile; opendir (WEBMAILDIR, "$webmaildir") or webmailerror("$lang_err{'couldnt_open'} $webmaildir!"); while (defined($currentfile = readdir(WEBMAILDIR))) { if ($currentfile =~ /^($thissession-att\d+)$/) { $currentfile = $1; unlink ("$webmaildir$currentfile"); } } closedir (WEBMAILDIR); } #################### END DELETEATTACHMENTS #########################
##################### FIRSTTIMEUSER ################################ sub firsttimeuser { my $html = ''; my $temphtml; open (FTUSER, "$templatesdir/$lang/firsttimeuser.template") or webmailerror("$lang_err{'couldnt_open'} firsttimeuser.template!"); while (<FTUSER>) { $html .= $_; } close (FTUSER); $html = applystyle($html);
printheader();
$temphtml = startform(-action=>"$prefsurl"); $temphtml .= hidden(-name=>'sessionid', -default=>$thissession, -override=>'1'); $temphtml .= hidden(-name=>'firsttimeuser', -default=>'yes', -override=>'1'); $temphtml .= hidden(-name=>'realname', -default=>(split(/,/,(getpwnam($user))[6]))[0] || "Your Name", -override=>'1'); $temphtml .= submit("$lang_text{'continue'}"); $temphtml .= end_form();
$html =~ s/\@\@\@CONTINUEBUTTON\@\@\@/$temphtml/; print $html;
printfooter(); } ################### END FIRSTTIMEUSER ##############################
###################### READPREFS ######################### sub readprefs { my ($key,$value); my %prefshash; if ( -f "$userprefsdir$user/config" ) { open (CONFIG,"$userprefsdir$user/config") or webmailerror("$lang_err{'couldnt_open'} config!"); while (<CONFIG>) { ($key, $value) = split(/=/, $_); chomp($value); if ($key eq 'style') { $value =~ s/\.//g; ## In case someone gets a bright idea... } $prefshash{"$key"} = $value; } close (CONFIG) or webmailerror("$lang_err{'couldnt_close'} config!"); } if ( -f "$userprefsdir$user/signature" ) { $prefshash{"signature"} = ''; open (SIGNATURE, "$userprefsdir$user/signature") or webmailerror("$lang_err{'couldnt_open'} signature!"); while (<SIGNATURE>) { $prefshash{"signature"} .= $_; } close (SIGNATURE) or webmailerror("$lang_err{'couldnt_close'} signature!"); } return \%prefshash; } ##################### END READPREFS ######################
###################### READSTYLE ######################### sub readstyle { my ($key,$value); my $stylefile = $prefs{"style"} || 'Default'; my %stylehash; unless ( -f "$stylesdir$stylefile") { $stylefile = 'Default'; } open (STYLE,"$stylesdir$stylefile") or webmailerror("$lang_err{'couldnt_open'} $stylefile!"); while (<STYLE>) { if (/###STARTSTYLESHEET###/) { $stylehash{"css"} = ''; while (<STYLE>) { $stylehash{"css"} .= $_; } } else { ($key, $value) = split(/=/, $_); chomp($value); $stylehash{"$key"} = $value; } } close (STYLE) or webmailerror("$lang_err{'couldnt_close'} $stylefile!"); return \%stylehash; } ##################### END READSTYLE ######################
##################### WRITELOG ############################ sub writelog { unless ( ($logfile eq 'no') || ( -l "$logfile" ) ) { open (LOGFILE,">>$logfile") or webmailerror("$lang_err{'couldnt_open'} $logfile!"); my $timestamp = localtime(); my $logaction = shift; my $loggeduser = $user || 'UNKNOWNUSER'; print LOGFILE "$timestamp - [$$] ($ENV{REMOTE_ADDR}) $loggeduser - $logaction\n"; close (LOGFILE); } } #################### END WRITELOG #########################
################### FILELOCK ################################# my %opentable; # Global, declared here for clarity of the sub
sub filelock { my ($filename, $lockflag)=@_; # Read sub params my $fh;
$fh=$opentable{$filename};
if (! defined($fh)) { # handle not found, $fh=FileHandle->new(); if (open($fh, "+<$filename")) { $opentable{$filename}=$fh; } else { return(0); } } return(flock($fh, $lockflag)); } ##################### END FILELOCK ###########################
###################### HEADERDB_GET_MESSAGE_ATTR ########################## sub headerdb_get_message_attr { my ($messageid, $headerdb)=@_; my (%HDB, @attr);
filelock("$headerdb.db", LOCK_SH); dbmopen(%HDB, "$headerdb.db", 0660); @attr=split(/@@@/, $HDB{$messageid} ); dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN); return(@attr); } ################### END HEADERDB_GET_MESSAGE_ATTR #########################
######################### UPDATE_HEADERDB ############################
sub update_headerdb { my ($headerdb, $spoolfile) = @_; my (%HDB, $metainfo, @datearray);
if ( -e "$headerdb.db" ) { filelock("$headerdb.db", LOCK_SH); dbmopen (%HDB, "$headerdb.db", 0660); $metainfo=$HDB{'METAINFO'}; dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN);
if ( $metainfo ne metainfo($spoolfile) ) { ($headerdb =~ /^(.+)$/) && ($headerdb = $1); # bypass taint check unlink "$headerdb.db"; unlink "$headerdb.dir"; unlink "$headerdb.pag"; } }
if ( !(-e "$headerdb.db") ) { open (SPOOL, $spoolfile); unless ( filelock($spoolfile, LOCK_SH) ) { if ( ! -d $spoolfile ) { webmailerror("$lang_err{'couldnt_lock'} $spoolfile!"); } }
dbmopen(%HDB, "$headerdb.db", 0660); filelock("$headerdb.db", LOCK_EX);
my $messagenumber = -1; my $lastline; my $line; my $inheader = 1; my $offset=0; my $total_size=0;
my ($db_message_id, $db_offset); my ($db_from, $db_to, $db_date, $db_subject); my ($db_content_type, $db_status, $db_messagesize);
while (defined($line = <SPOOL>)) {
$offset=$total_size; $total_size += length($line);
if (($line =~ /^From /) && !($db_content_type =~ /message\/rfc822/i)) {
unless ($messagenumber == -1) { ### Convert to readable text from MIME-encoded $db_from = decode_mimewords($db_from); $db_subject = decode_mimewords($db_subject); @datearray = split(/\s+/, $db_date); if ($datearray[0] =~ /[A-Za-z,]/) { shift @datearray; # Get rid of the day of the week } if ($datearray[0] =~ /[A-Za-z]/ ) { # looks like we have # a nonstandard date @datearray = ($datearray[1], $datearray[0], $datearray[3], $datearray[2], $timeoffset ); } ### Handle those darn 2-digit years if ($datearray[2] < 100) { if ($datearray[2] < 69) { $datearray[2] += 2000; } else { $datearray[2] += 1900; } } $db_date = "$month{$datearray[1]}/$datearray[0]/$datearray[2] $datearray[3] $datearray[4]";
### Hopefully avoid having updated status flags alter the id $db_message_id =~ s/\nstatus: [^\n]*//i; $db_message_id = MD5->hexhash($db_message_id);
unless ( ($db_offset == 0) && ($db_subject =~ /DON'T DELETE THIS MESSAGE/) ) { # We hate IMAP messages! :) $db_from =~ s/\@\@/\@\@ /g; $db_to =~ s/\@\@/\@\@ /g; $db_date =~ s/\@\@/\@\@ /g; $db_subject =~ s/\@\@/\@\@ /g; $db_content_type =~ s/\@\@/\@\@ /g; $db_status =~ s/\@\@/\@\@ /g; $HDB{$db_message_id}=join('@@@', $db_offset, $db_from, $db_to, $db_date, $db_subject, $db_content_type, $db_status, $db_messagesize); } }
$messagenumber++; $db_offset=$offset; $db_from = $db_to = $db_date = $db_subject = $db_content_type ='N/A'; $db_message_id = $line; $db_status = ''; $db_messagesize = length($line); $inheader = 1; $lastline = 'NONE';
} else { $db_message_id .= $line; $db_messagesize += length($line);
if ($inheader) { if (($line =~ /^\r*$/) && !($db_content_type =~ /message\/rfc822/i)) { $inheader = 0; } elsif ($line =~ /^\s/) { if ($lastline eq 'FROM') { $db_from .= $line } elsif ($lastline eq 'DATE') { $db_date .= $line } elsif ($lastline eq 'SUBJ') { $db_subject .= $line } elsif ($lastline eq 'TO') { $db_to .= $line } elsif ($lastline eq 'TYPE') { $db_content_type .= $line } } elsif ($line =~ /^from:\s+(.+)$/ig) { $db_from = $1; $lastline = 'FROM'; } elsif ($line =~ /^to:\s+(.+)$/ig) { $db_to = $1; $lastline = 'TO'; } elsif ($line =~ /^date:\s+(.+)$/ig) { $db_date = $1; $lastline = 'DATE'; } elsif ($line =~ /^subject:\s+(.+)$/ig) { $db_subject = $1; $lastline = 'SUBJ'; } elsif ($line =~ /^content-type:\s+(.+)$/ig) { $db_content_type = $1; $lastline = 'TYPE'; } elsif ($line =~ /^status:\s+(.+)$/ig) { $db_status = $1; $lastline = 'NONE'; } else { $lastline = 'NONE'; } } } }
###### Catch the last message, since there won't be a From: to trigger the capture
unless ($messagenumber == -1) { $db_from = decode_mimewords($db_from); $db_subject = decode_mimewords($db_subject); @datearray = split(/\s+/, $db_date); if ($datearray[0] =~ /[A-Za-z,]/) { shift @datearray; # Get rid of the day of the week } ### Handle those darn 2-digit years if ($datearray[2] < 100) { if ($datearray[2] < 69) { $datearray[2] += 2000; } else { $datearray[2] += 1900; } } $db_date = "$month{$datearray[1]}/$datearray[0]/$datearray[2] $datearray[3] $datearray[4]";
### Hopefully avoid having updated status flags alter the id $db_message_id =~ s/\nstatus: [^\n]*//i; $db_message_id = MD5->hexhash($db_message_id);
unless ( ($db_offset == 0) && ($db_subject =~ /DON'T DELETE THIS MESSAGE/) ) { # We hate IMAP messages! :) $db_from =~ s/\@\@/\@\@ /g; $db_to =~ s/\@\@/\@\@ /g; $db_date =~ s/\@\@/\@\@ /g; $db_subject =~ s/\@\@/\@\@ /g; $db_content_type =~ s/\@\@/\@\@ /g; $db_status =~ s/\@\@/\@\@ /g; $HDB{$db_message_id}=join('@@@', $db_offset, $db_from, $db_to, $db_date, $db_subject, $db_content_type, $db_status, $db_messagesize); } }
$HDB{'METAINFO'}=metainfo($spoolfile); dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN); close (SPOOL); } }
# return a string composed by the modify time & size of a file sub metainfo { my @l;
if (-e $_[0]) { # $dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks @l=stat($_[0]); return("mtime=$l[9] size=$l[7]"); } else { return(""); } }
sub headerdb_set_metainfo { my ($headerdb, $spoolfile)=@_; my %HDB;
filelock("$headerdb.db", LOCK_EX); dbmopen(%HDB, "$headerdb.db", 0660); $HDB{'METAINFO'}=metainfo($spoolfile); dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN); }
sub headerdb_set_message_attr { my ($messageid, $headerdb, @attr)=@_; my %HDB;
filelock("$headerdb.db", LOCK_EX); dbmopen(%HDB, "$headerdb.db", 0660); $HDB{$messageid}=join('@@@', @attr); dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN); }
sub headerdb_delete_message { my ($messageid, $headerdb)=@_; my %HDB;
filelock("$headerdb.db", LOCK_EX); dbmopen(%HDB, "$headerdb.db", 0660); delete $HDB{$messageid}; dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN); }
################## END UPDATE_HEADERDB ####################
############### GET_MESSAGEIDS_SORTED_BY_OFFSET #################
sub get_messageids_sorted_by_offset { my $headerdb=$_[0]; my (%HDB, %offset, @attr, $key, $data);
filelock("$headerdb.db", LOCK_SH); dbmopen(%HDB, "$headerdb.db", 0660);
while ( ($key, $data)=each(%HDB) ) { if ( $key eq 'METAINFO' || $key eq "" ) { next; } @attr=split(/@@@/, $data ); $offset{$key}=$attr[$_OFFSET]; }
dbmclose(%HDB); filelock("$headerdb.db", LOCK_UN);
return( sort { $offset{$a}<=>$offset{$b} } keys(%offset) ); }
############### END GET_MESSAGEIDS_SORTED_BY_OFFSET #################
##################### WEBMAILERROR ########################## sub webmailerror { unless ($headerprinted) { $headerprinted = 1; my $background = $style{"background"}; $background =~ s/"//g; print header(-pragma=>'no-cache', -charset=>$lang_charset), start_html(-"title"=>"WebMail version $version", -BGCOLOR=>$background, -BACKGROUND=>$bg_url); print '<style type="text/css">'; print $style{"css"}; print '</style>'; print "<FONT FACE=",$style{"fontface"},">\n"; } print '<BR><BR><BR><BR><BR><BR>'; print '<table border="0" align="center" width="40%" cellpadding="1" cellspacing="1">';
print '<tr><td bgcolor=',$style{"titlebar"},' align="left">', '<font color=',$style{"titlebar_text"},' face=',$style{"fontface"},' size="3"><b>WEBMAIL ERROR</b></font>', '</td></tr><tr><td align="center" bgcolor=',$style{"window_light"},'><BR>'; print shift; print '<BR><BR></td></tr></table>'; print '</BODY></HTML>'; exit 0; } ################### END WEBMAILERROR #######################
##################### PRINTHEADER ######################### sub printheader { my $cookie; unless ($headerprinted) { if ($setcookie) { $cookie = cookie( -name => 'sessionid', -"value" => $setcookie, -path => '/' ); } my $html = ''; $headerprinted = 1; open (HEADER, "$templatesdir/$lang/header.template") or webmailerror("$lang_err{'couldnt_open'} header.template!"); while (<HEADER>) { $html .= $_; } close (HEADER);
$html = applystyle($html); my $realname = $prefs{"realname"} || ''; if ($realname) { $realname = "- $realname"; }
$html =~ s/\@\@\@REALNAME\@\@\@/$realname/;
$html =~ s/\@\@\@BG_URL\@\@\@/$bg_url/g; if ($setcookie) { print header(-pragma=>'no-cache', -cookie=>$cookie, -charset=>$lang_charset); } else { print header(-pragma=>'no-cache', -charset=>$lang_charset); }
print $html; } } ################### END PRINTHEADER #######################
################### PRINTFOOTER ########################### sub printfooter { my $html = ''; open (FOOTER, "$templatesdir/$lang/footer.template") or webmailerror("$lang_err{'couldnt_open'} footer.template!"); while (<FOOTER>) { $html .= $_; } close (FOOTER); $html = applystyle($html); $html =~ s/\@\@\@VERSION\@\@\@/$version/g; print $html; } ################# END PRINTFOOTER #########################
################# APPLYSTYLE ############################## sub applystyle { my $template = shift; $template =~ s/\@\@\@LOGO_URL\@\@\@/$logo_url/g; $template =~ s/\@\@\@BACKGROUND\@\@\@/$style{"background"}/g; $template =~ s/\@\@\@TITLEBAR\@\@\@/$style{"titlebar"}/g; $template =~ s/\@\@\@TITLEBAR_TEXT\@\@\@/$style{"titlebar_text"}/g; $template =~ s/\@\@\@MENUBAR\@\@\@/$style{"menubar"}/g; $template =~ s/\@\@\@WINDOW_DARK\@\@\@/$style{"window_dark"}/g; $template =~ s/\@\@\@WINDOW_LIGHT\@\@\@/$style{"window_light"}/g; $template =~ s/\@\@\@ATTACHMENT_DARK\@\@\@/$style{"attachment_dark"}/g; $template =~ s/\@\@\@ATTACHMENT_LIGHT\@\@\@/$style{"attachment_light"}/g; $template =~ s/\@\@\@COLUMNHEADER\@\@\@/$style{"columnheader"}/g; $template =~ s/\@\@\@TABLEROW_LIGHT\@\@\@/$style{"tablerow_light"}/g; $template =~ s/\@\@\@TABLEROW_DARK\@\@\@/$style{"tablerow_dark"}/g; $template =~ s/\@\@\@FONTFACE\@\@\@/$style{"fontface"}/g; $template =~ s/\@\@\@SCRIPTURL\@\@\@/$scripturl/g; $template =~ s/\@\@\@PREFSURL\@\@\@/$prefsurl/g; $template =~ s/\@\@\@CSS\@\@\@/$style{"css"}/g; $template =~ s/\@\@\@VERSION\@\@\@/$version/g;
return $template; } ################ END APPLYSTYLE ###########################
################# URLESCAPE ############################### sub urlescape { my $encode = shift; $encode =~ s/([^a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg; return ($encode); } ############### END URLESCAPE #############################
|