1# --
2# Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
3# --
4# This software comes with ABSOLUTELY NO WARRANTY. For details, see
5# the enclosed file COPYING for license information (GPL). If you
6# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
7# --
8
9package Kernel::System::FetchMail;
10
11use strict;
12use warnings;
13
14use IPC::Open3;
15use Symbol;
16
17our @ObjectDependencies = (
18    'Kernel::System::Log',
19    'Kernel::Config',
20);
21
22=head1 NAME
23
24Kernel::System::FetchMail - FetchMail wrapper functions
25
26=head1 DESCRIPTION
27
28Functions for email fetch.
29
30
31=head2 new()
32
33create a FetchMail object. Do not use it directly, instead use:
34
35    my $FetchMailObject = $Kernel::OM->Get('Kernel::System::FetchMail');
36
37=cut
38
39sub new {
40    my ( $Type, %Param ) = @_;
41
42    # allocate new hash for object
43    my $Self = {};
44    bless( $Self, $Type );
45
46    return $Self;
47}
48
49=head2 Fetch()
50
51Retrieves messages from an email server using fetchmail backend.
52
53    my $Success = $FetchMailObject->Fetch(
54
55        # General Options:
56        Check        => 1,                          # Optional, check for messages without fetching
57        Silent       => 1,                          # Optional, work silently
58        Verbose      => 1,                          # Optional, work noisily (diagnostic output)
59        NoSoftBounce => 1,                          # Optional, fetchmail deletes permanently undeliverable messages.
60        SoftBounce   => 1,                          # Optional, keep permanently undeliverable messages on server (default).
61
62        # Disposal Options:
63        Keep       => 1,                            # Optional, save new messages after retrieval
64        NoKeep     => 1,                            # Optional, delete new messages after retrieval
65        Flush      => 1,                            # Optional, delete old messages from server
66        LimitFlush => 1,                            # Optional, delete oversized messages
67
68        # Protocol and Query Options:
69        Protocol       => 'imap',                   # Optional, (auto || pop2 || pop3 || apop || rpop || kpop || sdps
70                                                    #   || imap || etrn || odmr) specify retrieval protocol
71        UIDL           => 1,                        # Optional, force the use of UIDLs (pop3 only)
72        Service        => 123,                      # Optional, TCP service to connect to (can be numeric TCP port)
73        Principal      => 'SomePrincipal',          # Optional, mail service principal
74        Timeout        => 123,                      # Optional, server nonresponse timeout
75        Plugin         => 'SomeCommand',            # Optional, specify external command to open connection
76        Plugout        => 'SomeCommand',            # Optional, specify external command to open smtp connection
77        Folder         => 'SomeForlder',            # Optional, specify remote folder name
78        TracePolls     => 1,                        # Optional, add poll-tracing information to Received header
79        SSL            => 1,                        # Optional, enable ssl encrypted session
80        SSLCert        => 'SomeCertName',           # Optional, ssl client certificate
81        SSLKey         => 'SomeKeyName',            # Optional, ssl private key file
82        SSLProto       => 'SSL2',                   # Optional, (SSL2 || SSL3 || TLS1) force ssl protocol
83        SSLCertCheck   => 1,                        # Optional, do strict server certificate check (recommended)
84        SSLCertFile    => 'SomeCerName',            # Optional, path to trusted-CA ssl certificate file
85        SSLCertPath    => 'SomeCertPath',           # Optional, path to trusted-CA ssl certificate directory
86        SSLFingerprint => 'SomeFingerprint',        # Optional, fingerprint that must match that of the server's cert.
87
88        # Delivery Control Options:
89        SMTPHost     => 'SomeHosts',                # Optional, set SMTP forwarding host
90        FetchDomains => 'SomeDomains',              # Optional, fetch mail for specified domains
91        SMTPAddress  => 'SomeAddress',              # Optional, set SMTP delivery domain to use
92        SMTPName     => 'some@example.com',         # Optional, set SMTP full name username@domain
93        AntiSpam     => '123,456',                  # Optional, set antispam response values
94        MDA          => 'SomeCommand',              # Optional, set MDA to use for forwarding
95        LMTP         => 1,                          # Optional, use LMTP (RFC2033) for delivery
96        BSMTP        => 'SomeFile',                 # Optional, set output BSMTP file
97        BadHeader    => 'reject',                   # Optional, (reject || accept), specify policy for handling messages with bad headers
98
99        # Resource Limit Control Options
100        Limit          => 123,                      # Optional, don't fetch messages over given size
101        Warnings       => 123,                      # Optional, interval between warning mail notification
102        BatchLimit     => 123,                      # Optional, set batch limit for SMTP connections
103        FetchLimit     => 123,                      # Optional, set fetch limit for server connections
104        FetchSizeLimit => 123,                      # Optional, set fetch message size limit
105        FastUIDL       => 123,                      # Optional, do a binary search for UIDLs
106        Expunge        => 123,                      # Optional, set max deletions between expunges
107
108        # Authentication Options:
109        Username => 'SomeUserName',                 # Optional, specify users's login on server
110        Auth     => 'ssh',                          # Optional, (password || kerberos || ssh || otp) authentication type
111
112        # Miscellaneous Options:
113        FetchMailrc => 'SomeFile',                  # Optional, specify alternate run control file
114        IDFile      => 'SomeFile',                  # Optional, specify alternate UIDs file
115        NoRewrite   =>  1,                          # Optional, don't rewrite header addresses
116        Envelope    => 'SomeXHeader',               # Optional, envelope address header
117        QVirtual    => 'SomePrefix',                # Optional, prefix to remove from local user id
118
119        # Administrative Options:
120        Postmaster  => 'SomeName',                  # Optional, specify recipient of last resort
121        NoBouce     => 1,                           # Optional, redirect bounces from user to postmaster.
122    );
123
124Returns:
125    $Success = 1,       # or false in case of an error
126
127Note:
128To get more information about the parameters please check fetchmail man pages for the corresponding option
129
130=cut
131
132sub Fetch {
133    my ( $Self, %Param ) = @_;
134
135    # set possible locations for fetchmail bin
136    my @PossibleLocations = (
137        '/usr/bin/fetchmail',
138        '/usr/sbin/fetchmail',
139        '/usr/local/bin/fetchmail',
140        '/opt/local/bin/fetchmail',
141    );
142
143    # get SysConfig setting as a fall-back
144    my $ConfigLocation = $Kernel::OM->Get('Kernel::Config')->Get('Fetchmail::Bin') || '';
145
146    # check if setting is defined and valid
147    if ( $ConfigLocation && $ConfigLocation =~ m{[/|\w]+ fetchmail\z}msx ) {
148        push @PossibleLocations, $ConfigLocation;
149    }
150
151    my $FetchMailBin;
152
153    # set FetMail bin
154    LOCATION:
155    for my $Location (@PossibleLocations) {
156        if ( -e $Location ) {
157            $FetchMailBin = $Location;
158            last LOCATION;
159        }
160    }
161
162    if ( !$FetchMailBin ) {
163        $Kernel::OM->Get('Kernel::System::Log')->Log(
164            Priority => 'error',
165            Message  => "FetchMail bin was not found",
166        );
167        return;
168    }
169
170    my %ParamLookup = (
171
172        # General Options:
173        Check        => '--check',
174        Silent       => '--silent',
175        Verbose      => '--verbose',
176        NoSoftBounce => '--nosoftbounce',
177        SoftBounce   => '--softbounce',
178
179        # Disposal Options:
180        Keep       => '--keep',
181        NoKeep     => '--nokeep',
182        Flush      => '--flush',
183        LimitFlush => '--limitflush',
184
185        # Protocol and Query Options:
186        UIDL        => '--uidl',
187        TracePolls  => '--tracepolls',
188        SSL         => '--ssl',
189        SSLCertCeck => '--sslcertck',
190
191        # Delivery Control Options:
192        LMTP => '--lmtp',
193
194        # Miscellaneous Options:
195        NoRewrite => '--norewrite',
196
197        # Administrative Options:
198        NoBouce => '--nobounce',
199    );
200
201    my %HasValueParamLookup = (
202
203        # Protocol and Query Options:
204        Protocol       => '--protocol',
205        Service        => '--service',
206        Principal      => '--principal',
207        Timeout        => '--timeout',
208        Plugin         => '--plugin',
209        Plugout        => '--plugout',
210        Folder         => '--folder',
211        SSLCert        => '--sslcert',
212        SSLKey         => '--sslkey',
213        SSLProto       => '--sslproto',
214        SSLCertFile    => '--sslcertfile',
215        SSLCertPath    => '--sslcertpath',
216        SSLFingerprint => '--sslfingerprint',
217
218        # Delivery Control Options:
219        SMTPHost     => '--smtphost',
220        FetchDomains => '--fetchdomains',
221        SMTPAddress  => '--smtpaddress',
222        SMTPName     => '--smtpname',
223        AntiSpam     => '--antispam',
224        MDA          => '--mda',
225        BSMTP        => '--bsmtp',
226        BadHeader    => '--bad-header',
227
228        # Resource Limit Control Options
229        Limit          => '--limit',
230        Warnings       => '--warnings',
231        BatchLimit     => '--batchlimit',
232        FetchLimit     => '--fetchlimit',
233        FetchSizeLimit => '--fetchsizelimit',
234        FastUIDL       => '--fastuidl',
235        Expunge        => '--expunge',
236
237        # Authentication Options:
238        Username => '--username',
239        Auth     => '--auth',
240
241        # Miscellaneous Options:
242        FetchMailrc => '--fetchmailrc',
243        IDFile      => '--idfile',
244        Envelope    => '--envelope',
245        QVirtual    => '--qvirtual',
246
247        # Administrative Options:
248        Postmaster => '--postmaster',
249    );
250
251    # define base command
252    my $Command = "$FetchMailBin -a";
253
254    OPTION:
255    for my $Option ( sort keys %Param ) {
256
257        next OPTION if !$Param{$Option};
258
259        # check params without value
260        if ( $ParamLookup{$Option} ) {
261            $Command .= " $ParamLookup{$Option}";
262        }
263
264        # check params with values
265        elsif ( $HasValueParamLookup{$Option} ) {
266            $Command .= " $HasValueParamLookup{$Option} $Param{$Option}";
267        }
268    }
269
270    # to capture standard in, out and error
271    my ( $INFH, $OUTFH, $ERRFH );
272
273    # create a symbol for the error file handle
274    $ERRFH = gensym();
275
276    # call the command, capturing output and error
277    my $ProcessID;
278    eval {
279        $ProcessID = open3( $INFH, $OUTFH, $ERRFH, $Command );
280    };
281
282    my $ErrorMessage;
283    my $ExitCode;
284
285    if ($ProcessID) {
286
287        while (<$ERRFH>) {
288            $ErrorMessage .= $_;
289        }
290        waitpid( $ProcessID, 0 );
291        $ExitCode = $? >> 8;
292    }
293    else {
294        $ErrorMessage = $@;
295    }
296
297    my $Success = 1;
298
299    # fetchmail ExitCode 1 means no mails to retrieve (this is OK)
300    if ( $ExitCode == 1 ) {
301        $ExitCode = 0;
302    }
303
304    # fetchmail ExitCode 13 means early termination due to limit (this is OK)
305    elsif ( $ExitCode == 13 ) {
306        $Kernel::OM->Get('Kernel::System::Log')->Log(
307            Priority => 'notice',
308            Message  => "fetchmail: Poll terminated by a fetch limit",
309        );
310        $ExitCode = 0;
311    }
312
313    # check if there are errors
314    if ( $ErrorMessage || $ExitCode ) {
315
316        $ErrorMessage //= '';
317
318        my %ErrorMessageLookup = (
319            2  => 'Error opening a socket to retrieve mail.',
320            3  => 'User authentication step failed.',
321            4  => 'Fatal protocol error.',
322            5  => 'There was a syntax error in the arguments to fetchmail',
323            6  => 'The run control file had bad permissions.',
324            7  => 'There was an error condition reported by the server.',
325            8  => 'Fetchmail is already running',
326            9  => 'User authentication step failed because the server responded "lock  busy", try again later.',
327            10 => 'Fetchmail run failed while trying to do an SMTP port open or transaction.',
328            11 =>
329                'Fatal DNS error. Fetchmail encountered an error while performing a DNS lookup at startup and could not proceed.',
330            12 => 'BSMTP batch file could not be opened.',
331            14 => 'Server busy indication.',
332            23 => 'Internal error.',
333        );
334
335        if ( $ExitCode && !$ErrorMessage ) {
336            $ErrorMessage = $ErrorMessageLookup{$ExitCode} || 'Unknown';
337        }
338
339        $Kernel::OM->Get('Kernel::System::Log')->Log(
340            Priority => 'error',
341            Message  => "There was an error executing $Command: $ErrorMessage",
342        );
343
344        $Success = 0;
345    }
346
347    return $Success;
348
349}
350
3511;
352
353=head1 TERMS AND CONDITIONS
354
355This software is part of the OTRS project (L<https://otrs.org/>).
356
357This software comes with ABSOLUTELY NO WARRANTY. For details, see
358the enclosed file COPYING for license information (GPL). If you
359did not receive this file, see L<https://www.gnu.org/licenses/gpl-3.0.txt>.
360
361=cut
362