diff --git a/mailboxes/reply_mail.cgi b/mailboxes/reply_mail.cgi index 83e35a14c..9192006dc 100755 --- a/mailboxes/reply_mail.cgi +++ b/mailboxes/reply_mail.cgi @@ -318,6 +318,23 @@ else { ($quote, $html_edit, $body) = "ed_message($mail, $qu, $sig); # Load images using server in replies $quote = &disable_html_images($quote, 3); + + # Don't include the original body as an attachment + @attach = &remove_body_attachments($mail, \@attach); + if (!$in{'forward'} && !$in{'enew'}) { + # When replying, lose non-cid attachments + @attach = grep { $_->{'header'}->{'content-id'} || + $_->{'header'}->{'content-location'} } @attach; + } + + # For a HTML reply or forward, fix up the cid: to refer to attachments + # in the original message. + if ($html_edit) { + my $uuser = &urlize($in{'user'}); + $quote = &fix_cids($quote, \@attach, + "detach.cgi?user=$uuser&idx=$in{'idx'}&folder=$in{'folder'}$subs"); + } + if ($in{'forward'} || $in{'enew'}) { @attach = grep { $_ ne $body } @attach; } @@ -500,10 +517,20 @@ $viewurl = "view_mail.cgi?idx=$in{'idx'}&user=$euser&". $detachurl = "detach.cgi?idx=$in{'idx'}&user=$euser&". "&folder=$folder->{'index'}$subs"; $mailurl = "view_mail.cgi?user=$euser&folder=$folder->{'index'}$subs"; +my @non_body_attach; if (@attach) { - &attachments_table(\@attach, $folder, $viewurl, $detachurl, + @non_body_attach = &remove_cid_attachments($mail, \@attach); + } +if (@non_body_attach) { + &attachments_table(\@non_body_attach, $folder, $viewurl, $detachurl, $mailurl, 'idx', "forward"); - } + } +foreach my $a (@attach) { + if (&indexof($a, @non_body_attach) < 0) { + # Body attachment .. always include + print &ui_hidden("forward", $a->{'idx'}); + } + } # Display forwarded mails if (@fwdmail) { diff --git a/mailboxes/send_mail.cgi b/mailboxes/send_mail.cgi index 043760d26..60ce77ed0 100755 --- a/mailboxes/send_mail.cgi +++ b/mailboxes/send_mail.cgi @@ -47,6 +47,8 @@ if ($in{'from'} =~ /^(\S+)\@(\S+)$/ && $access{'fromname'}) { } @sub = split(/\0/, $in{'sub'}); $subs = join("", map { "&sub=$_" } @sub); +my @inline_images; +my %cidmap; # Construct the email $in{'from'} || &error($text{'send_efrom'}); @@ -71,9 +73,39 @@ if ($in{'body'} =~ /\S/) { if ($in{'html_edit'}) { $in{'body'} = &html_editor_substitute_classes_with_styles($in{'body'}); } + my $preplainbody = $in{'body'}; + my $prehtmlbody = $in{'body'}; + + # Extract inline images if any + @inline_images = ($in{'body'} =~ /(data:image\/.*?;base64,)(.*?)"/g); + if (@inline_images) { + my $iid = 1; + for (my $i = 0; $i < scalar(@inline_images) - 1; $i += 2) { + if ($inline_images[$i] =~ /data:image/) { + my ($type) = $inline_images[$i] =~ /data:image\/(.*?);base64,/; + my $cid = "ii_".(time() + $i).'@'."$type"; + my $replace_html = "$inline_images[$i]$inline_images[$i+1]"; + my @data = split('@', $cid); + $inline_images[$i] = \@data; + $inline_images[$i+1] = decode_base64($inline_images[$i+1]); + + # $cid = "cid:$cid\" style=\"width: 60%"; + $cid = "cid:$cid"; + + # Replace for HTML + $in{'body'} =~ s/\Q$replace_html/$cid/; + + # Replace for plain text + $preplainbody =~ s/]*>/[image: inline-image$iid.$type]/; + $iid++; + } + } + $prehtmlbody = $in{'body'}; + } # Perform spell check on body if requested - local $plainbody = $in{'html_edit'} ? &html_to_text($in{'body'}) - : $in{'body'}; + local $plainbody = $in{'html_edit'} ? &html_to_text($preplainbody) + : $prehtmlbody; + if ($in{'spell'}) { @errs = &spell_check_text($plainbody); if (@errs) { @@ -90,6 +122,14 @@ if ($in{'body'} =~ /\S/) { exit; } } + + # For a HTML body, replace images from detach.cgi on the original + # email with cid: references. + if ($in{'html_edit'}) { + $in{'body'} = &create_cids($in{'body'}, \%cidmap); + } + + # Create the body attachment local $mt = $in{'html_edit'} ? "text/html" : "text/plain"; if ($in{'charset'}) { $mt .= "; charset=$in{'charset'}"; @@ -145,29 +185,49 @@ if ($in{'body'} =~ /\S/) { } } +# Add inline images +if (@inline_images) { + my $iid = 1; + for (my $i = 0; $i < scalar(@inline_images) - 1; $i += 2) { + my $cid = $inline_images[$i][0]."@".$inline_images[$i][1]; + my $type = $inline_images[$i][1]; + my $image_name = "inline-image$iid.$type"; + my $data = $inline_images[$i + 1]; + push(@attach, + { 'data' => $data, + 'headers' => [['Content-type', "image/$type; name=\"$image_name\""], + ['Content-Disposition', "inline; filename=\"$image_name\""], + ['Content-ID', "<$cid>"], + ['Content-Transfer-Encoding', 'base64'] + ] + }); + $iid++; + } + } + +# Add uploaded attachment $attachsize = 0; for($i=0; defined($in{"attach$i"}); $i++) { - # Add uploaded attachment - next if (!$in{"attach$i"}); - for($j=0; $j<@{$in{"attach$i"}}; $j++) { - next if (!$in{"attach${i}"}->[$j]); - &test_max_attach(length($in{"attach${i}"}->[$j])); - local $filename = $in{"attach${i}_filename"}->[$j]; - $filename =~ s/^.*(\\|\/)//; - local $type = $in{"attach${i}_content_type"}->[$j]. - "; name=\"".$filename."\""; - local $disp = "attachment; filename=\"".$filename."\""; - push(@attach, { 'data' => $in{"attach${i}"}->[$j], - 'headers' => [ [ 'Content-type', $type ], - [ 'Content-Disposition', $disp ], - [ 'Content-Transfer-Encoding', - 'base64' ] ] }); - $atotal += length($in{"attach${i}"}->[$j]); - } - } + next if (!$in{"attach$i"}); + for($j=0; $j<@{$in{"attach$i"}}; $j++) { + next if (!$in{"attach${i}"}->[$j]); + &test_max_attach(length($in{"attach${i}"}->[$j])); + local $filename = $in{"attach${i}_filename"}->[$j]; + $filename =~ s/^.*(\\|\/)//; + local $type = $in{"attach${i}_content_type"}->[$j]. + "; name=\"".$filename."\""; + local $disp = "attachment; filename=\"".$filename."\""; + push(@attach, { 'data' => $in{"attach${i}"}->[$j], + 'headers' => [ [ 'Content-type', $type ], + [ 'Content-Disposition', $disp ], + [ 'Content-Transfer-Encoding', + 'base64' ] ] }); +$atotal += length($in{"attach${i}"}->[$j]); + } + } +# Add server-side attachment for($i=0; defined($in{"file$i"}); $i++) { - # Add server-side attachment next if (!$in{"file$i"} || !$access{'canattach'}); @uinfo = &get_mail_user($in{'user'}); @uinfo || &error($text{'view_eugone'}); @@ -193,9 +253,10 @@ for($i=0; defined($in{"file$i"}); $i++) { 'base64' ] ] }); $atotal += length($data); } + +# Add forwarded attachments @fwd = split(/\0/, $in{'forward'}); if (@fwd) { - # Add forwarded attachments @mail = &mailbox_list_mails($in{'idx'}, $in{'idx'}, $folder); $fwdmail = $mail[$in{'idx'}]; &parse_mail($fwdmail); @@ -207,15 +268,26 @@ if (@fwd) { $fwdmail = $amail; } - foreach $f (@fwd) { + foreach my $f (@fwd) { &test_max_attach(length($fwdmail->{'attach'}->[$f]->{'data'})); - push(@attach, $fwdmail->{'attach'}->[$f]); + $a = $fwdmail->{'attach'}->[$f]; + if ($cidmap{$f}) { + # This attachment has been inlined .. set a content-id + $a->{'headers'} = [ + grep { lc($_->[0]) ne 'content-id' && + lc($_->[0]) ne 'content-location' } + @{$a->{'headers'}} ]; + push(@{$a->{'headers'}}, + [ 'Content-Id', "<$cidmap{$f}>" ]); + } + push(@attach, $a); $atotal += length($fwdmail->{'attach'}->[$f]->{'data'}); } } @mailfwd = split(/\0/, $in{'mailforward'}); + +# Add forwarded emails if (@mailfwd) { - # Add forwarded emails @mail = &mailbox_list_mails($mailfwd[0], $mailfwd[@mailfwd-1], $folder); foreach $f (@mailfwd) { $fwdmail = $mail[$f];