~ubuntu-dev/ubuntu/lucid/dovecot/lucid-201002101901

« back to all changes in this revision

Viewing changes to src/deliver/deliver.c

  • Committer: Chuck Short
  • Date: 2010-01-21 20:21:25 UTC
  • mfrom: (4.1.11 squeeze)
  • Revision ID: zulcss@ubuntu.com-20100121202125-pme73o491kfwj5nc
* Merge from debian testing, remaining changes:
  + Add new binary pkg dovecot-postfix that integrates postfix and dovecot
    automatically: (LP: #164837)
  + debian/control:
    - add new binary with short description
    - set Architecture all for dovecot-postfix (LP: #329878)
  + debian/dovecot-postfix.postinst:
    - create initial certificate symlinks to snakeoil.
    - set up postfix with postconf to:
      - use Maildir/ as the default mailbox.
      - use dovecot as the sasl authentication server.
      - use dovecot LDA (deliver).
      - use tls for smtp{d} services.
    - fix certificates paths in postfix' main.cf
    - add reject_unauth_destination to postfix' recipient restrictions
    - add reject_unknown_sender_domain to postfix' sender restriction
    - rename configuration name on remove, delete on purge
    - restart dovecot after linking certificates
    - handle use case when postfix is unconfigurated
  + debian/dovecot-postfix.dirs: create backup directory for postfix's config
    configuration
  + restart postfix and dovecot.
  + debian/dovecot-postfix.postrm:
    - remove all dovecot related configuration from postfix.
    - restart postfix and dovecot.
  + debian/dovecot-common.init:
    - check if /etc/dovecot/dovecot-postfix.conf exists and use it
      as the configuration file if so.
  + debian/patches/warning-ubuntu-postfix.dpatch
    - add warning about dovecot-postfix.conf in dovecot default
      configuration file
  + debian/patches/dovecot-postfix.conf.diff:
    - Ubuntu server custom changes to the default dovecot configuration for
      better interfation with postfix.
    - enable sieve plugin.
    - Ubuntu server custom changes to the default dovecot configuration for
      better integration with postfix:
      - enable imap, pop3, imaps, pop3s and managesieve by default.
      - enable dovecot LDA (deliver).
      - enable SASL auth socket in postfix private directory
   + debian/rules:
     - copy, patch and install dovecot-postfix.conf in /etc/dovecot/.
     - build architecure independent packages too
   + Use Snakeoil SSL certificates by default.
     - debian/control: Depend on ssl-cert.
     - debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert
       paths to snakeoil.
     - debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
   + Add autopkgtest to debian/tests/*.
   + Fast TearDown: Update the lsb init header to not stop in level 6.
   + Add ufw integration:
     - Created debian/dovecot-common.ufw.profile.
     - debian/rules: install profile.
     - debian/control: suggest ufw.
   + debian/{control,rules}: enable PIE hardening.
   + dovecot-imapd, dovecot-pop3: Replaces dovecot-common (<< 1:1.1). (LP: #254721)
   + debian/control: Update Vcs-* headers.
   + Add SMTP-AUTH support for Outlook (login auth mechanism)
* New upstream release.
* debian/patches/gold-fix.patch: Removed. Fixed upstream.
* Moved libexec to lib corrections in dovecot-managesieve.patch and
  dovecot-managesieve-dist.patch to dovecot-example.patch
* debian/patches/dovecot-mboxlocking.patch: Regenerated to avoid FTBFS
  when quilt isn't installed.
* debian/patches/quota-mountpoint.patch: Removed. Not needed anymore.
* debian/patches/dovecot-quota.patch: Removed. Quotas aren't properly
  enabled unless mail_plugins = quota imap_quota.
* debian/patches/gold-fix.patch: Fixed configure script to build even
  with binutils-gold or --no-add-needed linker flag (Closes: #554306)
* debian/dovecot-common.init: fixed LSB headers. Thanks to Pascal Volk.
  (Closes: #558040)
* debian/changelog: added CVE references to previous changelog entry.
* debian/rules: checked up the build system. It's not fragile anymore.
  (Closes: 493803)
* debian/dovecot-common.postinst: Now invoking dpkg-reconfigure
  on dovecot-common is enough to generate new certificates
  if the previous ones were removed. (Closes: #545582)
* debian/rules: No longer install convert-tool in /usr/bin.
  It isn't an user utility and it should stay in /usr/lib/dovecot
  like all other similar tool.
* New upstream release. (Closes: #557601)
* [SECURITY] Fixes local information disclosure and denial of service.
  (see: http://www.dovecot.org/list/dovecot-news/2009-November/000143.html
  and CVE-2009-3897)
* Added myself to uploaders.
* Switched to the new source format "3.0 (quilt)":
  - removed dpatch from build-depends
  - removed debian/README.source because now we use only standard
    dpkg features
  - regenerated all patches
* Prepared to switch to multi-origin source:
  - recreated dovecot-libsieve.patch and dovecot-managesieve-dist.patch
    starting from the upstream tarball
  - removed all autotools related build-depends and build-conflict
  - renamed dovecot-libsieve and dovecot-managesieve directories
    to libsieve and managesieve.
* debian/rules: Moved the configuration of libsieve and managesieve from
  the build phase to the configuration phase
* Added dovecot-dbg package  with debugging symbols.  Thanks Stephan Bosch.
  (Closes: #554710)
* Fixed some stray libexec'isms in the default configuration.
* New upstream release.
* debian/dovecot-common.init:
  - use $CONF when starting the daemon. (Closes: #549944)
  - always output start/stop messages. (Closes: #523810)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
2
 
 
3
/* This is getting pretty horrible. Especially the config file parsing.
 
4
   Dovecot v2.0 should have a config file handling process which should help
 
5
   with this.. */
 
6
 
 
7
#include "lib.h"
 
8
#include "lib-signals.h"
 
9
#include "file-lock.h"
 
10
#include "array.h"
 
11
#include "ioloop.h"
 
12
#include "hostpid.h"
 
13
#include "home-expand.h"
 
14
#include "env-util.h"
 
15
#include "fd-set-nonblock.h"
 
16
#include "istream.h"
 
17
#include "istream-seekable.h"
 
18
#include "module-dir.h"
 
19
#include "str.h"
 
20
#include "str-sanitize.h"
 
21
#include "strescape.h"
 
22
#include "safe-mkstemp.h"
 
23
#include "mkdir-parents.h"
 
24
#include "eacces-error.h"
 
25
#include "close-keep-errno.h"
 
26
#include "var-expand.h"
 
27
#include "rfc822-parser.h"
 
28
#include "message-address.h"
 
29
#include "mail-namespace.h"
 
30
#include "raw-storage.h"
 
31
#include "imap-utf7.h"
 
32
#include "dict.h"
 
33
#include "auth-client.h"
 
34
#include "mail-send.h"
 
35
#include "duplicate.h"
 
36
#include "mbox-from.h"
 
37
#include "../master/syslog-util.h"
 
38
#include "../master/syslog-util.c" /* ugly, ugly.. */
 
39
#include "deliver.h"
 
40
 
 
41
#include <stdio.h>
 
42
#include <stdlib.h>
 
43
#include <unistd.h>
 
44
#include <fcntl.h>
 
45
#include <pwd.h>
 
46
#include <syslog.h>
 
47
 
 
48
#define DEFAULT_CONFIG_FILE SYSCONFDIR"/dovecot.conf"
 
49
#define DEFAULT_SENDMAIL_PATH "/usr/lib/sendmail"
 
50
#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
 
51
 
 
52
/* After buffer grows larger than this, create a temporary file to /tmp
 
53
   where to read the mail. */
 
54
#define MAIL_MAX_MEMORY_BUFFER (1024*128)
 
55
 
 
56
static const char *wanted_headers[] = {
 
57
        "From", "Message-ID", "Subject", "Return-Path",
 
58
        NULL
 
59
};
 
60
 
 
61
struct deliver_settings *deliver_set;
 
62
deliver_mail_func_t *deliver_mail = NULL;
 
63
bool tried_default_save = FALSE;
 
64
 
 
65
/* FIXME: these two should be in some context struct instead of as globals.. */
 
66
static const char *default_mailbox_name = NULL;
 
67
static bool saved_mail = FALSE;
 
68
static char *explicit_envelope_sender = NULL;
 
69
 
 
70
static struct module *modules;
 
71
static struct ioloop *ioloop;
 
72
 
 
73
static pool_t plugin_pool;
 
74
static ARRAY_DEFINE(lda_envs, const char *);
 
75
static ARRAY_DEFINE(plugin_envs, const char *);
 
76
 
 
77
static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
 
78
{
 
79
        /* warn about being killed because of some signal, except SIGINT (^C)
 
80
           which is too common at least while testing :) */
 
81
        if (si->si_signo != SIGINT) {
 
82
                i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
 
83
                          si->si_signo, dec2str(si->si_pid),
 
84
                          dec2str(si->si_uid),
 
85
                          lib_signal_code_to_str(si->si_signo, si->si_code));
 
86
        }
 
87
        io_loop_stop(current_ioloop);
 
88
}
 
89
 
 
90
static const char *deliver_get_address(struct mail *mail, const char *header)
 
91
{
 
92
        struct message_address *addr;
 
93
        const char *str;
 
94
 
 
95
        if (mail_get_first_header(mail, header, &str) <= 0)
 
96
                return NULL;
 
97
        addr = message_address_parse(pool_datastack_create(),
 
98
                                     (const unsigned char *)str,
 
99
                                     strlen(str), 1, FALSE);
 
100
        return addr == NULL || addr->mailbox == NULL || addr->domain == NULL ||
 
101
                *addr->mailbox == '\0' || *addr->domain == '\0' ?
 
102
                NULL : t_strconcat(addr->mailbox, "@", addr->domain, NULL);
 
103
}
 
104
 
 
105
static const struct var_expand_table *
 
106
get_log_var_expand_table(struct mail *mail, const char *message)
 
107
{
 
108
        static struct var_expand_table static_tab[] = {
 
109
                { '$', NULL, NULL },
 
110
                { 'm', NULL, "msgid" },
 
111
                { 's', NULL, "subject" },
 
112
                { 'f', NULL, "from" },
 
113
                { '\0', NULL, NULL }
 
114
        };
 
115
        struct var_expand_table *tab;
 
116
        unsigned int i;
 
117
 
 
118
        tab = t_malloc(sizeof(static_tab));
 
119
        memcpy(tab, static_tab, sizeof(static_tab));
 
120
 
 
121
        tab[0].value = message;
 
122
        (void)mail_get_first_header(mail, "Message-ID", &tab[1].value);
 
123
        (void)mail_get_first_header_utf8(mail, "Subject", &tab[2].value);
 
124
        tab[3].value = deliver_get_address(mail, "From");
 
125
        for (i = 1; tab[i].key != '\0'; i++)
 
126
                tab[i].value = str_sanitize(tab[i].value, 80);
 
127
        return tab;
 
128
}
 
129
 
 
130
static void
 
131
deliver_log(struct mail *mail, const char *fmt, ...) ATTR_FORMAT(2, 3);
 
132
 
 
133
static void deliver_log(struct mail *mail, const char *fmt, ...)
 
134
{
 
135
        va_list args;
 
136
        string_t *str;
 
137
        const char *msg;
 
138
 
 
139
        va_start(args, fmt);
 
140
        msg = t_strdup_vprintf(fmt, args);
 
141
 
 
142
        str = t_str_new(256);
 
143
        var_expand(str, deliver_set->log_format,
 
144
                   get_log_var_expand_table(mail, msg));
 
145
        i_info("%s", str_c(str));
 
146
        va_end(args);
 
147
}
 
148
 
 
149
static struct mailbox *
 
150
mailbox_open_or_create_synced(struct mail_namespace *namespaces,
 
151
                              struct mail_storage **storage_r,
 
152
                              const char *name)
 
153
{
 
154
        struct mail_namespace *ns;
 
155
        struct mailbox *box;
 
156
        enum mail_error error;
 
157
        enum mailbox_open_flags open_flags = MAILBOX_OPEN_FAST |
 
158
                MAILBOX_OPEN_KEEP_RECENT | MAILBOX_OPEN_SAVEONLY |
 
159
                MAILBOX_OPEN_POST_SESSION;
 
160
 
 
161
        if (strcasecmp(name, "INBOX") == 0) {
 
162
                /* deliveries to INBOX must always succeed,
 
163
                   regardless of ACLs */
 
164
                open_flags |= MAILBOX_OPEN_IGNORE_ACLS;
 
165
        }
 
166
 
 
167
        ns = mail_namespace_find(namespaces, &name);
 
168
        if (ns == NULL) {
 
169
                *storage_r = NULL;
 
170
                return NULL;
 
171
        }
 
172
        *storage_r = ns->storage;
 
173
 
 
174
        if (*name == '\0') {
 
175
                /* delivering to a namespace prefix means we actually want to
 
176
                   deliver to the INBOX instead */
 
177
                return NULL;
 
178
        }
 
179
 
 
180
        box = mailbox_open(storage_r, name, NULL, open_flags);
 
181
        if (box != NULL || !deliver_set->mailbox_autocreate)
 
182
                return box;
 
183
 
 
184
        (void)mail_storage_get_last_error(*storage_r, &error);
 
185
        if (error != MAIL_ERROR_NOTFOUND)
 
186
                return NULL;
 
187
 
 
188
        /* try creating it. */
 
189
        if (mail_storage_mailbox_create(*storage_r, name, FALSE) < 0)
 
190
                return NULL;
 
191
        if (deliver_set->mailbox_autosubscribe) {
 
192
                /* (try to) subscribe to it */
 
193
                (void)mailbox_list_set_subscribed(ns->list, name, TRUE);
 
194
        }
 
195
 
 
196
        /* and try opening again */
 
197
        box = mailbox_open(storage_r, name, NULL, open_flags);
 
198
        if (box == NULL)
 
199
                return NULL;
 
200
 
 
201
        if (mailbox_sync(box, 0, 0, NULL) < 0) {
 
202
                mailbox_close(&box);
 
203
                return NULL;
 
204
        }
 
205
        return box;
 
206
}
 
207
 
 
208
static const char *mailbox_name_get_printable(const char *mailbox_mutf7)
 
209
{
 
210
        string_t *str = t_str_new(128);
 
211
 
 
212
        if (imap_utf7_to_utf8(mailbox_mutf7, str) < 0) {
 
213
                str_truncate(str, 0);
 
214
                str_append(str, mailbox_mutf7);
 
215
        }
 
216
        return str_sanitize(str_c(str), 80);
 
217
}
 
218
 
 
219
int deliver_save(struct mail_namespace *namespaces,
 
220
                 struct mail_storage **storage_r, const char *mailbox,
 
221
                 struct mail *mail, enum mail_flags flags,
 
222
                 const char *const *keywords)
 
223
{
 
224
        struct mailbox *box;
 
225
        struct mailbox_transaction_context *t;
 
226
        struct mail_save_context *save_ctx;
 
227
        struct mail_keywords *kw;
 
228
        enum mail_error error;
 
229
        const char *mailbox_name;
 
230
        bool default_save;
 
231
        int ret = 0;
 
232
 
 
233
        default_save = strcmp(mailbox, default_mailbox_name) == 0;
 
234
        if (default_save)
 
235
                tried_default_save = TRUE;
 
236
 
 
237
        mailbox_name = mailbox_name_get_printable(mailbox);
 
238
        box = mailbox_open_or_create_synced(namespaces, storage_r, mailbox);
 
239
        if (box == NULL) {
 
240
                if (*storage_r == NULL) {
 
241
                        deliver_log(mail,
 
242
                                    "save failed to %s: Unknown namespace",
 
243
                                    mailbox_name);
 
244
                        return -1;
 
245
                }
 
246
                if (default_save &&
 
247
                    strcmp((*storage_r)->ns->prefix, mailbox) == 0) {
 
248
                        /* silently store to the INBOX instead */
 
249
                        return -1;
 
250
                }
 
251
                deliver_log(mail, "save failed to %s: %s", mailbox_name,
 
252
                            mail_storage_get_last_error(*storage_r, &error));
 
253
                return -1;
 
254
        }
 
255
 
 
256
        t = mailbox_transaction_begin(box, MAILBOX_TRANSACTION_FLAG_EXTERNAL);
 
257
 
 
258
        kw = str_array_length(keywords) == 0 ? NULL :
 
259
                mailbox_keywords_create_valid(box, keywords);
 
260
        save_ctx = mailbox_save_alloc(t);
 
261
        mailbox_save_set_flags(save_ctx, flags, kw);
 
262
        if (mailbox_copy(&save_ctx, mail) < 0)
 
263
                ret = -1;
 
264
        mailbox_keywords_free(box, &kw);
 
265
 
 
266
        if (ret < 0)
 
267
                mailbox_transaction_rollback(&t);
 
268
        else
 
269
                ret = mailbox_transaction_commit(&t);
 
270
 
 
271
        if (ret == 0) {
 
272
                saved_mail = TRUE;
 
273
                deliver_log(mail, "saved mail to %s", mailbox_name);
 
274
        } else {
 
275
                deliver_log(mail, "save failed to %s: %s", mailbox_name,
 
276
                            mail_storage_get_last_error(*storage_r, &error));
 
277
        }
 
278
 
 
279
        mailbox_close(&box);
 
280
        return ret;
 
281
}
 
282
 
 
283
const char *deliver_get_return_address(struct mail *mail)
 
284
{
 
285
        if (explicit_envelope_sender != NULL)
 
286
                return explicit_envelope_sender;
 
287
 
 
288
        return deliver_get_address(mail, "Return-Path");
 
289
}
 
290
 
 
291
const char *deliver_get_new_message_id(void)
 
292
{
 
293
        static int count = 0;
 
294
 
 
295
        return t_strdup_printf("<dovecot-%s-%s-%d@%s>",
 
296
                               dec2str(ioloop_timeval.tv_sec),
 
297
                               dec2str(ioloop_timeval.tv_usec),
 
298
                               count++, deliver_set->hostname);
 
299
}
 
300
 
 
301
#include "settings.h"
 
302
#include "../master/master-settings.h"
 
303
#include "../master/master-settings-defs.c"
 
304
 
 
305
#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
 
306
 
 
307
static bool setting_is_bool(const char *name)
 
308
{
 
309
        const struct setting_def *def;
 
310
 
 
311
        for (def = setting_defs; def->name != NULL; def++) {
 
312
                if (strcmp(def->name, name) == 0)
 
313
                        return def->type == SET_BOOL;
 
314
        }
 
315
        if (strncmp(name, "NAMESPACE_", 10) == 0) {
 
316
                return strstr(name, "_list") != NULL ||
 
317
                        strstr(name, "_inbox") != NULL ||
 
318
                        strstr(name, "_hidden") != NULL ||
 
319
                        strstr(name, "_subscriptions") != NULL;
 
320
        }
 
321
        if (strcmp(name, "quota_full_tempfail") == 0)
 
322
                return TRUE;
 
323
        return FALSE;
 
324
}
 
325
 
 
326
/* more ugly kludging because we have our own config parsing code.
 
327
   hopefully this goes away in v1.2. */
 
328
static struct {
 
329
        const char *name;
 
330
        bool set;
 
331
} default_yes_settings[] = {
 
332
        { "dotlock_use_excl", TRUE },
 
333
        { "maildir_copy_with_hardlinks", TRUE },
 
334
        { "mbox_dirty_syncs", TRUE },
 
335
        { "mbox_lazy_writes", TRUE }
 
336
};
 
337
 
 
338
static void config_file_init(const char *path)
 
339
{
 
340
        struct istream *input;
 
341
        const char *key, *value, *str, *ukey;
 
342
        char *line, *p, quote;
 
343
        int fd, sections = 0;
 
344
        bool lda_section = FALSE, pop3_section = FALSE, plugin_section = FALSE;
 
345
        bool ns_section = FALSE, ns_location = FALSE, ns_list = FALSE;
 
346
        bool ns_subscriptions = FALSE;
 
347
        unsigned int i, ns_idx = 0;
 
348
        size_t len;
 
349
 
 
350
        plugin_pool = pool_alloconly_create("Plugin strings", 512);
 
351
        i_array_init(&lda_envs, 16);
 
352
        i_array_init(&plugin_envs, 16);
 
353
 
 
354
        fd = open(path, O_RDONLY);
 
355
        if (fd < 0)
 
356
                i_fatal_status(EX_TEMPFAIL, "open(%s) failed: %m", path);
 
357
 
 
358
        input = i_stream_create_fd(fd, (size_t)-1, TRUE);
 
359
        i_stream_set_return_partial_line(input, TRUE);
 
360
        while ((line = i_stream_read_next_line(input)) != NULL) {
 
361
                /* @UNSAFE: line is modified */
 
362
 
 
363
                /* skip whitespace */
 
364
                while (IS_WHITE(*line))
 
365
                        line++;
 
366
 
 
367
                /* ignore comments or empty lines */
 
368
                if (*line == '#' || *line == '\0')
 
369
                        continue;
 
370
 
 
371
                /* strip away comments. pretty kludgy way really.. */
 
372
                for (p = line; *p != '\0'; p++) {
 
373
                        if (*p == '\'' || *p == '"') {
 
374
                                quote = *p;
 
375
                                for (p++; *p != quote && *p != '\0'; p++) {
 
376
                                        if (*p == '\\' && p[1] != '\0')
 
377
                                                p++;
 
378
                                }
 
379
                                if (*p == '\0')
 
380
                                        break;
 
381
                        } else if (*p == '#') {
 
382
                                *p = '\0';
 
383
                                break;
 
384
                        }
 
385
                }
 
386
 
 
387
                /* remove whitespace from end of line */
 
388
                len = strlen(line);
 
389
                while (IS_WHITE(line[len-1]))
 
390
                        len--;
 
391
                line[len] = '\0';
 
392
 
 
393
                if (strncmp(line, "!include_try ", 13) == 0)
 
394
                        continue;
 
395
                if (strncmp(line, "!include ", 9) == 0) {
 
396
                        i_fatal_status(EX_TEMPFAIL, "Error in config file %s: "
 
397
                                       "deliver doesn't support !include directive", path);
 
398
                }
 
399
 
 
400
                value = p = strchr(line, '=');
 
401
                if (value == NULL) {
 
402
                        if (strchr(line, '{') != NULL) {
 
403
                                if (strcmp(line, "protocol lda {") == 0)
 
404
                                        lda_section = TRUE;
 
405
                                else if (strcmp(line, "plugin {") == 0)
 
406
                                        plugin_section = TRUE;
 
407
                                else if (strcmp(line, "protocol pop3 {") == 0)
 
408
                                        pop3_section = TRUE;
 
409
                                else if (strncmp(line, "namespace ", 10) == 0) {
 
410
                                        ns_section = TRUE;
 
411
                                        ns_idx++;
 
412
                                        line += 10;
 
413
                                        env_put(t_strdup_printf(
 
414
                                                "NAMESPACE_%u_TYPE=%s", ns_idx,
 
415
                                                t_strcut(line, ' ')));
 
416
                                }
 
417
                                sections++;
 
418
                        }
 
419
                        if (*line == '}') {
 
420
                                sections--;
 
421
                                lda_section = FALSE;
 
422
                                plugin_section = FALSE;
 
423
                                pop3_section = FALSE;
 
424
                                if (ns_section) {
 
425
                                        ns_section = FALSE;
 
426
                                        if (ns_location)
 
427
                                                ns_location = FALSE;
 
428
                                        else {
 
429
                                                env_put(t_strdup_printf(
 
430
                                                        "NAMESPACE_%u=", ns_idx));
 
431
                                        }
 
432
                                        if (ns_list)
 
433
                                                ns_list = FALSE;
 
434
                                        else {
 
435
                                                env_put(t_strdup_printf(
 
436
                                                        "NAMESPACE_%u_LIST=1", ns_idx));
 
437
                                        }
 
438
                                        if (ns_subscriptions)
 
439
                                                ns_subscriptions = FALSE;
 
440
                                        else {
 
441
                                                env_put(t_strdup_printf(
 
442
                                                        "NAMESPACE_%u_SUBSCRIPTIONS=1",
 
443
                                                        ns_idx));
 
444
                                        }
 
445
                                }
 
446
                        }
 
447
                        continue;
 
448
                }
 
449
 
 
450
                while (p > line && IS_WHITE(p[-1])) p--;
 
451
                key = t_strdup_until(line, p);
 
452
 
 
453
                if (sections > 0 && !lda_section && !plugin_section) {
 
454
                        if (pop3_section) {
 
455
                                if (strcmp(key, "pop3_uidl_format") != 0)
 
456
                                        continue;
 
457
                        } else if (ns_section) {
 
458
                                if (strcmp(key, "separator") == 0) {
 
459
                                        key = t_strdup_printf(
 
460
                                                "NAMESPACE_%u_SEP", ns_idx);
 
461
                                } else if (strcmp(key, "location") == 0) {
 
462
                                        ns_location = TRUE;
 
463
                                        key = t_strdup_printf("NAMESPACE_%u",
 
464
                                                              ns_idx);
 
465
                                } else {
 
466
                                        if (strcmp(key, "list") == 0)
 
467
                                                ns_list = TRUE;
 
468
                                        if (strcmp(key, "subscriptions") == 0)
 
469
                                                ns_subscriptions = TRUE;
 
470
                                        key = t_strdup_printf("NAMESPACE_%u_%s",
 
471
                                                              ns_idx, key);
 
472
                                }
 
473
                        } else {
 
474
                                /* unwanted section */
 
475
                                continue;
 
476
                        }
 
477
                }
 
478
 
 
479
                do {
 
480
                        value++;
 
481
                } while (IS_WHITE(*value));
 
482
 
 
483
                len = strlen(value);
 
484
                if (len > 0 &&
 
485
                    ((*value == '"' && value[len-1] == '"') ||
 
486
                     (*value == '\'' && value[len-1] == '\''))) {
 
487
                        value = str_unescape(p_strndup(unsafe_data_stack_pool,
 
488
                                                       value+1, len - 2));
 
489
                }
 
490
                ukey = t_str_ucase(key);
 
491
                if (setting_is_bool(key) && strcasecmp(value, "yes") != 0) {
 
492
                        for (i = 0; i < N_ELEMENTS(default_yes_settings); i++) {
 
493
                                if (strcmp(default_yes_settings[i].name,
 
494
                                           key) == 0)
 
495
                                        default_yes_settings[i].set = FALSE;
 
496
                        }
 
497
                        env_remove(ukey);
 
498
                        continue;
 
499
                }
 
500
 
 
501
                if (lda_section) {
 
502
                        str = p_strconcat(plugin_pool, ukey, "=", value, NULL);
 
503
                        array_append(&lda_envs, &str, 1);
 
504
                }
 
505
                if (!plugin_section) {
 
506
                        env_put(t_strconcat(ukey, "=", value, NULL));
 
507
                } else {
 
508
                        /* %variables need to be expanded.
 
509
                           store these for later. */
 
510
                        value = p_strconcat(plugin_pool,
 
511
                                            ukey, "=", value, NULL);
 
512
                        array_append(&plugin_envs, &value, 1);
 
513
                }
 
514
        }
 
515
        i_stream_unref(&input);
 
516
 
 
517
        for (i = 0; i < N_ELEMENTS(default_yes_settings); i++) {
 
518
                if (default_yes_settings[i].set) {
 
519
                        key = default_yes_settings[i].name;
 
520
                        env_put(t_strconcat(t_str_ucase(key), "=1", NULL));
 
521
                }
 
522
        }
 
523
}
 
524
 
 
525
static const struct var_expand_table *
 
526
get_var_expand_table(const char *user, const char *home)
 
527
{
 
528
        static struct var_expand_table static_tab[] = {
 
529
                { 'u', NULL, "user" },
 
530
                { 'n', NULL, "username" },
 
531
                { 'd', NULL, "domain" },
 
532
                { 's', NULL, "service" },
 
533
                { 'h', NULL, "home" },
 
534
                { 'l', NULL, "lip" },
 
535
                { 'r', NULL, "rip" },
 
536
                { 'p', NULL, "pid" },
 
537
                { 'i', NULL, "uid" },
 
538
                { '\0', NULL, NULL }
 
539
        };
 
540
        struct var_expand_table *tab;
 
541
 
 
542
        tab = t_malloc(sizeof(static_tab));
 
543
        memcpy(tab, static_tab, sizeof(static_tab));
 
544
 
 
545
        tab[0].value = user;
 
546
        tab[1].value = t_strcut(user, '@');
 
547
        tab[2].value = strchr(user, '@');
 
548
        if (tab[2].value != NULL) tab[2].value++;
 
549
        tab[3].value = "DELIVER";
 
550
        tab[4].value = home != NULL ? home :
 
551
                "/HOME_DIRECTORY_USED_BUT_NOT_GIVEN_BY_USERDB";
 
552
        tab[5].value = NULL;
 
553
        tab[6].value = NULL;
 
554
        tab[7].value = my_pid;
 
555
        tab[8].value = dec2str(geteuid());
 
556
 
 
557
        return tab;
 
558
}
 
559
 
 
560
static const char *
 
561
expand_mail_env(const char *env, const struct var_expand_table *table)
 
562
{
 
563
        string_t *str;
 
564
        const char *p;
 
565
 
 
566
        str = t_str_new(256);
 
567
 
 
568
        /* it's either type:data or just data */
 
569
        p = strchr(env, ':');
 
570
        if (p != NULL) {
 
571
                while (env != p) {
 
572
                        str_append_c(str, *env);
 
573
                        env++;
 
574
                }
 
575
 
 
576
                str_append_c(str, *env++);
 
577
        }
 
578
 
 
579
        if (env[0] == '~' && env[1] == '/') {
 
580
                /* expand home */
 
581
                env = t_strconcat("%h", env+1, NULL);
 
582
        }
 
583
 
 
584
        /* expand %vars */
 
585
        var_expand(str, env, table);
 
586
        return str_c(str);
 
587
}
 
588
 
 
589
static const char *escape_local_part(const char *local_part)
 
590
{
 
591
        const char *p;
 
592
 
 
593
        /* if local_part isn't dot-atom-text, we need to return quoted-string
 
594
           dot-atom-text = 1*atext *("." 1*atext) */
 
595
        for (p = local_part; *p != '\0'; p++) {
 
596
                if (!IS_ATEXT(*p) && *p != '.')
 
597
                        break;
 
598
        }
 
599
        if (*p != '\0' || *local_part == '.' ||
 
600
            (p != local_part && p[-1] == '.'))
 
601
                local_part = t_strdup_printf("\"%s\"", str_escape(local_part));
 
602
        return local_part;
 
603
}
 
604
 
 
605
static const char *address_sanitize(const char *address)
 
606
{
 
607
        struct message_address *addr;
 
608
        const char *ret, *mailbox;
 
609
        pool_t pool;
 
610
 
 
611
        pool = pool_alloconly_create("address sanitizer", 256);
 
612
        addr = message_address_parse(pool, (const unsigned char *)address,
 
613
                                     strlen(address), 1, FALSE);
 
614
 
 
615
        if (addr == NULL || addr->mailbox == NULL || addr->domain == NULL ||
 
616
            *addr->mailbox == '\0')
 
617
                ret = DEFAULT_ENVELOPE_SENDER;
 
618
        else {
 
619
                mailbox = escape_local_part(addr->mailbox);
 
620
                if (*addr->domain == '\0')
 
621
                        ret = t_strdup(mailbox);
 
622
                else
 
623
                        ret = t_strdup_printf("%s@%s", mailbox, addr->domain);
 
624
        }
 
625
        pool_unref(&pool);
 
626
        return ret;
 
627
}
 
628
 
 
629
static int deliver_create_dir(struct mail_user *user, const char *dir)
 
630
{
 
631
        struct mail_namespace *ns;
 
632
        const char *origin;
 
633
        mode_t mode;
 
634
        gid_t gid;
 
635
 
 
636
        ns = mail_namespace_find_inbox(user->namespaces);
 
637
        if (ns == NULL)
 
638
                ns = user->namespaces;
 
639
 
 
640
        mailbox_list_get_dir_permissions(ns->list, NULL, &mode, &gid, &origin);
 
641
        if (mkdir_parents_chgrp(dir, mode, gid, origin) == 0) {
 
642
                return 0;
 
643
        } else if (errno == EACCES) {
 
644
                i_error("%s", eacces_error_get_creating("mkdir_parents_chown",
 
645
                                                        dir));
 
646
                return -1;
 
647
        } else {
 
648
                i_error("mkdir_parents_chown(%s, gid=%s) failed: %m",
 
649
                        dir, dec2str(gid));
 
650
                return -1;
 
651
        }
 
652
}
 
653
 
 
654
static int seekable_fd_callback(const char **path_r, void *context)
 
655
{
 
656
        struct mail_user *user = context;
 
657
        const char *dir, *p;
 
658
        string_t *path;
 
659
        int fd;
 
660
 
 
661
        path = t_str_new(128);
 
662
        str_append(path, mail_user_get_temp_prefix(user));
 
663
        fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
 
664
        if (fd == -1 && errno == ENOENT) {
 
665
                dir = str_c(path);
 
666
                p = strrchr(dir, '/');
 
667
                if (p != NULL) {
 
668
                        dir = t_strdup_until(dir, p);
 
669
                        if (deliver_create_dir(user, dir) < 0)
 
670
                                return -1;
 
671
                        fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
 
672
                }
 
673
        }
 
674
        if (fd == -1) {
 
675
                i_error("safe_mkstemp(%s) failed: %m", str_c(path));
 
676
                return -1;
 
677
        }
 
678
 
 
679
        /* we just want the fd, unlink it */
 
680
        if (unlink(str_c(path)) < 0) {
 
681
                /* shouldn't happen.. */
 
682
                i_error("unlink(%s) failed: %m", str_c(path));
 
683
                close_keep_errno(fd);
 
684
                return -1;
 
685
        }
 
686
 
 
687
        *path_r = str_c(path);
 
688
        return fd;
 
689
}
 
690
 
 
691
static struct istream *
 
692
create_raw_stream(struct mail_user *user, int fd, time_t *mtime_r)
 
693
{
 
694
        struct istream *input, *input2, *input_list[2];
 
695
        const unsigned char *data;
 
696
        char *sender = NULL;
 
697
        size_t i, size;
 
698
        int ret, tz;
 
699
 
 
700
        *mtime_r = (time_t)-1;
 
701
        fd_set_nonblock(fd, FALSE);
 
702
 
 
703
        input = i_stream_create_fd(fd, 4096, FALSE);
 
704
        input->blocking = TRUE;
 
705
        /* If input begins with a From-line, drop it */
 
706
        ret = i_stream_read_data(input, &data, &size, 5);
 
707
        if (ret > 0 && size >= 5 && memcmp(data, "From ", 5) == 0) {
 
708
                /* skip until the first LF */
 
709
                i_stream_skip(input, 5);
 
710
                while ((ret = i_stream_read_data(input, &data, &size, 0)) > 0) {
 
711
                        for (i = 0; i < size; i++) {
 
712
                                if (data[i] == '\n')
 
713
                                        break;
 
714
                        }
 
715
                        if (i != size) {
 
716
                                (void)mbox_from_parse(data, i, mtime_r, &tz,
 
717
                                                      &sender);
 
718
                                i_stream_skip(input, i + 1);
 
719
                                break;
 
720
                        }
 
721
                        i_stream_skip(input, size);
 
722
                }
 
723
        }
 
724
 
 
725
        if (sender != NULL && explicit_envelope_sender == NULL) {
 
726
                /* use the envelope sender from From_-line, but only if it
 
727
                   hasn't been specified with -f already. */
 
728
                explicit_envelope_sender = i_strdup(sender);
 
729
        }
 
730
        i_free(sender);
 
731
 
 
732
        if (input->v_offset == 0) {
 
733
                input2 = input;
 
734
                i_stream_ref(input2);
 
735
        } else {
 
736
                input2 = i_stream_create_limit(input, (uoff_t)-1);
 
737
        }
 
738
        i_stream_unref(&input);
 
739
 
 
740
        input_list[0] = input2; input_list[1] = NULL;
 
741
        input = i_stream_create_seekable(input_list, MAIL_MAX_MEMORY_BUFFER,
 
742
                                         seekable_fd_callback, user);
 
743
        i_stream_unref(&input2);
 
744
        return input;
 
745
}
 
746
 
 
747
static void failure_exit_callback(int *status)
 
748
{
 
749
        /* we want all our exit codes to be sysexits.h compatible.
 
750
           if we failed because of a logging related error, we most likely
 
751
           aren't writing to stderr, so try writing there to give some kind of
 
752
           a clue what's wrong. FATAL_LOGOPEN failure already wrote to
 
753
           stderr, so don't duplicate it. */
 
754
        switch (*status) {
 
755
        case FATAL_LOGWRITE:
 
756
                fputs("Failed to write to log file", stderr);
 
757
                break;
 
758
        case FATAL_LOGERROR:
 
759
                fputs("Internal logging error", stderr);
 
760
                break;
 
761
        case FATAL_LOGOPEN:
 
762
        case FATAL_OUTOFMEM:
 
763
        case FATAL_EXEC:
 
764
        case FATAL_DEFAULT:
 
765
                break;
 
766
        default:
 
767
                return;
 
768
        }
 
769
        *status = EX_TEMPFAIL;
 
770
}
 
771
 
 
772
static void open_logfile(const char *username)
 
773
{
 
774
        const char *prefix, *log_path, *stamp;
 
775
 
 
776
        prefix = t_strdup_printf("deliver(%s): ", username);
 
777
        log_path = home_expand(getenv("LOG_PATH"));
 
778
        if (log_path == NULL || *log_path == '\0') {
 
779
                const char *env = getenv("SYSLOG_FACILITY");
 
780
                int facility;
 
781
 
 
782
                if (env == NULL || !syslog_facility_find(env, &facility))
 
783
                        facility = LOG_MAIL;
 
784
                i_set_failure_prefix(prefix);
 
785
                i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
 
786
        } else {
 
787
                /* log to file or stderr */
 
788
                i_set_failure_file(log_path, prefix);
 
789
        }
 
790
 
 
791
        log_path = home_expand(getenv("INFO_LOG_PATH"));
 
792
        if (log_path != NULL && *log_path != '\0')
 
793
                i_set_info_file(log_path);
 
794
 
 
795
        stamp = getenv("LOG_TIMESTAMP");
 
796
        if (stamp == NULL)
 
797
                stamp = DEFAULT_FAILURE_STAMP_FORMAT;
 
798
        i_set_failure_timestamp_format(stamp);
 
799
}
 
800
 
 
801
static void print_help(void)
 
802
{
 
803
        printf(
 
804
"Usage: deliver [-c <config file>] [-a <address>] [-d <username>] [-p <path>]\n"
 
805
"               [-f <envelope sender>] [-m <mailbox>] [-n] [-s] [-e] [-k]\n");
 
806
}
 
807
 
 
808
void deliver_env_clean(bool preserve_home)
 
809
{
 
810
        const char *tz, *home;
 
811
 
 
812
        tz = getenv("TZ");
 
813
        if (tz != NULL)
 
814
                tz = t_strconcat("TZ=", tz, NULL);
 
815
        home = preserve_home ? getenv("HOME") : NULL;
 
816
        if (home != NULL)
 
817
                home = t_strconcat("HOME=", home, NULL);
 
818
 
 
819
        /* Note that if the original environment was set with env_put(), the
 
820
           environment strings will be invalid after env_clean(). That's why
 
821
           we t_strconcat() them above. */
 
822
        env_clean();
 
823
 
 
824
        if (tz != NULL) env_put(tz);
 
825
        if (home != NULL) env_put(home);
 
826
}
 
827
 
 
828
static void expand_envs(const char *user)
 
829
{
 
830
        const struct var_expand_table *table;
 
831
        const char *value, *const *envs, *home, *env_name;
 
832
        unsigned int i, count;
 
833
        string_t *str;
 
834
 
 
835
        home = getenv("HOME");
 
836
 
 
837
        str = t_str_new(256);
 
838
        table = get_var_expand_table(user, home);
 
839
        envs = array_get(&plugin_envs, &count);
 
840
        for (i = 0; i < count; i++) {
 
841
                str_truncate(str, 0);
 
842
                var_expand(str, envs[i], table);
 
843
                env_put(str_c(str));
 
844
        }
 
845
        /* add LDA envs again to make sure they override plugin settings */
 
846
        envs = array_get(&lda_envs, &count);
 
847
        for (i = 0; i < count; i++)
 
848
                env_put(envs[i]);
 
849
 
 
850
        /* get the table again in case plugin envs provided the home
 
851
           directory (yea, kludgy) */
 
852
        if (home == NULL)
 
853
                home = getenv("HOME");
 
854
        table = get_var_expand_table(user, home);
 
855
 
 
856
        value = getenv("MAIL_LOCATION");
 
857
        if (value != NULL)
 
858
                value = expand_mail_env(value, table);
 
859
        env_put(t_strconcat("MAIL=", value, NULL));
 
860
 
 
861
        for (i = 1;; i++) {
 
862
                env_name = t_strdup_printf("NAMESPACE_%u", i);
 
863
                value = getenv(env_name);
 
864
                if (value == NULL)
 
865
                        break;
 
866
 
 
867
                value = expand_mail_env(value, table);
 
868
                env_put(t_strconcat(env_name, "=", value, NULL));
 
869
 
 
870
                env_name = t_strdup_printf("NAMESPACE_%u_PREFIX", i);
 
871
                value = getenv(env_name);
 
872
                if (value != NULL) {
 
873
                        str_truncate(str, 0);
 
874
                        var_expand(str, value, table);
 
875
                        env_put(t_strconcat(env_name, "=", str_c(str), NULL));
 
876
                }
 
877
        }
 
878
}
 
879
 
 
880
static void putenv_extra_fields(const ARRAY_TYPE(const_string) *extra_fields)
 
881
{
 
882
        const char *const *fields;
 
883
        const char *key, *p;
 
884
        unsigned int i, count;
 
885
 
 
886
        fields = array_get(extra_fields, &count);
 
887
        for (i = 0; i < count; i++) {
 
888
                p = strchr(fields[i], '=');
 
889
                if (p == NULL)
 
890
                        env_put(t_strconcat(fields[i], "=1", NULL));
 
891
                else {
 
892
                        key = t_str_ucase(t_strdup_until(fields[i], p));
 
893
                        env_put(t_strconcat(key, p, NULL));
 
894
                }
 
895
        }
 
896
}
 
897
 
 
898
int main(int argc, char *argv[])
 
899
{
 
900
        const char *config_path = DEFAULT_CONFIG_FILE;
 
901
        const char *mailbox = "INBOX";
 
902
        const char *auth_socket;
 
903
        const char *home, *destaddr, *user, *value, *errstr, *path, *orig_user;
 
904
        ARRAY_TYPE(const_string) extra_fields = ARRAY_INIT;
 
905
        struct mail_user *mail_user, *raw_mail_user;
 
906
        struct mail_namespace *raw_ns;
 
907
        struct mail_storage *storage;
 
908
        struct mailbox *box;
 
909
        struct raw_mailbox *raw_box;
 
910
        struct istream *input;
 
911
        struct mailbox_transaction_context *t;
 
912
        struct mailbox_header_lookup_ctx *headers_ctx;
 
913
        struct mail *mail;
 
914
        char cwd[PATH_MAX];
 
915
        uid_t process_euid;
 
916
        bool stderr_rejection = FALSE;
 
917
        bool keep_environment = FALSE;
 
918
        bool user_auth = FALSE;
 
919
        time_t mtime;
 
920
        int i, ret;
 
921
        pool_t userdb_pool = NULL;
 
922
        string_t *str;
 
923
        enum mail_error error;
 
924
 
 
925
        if (getuid() != geteuid() && geteuid() == 0) {
 
926
                /* running setuid - don't allow this if deliver is
 
927
                   executable by anyone */
 
928
                struct stat st;
 
929
 
 
930
                if (stat(argv[0], &st) < 0) {
 
931
                        fprintf(stderr, "stat(%s) failed: %s\n",
 
932
                                argv[0], strerror(errno));
 
933
                        return EX_TEMPFAIL;
 
934
                } else if ((st.st_mode & 1) != 0 && (st.st_mode & 04000) != 0) {
 
935
                        fprintf(stderr, "%s must not be both world-executable "
 
936
                                "and setuid-root. This allows root exploits. "
 
937
                                "See http://wiki.dovecot.org/LDA#multipleuids\n",
 
938
                                argv[0]);
 
939
                        return EX_TEMPFAIL;
 
940
                }
 
941
        }
 
942
 
 
943
        i_set_failure_exit_callback(failure_exit_callback);
 
944
 
 
945
        lib_init();
 
946
        ioloop = io_loop_create();
 
947
 
 
948
        lib_signals_init();
 
949
        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
 
950
        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
 
951
        lib_signals_ignore(SIGPIPE, TRUE);
 
952
        lib_signals_ignore(SIGALRM, FALSE);
 
953
#ifdef SIGXFSZ
 
954
        lib_signals_ignore(SIGXFSZ, TRUE);
 
955
#endif
 
956
 
 
957
        deliver_set = i_new(struct deliver_settings, 1);
 
958
        deliver_set->mailbox_autocreate = TRUE;
 
959
 
 
960
        destaddr = user = path = NULL;
 
961
        for (i = 1; i < argc; i++) {
 
962
                if (strcmp(argv[i], "-a") == 0) {
 
963
                        /* destination address */
 
964
                        i++;
 
965
                        if (i == argc)
 
966
                                i_fatal_status(EX_USAGE, "Missing -a argument");
 
967
                        destaddr = argv[i];
 
968
                } else if (strcmp(argv[i], "-d") == 0) {
 
969
                        /* destination user */
 
970
                        i++;
 
971
                        if (i == argc)
 
972
                                i_fatal_status(EX_USAGE, "Missing -d argument");
 
973
                        user = argv[i];
 
974
                        user_auth = TRUE;
 
975
                } else if (strcmp(argv[i], "-p") == 0) {
 
976
                        /* input path */
 
977
                        i++;
 
978
                        if (i == argc)
 
979
                                i_fatal_status(EX_USAGE, "Missing -p argument");
 
980
                        path = argv[i];
 
981
                        if (*path != '/') {
 
982
                                /* expand relative paths before we chdir */
 
983
                                if (getcwd(cwd, sizeof(cwd)) == NULL)
 
984
                                        i_fatal("getcwd() failed: %m");
 
985
                                path = t_strconcat(cwd, "/", path, NULL);
 
986
                        }
 
987
                } else if (strcmp(argv[i], "-e") == 0) {
 
988
                        stderr_rejection = TRUE;
 
989
                } else if (strcmp(argv[i], "-c") == 0) {
 
990
                        /* config file path */
 
991
                        i++;
 
992
                        if (i == argc) {
 
993
                                i_fatal_status(EX_USAGE,
 
994
                                        "Missing config file path argument");
 
995
                        }
 
996
                        config_path = argv[i];
 
997
                } else if (strcmp(argv[i], "-k") == 0) {
 
998
                        keep_environment = TRUE;
 
999
                } else if (strcmp(argv[i], "-m") == 0) {
 
1000
                        /* destination mailbox */
 
1001
                        i++;
 
1002
                        if (i == argc)
 
1003
                                i_fatal_status(EX_USAGE, "Missing -m argument");
 
1004
                        /* Ignore -m "". This allows doing -m ${extension}
 
1005
                           in Postfix to handle user+mailbox */
 
1006
                        if (*argv[i] != '\0') {
 
1007
                                str = t_str_new(256);
 
1008
                                if (imap_utf8_to_utf7(argv[i], str) < 0) {
 
1009
                                        i_fatal("Mailbox name not UTF-8: %s",
 
1010
                                                mailbox);
 
1011
                                }
 
1012
                                mailbox = str_c(str);
 
1013
                        }
 
1014
                } else if (strcmp(argv[i], "-n") == 0) {
 
1015
                        deliver_set->mailbox_autocreate = FALSE;
 
1016
                } else if (strcmp(argv[i], "-s") == 0) {
 
1017
                        deliver_set->mailbox_autosubscribe = TRUE;
 
1018
                } else if (strcmp(argv[i], "-f") == 0) {
 
1019
                        /* envelope sender address */
 
1020
                        i++;
 
1021
                        if (i == argc)
 
1022
                                i_fatal_status(EX_USAGE, "Missing -f argument");
 
1023
                        explicit_envelope_sender =
 
1024
                                i_strdup(address_sanitize(argv[i]));
 
1025
                } else if (argv[i][0] != '\0') {
 
1026
                        print_help();
 
1027
                        i_fatal_status(EX_USAGE,
 
1028
                                       "Unknown argument: %s", argv[i]);
 
1029
                }
 
1030
        }
 
1031
 
 
1032
        if (user == NULL)
 
1033
                user = getenv("USER");
 
1034
        if (!keep_environment)
 
1035
                deliver_env_clean(!user_auth);
 
1036
 
 
1037
        process_euid = geteuid();
 
1038
        if (user_auth)
 
1039
                ;
 
1040
        else if (process_euid != 0) {
 
1041
                /* we're non-root. get our username and possibly our home. */
 
1042
                struct passwd *pw;
 
1043
 
 
1044
                home = getenv("HOME");
 
1045
                if (user != NULL && home != NULL) {
 
1046
                        /* no need for a pw lookup */
 
1047
                } else if ((pw = getpwuid(process_euid)) != NULL) {
 
1048
                        user = t_strdup(pw->pw_name);
 
1049
                        if (home == NULL)
 
1050
                                env_put(t_strconcat("HOME=", pw->pw_dir, NULL));
 
1051
                } else if (user == NULL) {
 
1052
                        i_fatal_status(EX_USAGE,
 
1053
                                       "Couldn't lookup our username (uid=%s)",
 
1054
                                       dec2str(process_euid));
 
1055
                }
 
1056
        } else {
 
1057
                i_fatal_status(EX_USAGE,
 
1058
                        "destination user parameter (-d user) not given");
 
1059
        }
 
1060
 
 
1061
        T_BEGIN {
 
1062
                config_file_init(config_path);
 
1063
        } T_END;
 
1064
        open_logfile(user);
 
1065
 
 
1066
        if (getenv("MAIL_DEBUG") != NULL)
 
1067
                env_put("DEBUG=1");
 
1068
 
 
1069
        if (getenv("MAIL_PLUGINS") == NULL)
 
1070
                modules = NULL;
 
1071
        else {
 
1072
                const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
 
1073
                const char *version;
 
1074
 
 
1075
                if (plugin_dir == NULL)
 
1076
                        plugin_dir = MODULEDIR"/lda";
 
1077
 
 
1078
                version = getenv("VERSION_IGNORE") != NULL ?
 
1079
                        NULL : PACKAGE_VERSION;
 
1080
                modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
 
1081
                                          TRUE, version);
 
1082
        }
 
1083
 
 
1084
        if (user_auth) {
 
1085
                auth_socket = getenv("AUTH_SOCKET_PATH");
 
1086
                if (auth_socket == NULL) {
 
1087
                        const char *base_dir = getenv("BASE_DIR");
 
1088
                        if (base_dir == NULL)
 
1089
                                base_dir = PKG_RUNDIR;
 
1090
                        auth_socket = t_strconcat(base_dir, "/auth-master",
 
1091
                                                  NULL);
 
1092
                }
 
1093
 
 
1094
                userdb_pool = pool_alloconly_create("userdb lookup replys", 512);
 
1095
                orig_user = user;
 
1096
                ret = auth_client_lookup_and_restrict(auth_socket,
 
1097
                                                      &user, process_euid,
 
1098
                                                      userdb_pool,
 
1099
                                                      &extra_fields);
 
1100
                if (ret != 0)
 
1101
                        return ret;
 
1102
 
 
1103
                if (strcmp(user, orig_user) != 0) {
 
1104
                        /* auth lookup changed the user. */
 
1105
                        if (getenv("DEBUG") != NULL)
 
1106
                                i_info("userdb changed username to %s", user);
 
1107
                        i_set_failure_prefix(t_strdup_printf("deliver(%s): ",
 
1108
                                                             user));
 
1109
                }
 
1110
                /* if user was changed, it was allocated from userdb_pool
 
1111
                   which we'll free soon. */
 
1112
                user = t_strdup(user);
 
1113
        }
 
1114
 
 
1115
        expand_envs(user);
 
1116
        if (userdb_pool != NULL) {
 
1117
                putenv_extra_fields(&extra_fields);
 
1118
                pool_unref(&userdb_pool);
 
1119
        }
 
1120
 
 
1121
        /* Fix namespaces with empty locations */
 
1122
        for (i = 1;; i++) {
 
1123
                value = getenv(t_strdup_printf("NAMESPACE_%u", i));
 
1124
                if (value == NULL)
 
1125
                        break;
 
1126
 
 
1127
                if (*value == '\0') {
 
1128
                        env_put(t_strdup_printf("NAMESPACE_%u=%s", i,
 
1129
                                                getenv("MAIL")));
 
1130
                }
 
1131
        }
 
1132
 
 
1133
        /* If possible chdir to home directory, so that core file
 
1134
           could be written in case we crash. */
 
1135
        home = getenv("HOME");
 
1136
        if (home != NULL) {
 
1137
                if (chdir(home) < 0) {
 
1138
                        if (errno != ENOENT)
 
1139
                                i_error("chdir(%s) failed: %m", home);
 
1140
                        else if (getenv("DEBUG") != NULL)
 
1141
                                i_info("Home dir not found: %s", home);
 
1142
                }
 
1143
        }
 
1144
 
 
1145
        env_put(t_strconcat("USER=", user, NULL));
 
1146
        (void)umask(0077);
 
1147
 
 
1148
        deliver_set->hostname = getenv("HOSTNAME");
 
1149
        if (deliver_set->hostname == NULL)
 
1150
                deliver_set->hostname = my_hostname;
 
1151
        deliver_set->postmaster_address = getenv("POSTMASTER_ADDRESS");
 
1152
        if (deliver_set->postmaster_address == NULL) {
 
1153
                i_fatal_status(EX_TEMPFAIL,
 
1154
                               "postmaster_address setting not given");
 
1155
        }
 
1156
        deliver_set->sendmail_path = getenv("SENDMAIL_PATH");
 
1157
        if (deliver_set->sendmail_path == NULL)
 
1158
                deliver_set->sendmail_path = DEFAULT_SENDMAIL_PATH;
 
1159
        deliver_set->rejection_subject = getenv("REJECTION_SUBJECT");
 
1160
        if (deliver_set->rejection_subject == NULL)
 
1161
                deliver_set->rejection_subject = DEFAULT_MAIL_REJECTION_SUBJECT;
 
1162
        deliver_set->rejection_reason = getenv("REJECTION_REASON");
 
1163
        if (deliver_set->rejection_reason == NULL) {
 
1164
                deliver_set->rejection_reason =
 
1165
                        DEFAULT_MAIL_REJECTION_HUMAN_REASON;
 
1166
        }
 
1167
        deliver_set->log_format = getenv("DELIVER_LOG_FORMAT");
 
1168
        if (deliver_set->log_format == NULL)
 
1169
                deliver_set->log_format = DEFAULT_LOG_FORMAT;
 
1170
 
 
1171
        dict_drivers_register_builtin();
 
1172
        duplicate_init();
 
1173
        mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL);
 
1174
        mail_storage_init();
 
1175
        mail_storage_register_all();
 
1176
        mailbox_list_register_all();
 
1177
 
 
1178
        module_dir_init(modules);
 
1179
 
 
1180
        mail_user = mail_user_init(user);
 
1181
        mail_user_set_home(mail_user, home);
 
1182
        if (mail_namespaces_init(mail_user) < 0)
 
1183
                i_fatal("Namespace initialization failed");
 
1184
 
 
1185
        /* create a separate mail user for the internal namespace */
 
1186
        raw_mail_user = mail_user_init(user);
 
1187
        mail_user_set_home(raw_mail_user, NULL);
 
1188
        raw_ns = mail_namespaces_init_empty(raw_mail_user);
 
1189
        raw_ns->flags |= NAMESPACE_FLAG_NOQUOTA | NAMESPACE_FLAG_NOACL;
 
1190
 
 
1191
        if (mail_storage_create(raw_ns, "raw", "/tmp",
 
1192
                                MAIL_STORAGE_FLAG_FULL_FS_ACCESS,
 
1193
                                FILE_LOCK_METHOD_FCNTL, &errstr) < 0)
 
1194
                i_fatal("Couldn't create internal raw storage: %s", errstr);
 
1195
        if (path == NULL) {
 
1196
                input = create_raw_stream(mail_user, 0, &mtime);
 
1197
                box = mailbox_open(&raw_ns->storage, "Dovecot Delivery Mail",
 
1198
                                   input, MAILBOX_OPEN_NO_INDEX_FILES);
 
1199
                i_stream_unref(&input);
 
1200
        } else {
 
1201
                mtime = (time_t)-1;
 
1202
                box = mailbox_open(&raw_ns->storage, path, NULL,
 
1203
                                   MAILBOX_OPEN_NO_INDEX_FILES);
 
1204
        }
 
1205
        if (box == NULL) {
 
1206
                i_fatal("Can't open delivery mail as raw: %s",
 
1207
                        mail_storage_get_last_error(raw_ns->storage, &error));
 
1208
        }
 
1209
        if (mailbox_sync(box, 0, 0, NULL) < 0) {
 
1210
                i_fatal("Can't sync delivery mail: %s",
 
1211
                        mail_storage_get_last_error(raw_ns->storage, &error));
 
1212
        }
 
1213
        raw_box = (struct raw_mailbox *)box;
 
1214
        raw_box->envelope_sender = explicit_envelope_sender != NULL ?
 
1215
                explicit_envelope_sender : DEFAULT_ENVELOPE_SENDER;
 
1216
        raw_box->mtime = mtime;
 
1217
 
 
1218
        t = mailbox_transaction_begin(box, 0);
 
1219
        headers_ctx = mailbox_header_lookup_init(box, wanted_headers);
 
1220
        mail = mail_alloc(t, 0, headers_ctx);
 
1221
        mail_set_seq(mail, 1);
 
1222
 
 
1223
        if (destaddr == NULL) {
 
1224
                destaddr = deliver_get_address(mail, "Envelope-To");
 
1225
                if (destaddr == NULL) {
 
1226
                        destaddr = strchr(user, '@') != NULL ? user :
 
1227
                                t_strconcat(user, "@",
 
1228
                                            deliver_set->hostname, NULL);
 
1229
                }
 
1230
        }
 
1231
 
 
1232
        storage = NULL;
 
1233
        default_mailbox_name = mailbox;
 
1234
        if (deliver_mail == NULL)
 
1235
                ret = -1;
 
1236
        else {
 
1237
                if (deliver_mail(mail_user->namespaces, &storage, mail,
 
1238
                                 destaddr, mailbox) <= 0) {
 
1239
                        /* if message was saved, don't bounce it even though
 
1240
                           the script failed later. */
 
1241
                        ret = saved_mail ? 0 : -1;
 
1242
                } else {
 
1243
                        /* success. message may or may not have been saved. */
 
1244
                        ret = 0;
 
1245
                }
 
1246
        }
 
1247
 
 
1248
        if (ret < 0 && !tried_default_save) {
 
1249
                /* plugins didn't handle this. save into the default mailbox. */
 
1250
                ret = deliver_save(mail_user->namespaces,
 
1251
                                   &storage, mailbox, mail, 0, NULL);
 
1252
        }
 
1253
        if (ret < 0 && strcasecmp(mailbox, "INBOX") != 0) {
 
1254
                /* still didn't work. try once more to save it
 
1255
                   to INBOX. */
 
1256
                ret = deliver_save(mail_user->namespaces,
 
1257
                                   &storage, "INBOX", mail, 0, NULL);
 
1258
        }
 
1259
 
 
1260
        if (ret < 0 ) {
 
1261
                const char *error_string;
 
1262
                enum mail_error error;
 
1263
 
 
1264
                if (storage == NULL) {
 
1265
                        /* This shouldn't happen */
 
1266
                        i_error("BUG: Saving failed for unknown storage");
 
1267
                        return EX_TEMPFAIL;
 
1268
                }
 
1269
 
 
1270
                error_string = mail_storage_get_last_error(storage, &error);
 
1271
 
 
1272
                if (stderr_rejection) {
 
1273
                        /* write to stderr also for tempfails so that MTA
 
1274
                           can log the reason if it wants to. */
 
1275
                        fprintf(stderr, "%s\n", error_string);
 
1276
                }
 
1277
 
 
1278
                if (error != MAIL_ERROR_NOSPACE ||
 
1279
                    getenv("QUOTA_FULL_TEMPFAIL") != NULL) {
 
1280
                        /* Saving to INBOX should always work unless
 
1281
                           we're over quota. If it didn't, it's probably a
 
1282
                           configuration problem. */
 
1283
                        return EX_TEMPFAIL;
 
1284
                }
 
1285
 
 
1286
                /* we'll have to reply with permanent failure */
 
1287
                deliver_log(mail, "rejected: %s",
 
1288
                            str_sanitize(error_string, 512));
 
1289
 
 
1290
                if (stderr_rejection)
 
1291
                        return EX_NOPERM;
 
1292
                ret = mail_send_rejection(mail, user, error_string);
 
1293
                if (ret != 0)
 
1294
                        return ret < 0 ? EX_TEMPFAIL : ret;
 
1295
                /* ok, rejection sent */
 
1296
        }
 
1297
        i_free(explicit_envelope_sender);
 
1298
 
 
1299
        mail_free(&mail);
 
1300
        mailbox_header_lookup_unref(&headers_ctx);
 
1301
        mailbox_transaction_rollback(&t);
 
1302
        mailbox_close(&box);
 
1303
 
 
1304
        mail_user_unref(&mail_user);
 
1305
        mail_user_unref(&raw_mail_user);
 
1306
 
 
1307
        module_dir_unload(&modules);
 
1308
        mail_storage_deinit();
 
1309
        mail_users_deinit();
 
1310
 
 
1311
        duplicate_deinit();
 
1312
        dict_drivers_unregister_builtin();
 
1313
        lib_signals_deinit();
 
1314
 
 
1315
        io_loop_destroy(&ioloop);
 
1316
        lib_deinit();
 
1317
 
 
1318
        return EX_OK;
 
1319
}