/*
* mailbox.c:
* Generic mailbox support for tpop3d.
*
* Copyright (c) 2001 Chris Lightfoot, Paul Makepeace. All rights reserved.
*
* $Id: mailbox.c,v 1.11 2002/11/13 20:08:59 chris Exp $
*
*/
#ifdef HAVE_CONFIG_H
#include "configuration.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "authswitch.h"
#include "config.h"
#include "mailbox.h"
#include "tokenise.h"
#include "util.h"
/* mbox_drivers:
* References various mailbox drivers, New ones should be added as below. Note
* that the first driver in this list will be used if mailbox_new is called
* with a NULL mailspool type, so it should be a sensible default. */
#define _X(String) (String)
struct mboxdrv mbox_drivers[] = {
#ifdef MBOX_BSD
/* Traditional, `From ' separated mail spool. */
{"bsd",
#ifdef MBOX_BSD_SAVE_INDICES
_X("BSD (`Unix') mailspool, with index saving support"),
#else
_X("BSD (`Unix') mailspool"),
#endif
mailspool_new_from_file},
#endif /* MBOX_BSD */
#ifdef MBOX_MAILDIR
/* The maildir format of qmail. */
{"maildir",
_X("Qmail-style maildir"),
maildir_new},
#endif /* WITH_MAILDIR */
/* A null mailspool implementation. Must be the last driver listed. */
{"empty",
_X("Empty mailbox"),
emptymbox_new}
};
#define NUM_MBOX_DRIVERS (sizeof(mbox_drivers) / sizeof(struct mboxdrv))
#define mbox_drivers_end mbox_drivers + NUM_MBOX_DRIVERS
/* mailbox_describe:
* Describe available mailbox drivers. */
void mailbox_describe(FILE *fp) {
const struct mboxdrv *mr;
fprintf(fp, _("Available mailbox drivers:\n\n"));
for (mr = mbox_drivers; mr < mbox_drivers_end; ++mr) {
fprintf(fp, " %-16s %s\n", mr->name, _(mr->description));
}
fprintf(fp, "\n");
}
/* mailbox_new:
* Create a new mailspool of the specified type, or a default type if the
* passed value is NULL. */
mailbox mailbox_new(const char *filename, const char *type) {
struct mboxdrv *mr;
if (!type) return mbox_drivers[0].m_new(filename);
for (mr = mbox_drivers; mr < mbox_drivers_end; ++mr)
if (strcmp(type, mr->name) == 0) return mr->m_new(filename);
log_print(LOG_ERR, _("mailbox_new(%s): request for unknown mailbox type %s"), filename, type);
return MBOX_NOENT;
}
/* mailbox_delete:
* Delete a mailbox object (but don't actually delete messages in the
* mailspool... the terminology is from C++ so it doesn't have to be logical).
*
* Note that this does `generic' deletion; there should be specific
* destructors for each type of mailbox. */
void mailbox_delete(mailbox m) {
if (!m) return;
if (m->index) {
struct indexpoint *i;
for (i = m->index; i < m->index + m->num; ++i)
if (i->filename) xfree(i->filename); /* should this be in a maildir-specific destructor? */
xfree(m->index);
}
if (m->name) xfree(m->name);
xfree(m);
}
/* mailbox_add_indexpoint:
* Add an indexpoint to a mailbox. */
void mailbox_add_indexpoint(mailbox m, const struct indexpoint *i) {
if (m->num == m->size) {
m->index = xrealloc(m->index, m->size * sizeof(struct indexpoint) * 2);
m->size *= 2;
}
m->index[m->num++] = *i;
}
/* emptymbox_new:
* New empty mailbox. */
mailbox emptymbox_new(const char *unused) {
mailbox M;
alloc_struct(_mailbox, M);
if (!M) return NULL;
M->delete = mailbox_delete; /* generic destructor */
M->apply_changes = emptymbox_apply_changes;
M->sendmessage = NULL; /* should never be called */
M->name = xstrdup(_("[empty mailbox]"));
M->index = NULL;
return M;
}
/* emptymbox_apply_changes:
* Null function for empty mailbox. */
int emptymbox_apply_changes(mailbox M) {
return 1;
}
/* try_mailbox_locations:
* Helper function for find_mailbox. */
static mailbox try_mailbox_locations(const char *specs, const char *user, const char *local_part, const char *domain, const char *home) {
tokens t;
mailbox m = NULL;
int i;
if (!(t = tokens_new(specs, " \t"))) return NULL;
for (i = 0; i < t->num; ++i) {
char *str, *mdrv = NULL, *subspec, *path;
struct sverr err;
str = t->toks[i];
subspec = strchr(str, ':');
if (subspec) {
mdrv = str;
*subspec++ = 0;
} else subspec = str;
path = substitute_variables(subspec, &err, 4, "user", user, "local_part", local_part, "domain", domain, "home", home);
if (!path)
/* Some sort of syntax error. */
log_print(LOG_ERR, _("try_mailbox_locations: %s near `%.16s'"), err.msg, subspec + err.offset);
else {
m = mailbox_new(path, mdrv);
xfree(path);
if (!m || m != MBOX_NOENT) break; /* Return in case of error or if we found the mailspool. */
}
}
tokens_delete(t);
return m;
}
/* find_mailbox:
* Try to find a user's mailbox. This first tries the locations in the
* $(authdrv)-mailbox: config option, or, failing that, the global mailbox:
* config option, or, failing that, MAILSPOOL_DIR/$(user).
*
* The config options may contain a number of options of the form
* $(mboxdrv):<substitution string>, or <substitution string>; in the latter
* case, then a default mailbox driver is assumed.
*
* This distinguishes between nonexistent mailboxes and mailboxes which
* couldn't be opened because of an error. This is to prevent SNAFUs where,
* say, bsd:/var/spool/mail/$(user) and maildir:$(home)/Maildir are allowed
* mailbox names, both exist, and the former is locked. It is important that
* the view of the mailbox presented to the user is consistent, so a failure
* to lock a given mailspool must not cause the program to go off and use a
* different one. */
mailbox find_mailbox(authcontext a) {
mailbox m = MBOX_NOENT;
char *buffer;
char *s;
/* Try the driver-specific config option. */
buffer = xmalloc(strlen("auth--mailbox") + strlen(a->auth) + 1);
sprintf(buffer, "auth-%s-mailbox", a->auth);
if ((s = config_get_string(buffer)))
m = try_mailbox_locations(s, a->user, a->local_part, a->domain, a->home);
xfree(buffer);
/* Then the global one. */
if (m == MBOX_NOENT && (s = config_get_string("mailbox")))
m = try_mailbox_locations(s, a->user, a->local_part, a->domain, a->home);
#ifdef MAILSPOOL_DIR
/* Then the compiled-in default. */
if (m == MBOX_NOENT) {
buffer = xmalloc(strlen(MAILSPOOL_DIR) + 1 + strlen(a->user) + 1);
sprintf(buffer, MAILSPOOL_DIR "/%s", a->user);
m = mailbox_new(buffer, NULL);
xfree(buffer);
}
#endif
/* No good. Give the user an empty mailbox. */
if (m == MBOX_NOENT) {
m = emptymbox_new(NULL);
log_print(LOG_WARNING, _("find_mailbox: using empty mailbox for user %s"), a->user);
}
return m;
}
syntax highlighted by Code2HTML, v. 0.9.1