# Copyright (C) all contributors # License: AGPL-3.0+ # lcat: local cat, display a local message by Message-ID or blob, # extracting from URL necessary # "lei lcat " package PublicInbox::LeiLcat; use strict; use v5.10.1; use PublicInbox::LeiViewText; use URI::Escape qw(uri_unescape); use PublicInbox::MID qw($MID_EXTRACT); sub lcat_folder ($$;$$) { my ($lei, $folder, $beg, $end) = @_; my $lms = $lei->{-lms_rw} //= $lei->lms // return; my $folders = [ $folder ]; eval { $lms->arg2folder($lei, $folders) }; return $lei->child_error(0, "# unknown folder: $folder") if $@; my %range; if (defined($beg)) { # NNTP article range $range{min} = $beg; $range{max} = $end // $beg; } for my $f (@$folders) { my $fid = $lms->fid_for($f); push @{$lei->{lcat_todo}}, { fid => $fid, %range }; } } sub lcat_imap_uri ($$) { my ($lei, $uri) = @_; # cf. LeiXSearch->lcat_dump my $lms = $lei->{-lms_rw} //= $lei->lms // return; if (defined $uri->uid) { push @{$lei->{lcat_todo}}, $lms->imap_oidhex($lei, $uri); } elsif (defined(my $fid = $lms->fid_for($$uri))) { push @{$lei->{lcat_todo}}, { fid => $fid }; } else { lcat_folder($lei, $$uri); } } sub lcat_nntp_uri ($$) { my ($lei, $uri) = @_; my $mid = $uri->message; # already unescaped by URI::news return "mid:$mid" if defined($mid); my $lms = $lei->{-lms_rw} //= $lei->lms // return; my ($ng, $beg, $end) = $uri->group; $uri->group($ng); lcat_folder($lei, $$uri, $beg, $end); '""'; } sub extract_1 ($$) { my ($lei, $x) = @_; if ($x =~ m!\b(maildir:.+)!i) { lcat_folder($lei, $1); '""'; # blank query, using {lcat_todo} } elsif ($x =~ m!\b(([a-z]+)://\S+)!i) { my ($u, $scheme) = ($1, $2); $u =~ s/[\>\]\)\,\.\;]+\z//; if ($scheme =~ m!\A(imaps?)\z!i) { require PublicInbox::URIimap; lcat_imap_uri($lei, PublicInbox::URIimap->new($u)); return '""'; # blank query, using {lcat_todo} } elsif ($scheme =~ m!\A(?:nntps?|s?news)\z!i) { require PublicInbox::URInntps; $u = PublicInbox::URInntps->new($u); return lcat_nntp_uri($lei, $u); } # http, or something else: require URI; $u = URI->new($u); my $p = $u->path; my $term; if ($p =~ m!([^/]+\@[^/]+)!) { # common msgid pattern $term = 'mid:'.uri_unescape($1); # is it a URL which returns the full thread? if ($u->scheme =~ /\Ahttps?/i && $p =~ m!/(?:T/?|t/?|t\.mbox\.gz|t\.atom)\b!) { $lei->{mset_opt}->{threads} = 1; } } elsif ($u->scheme =~ /\Ahttps?/i && # some msgids don't have '@', see if it looks like # a public-inbox URL: $p =~ m!/([^/]+)/(raw|t/?|T/?| t\.mbox\.gz|t\.atom)\z!x) { $lei->{mset_opt}->{threads} = 1 if $2 && $2 ne 'raw'; $term = 'mid:'.uri_unescape($1); } $term; } elsif ($x =~ $MID_EXTRACT) { # <$MSGID> "mid:$1"; } elsif ($x =~ /\b((?:m|mid):\S+)/) { # our own prefixes (and mairix) $1; } elsif ($x =~ /\bid:(\S+)/) { # notmuch convention "mid:$1"; } elsif ($x =~ /\bblob:([0-9a-f]{7,})\b/) { push @{$lei->{lcat_todo}}, $1; # cf. LeiToMail->wq_atexit_child '""'; # blank query } else { undef; } } sub extract_all { my ($lei, @argv) = @_; my $strict = !$lei->{opt}->{stdin}; my @q; for my $x (@argv) { if (my $term = extract_1($lei, $x)) { push @q, $term; } elsif ($strict) { return $lei->fail(<<""); could not extract Message-ID from $x } } delete $lei->{-lms_rw}; @q ? join(' OR ', @q) : $lei->fail("no Message-ID in: @argv"); } sub do_lcat { # lei->do_env cb my ($lei) = @_; my @argv = split(/\s+/, delete($lei->{stdin_buf})); $lei->{mset_opt}->{qstr} = extract_all($lei, @argv) or return; $lei->_start_query; } sub lei_lcat { my ($lei, @argv) = @_; my $lxs = $lei->lxs_prepare or return; $lei->ale->refresh_externals($lxs, $lei); $lei->_lei_store(1); my $opt = $lei->{opt}; my %mset_opt; $mset_opt{asc} = $opt->{'reverse'} ? 1 : 0; $opt->{sort} //= 'relevance'; $mset_opt{relevance} = 1; $lei->{mset_opt} = \%mset_opt; $opt->{'format'} //= 'text' unless defined($opt->{output}); if ($lei->{opt}->{stdin}) { return $lei->fail(<<'') if @argv; no args allowed on command-line with --stdin return $lei->slurp_stdin(\&do_lcat); } $lei->{mset_opt}->{qstr} = extract_all($lei, @argv) or return; $lei->_start_query; } sub _complete_lcat { require PublicInbox::LeiRefreshMailSync; PublicInbox::LeiRefreshMailSync::_complete_refresh_mail_sync(@_); # TODO: message-ids?, blobs? could get expensive... } 1;