#!/usr/local/bin/perl # view_mail.cgi # View a single email message ## kabe 2007/02/19: ## fixed display of ISO-2022-JP encoded From: display require './mailbox-lib.pl'; &ReadParse(); foreach $a (&list_addresses()) { $inbook{lc($a->[0])}++; } # Get the actual email being viewed, even if is a sub-message @folders = &list_folders_sorted(); ($folder) = grep { $_->{'index'} == $in{'folder'} } @folders; $qid = &urlize($in{'id'}); $mail = &mailbox_get_mail($folder, $in{'id'}, 0); $mail || &error($text{'view_egone'}); ¬es_decode($mail, $folder); &parse_mail($mail, undef, $in{'raw'}); @sub = split(/\0/, $in{'sub'}); $subs = join("", map { "&sub=$_" } @sub); foreach $s (@sub) { # We are looking at a mail within a mail .. &decrypt_attachments($mail); local $amail = &extract_mail($mail->{'attach'}->[$s]->{'data'}); &parse_mail($amail, undef, $in{'raw'}); $mail = $amail; } # Work out base URL for self links $baseurl = "view_mail.cgi?id=$qid&folder=$in{'folder'}&start=$in{'start'}$subs"; # Mark this mail as read &open_read_hash(); $mid = $mail->{'header'}->{'message-id'}; if ($userconfig{'auto_mark'}) { eval { $read{$mid} = 1 } if (!$read{$mid}); } # Possibly send a DSN, or check if one is needed $dsn_req = &requires_delivery_notification($mail); if (!@sub && $dsn_req && !$folder->{'sent'} && !$folder->{'drafts'}) { dbmopen(%dsn, "$user_module_config_directory/dsn", 0600); if ($userconfig{'send_dsn'} == 1 && !$dsn{$mid}) { # Send a DSN for this mail now local $dsnaddr = &send_delivery_notification($mail, undef, 0); if ($dsnaddr) { $dsn{$mid} = time()." ".$dsnaddr; $sent_dsn = 1; } } elsif ($userconfig{'send_dsn'} == 2 && !$dsn{$mid}) { # User may want to send one $send_dsn_button = 1; } ($sent_dsn_at, $sent_dsn_to) = split(/\s+/, $dsn{$mid}, 2); dbmclose(%dsn); } # Check if we have gotten back a DSN for *this* email &update_delivery_notification($mail, $folder); &open_dsn_hash(); if (defined($dsnreplies{$mid}) && $dsnreplies{$mid} != 1) { ($got_dsn, $got_dsn_from) = split(/\s+/, $dsnreplies{$mid}, 2); } if (defined($delreplies{$mid}) && $delreplies{$mid} != 1) { local @del = split(/\s+/, $delreplies{$mid}); local $i; for($i=0; $i<@del; $i+=2) { local $tm = localtime($del[$i]); if ($del[$i+1] =~ /^\!(.*)$/) { push(@delmsgs, &text('view_delfailed', "$1", $tm)); } else { push(@delmsgs, &text('view_delok', $del[$i+1], $tm)); } } } if ($in{'raw'}) { # Special mode - viewing whole raw message print "Content-type: text/plain\n\n"; if ($mail->{'fromline'}) { print $mail->{'fromline'},"\n"; } if (defined($mail->{'rawheaders'})) { #$mail->{'rawheaders'} =~ s/(\S)\t/$1\n\t/g; print $mail->{'rawheaders'}; } else { foreach $h (@{$mail->{'headers'}}) { #$h->[1] =~ s/(\S)\t/$1\n\t/g; print "$h->[0]: $h->[1]\n"; } } print "\n"; print $mail->{'body'}; exit; } # Check for encryption ($deccode, $decmessage) = &decrypt_attachments($mail); @attach = @{$mail->{'attach'}}; # Find body attachment and type ($textbody, $htmlbody, $body) = &find_body($mail, $userconfig{'view_html'}); $body = $htmlbody if ($in{'body'} == 2); $body = $textbody if ($in{'body'} == 1); # Show pre-body HTML if ($body && $body eq $htmlbody && $userconfig{'head_html'}) { $headstuff = &head_html($body->{'data'}); } # Get the character set if ($body) { $ctype = $body->{'header'}->{'content-type'} || $mail->{'header'}->{'content-type'}; if ($ctype =~ /charset="([a-z0-9\-]+)"/i || $ctype =~ /charset='([a-z0-9\-]+)'/i || $ctype =~ /charset=([a-z0-9\-]+)/i) { $charset = $1; } } ## Special handling of HTML header charset ($force_charset): ## For japanese text(ISO-2022-JP/EUC=JP/SJIS), the HTML output and ## text contents ($bodycontents) are already converted to EUC, ## so overriding HTML charset to that in the mail header ($charset) ## is generally wrong. (cf. mailbox/boxes-lib.pl:eucconv()) if ( &get_charset() =~ /^EUC/i ) { # EUC-JP,EUC-KR # use default charset output for HTML } else { $force_charset = $charset; } &set_module_index($in{'folder'}); &mail_page_header($text{'view_title'}, $headstuff); print &check_clicks_function(); &show_arrows(); print "
\n"; # Start of form print &ui_form_start("reply_mail.cgi"); print &ui_hidden("id", $in{'id'}),"\n"; print &ui_hidden("folder", $in{'folder'}),"\n"; print &ui_hidden("mod", &modification_time($folder)),"\n"; print &ui_hidden("body", $in{'body'}),"\n"; print &ui_hidden("start", $in{'start'}),"\n"; foreach $s (@sub) { print &ui_hidden("sub", $s),"\n"; } # Find any delivery status attachment ($dstatus) = grep { $_->{'type'} eq 'message/delivery-status' } @attach; # XXX look for text/calendar body # Check for signing if (&has_command("gpg") && &foreign_check("gnupg")) { # Check for GnuPG signatures local $sig; foreach $a (@attach) { $sig = $a if ($a->{'type'} =~ /^application\/pgp-signature/); } if ($sig) { # Verify the signature against the rest of the attachment &foreign_require("gnupg", "gnupg-lib.pl"); local $rest = $sig->{'parent'}->{'attach'}->[0]; $rest->{'raw'} =~ s/\r//g; $rest->{'raw'} =~ s/\n/\r\n/g; ($sigcode, $sigmessage) = &foreign_call("gnupg", "verify_data", $rest->{'raw'}, $sig->{'data'}); @attach = grep { $_ ne $sig } @attach; $sindex = $sig->{'idx'}; } elsif ($textbody && $textbody->{'data'} =~ /(-+BEGIN PGP SIGNED MESSAGE-+\n(Hash:\s+(\S+)\n\n)?([\000-\377]+\n)-+BEGIN PGP SIGNATURE-+\n([\000-\377]+)-+END PGP SIGNATURE-+\n)/i) { # Signature is in body text! local $sig = $1; local $text = $4; &foreign_require("gnupg", "gnupg-lib.pl"); ($sigcode, $sigmessage) = &foreign_call("gnupg", "verify_data", $sig); $body = $textbody; if ($sigcode == 0 || $sigcode == 1) { # XXX what about replying? $body->{'data'} = $text; } } } if ($userconfig{'top_buttons'} == 2 && &editable_mail($mail)) { &show_buttons(1, scalar(@sub)); print "
\n"; } # Start of headers section @hmode = ( ); if ($in{'headers'}) { push(@hmode, "$text{'view_noheaders'}"); } else { push(@hmode, "$text{'view_allheaders'}"); } push(@hmode, "$text{'view_raw'}"); $hmode = &ui_links_row(\@hmode); $hmode =~ s/
//g; print &ui_table_start(&left_right_align("$text{'view_headers'}", $hmode), "width=100%", 2, [ "width=10% nowrap" ]); if ($in{'headers'}) { # Show all the headers if ($mail->{'fromline'}) { print &ui_table_row($text{'mail_rfc'}, &eucconv_and_escape($mail->{'fromline'})); } foreach $h (@{$mail->{'headers'}}) { print &ui_table_row("$h->[0]:", &eucconv_and_escape(&decode_mimewords($h->[1]))); } } else { # Just show the most useful headers local @addrs = &split_addresses(&decode_mimewords( $mail->{'header'}->{'from'})); local @toaddrs = &split_addresses(&decode_mimewords( $mail->{'header'}->{'to'})); print &ui_table_row($text{'mail_from'}, &left_right_align(&address_link($mail->{'header'}->{'from'}), &search_link("from", $addrs[0]->[0], $text{'mail_fromsrch'}))); print &ui_table_row($text{'mail_to'}, &left_right_align(&address_link($mail->{'header'}->{'to'}), &search_link("to", $toaddrs[0]->[0], $text{'mail_tosrch'}))); if ($mail->{'header'}->{'cc'}) { print &ui_table_row($text{'mail_cc'}, &address_link($mail->{'header'}->{'cc'})); } if ($mail->{'header'}->{'bcc'}) { print &ui_table_row($text{'mail_bcc'}, &address_link($mail->{'header'}->{'bcc'})); } print &ui_table_row($text{'mail_date'}, &eucconv_and_escape($mail->{'header'}->{'date'})); local $subj = $mail->{'header'}->{'subject'}; $subj =~ s/^((Re:|Fwd:|\[\S+\])\s*)+//g; print &ui_table_row($text{'mail_subject'}, &left_right_align(&eucconv_and_escape(&decode_mimewords( $mail->{'header'}->{'subject'})), &search_link("subject", $subj, $text{'mail_subsrch'}))); } print &ui_table_end(); # Show body attachment, with properly linked URLs $image_mode = defined($in{'images'}) ? $in{'images'} : $userconfig{'view_images'}; if ($body && $body->{'data'} =~ /\S/) { if ($body eq $textbody) { # Show plain text $bodycontents = "
";
		foreach $l (&wrap_lines(&eucconv($body->{'data'}),
					$userconfig{'wrap_width'})) {
			$bodycontents .= &link_urls_and_escape($l,
						$userconfig{'link_mode'})."\n";
			}
		$bodycontents .= "
"; if ($htmlbody) { # Link to show HTML push(@bodyright, "$text{'view_ashtml'}"); } } elsif ($body eq $htmlbody) { # Attempt to show HTML ($bodycontents, $bodystuff) = &safe_html($body->{'data'}); @imagesurls = ( ); $bodycontents = &disable_html_images($bodycontents, $image_mode, \@imageurls); $bodycontents = &fix_cids($bodycontents, \@attach, "detach.cgi?id=$qid&folder=$in{'folder'}$subs"); if ($textbody) { # Link to show text push(@bodyright, "$text{'view_astext'}"); } if (@imageurls && $image_mode) { # Link to show images push(@bodyright, "$text{'view_images'}"); } } } if ($bodycontents) { if (@bodyright) { $bodyright = &ui_links_row(\@bodyright); $bodyright =~ s/
\s*$//; } print &ui_table_start( &left_right_align("$text{'view_body'}", $bodyright), "width=100%", 1); print &ui_table_row(undef, $bodycontents, undef, [ undef, $bodystuff ]); print &ui_table_end(); } # If *this* message is a delivery status, display it if ($dstatus) { local $ds = &parse_delivery_status($dstatus->{'data'}); $dtxt = $ds->{'status'} =~ /^2\./ ? $text{'view_dstatusok'} : $text{'view_dstatus'}; print &ui_table_start($dtxt, "width=100%", 2, [ "width=10% nowrap" ]); foreach $dsh ('final-recipient', 'diagnostic-code', 'remote-mta', 'reporting-mta') { if ($ds->{$dsh}) { $ds->{$dsh} =~ s/^\S+;//; print &ui_table_row($text{'view_'.$dsh}, &html_escape($ds->{$dsh})); } } print &ui_table_end(); } # Display other attachments @attach = &remove_body_attachments($mail, \@attach); @attach = &remove_cid_attachments($mail, \@attach); if (@attach) { # Table of attachments @detach = &attachments_table(\@attach, $folder, $in{'id'}, $subs); # Links to download all / slideshow @links = ( ); if (@attach > 1 && &can_download_all()) { push(@links, "$text{'view_aall'}"); } @iattach = grep { $_->{'type'} =~ /^image\// } @attach; if (@iattach > 1) { push(@links, "$text{'view_aslideshow'}"); } print &ui_links_row(\@links) if (@links); # Show form to detact to server, if enabled if ($config{'server_attach'} == 2 && @detach) { print &ui_table_start($text{'view_dheader'}, "width=100%", 1); $dtach = &ui_submit($text{'view_detach'}, 'detach'); $dtach .= &ui_hidden("bindex", $body->{'idx'}) if ($body); $dtach .= &ui_hidden("sindex", $sindex) if (defined($sindex)); $dtach .= &ui_select("attach", undef, [ [ '*', $text{'view_dall'} ], @detach ]); $dtach .= "$text{'view_dir'}\n"; $dtach .= &ui_textbox("dir", undef, 60)." ". &file_chooser_button("dir", 1); print &ui_table_row(undef, $dtach); print &ui_table_end(); } } # Display GnuPG results if (defined($sigcode)) { print &ui_table_start($text{'view_gnupg'}, "width=100%", 1); $sigmessage = &html_escape($sigmessage); $sigmessage = $sigmessage if ($sigcode == 4); print &ui_table_row(undef, &text('view_gnupg_'.$sigcode, $sigmessage)); if ($sigcode == 3) { print &ui_table_row(undef, &text('view_recv', $sigmessage, "/gnupg/recv.cgi?id=$sigmessage&return=".&urlize($baseurl)."&returnmsg=".&urlize($text{'view_return'}))); } print &ui_table_end(); } if ($deccode) { print &ui_table_start($text{'view_crypt'}, "width=100%", 1); print &ui_table_row(undef, &text('view_crypt_'.$deccode, "
$decmessage
")); print &ui_table_end(); } # Display DSN status if ($sent_dsn_to || $send_dsn_button || $got_dsn || @delmsgs) { print &ui_table_start($text{'view_dsn'}, "width=100%", 1); if ($sent_dsn_to) { print &ui_table_row(undef, &text($sent_dsn ? 'view_dnsnow' : 'view_dsnbefore', &html_escape($sent_dsn_to), ($dsntm = localtime($sent_dsn_at)))); } elsif ($send_dsn_button) { print &ui_table_row(undef, &text('view_dsnreq', &html_escape($dsn_req))."
". &ui_submit($text{'view_dsnsend'}, "dsn")); } elsif ($got_dsn) { print &ui_table_row(undef, &text('view_dsngot', &html_escape($got_dsn_from), ($dsntm = localtime($got_dsn)))); } elsif (@delmsgs) { print &ui_table_row(undef, join("
\n", @delmsgs)); } print &ui_table_end(); } if (!$folder->{'sent'} && !$folder->{'drafts'}) { # Show quick reply form print &ui_hidden_table_start($text{'view_quick'}, "width=100%", 1, "quick", 0); $wcols = $userconfig{'wrap_compose'}; print &ui_table_row(undef, &ui_textarea("quick", undef, 5, $wcols || 80, $wcols ? "hard" : "", 0, $wcols ? "" : "style='width:100%'"), 2); print &ui_table_row(undef, &ui_submit($text{'view_quick_send'}, "quick_send")." ". &ui_checkbox("quick_all", 1, $text{'view_quick_all'}, 0)." ". &ui_checkbox("quick_quote", 1, $text{'view_quick_quote'}, 1),2); } print &ui_hidden_table_end(); &show_buttons(2, scalar(@sub)) if (&editable_mail($mail)); if ($userconfig{'arrows'} == 2 && !@sub) { print "
\n"; &show_arrows(); } print "\n"; dbmclose(%read); # Show footer links local @sr = !@sub ? ( ) : ( "view_mail.cgi?id=$qid&folder=$in{'folder'}", $text{'view_return'} ), &mail_page_footer(@sub ? ( "view_mail.cgi?idx=$qid&folder=$in{'folder'}", $text{'view_return'} ) : ( ), "index.cgi?folder=$in{'folder'}&start=$in{'start'}", $text{'mail_return'}); &pop3_logout_all(); # show_buttons(pos, submode) sub show_buttons { local $spacer = " \n"; if ($folder->{'sent'} || $folder->{'drafts'}) { print &ui_submit($text{'view_enew'}, "enew"); } else { print &ui_submit($text{'view_reply'}, "reply"); print &ui_submit($text{'view_reply2'}, "rall"); } print $spacer; if ($userconfig{'open_mode'}) { # Compose button needs to pop up a window print &ui_submit($text{'mail_compose'}, "new", undef, "onClick='window.open(\"reply_mail.cgi?new=1\", \"compose\", \"toolbar=no,menubar=no,scrollbars=yes,width=1024,height=768\"); return false'>"); } else { # Compose button can just submit and redirect print &ui_submit($text{'mail_compose'}, "new"); } print $spacer; print &ui_submit($text{'view_forward'}, "forward"); print $spacer; if (!$_[1]) { # Show mark buttons, except for current mode if (!$folder->{'sent'} && !$folder->{'drafts'}) { $m = &get_mail_read($folder, $mail); foreach $i (0 .. 2) { if ($m != $i) { print &ui_submit($text{'view_markas'.$i}, "markas".$i); } } print $spacer; } if (@folders > 1) { print &movecopy_select($_[0], \@folders, $folder); print $spacer; } print &ui_submit($text{'view_delete'}, "delete"); print $spacer; } else { if (@folders > 1) { print &movecopy_select($_[0], \@folders, $folder, 1); print $spacer; } } print &ui_submit($text{'view_print'}, "print"); print $spacer; if (!$_[1]) { # Show spam and/or ham report buttons if (&can_report_spam($folder) && $userconfig{'spam_buttons'} =~ /mail/) { print &ui_submit($text{'view_black'}, "black"); if ($userconfig{'spam_del'}) { print &ui_submit($text{'view_razordel'}, "razor"); } else { print &ui_submit($text{'view_razor'}, "razor"); } print $spacer; } if (&can_report_ham($folder) && $userconfig{'ham_buttons'} =~ /mail/) { if ($userconfig{'white_move'} && $folder->{'spam'}) { print &ui_submit($text{'view_whitemove'}, "white"); } else { print &ui_submit($text{'view_white'}, "white"); } if ($userconfig{'ham_move'} && $folder->{'spam'}) { print &ui_submit($text{'view_hammove'}, "ham"); } else { print &ui_submit($text{'view_ham'}, "ham"); } print $spacer; } } print "
\n"; } sub show_arrows { print "
\n"; if (!@sub) { # Get next and previous emails, where they exist local $c = &mailbox_folder_size($folder, 1); local $prv = $mail->{'sortidx'} == 0 ? 0 : $mail->{'sortidx'}-1; local $nxt = $mail->{'sortidx'} == $c-1 ? $c-1 : $mail->{'sortidx'}+1; local @beside = &mailbox_list_mails_sorted($prv, $nxt, $folder, 1); if ($mail->{'sortidx'} != 0) { local $mailprv = $beside[$prv]; print "", "\n"; } else { print "\n"; } print "",&text('view_desc', $mail->{'sortidx'}+1, $folder->{'name'}),"\n"; if ($mail->{'sortidx'} < $c-1) { local $mailnxt = $beside[$nxt]; print "", "\n"; } else { print "\n"; } } else { print "$text{'view_sub'}\n"; } print "
\n"; } # search_link(field, what, text) sub search_link { local $fid; if ($userconfig{'related_search'}) { # Search is across all folders $fid = -$userconfig{'related_search'}; } elsif (!$folder->{'searchable'}) { # Search is source folder if ($mail->{'subfolder'}) { $fid = $mail->{'subfolder'}->{'index'}; } else { return undef; } } else { # Search is in this folder $fid = $in{'folder'}; } if ($_[1]) { return "$_[2]"; } else { return undef; } }