1 # Copyright (C) 2021 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 # front-end for the "lei convert" sub-command
5 package PublicInbox::LeiConvert;
8 use parent qw(PublicInbox::IPC);
10 use PublicInbox::InboxWritable qw(eml_from_path);
11 use PublicInbox::PktOp;
12 use PublicInbox::LeiStore;
13 use PublicInbox::LeiOverview;
16 my ($eml, $self) = @_;
17 my @kw = PublicInbox::LeiStore::mbox_keywords($eml);
18 $eml->header_set($_) for qw(Status X-Status);
19 $self->{wcb}->(undef, { kw => \@kw }, $eml);
22 sub imap_cb { # ->imap_each
23 my ($url, $uid, $kw, $eml, $self) = @_;
24 $self->{wcb}->(undef, { kw => $kw }, $eml);
28 my ($kw, $eml, $self) = @_;
29 $self->{wcb}->(undef, { kw => $kw }, $eml);
32 sub do_convert { # via wq_do
34 my $lei = $self->{lei};
35 my $in_fmt = $lei->{opt}->{'in-format'};
36 if (my $stdin = delete $self->{0}) {
37 PublicInbox::MboxReader->$in_fmt($stdin, \&mbox_cb, $self);
39 for my $input (@{$self->{inputs}}) {
40 my $ifmt = lc($in_fmt // '');
41 if ($input =~ m!\A(?:imap|nntp)s?://!) { # TODO: nntp
42 $lei->{nrd}->imap_each($input, \&imap_cb, $self);
44 } elsif ($input =~ s!\A([a-z0-9]+):!!i) {
48 open my $fh, '<', $input or
49 return $lei->fail("open $input: $!");
50 PublicInbox::MboxReader->$ifmt($fh, \&mbox_cb, $self);
52 PublicInbox::MdirReader::maildir_each_eml($input,
55 die "BUG: $input unhandled"; # should've failed earlier
59 delete $self->{wcb}; # commit
65 '!' => [ $lei->can('fail_handler'), $lei ],
66 '|' => [ $lei->can('sigpipe_handler'), $lei ],
67 'x_it' => [ $lei->can('x_it'), $lei ],
68 'child_error' => [ $lei->can('child_error'), $lei ],
69 '' => [ $lei->can('dclose'), $lei ],
71 ($lei->{pkt_op_c}, $lei->{pkt_op_p}) = PublicInbox::PktOp->pair($ops);
72 my $self = $lei->{cnv};
73 $self->wq_workers_start('lei_convert', 1, $lei->oldset, {lei => $lei});
74 my $op = delete $lei->{pkt_op_c};
75 delete $lei->{pkt_op_p};
76 $self->wq_io_do('do_convert', []);
78 $lei->event_step_init; # wait for shutdowns
79 if ($lei->{oneshot}) {
80 while ($op->{sock}) { $op->event_step }
84 sub call { # the main "lei convert" method
85 my ($cls, $lei, @inputs) = @_;
86 my $opt = $lei->{opt};
88 my $self = $lei->{cnv} = bless {}, $cls;
89 my $in_fmt = $opt->{'in-format'};
91 $opt->{dedupe} //= 'none';
92 my $ovv = PublicInbox::LeiOverview->new($lei, 'out-format');
94 $lei->fail("output not specified or is not a mail destination");
95 $opt->{augment} = 1 unless $ovv->{dst} eq '/dev/stdout';
97 @inputs and return $lei->fail("--stdin and @inputs do not mix");
98 $lei->check_input_format(undef, 'in-format') or return;
99 $self->{0} = $lei->{0};
101 # e.g. Maildir:/home/user/Mail/ or imaps://example.com/INBOX
102 for my $input (@inputs) {
103 my $input_path = $input;
104 if ($input =~ m!\A(?:imap|nntp)s?://!i) {
105 require PublicInbox::NetReader;
106 $nrd //= PublicInbox::NetReader->new;
107 $nrd->add_url($input);
108 } elsif ($input_path =~ s/\A([a-z0-9]+)://is) {
110 if (($in_fmt // $ifmt) ne $ifmt) {
111 return $lei->fail(<<"");
112 --in-format=$in_fmt and `$ifmt:' conflict
115 if (-f $input_path) {
116 require PublicInbox::MboxReader;
117 PublicInbox::MboxReader->can($ifmt) or return
118 $lei->fail("$ifmt not supported");
120 require PublicInbox::MdirReader;
121 $ifmt eq 'maildir' or return
122 $lei->fail("$ifmt not supported");
124 return $lei->fail("Unable to handle $input");
126 } elsif (-f $input) { push @f, $input }
127 elsif (-d _) { push @d, $input }
128 else { return $lei->fail("Unable to handle $input") }
130 if (@f) { $lei->check_input_format(\@f, 'in-format') or return }
131 if (@d) { # TODO: check for MH vs Maildir, here
132 require PublicInbox::MdirReader;
134 $self->{inputs} = \@inputs;
135 return convert_start($lei) if !$nrd;
137 if (my $err = $nrd->errors) {
138 return $lei->fail($err);
140 $nrd->{quiet} = $opt->{quiet};
142 require PublicInbox::LeiAuth;
143 my $auth = $lei->{auth} = PublicInbox::LeiAuth->new($nrd);
144 $auth->auth_start($lei, \&convert_start, $lei);
147 sub ipc_atfork_child {
149 my $lei = $self->{lei};
150 $lei->lei_atfork_child;
151 my $l2m = delete $lei->{l2m};
152 $l2m->pre_augment($lei);
153 $l2m->do_augment($lei);
154 $l2m->post_augment($lei);
155 $self->{wcb} = $l2m->write_cb($lei);
156 $SIG{__WARN__} = PublicInbox::Eml::warn_ignore_cb();
157 $self->SUPER::ipc_atfork_child;