#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <signal.h>
#include <ctype.h>
#include "acl.h"
#include "append.h"
#include "mboxlist.h"
#include "global.h"
#include "exitcodes.h"
#include "imap_err.h"
#include "mupdate-client.h"
#include "util.h"
#include "xmalloc.h"
const char *BB;
int forcedowncase;
extern int optind;
struct protstream *map_in, *map_out;
static struct namespace map_namespace;
const int config_need_data = 0;
extern void setproctitle_init(int argc, char **argv, char **envp);
int begin_handling(void);
void shut_down(int code) __attribute__((noreturn));
void shut_down(int code)
{
if (map_in) prot_free(map_in);
if (map_out) prot_free(map_out);
cyrus_reset_stdio();
mboxlist_close();
mboxlist_done();
quotadb_close();
quotadb_done();
cyrus_done();
exit(code);
}
void fatal(const char* s, int code)
{
static int recurse_code = 0;
if (recurse_code) {
exit(code);
}
recurse_code = code;
syslog(LOG_ERR, "Fatal error: %s", s);
shut_down(code);
}
int service_init(int argc, char **argv, char **envp)
{
int r;
if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
setproctitle_init(argc, argv, envp);
signals_set_shutdown(&shut_down);
signal(SIGPIPE, SIG_IGN);
BB = config_getstring(IMAPOPT_POSTUSER);
forcedowncase = config_getswitch(IMAPOPT_LMTP_DOWNCASE_RCPT);
mboxlist_init(0);
mboxlist_open(NULL);
quotadb_init(0);
quotadb_open(NULL);
if ((r = mboxname_init_namespace(&map_namespace, 1)) != 0) {
syslog(LOG_ERR, error_message(r));
fatal(error_message(r), EC_CONFIG);
}
return 0;
}
void service_abort(int error)
{
shut_down(error);
}
int service_main(int argc __attribute__((unused)),
char **argv __attribute__((unused)),
char **envp __attribute__((unused)))
{
int r;
map_in = prot_new(0, 0);
map_out = prot_new(1, 1);
prot_setflushonread(map_in, map_out);
prot_settimeout(map_in, 360);
r = begin_handling();
shut_down(r);
}
int verify_user(const char *key, long quotacheck,
struct auth_state *authstate)
{
char rcpt[MAX_MAILBOX_NAME+1], namebuf[MAX_MAILBOX_NAME+1] = "";
char *user = rcpt, *domain = NULL, *mailbox = NULL;
int r = 0;
strlcpy(rcpt, key, sizeof(rcpt));
if (config_virtdomains && (domain = strrchr(rcpt, '@'))) {
*domain++ = '\0';
if (config_defdomain && !strcasecmp(config_defdomain, domain))
domain = NULL;
}
mboxname_hiersep_tointernal(&map_namespace, rcpt, 0);
if ((mailbox = strchr(rcpt, '+'))) *mailbox++ = '\0';
if (forcedowncase) {
lcase(user);
if (domain) lcase(domain);
}
if (!strcmp(user, BB)) user = NULL;
if ((!user && !mailbox) ||
(domain && (strlen(domain) + 1 > sizeof(namebuf)))) {
r = IMAP_MAILBOX_NONEXISTENT;
} else {
if (domain) snprintf(namebuf, sizeof(namebuf), "%s!", domain);
if (!user) {
if (strlen(namebuf) + strlen(mailbox) > sizeof(namebuf)) {
r = IMAP_MAILBOX_NONEXISTENT;
} else {
strlcat(namebuf, mailbox, sizeof(namebuf));
}
} else {
if (strlen(namebuf) + 5 + strlen(user) > sizeof(namebuf)) {
r = IMAP_MAILBOX_NONEXISTENT;
} else {
strlcat(namebuf, "user.", sizeof(namebuf));
strlcat(namebuf, user, sizeof(namebuf));
}
}
}
if (!r) {
long aclcheck = !user ? ACL_POST : 0;
int type;
char *acl;
r = mboxlist_detail(namebuf, &type, NULL, NULL, &acl, NULL);
if (r == IMAP_MAILBOX_NONEXISTENT && config_mupdate_server) {
kick_mupdate();
r = mboxlist_detail(namebuf, &type, NULL, NULL, &acl, NULL);
}
if (!r && (type & MBTYPE_REMOTE)) {
int access = cyrus_acl_myrights(authstate, acl);
if ((access & aclcheck) != aclcheck) {
r = (access & ACL_LOOKUP) ?
IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
} else {
r = 0;
}
} else if (!r) {
r = append_check(namebuf, MAILBOX_FORMAT_NORMAL, authstate,
aclcheck, quotacheck > 0 ? 0 : quotacheck);
}
}
if (r) syslog(LOG_DEBUG, "verify_user(%s) failed: %s", namebuf,
error_message(r));
return r;
}
#define MAXREQUEST 1024
int begin_handling(void)
{
int c;
while ((c = prot_getc(map_in)) != EOF) {
int r = 0, sawdigit = 0, len = 0, size = 0;
struct auth_state *authstate = NULL;
char request[MAXREQUEST+1];
char *mapname = NULL, *key = NULL;
const char *errstring = NULL;
if (signals_poll() == SIGHUP) {
return 0;
}
while (isdigit(c)) {
sawdigit = 1;
len = len*10 + c - '0';
if (len > MAXREQUEST || len < 0) {
fatal("string too big", EC_IOERR);
}
c = prot_getc(map_in);
}
if (c == EOF) {
errstring = prot_error(map_in);
r = IMAP_IOERROR;
}
if (!sawdigit || c != ':') {
errstring = "missing length";
r = IMAP_PROTOCOL_ERROR;
}
if (!r && prot_read(map_in, request, len) != len) {
errstring = "request size doesn't match length";
r = IMAP_PROTOCOL_ERROR;
}
if (!r && (c = prot_getc(map_in)) != ',') {
errstring = "missing terminator";
r = IMAP_PROTOCOL_ERROR;
}
if (!r) {
request[len] = '\0';
mapname = request;
if (!(key = strchr(request, ' '))) {
errstring = "missing key";
r = IMAP_PROTOCOL_ERROR;
}
}
if (!r) {
*key++ = '\0';
r = verify_user(key, size, authstate);
}
switch (r) {
case 0:
prot_printf(map_out, "%d:OK %s,", 3+strlen(key), key);
break;
case IMAP_MAILBOX_NONEXISTENT:
prot_printf(map_out, "%d:NOTFOUND %s,",
9+strlen(error_message(r)), error_message(r));
break;
case IMAP_QUOTA_EXCEEDED:
if (!config_getswitch(IMAPOPT_LMTP_OVER_QUOTA_PERM_FAILURE)) {
prot_printf(map_out, "%d:TEMP %s,", strlen(error_message(r))+5,
error_message(r));
break;
}
default:
if (errstring)
prot_printf(map_out, "%d:PERM %s (%s),",
5+strlen(error_message(r))+3+strlen(errstring),
error_message(r), errstring);
else
prot_printf(map_out, "%d:PERM %s,",
5+strlen(error_message(r)), error_message(r));
break;
}
}
return 0;
}
void printstring(const char *s __attribute__((unused)))
{
fatal("printstring() executed, but its not used for smmapd!",
EC_SOFTWARE);
}