X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FWwwAttach.pm;h=0b2cda900ea00be6c181c288f3a5379f82588d87;hb=0821af5f21fdb083020ae2e3e79e4227ef59cd4f;hp=cda1c6c8db3a0745e8ad28a1cffaad05e10de6cf;hpb=40f250660b42caa69f5533da5501f8d3f31f30ac;p=public-inbox.git diff --git a/lib/PublicInbox/WwwAttach.pm b/lib/PublicInbox/WwwAttach.pm index cda1c6c8..0b2cda90 100644 --- a/lib/PublicInbox/WwwAttach.pm +++ b/lib/PublicInbox/WwwAttach.pm @@ -1,48 +1,85 @@ -# Copyright (C) 2016-2019 all contributors +# Copyright (C) 2016-2020 all contributors # License: AGPL-3.0+ # For retrieving attachments from messages in the WWW interface package PublicInbox::WwwAttach; # internal package use strict; -use warnings; +use parent qw(PublicInbox::GzipFilter); use bytes (); # only for bytes::length -use Email::MIME::ContentType qw(parse_content_type); -use PublicInbox::MIME; -use PublicInbox::MsgIter; - -sub get_attach_i { # msg_iter callback - my ($part, $depth, @idx) = @{$_[0]}; - my $res = $_[1]; - return if join('.', @idx) ne $res->[3]; # $idx +use PublicInbox::EmlContentFoo qw(parse_content_type); +use PublicInbox::Eml; + +sub get_attach_i { # ->each_part callback + my ($part, $depth, $idx) = @{$_[0]}; + my $ctx = $_[1]; + return if $idx ne $ctx->{idx}; # [0-9]+(?:\.[0-9]+)+ + my $res = $ctx->{res}; $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')) { + if ($ct && (($ct->{type} || '') eq 'text')) { # display all text as text/plain: my $cset = $ct->{attributes}->{charset}; if ($cset && ($cset =~ /\A[a-zA-Z0-9_\-]+\z/)) { $res->[1]->[1] .= qq(; charset=$cset); } + $ctx->{gz} = PublicInbox::GzipFilter::gz_or_noop($res->[1], + $ctx->{env}); + $part = $ctx->zflush($part->body); } else { # TODO: allow user to configure safe types $res->[1]->[1] = 'application/octet-stream'; + $part = $part->body; } - $part = $part->body; push @{$res->[1]}, 'Content-Length', bytes::length($part); $res->[2]->[0] = $part; } +sub async_eml { # for async_blob_cb + my ($ctx, $eml) = @_; + eval { $eml->each_part(\&get_attach_i, $ctx, 1) }; + if ($@) { + $ctx->{res}->[0] = 500; + warn "E: $@"; + } +} + +sub async_next { + my ($http) = @_; + my $ctx = $http->{forward} or return; # client aborted + # finally, we call the user-supplied callback + eval { $ctx->{wcb}->($ctx->{res}) }; + warn "E: $@" if $@; +} + +sub scan_attach ($) { # public-inbox-httpd only + my ($ctx) = @_; + $ctx->{env}->{'psgix.io'}->{forward} = $ctx; + $ctx->smsg_blob($ctx->{smsg}); +} + # /$LISTNAME/$MESSAGE_ID/$IDX-$FILENAME sub get_attach ($$$) { my ($ctx, $idx, $fn) = @_; - my $res = [ 404, [ 'Content-Type', 'text/plain' ], [ "Not found\n" ] ]; - my $mime = $ctx->{-inbox}->msg_by_mid($ctx->{mid}) or return $res; - $mime = PublicInbox::MIME->new($mime); - $res->[3] = $idx; - msg_iter($mime, \&get_attach_i, $res); - pop @$res; # cleanup before letting PSGI server see it - $res + $ctx->{res} = [ 404, [ 'Content-Type' => 'text/plain' ], + [ "Not found\n" ] ]; + $ctx->{idx} = $idx; + bless $ctx, __PACKAGE__; + my $eml; + if ($ctx->{smsg} = $ctx->{-inbox}->smsg_by_mid($ctx->{mid})) { + return sub { # public-inbox-httpd-only + $ctx->{wcb} = $_[0]; + scan_attach($ctx); + } if $ctx->{env}->{'pi-httpd.async'}; + # generic PSGI: + $eml = $ctx->{-inbox}->smsg_eml($ctx->{smsg}); + } elsif (!$ctx->{-inbox}->over) { + if (my $bref = $ctx->{-inbox}->msg_by_mid($ctx->{mid})) { + $eml = PublicInbox::Eml->new($bref); + } + } + $eml->each_part(\&get_attach_i, $ctx, 1) if $eml; + $ctx->{res} } 1;