]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/LeiImport.pm
ds: inline set_cloexec
[public-inbox.git] / lib / PublicInbox / LeiImport.pm
1 # Copyright (C) 2021 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3
4 # front-end for the "lei import" sub-command
5 package PublicInbox::LeiImport;
6 use strict;
7 use v5.10.1;
8 use parent qw(PublicInbox::IPC PublicInbox::LeiInput);
9 use PublicInbox::InboxWritable qw(eml_from_path);
10
11 # /^input_/ subs are used by (or override) PublicInbox::LeiInput superclass
12
13 sub input_eml_cb { # used by PublicInbox::LeiInput::input_fh
14         my ($self, $eml, $vmd) = @_;
15         my $xoids = $self->{lei}->{ale}->xoids_for($eml);
16         if (my $all_vmd = $self->{all_vmd}) {
17                 @$vmd{keys %$all_vmd} = values %$all_vmd;
18         }
19         $self->{lei}->{sto}->wq_do('set_eml', $eml, $vmd, $xoids);
20 }
21
22 sub input_mbox_cb { # MboxReader callback
23         my ($eml, $self) = @_;
24         my $vmd;
25         if ($self->{-import_kw}) {
26                 my $kw = PublicInbox::MboxReader::mbox_keywords($eml);
27                 $vmd = { kw => $kw } if scalar(@$kw);
28         }
29         input_eml_cb($self, $eml, $vmd);
30 }
31
32 sub pmdir_cb { # called via wq_io_do from LeiPmdir->each_mdir_fn
33         my ($self, $f, $fl) = @_;
34         my ($folder, $bn) = ($f =~ m!\A(.+?)/(?:new|cur)/([^/]+)\z!) or
35                 die "BUG: $f was not from a Maildir?\n";
36         my $kw = PublicInbox::MdirReader::flags2kw($fl);
37         substr($folder, 0, 0) = 'maildir:'; # add prefix
38         my $lse = $self->{lse} //= $self->{lei}->{sto}->search;
39         my $lms = $self->{-lms_ro} //= $self->{lei}->lms; # may be 0 or undef
40         my @oidbin = $lms ? $lms->name_oidbin($folder, $bn) : ();
41         @oidbin > 1 and $self->{lei}->err("W: $folder/*/$$bn not unique:\n",
42                                 map { "\t".unpack('H*', $_)."\n" } @oidbin);
43         my %seen;
44         my @docids = sort { $a <=> $b } grep { !$seen{$_}++ }
45                         map { $lse->over->oidbin_exists($_) } @oidbin;
46         my $vmd = $self->{-import_kw} ? { kw => $kw } : undef;
47         if (scalar @docids) {
48                 $lse->kw_changed(undef, $kw, \@docids) or return;
49         }
50         if (my $eml = eml_from_path($f)) {
51                 $vmd->{sync_info} = [ $folder, \$bn ] if $self->{-mail_sync};
52                 $self->input_eml_cb($eml, $vmd);
53         }
54 }
55
56 sub input_net_cb { # imap_each / nntp_each
57         my ($uri, $uid, $kw, $eml, $self) = @_;
58         if (defined $eml) {
59                 my $vmd = $self->{-import_kw} ? { kw => $kw } : undef;
60                 $vmd->{sync_info} = [ $$uri, $uid ] if $self->{-mail_sync};
61                 $self->input_eml_cb($eml, $vmd);
62         } elsif (my $ikw = $self->{lei}->{ikw}) { # old message, kw only
63                 # we send $uri as a bare SCALAR and not a URIimap ref to
64                 # reduce socket traffic:
65                 $ikw->wq_io_do('ck_update_kw', [], $$uri, $uid, $kw);
66         }
67 }
68
69 sub do_import_index ($$@) {
70         my ($self, $lei, @inputs) = @_;
71         my $sto = $lei->_lei_store(1);
72         $sto->write_prepare($lei);
73         $self->{-import_kw} = $lei->{opt}->{kw} // 1;
74         my $vmd_mod = $self->vmd_mod_extract(\@inputs);
75         return $lei->fail(join("\n", @{$vmd_mod->{err}})) if $vmd_mod->{err};
76         $self->{all_vmd} = $vmd_mod if scalar keys %$vmd_mod;
77         $lei->ale; # initialize for workers to read (before LeiPmdir->new)
78         $self->{-mail_sync} = $lei->{opt}->{'mail-sync'} // 1;
79         $self->prepare_inputs($lei, \@inputs) or return;
80
81         my $j = $lei->{opt}->{jobs} // 0;
82         $j =~ /\A([0-9]+),[0-9]+\z/ and $j = $1 + 0;
83         $j ||= scalar(@{$self->{inputs}}) || 1;
84         my $ikw;
85         my $net = $lei->{net};
86         if ($net) {
87                 # $j = $net->net_concurrency($j); TODO
88                 if ($lei->{opt}->{incremental} // 1) {
89                         $net->{incremental} = 1;
90                         $net->{-lms_ro} = $lei->lms // 0;
91                         if ($self->{-import_kw} && $net->{-lms_ro} &&
92                                         !$lei->{opt}->{'new-only'} &&
93                                         $net->{imap_order}) {
94                                 require PublicInbox::LeiImportKw;
95                                 $ikw = PublicInbox::LeiImportKw->new($lei);
96                                 $net->{each_old} = 1;
97                         }
98                 }
99         } else {
100                 my $nproc = $self->detect_nproc;
101                 $j = $nproc if $j > $nproc;
102         }
103         if ($lei->{opt}->{'new-only'} && (!$net || !$net->{imap_order})) {
104                 $lei->err('# --new-only is only for IMAP');
105         }
106         my $ops = {};
107         $lei->{auth}->op_merge($ops, $self) if $lei->{auth};
108         $lei->{-eml_noisy} = 1;
109         (my $op_c, $ops) = $lei->workers_start($self, $j, $ops);
110         $lei->{wq1} = $self;
111         $lei->{-err_type} = 'non-fatal';
112         net_merge_all_done($self) unless $lei->{auth};
113         $lei->wait_wq_events($op_c, $ops);
114 }
115
116 sub lei_import { # the main "lei import" method
117         my ($lei, @inputs) = @_;
118         my $self = bless {}, __PACKAGE__;
119         do_import_index($self, $lei, @inputs);
120 }
121
122 sub _complete_import {
123         my ($lei, @argv) = @_;
124         my ($re, $cur, $match_cb) = $lei->complete_url_prepare(\@argv);
125         my @k = $lei->url_folder_cache->keys($argv[-1] // undef, 1);
126         my @m = map { $match_cb->($_) } @k;
127         my %f = map { $_ => 1 } (@m ? @m : @k);
128         if (my $lms = $lei->lms) {
129                 @k = $lms->folders($argv[-1] // undef, 1);
130                 @m = map { $match_cb->($_) } @k;
131                 if (@m) { @f{@m} = @m } else { @f{@k} = @k }
132         }
133         keys %f;
134 }
135
136 no warnings 'once';
137 *ipc_atfork_child = \&PublicInbox::LeiInput::input_only_atfork_child;
138 *net_merge_all_done = \&PublicInbox::LeiInput::input_only_net_merge_all_done;
139
140 1;