use Date::Parse qw/str2time/;
use Encode qw/find_encoding/;
use Encode::MIME::Header;
-use Email::MIME::ContentType qw/parse_content_type/;
use PublicInbox::Hval qw/ascii_html/;
use PublicInbox::Linkify;
use PublicInbox::MID qw/mid_clean id_compress mid2path mid_mime/;
$footer = defined($footer) ? "\n$footer" : '';
my $hdr = $mime->header_obj;
headers_to_html_header($hdr, $ctx) .
- multipart_text_as_html($mime) .
+ multipart_text_as_html($mime, '') .
'</pre><hr /><pre>' .
html_footer($hdr, 1, $ctx, 'R/') .
$footer .
my $mhref = "${path}$href/";
# scan through all parts, looking for displayable text
- msg_iter($mime, sub { index_walk($fh, $_[0]) });
+ msg_iter($mime, sub { index_walk($fh, $mhref, $_[0]) });
$rv = "\n" . html_footer($hdr, 0, $ctx, "$path$href/R/");
if (defined $irt) {
}
sub index_walk {
- my ($fh, $p) = @_;
- my $s = add_text_body($p);
+ my ($fh, $upfx, $p) = @_;
+ my $s = add_text_body($upfx, $p);
return if $s eq '';
}
sub multipart_text_as_html {
- my ($mime) = @_;
+ my ($mime, $upfx) = @_;
my $rv = "";
# scan through all parts, looking for displayable text
msg_iter($mime, sub {
my ($p) = @_;
- $p = add_text_body($p);
+ $p = add_text_body($upfx, $p);
$rv .= $p;
$rv .= "\n" if $p ne '';
});
$$s .= qq(<span\nclass="q">) . $rv . '</span>'
}
-sub attach_link ($$$) {
- my ($ct, $p, $fn) = @_;
+sub attach_link ($$$$) {
+ my ($upfx, $ct, $p, $fn) = @_;
my ($part, $depth, @idx) = @$p;
my $nl = $idx[-1] > 1 ? "\n" : '';
my $idx = join('.', @idx);
$desc = $fn unless defined $desc;
$desc = '' unless defined $desc;
$desc = ': '.$desc if $desc;
- "$nl<b>[-- Attachment #$idx$desc --]\n" .
- "[-- Type: $ct, Size: $size bytes --]</b>"
+ my $sfn;
+ if (defined $fn && $fn =~ /\A[\w-]+\.[a-z0-9]+\z/) {
+ $sfn = $fn;
+ } elsif ($ct eq 'text/plain') {
+ $sfn = 'a.txt';
+ } else {
+ $sfn = 'a.bin';
+ }
+ qq($nl<a\nhref="$upfx$idx-$sfn">[-- Attachment #$idx$desc --]\n) .
+ "[-- Type: $ct, Size: $size bytes --]</a>"
}
sub add_text_body {
- my ($p) = @_; # from msg_iter: [ Email::MIME, depth, @idx ]
+ my ($upfx, $p) = @_; # from msg_iter: [ Email::MIME, depth, @idx ]
my ($part, $depth, @idx) = @$p;
my $ct = $part->content_type;
my $fn = $part->filename;
if (defined $ct && $ct =~ m!\btext/x?html\b!i) {
- return attach_link($ct, $p, $fn);
+ return attach_link($upfx, $ct, $p, $fn);
}
my $s = eval { $part->body_str };
# badly-encoded message? tell the world about it!
- return attach_link($ct, $p, $fn) if $@;
+ return attach_link($upfx, $ct, $p, $fn) if $@;
my @lines = split(/^/m, $s);
$s = '';
- if (defined($fn) || $depth > 1 || $idx[0] > 1) {
- $s .= attach_link($ct, $p, $fn);
+ if (defined($fn) || $depth > 0) {
+ $s .= attach_link($upfx, $ct, $p, $fn);
$s .= "\n\n";
}
my @quot;
our $INBOX_RE = qr!\A/([\w\.\-]+)!;
our $MID_RE = qr!([^/]+)!;
our $END_RE = qr!(T/|t/|R/|t\.mbox(?:\.gz)?|t\.atom|raw|)!;
+our $ATTACH_RE = qr!(\d[\.\d]*)-([\w-]+\.[a-z0-9]+)!i;
sub new {
my ($class, $pi_config) = @_;
} elsif ($path_info =~ m!$INBOX_RE/$MID_RE/$END_RE\z!o) {
msg_page($self, $ctx, $1, $2, $3);
+ } elsif ($path_info =~ m!$INBOX_RE/$MID_RE/$ATTACH_RE\z!o) {
+ my ($idx, $fn) = ($3, $4);
+ invalid_inbox_mid($self, $ctx, $1, $2) ||
+ get_attach($ctx, $idx, $fn);
# in case people leave off the trailing slash:
} elsif ($path_info =~ m!$INBOX_RE/$MID_RE/(T|t|R)\z!o) {
my ($inbox, $mid, $suffix) = ($1, $2, $3);
$self->{news_www} = PublicInbox::NewsWWW->new($self->{pi_config});
}
+sub get_attach {
+ my ($ctx, $idx, $fn) = @_;
+ require PublicInbox::WwwAttach;
+ PublicInbox::WwwAttach::get_attach($ctx, $idx, $fn);
+}
+
1;
--- /dev/null
+# Copyright (C) 2016 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# For retrieving attachments from messages in the WWW interface
+package PublicInbox::WwwAttach; # internal package
+use strict;
+use warnings;
+use Email::MIME;
+use Email::MIME::ContentType qw(parse_content_type);
+$Email::MIME::ContentType::STRICT_PARAMS = 0;
+use PublicInbox::MID qw(mid2path);
+use PublicInbox::MsgIter;
+
+# /$LISTNAME/$MESSAGE_ID/$IDX-$FILENAME
+sub get_attach ($$$) {
+ my ($ctx, $idx, $fn) = @_;
+ my $path = mid2path($ctx->{mid});
+
+ my $res = [ 404, [ 'Content-Type', 'text/plain' ], [ "Not found\n" ] ];
+ my $mime = $ctx->{git}->cat_file("HEAD:$path") or return $res;
+ $mime = Email::MIME->new($mime);
+ msg_iter($mime, sub {
+ my ($part, $depth, @idx) = @{$_[0]};
+ return if join('.', @idx) ne $idx;
+ $res->[0] = 200;
+ my $ct = $part->content_type;
+ $ct = parse_content_type($ct) if $ct;
+
+ # discrete == type, we remain Debian wheezy-compatible
+ if ($ct && (($ct->{discrete} || '') eq 'text')) {
+ # display all text as text/plain:
+ my $cset = $ct->{attributes}->{charset};
+ if ($cset && ($cset =~ /\A[\w-]+\z/)) {
+ $res->[1]->[1] .= qq(; charset=$cset);
+ }
+ } else { # TODO: allow user to configure safe types
+ $res->[1]->[1] = 'application/octet-stream';
+ }
+ $part = $part->body;
+ push @{$res->[1]}, 'Content-Length', bytes::length($part);
+ $res->[2]->[0] = $part;
+ });
+ $res;
+}
+
+1;