diff options
Diffstat (limited to 'lib/PublicInbox/WwwAtomStream.pm')
-rw-r--r-- | lib/PublicInbox/WwwAtomStream.pm | 115 |
1 files changed, 66 insertions, 49 deletions
diff --git a/lib/PublicInbox/WwwAtomStream.pm b/lib/PublicInbox/WwwAtomStream.pm index aa917ed8..26b366f5 100644 --- a/lib/PublicInbox/WwwAtomStream.pm +++ b/lib/PublicInbox/WwwAtomStream.pm @@ -1,44 +1,65 @@ -# Copyright (C) 2016-2020 all contributors <meta@public-inbox.org> +# Copyright (C) all contributors <meta@public-inbox.org> # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt> # -# Atom body stream for which yields getline+close methods -# public-inbox-httpd favors "getline" response bodies to take a -# "pull"-based approach to feeding slow clients (as opposed to a -# more common "push" model) +# Atom body stream for HTTP responses +# See PublicInbox::GzipFilter for details. package PublicInbox::WwwAtomStream; use strict; -use warnings; +use parent 'PublicInbox::GzipFilter'; use POSIX qw(strftime); -use Digest::SHA qw(sha1_hex); +use PublicInbox::SHA qw(sha1_hex); use PublicInbox::Address; use PublicInbox::Hval qw(ascii_html mid_href); use PublicInbox::MsgTime qw(msg_timestamp); -# called by PSGI server after getline: -sub close {} - sub new { my ($class, $ctx, $cb) = @_; + $ctx->{feed_base_url} = $ctx->{ibx}->base_url($ctx->{env}); + $ctx->{-spfx} = $ctx->{feed_base_url} if $ctx->{ibx}->{coderepo}; + $ctx->{cb} = $cb || \&PublicInbox::GzipFilter::close; $ctx->{emit_header} = 1; - $ctx->{feed_base_url} = $ctx->{-inbox}->base_url($ctx->{env}); - bless { cb => $cb || \&close, ctx => $ctx }, $class; + bless $ctx, $class; +} + +sub async_next ($) { + my ($http) = @_; # PublicInbox::HTTP + my $ctx = $http->{forward} or return; + eval { + if (my $smsg = $ctx->{smsg} = $ctx->{cb}->($ctx)) { + $ctx->smsg_blob($smsg); + } else { + $ctx->write('</feed>'); + $ctx->close; + } + }; + warn "E: $@" if $@; +} + +sub async_eml { # for async_blob_cb + my ($ctx, $eml) = @_; + my $smsg = delete $ctx->{smsg}; + $smsg->{mid} // $smsg->populate($eml); + $ctx->write(feed_entry($ctx, $smsg, $eml)); } sub response { - my ($class, $ctx, $code, $cb) = @_; - [ $code, [ 'Content-Type', 'application/atom+xml' ], - $class->new($ctx, $cb) ] + my ($class, $ctx, $cb) = @_; + my $res_hdr = [ 'Content-Type' => 'application/atom+xml' ]; + $class->new($ctx, $cb); + $ctx->psgi_response(200, $res_hdr); } # called once for each message by PSGI server sub getline { my ($self) = @_; - if (my $middle = $self->{cb}) { - my $smsg = $middle->($self->{ctx}); - return feed_entry($self, $smsg) if $smsg; + my $cb = $self->{cb} or return; + while (my $smsg = $cb->($self)) { + my $eml = $self->{ibx}->smsg_eml($smsg) or next; + return $self->translate(feed_entry($self, $smsg, $eml)); } - delete $self->{cb} ? '</feed>' : undef; + delete $self->{cb}; + $self->zflush('</feed>'); } # private @@ -63,7 +84,7 @@ sub to_uuid ($) { sub atom_header { my ($ctx, $title) = @_; - my $ibx = $ctx->{-inbox}; + my $ibx = $ctx->{ibx}; my $base_url = $ctx->{feed_base_url}; my $search_q = $ctx->{search_query}; my $self_url = $base_url; @@ -78,13 +99,16 @@ sub atom_header { $base_url .= '?' . $search_q->qs_html(x => undef); $self_url .= '?' . $search_q->qs_html; $page_id = to_uuid("q\n".$query); + } elsif (defined(my $cat = $ctx->{topic_category})) { + $title = title_tag("$cat topics - ".$ibx->description); + $self_url .= "topics_$cat.atom"; } else { $title = title_tag($ibx->description); $self_url .= 'new.atom'; - $page_id = "mailto:$ibx->{-primary_address}"; + my $addr = $ibx->{-primary_address}; + $page_id = "mailto:$addr" if defined $addr; } - my $mtime = (stat($ibx->{inboxdir}))[9] || time; - + $page_id //= to_uuid($self_url); qq(<?xml version="1.0" encoding="us-ascii"?>\n) . qq(<feed\nxmlns="http://www.w3.org/2005/Atom"\n) . qq(xmlns:thr="http://purl.org/syndication/thread/1.0">) . @@ -93,17 +117,14 @@ sub atom_header { qq(\nhref="$base_url"/>) . qq(<link\nrel="self"\nhref="$self_url"/>) . qq(<id>$page_id</id>) . - feed_updated(gmtime($mtime)); + feed_updated($ibx->modified); } # returns undef or string sub feed_entry { - my ($self, $smsg) = @_; - my $ctx = $self->{ctx}; - my $mid = $smsg->mid; # may extract Message-ID from {mime} - my $mime = delete $smsg->{mime}; - my $hdr = $mime->header_obj; - my $irt = PublicInbox::View::in_reply_to($hdr); + my ($ctx, $smsg, $eml) = @_; + my $mid = $smsg->{mid}; + my $irt = PublicInbox::View::in_reply_to($eml); my $uuid = to_uuid($mid); my $base = $ctx->{feed_base_url}; if (defined $irt) { @@ -115,40 +136,36 @@ sub feed_entry { $irt = ''; } my $href = $base . mid_href($mid) . '/'; - my $t = msg_timestamp($hdr); - my @t = gmtime(defined $t ? $t : time); - my $updated = feed_updated(@t); + my $updated = feed_updated(msg_timestamp($eml)); - my $title = $hdr->header('Subject'); + my $title = $eml->header('Subject'); $title = '(no subject)' unless defined $title && $title ne ''; $title = title_tag($title); - my $from = $hdr->header('From') or return; + my $from = $eml->header('From') // $eml->header('Sender') // + $ctx->{ibx}->{-primary_address}; my ($email) = PublicInbox::Address::emails($from); - my $name = join(', ',PublicInbox::Address::names($from)); - $name = ascii_html($name); - $email = ascii_html($email); + my $name = ascii_html(join(', ', PublicInbox::Address::names($from))); + $email = ascii_html($email // $ctx->{ibx}->{-primary_address}); - my $s = ''; - if (delete $ctx->{emit_header}) { - $s .= atom_header($ctx, $title); - } - $s .= "<entry><author><name>$name</name><email>$email</email>" . + print { $ctx->zfh } + (delete($ctx->{emit_header}) ? atom_header($ctx, $title) : ''), + "<entry><author><name>$name</name><email>$email</email>" . "</author>$title$updated" . - qq(<link\nhref="$href"/>). + qq(<link\nhref="$href"/>) . "<id>$uuid</id>$irt" . qq{<content\ntype="xhtml">} . qq{<div\nxmlns="http://www.w3.org/1999/xhtml">} . qq(<pre\nstyle="white-space:pre-wrap">); - $ctx->{obuf} = \$s; $ctx->{mhref} = $href; - PublicInbox::View::multipart_text_as_html($mime, $ctx); - delete $ctx->{obuf}; - $s .= '</pre></div></content></entry>'; + $ctx->{changed_href} = "${href}#related"; + $eml->each_part(\&PublicInbox::View::add_text_body, $ctx, 1); + '</pre></div></content></entry>'; } sub feed_updated { - '<updated>' . strftime('%Y-%m-%dT%H:%M:%SZ', @_) . '</updated>'; + my ($t) = @_; + '<updated>' . strftime('%Y-%m-%dT%H:%M:%SZ', gmtime($t)) . '</updated>'; } 1; |