/* * 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 #include #include #include #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):, or ; 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; }