X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FLeiInput.pm;h=d11d23d4064d374789b8200cab8c23101377e804;hb=b6b86cfd238c170ea3e2c4d4179f06c7af139086;hp=40d71f9eb6a8fe60d87c80f9a7aebda0d15e95d2;hpb=8fa51e510fbe629a05c2da82482053f77ece7de5;p=public-inbox.git diff --git a/lib/PublicInbox/LeiInput.pm b/lib/PublicInbox/LeiInput.pm index 40d71f9e..d11d23d4 100644 --- a/lib/PublicInbox/LeiInput.pm +++ b/lib/PublicInbox/LeiInput.pm @@ -7,6 +7,38 @@ use strict; use v5.10.1; use PublicInbox::DS; +# JMAP RFC 8621 4.1.1 +# https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml +our @KW = (qw(seen answered flagged draft), # widely-compatible + qw(forwarded), # IMAP + Maildir + qw(phishing junk notjunk)); # rarely supported + +# note: RFC 8621 states "Users may add arbitrary keywords to an Email", +# but is it good idea? Stick to the system and reserved ones, for now. +# The widely-compatible ones map to IMAP system flags, Maildir flags +# and mbox Status/X-Status headers. +my %KW = map { $_ => 1 } @KW; +my $L_MAX = 244; # Xapian term limit - length('L') + +# RFC 8621, sec 2 (Mailboxes) a "label" for us is a JMAP Mailbox "name" +# "Servers MAY reject names that violate server policy" +my %ERR = ( + L => sub { + my ($label) = @_; + length($label) >= $L_MAX and + return "`$label' too long (must be <= $L_MAX)"; + $label =~ m{\A[a-z0-9_](?:[a-z0-9_\-\./\@,]*[a-z0-9])?\z}i ? + undef : "`$label' is invalid"; + }, + kw => sub { + my ($kw) = @_; + $KW{$kw} ? undef : <reads($fmt) or return $lei->fail("--$opt_key=$fmt unrecognized"); 1; @@ -52,11 +83,13 @@ sub input_path_url { my $ifmt = lc($lei->{opt}->{'in-format'} // ''); # TODO auto-detect? if ($input =~ m!\Aimaps?://!i) { - $lei->{net}->imap_each($input, $self->can('input_net_cb'), + $lei->{net}->imap_each($input, $self->can('input_imap_cb') // + $self->can('input_net_cb'), $self, @args); return; } elsif ($input =~ m!\A(?:nntps?|s?news)://!i) { - $lei->{net}->nntp_each($input, $self->can('input_net_cb'), + $lei->{net}->nntp_each($input, $self->can('input_nntp_cb') // + $self->can('input_net_cb'), $self, @args); return; } @@ -88,7 +121,7 @@ sub input_path_url { return $lei->fail(<new->maildir_each_eml($input, $self->can('input_maildir_cb'), $self, @args); } else { @@ -99,11 +132,13 @@ EOM sub prepare_inputs { # returns undef on error my ($self, $lei, $inputs) = @_; my $in_fmt = $lei->{opt}->{'in-format'}; + my $sync = $lei->{opt}->{sync} ? {} : undef; # using LeiMailSync if ($lei->{opt}->{stdin}) { @$inputs and return $lei->fail("--stdin and @$inputs do not mix"); check_input_format($lei) or return; push @$inputs, '/dev/stdin'; + push @{$sync->{no}}, '/dev/stdin' if $sync; } my $net = $lei->{net}; # NetWriter may be created by l2m my (@f, @d); @@ -114,6 +149,13 @@ sub prepare_inputs { # returns undef on error require PublicInbox::NetReader; $net //= PublicInbox::NetReader->new; $net->add_url($input); + if ($sync) { + if ($input =~ m!\Aimaps?://!) { + push @{$sync->{ok}}, $input; + } else { + push @{$sync->{no}}, $input; + } + } } elsif ($input_path =~ s/\A([a-z0-9]+)://is) { my $ifmt = lc $1; if (($in_fmt // $ifmt) ne $ifmt) { @@ -121,6 +163,13 @@ sub prepare_inputs { # returns undef on error --in-format=$in_fmt and `$ifmt:' conflict } + if ($sync) { + if ($ifmt =~ /\A(?:maildir|mh)\z/i) { + push @{$sync->{ok}}, $input; + } else { + push @{$sync->{no}}, $input; + } + } my $devfd = $lei->path_to_fd($input_path) // return; if ($devfd >= 0 || (-f $input_path || -p _)) { require PublicInbox::MboxLock; @@ -131,6 +180,7 @@ sub prepare_inputs { # returns undef on error require PublicInbox::MdirReader; $ifmt eq 'maildir' or return $lei->fail("$ifmt not supported"); + $input = $lei->abs_path($input) if $sync; } else { return $lei->fail("Unable to handle $input"); } @@ -139,12 +189,18 @@ sub prepare_inputs { # returns undef on error $input is `eml', not --in-format=$in_fmt require PublicInbox::Eml; + push @{$sync->{no}}, $input if $sync; } else { my $devfd = $lei->path_to_fd($input) // return; if ($devfd >= 0 || -f $input || -p _) { - push @f, $input + push @{$sync->{no}}, $input if $sync; + push @f, $input; } elsif (-d $input) { - push @d, $input + if ($sync) { + $input = $lei->abs_path($input); + push @{$sync->{ok}}, $input; + } + push @d, $input; } else { return $lei->fail("Unable to handle $input") } @@ -154,6 +210,14 @@ $input is `eml', not --in-format=$in_fmt if (@d) { # TODO: check for MH vs Maildir, here require PublicInbox::MdirReader; } + if ($sync && $sync->{no}) { + return $lei->fail(<<"") if !$sync->{ok}; +--sync specified but no inputs support it + + # non-fatal if some inputs support support sync + $lei->err("# --sync will only be used for @{$sync->{ok}}"); + $lei->err("# --sync is not supported for: @{$sync->{no}}"); + } if ($net) { if (my $err = $net->errors) { return $lei->fail($err); @@ -183,4 +247,26 @@ sub input_only_atfork_child { undef; } +# like Getopt::Long, but for +kw:FOO and -kw:FOO to prepare +# for update_xvmd -> update_vmd +sub vmd_mod_extract { + my $argv = $_[-1]; + my $vmd_mod = {}; + my @new_argv; + for my $x (@$argv) { + if ($x =~ /\A(\+|\-)(kw|L):(.+)\z/) { + my ($op, $pfx, $val) = ($1, $2, $3); + if (my $err = $ERR{$pfx}->($val)) { + push @{$vmd_mod->{err}}, $err; + } else { # set "+kw", "+L", "-L", "-kw" + push @{$vmd_mod->{$op.$pfx}}, $val; + } + } else { + push @new_argv, $x; + } + } + @$argv = @new_argv; + $vmd_mod; +} + 1;