deliver.c revision 56d6c770dc3641d9ef545c02b26dfd536356b0a6
/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "lib-signals.h"
#include "ioloop.h"
#include "env-util.h"
#include "fd-set-nonblock.h"
#include "istream.h"
#include "istream-seekable.h"
#include "str.h"
#include "str-sanitize.h"
#include "strescape.h"
#include "var-expand.h"
#include "rfc822-parser.h"
#include "message-address.h"
#include "imap-utf7.h"
#include "settings-parser.h"
#include "master-service.h"
#include "master-service-settings.h"
#include "mail-storage-service.h"
#include "mail-namespace.h"
#include "raw-storage.h"
#include "mail-send.h"
#include "duplicate.h"
#include "mbox-from.h"
#include "deliver.h"
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#define DEFAULT_ENVELOPE_SENDER "MAILER-DAEMON"
/* After buffer grows larger than this, create a temporary file to /tmp
where to read the mail. */
static const char *wanted_headers[] = {
"From", "Message-ID", "Subject", "Return-Path",
};
const struct deliver_settings *deliver_set;
bool mailbox_autosubscribe;
bool mailbox_autocreate;
bool tried_default_save = FALSE;
/* FIXME: these two should be in some context struct instead of as globals.. */
static const char *default_mailbox_name = NULL;
static bool saved_mail = FALSE;
static char *explicit_envelope_sender = NULL;
static struct master_service *service;
{
struct message_address *addr;
const char *str;
return NULL;
(const unsigned char *)str,
}
static const struct var_expand_table *
{
static struct var_expand_table static_tab[] = {
};
struct var_expand_table *tab;
unsigned int i;
return tab;
}
static void
{
const char *msg;
}
static struct mailbox *
struct mail_storage **storage_r,
const char *name)
{
struct mail_namespace *ns;
enum mail_error error;
/* deliveries to INBOX must always succeed,
regardless of ACLs */
}
return NULL;
}
if (*name == '\0') {
/* delivering to a namespace prefix means we actually want to
deliver to the INBOX instead */
return NULL;
}
return box;
if (error != MAIL_ERROR_NOTFOUND)
return NULL;
/* try creating it. */
return NULL;
if (mailbox_autosubscribe) {
/* (try to) subscribe to it */
}
/* and try opening again */
return NULL;
mailbox_close(&box);
return NULL;
}
return box;
}
const char *const *keywords)
{
struct mailbox_transaction_context *t;
struct mail_save_context *save_ctx;
struct mail_keywords *kw;
enum mail_error error;
const char *mailbox_name;
bool default_save;
int ret = 0;
if (default_save)
"save failed to %s: Unknown namespace",
return -1;
}
if (default_save &&
/* silently store to the INBOX instead */
return -1;
}
return -1;
}
save_ctx = mailbox_save_alloc(t);
ret = -1;
if (ret < 0)
else
ret = mailbox_transaction_commit(&t);
if (ret == 0) {
saved_mail = TRUE;
} else {
}
mailbox_close(&box);
return ret;
}
{
if (explicit_envelope_sender != NULL)
return explicit_envelope_sender;
}
const char *deliver_get_new_message_id(void)
{
static int count = 0;
return t_strdup_printf("<dovecot-%s-%s-%d@%s>",
}
static const char *escape_local_part(const char *local_part)
{
const char *p;
/* if there are non-atext chars, we need to return quoted-string */
for (p = local_part; *p != '\0'; p++) {
if (!IS_ATEXT(*p)) {
return t_strdup_printf("\"%s\"",
}
}
return local_part;
}
static const char *address_sanitize(const char *address)
{
struct message_address *addr;
else {
else
}
pool_unref(&pool);
return ret;
}
static struct istream *
{
const unsigned char *data;
/* If input begins with a From-line, drop it */
/* skip until the first LF */
for (i = 0; i < size; i++) {
if (data[i] == '\n')
break;
}
if (i != size) {
&sender);
break;
}
}
}
/* use the envelope sender from From_-line, but only if it
hasn't been specified with -f already. */
}
} else {
}
return input;
}
static void failure_exit_callback(int *status)
{
/* we want all our exit codes to be sysexits.h compatible.
if we failed because of a logging related error, we most likely
aren't writing to stderr, so try writing there to give some kind of
a clue what's wrong. FATAL_LOGOPEN failure already wrote to
stderr, so don't duplicate it. */
switch (*status) {
case FATAL_LOGWRITE:
break;
case FATAL_LOGERROR:
break;
case FATAL_LOGOPEN:
case FATAL_OUTOFMEM:
case FATAL_EXEC:
case FATAL_DEFAULT:
break;
default:
return;
}
*status = EX_TEMPFAIL;
}
static void print_help(void)
{
"Usage: deliver [-c <config file>] [-a <address>] [-d <username>] [-p <path>]\n"
" [-f <envelope sender>] [-m <mailbox>] [-n] [-s] [-e] [-k]\n");
}
{
enum mail_storage_service_flags service_flags = 0;
const char *mailbox = "INBOX";
struct mail_namespace *raw_ns;
struct mail_namespace_settings raw_ns_set;
struct mail_storage *storage;
struct raw_mailbox *raw_box;
struct mailbox_transaction_context *t;
struct mailbox_header_lookup_ctx *headers_ctx;
void **sets;
bool stderr_rejection = FALSE;
int ret, c;
enum mail_error error;
/* running setuid - don't allow this if deliver is
executable by anyone */
return EX_CONFIG;
"and setuid-root. This allows root exploits. "
"See http://wiki.dovecot.org/LDA#multipleuids\n",
argv[0]);
return EX_CONFIG;
}
}
#ifdef SIGXFSZ
#endif
switch (c) {
case 'a':
/* destination address */
break;
case 'd':
/* destination user */
break;
case 'p':
/* input path */
if (*path != '/') {
/* expand relative paths before we chdir */
i_fatal("getcwd() failed: %m");
}
break;
case 'e':
break;
case 'm':
/* destination mailbox.
Ignore -m "". This allows doing -m ${extension}
in Postfix to handle user+mailbox */
if (*optarg != '\0') {
i_fatal("Mailbox name not UTF-8: %s",
mailbox);
}
}
break;
case 'n':
break;
case 's':
break;
case 'f':
/* envelope sender address */
break;
default:
print_help();
"Unknown argument: %c", c);
}
break;
}
}
print_help();
}
process_euid = geteuid();
if ((service_flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0)
;
else if (process_euid != 0) {
/* we're non-root. get our username and possibly our home. */
const char *home;
/* no need for a pw lookup */
"Couldn't lookup our username (uid=%s)",
}
} else {
"destination user parameter (-d user) not given");
}
/* create a separate mail user for the internal namespace */
i_unreached();
} else {
}
i_fatal("Can't open delivery mail as raw: %s",
}
i_fatal("Can't sync delivery mail: %s",
}
t = mailbox_transaction_begin(box, 0);
}
}
if (deliver_mail == NULL)
ret = -1;
else {
/* if message was saved, don't bounce it even though
the script failed later. */
} else {
/* success. message may or may not have been saved. */
ret = 0;
}
}
if (ret < 0 && !tried_default_save) {
/* plugins didn't handle this. save into the default mailbox. */
}
/* still didn't work. try once more to save it
to INBOX. */
}
if (ret < 0 ) {
/* This shouldn't happen */
i_error("BUG: Saving failed for unknown storage");
return EX_TEMPFAIL;
}
if (stderr_rejection) {
/* write to stderr also for tempfails so that MTA
can log the reason if it wants to. */
}
if (error != MAIL_ERROR_NOSPACE ||
/* Saving to INBOX should always work unless
we're over quota. If it didn't, it's probably a
configuration problem. */
return EX_TEMPFAIL;
}
/* we'll have to reply with permanent failure */
if (stderr_rejection)
return EX_NOPERM;
if (ret != 0)
/* ok, rejection sent */
}
mailbox_close(&box);
return EX_OK;
}