EXPORTED void quotadb_open(const char *fname) { int ret; char *tofree = NULL; int flags = CYRUSDB_CREATE; if (!fname) fname = config_getstring(IMAPOPT_QUOTA_DB_PATH); /* create db file name */ if (!fname) { tofree = strconcat(config_dir, FNAME_QUOTADB, (char *)NULL); fname = tofree; } if (config_getswitch(IMAPOPT_IMPROVED_MBOXLIST_SORT)) flags |= CYRUSDB_MBOXSORT; ret = cyrusdb_open(QDB, fname, flags, &qdb); if (ret != 0) { syslog(LOG_ERR, "DBERROR: opening %s: %s", fname, cyrusdb_strerror(ret)); /* Exiting TEMPFAIL because Sendmail thinks this EC_OSFILE == permanent failure. */ fatal("can't read quotas file", EC_TEMPFAIL); } free(tofree); quota_dbopen = 1; }
static int user_deletesieve(const char *user) { const char *sieve_path; char filename[2048]; DIR *mbdir; struct dirent *next = NULL; /* oh well */ if(config_getswitch(IMAPOPT_SIEVEUSEHOMEDIR)) return 0; sieve_path = user_sieve_path(user); mbdir = opendir(sieve_path); if (mbdir) { while((next = readdir(mbdir)) != NULL) { if (!strcmp(next->d_name, ".") || !strcmp(next->d_name, "..")) continue; snprintf(filename, sizeof(filename), "%s/%s", sieve_path, next->d_name); unlink(filename); } closedir(mbdir); /* remove mbdir */ rmdir(sieve_path); } return 0; }
static int sync_log_enabled(const char *channel) { if (!config_getswitch(IMAPOPT_SYNC_LOG)) return 0; /* entire mechanism is disabled */ if (!sync_log_suppressed) return 1; /* _suppress() wasn't called */ if (unsuppressable && strarray_find(unsuppressable, channel, 0) >= 0) return 1; /* channel is unsuppressable */ return 0; /* suppressed */ }
static int compact_closerename(struct backup **originalp, struct backup **compactp, time_t now) { struct backup *original = *originalp; struct backup *compact = *compactp; struct buf ts_data_fname = BUF_INITIALIZER; struct buf ts_index_fname = BUF_INITIALIZER; int r; buf_printf(&ts_data_fname, "%s.%ld", original->data_fname, now); buf_printf(&ts_index_fname, "%s.%ld", original->index_fname, now); /* link original files into timestamped names */ r = link(original->data_fname, buf_cstring(&ts_data_fname)); if (!r) link(original->index_fname, buf_cstring(&ts_index_fname)); if (r) { /* on error, trash the new links and bail out */ unlink(buf_cstring(&ts_data_fname)); unlink(buf_cstring(&ts_index_fname)); goto done; } /* replace original files with compacted files */ r = rename(compact->data_fname, original->data_fname); if (!r) r = rename(compact->index_fname, original->index_fname); if (r) { /* on error, put original files back */ unlink(original->data_fname); unlink(original->index_fname); link(buf_cstring(&ts_data_fname), original->data_fname); link(buf_cstring(&ts_index_fname), original->index_fname); goto done; } /* finally, clean up the timestamped ones */ if (!config_getswitch(IMAPOPT_BACKUP_KEEP_PREVIOUS)) { unlink(buf_cstring(&ts_data_fname)); unlink(buf_cstring(&ts_index_fname)); } /* release our locks */ backup_close(originalp); backup_close(compactp); done: buf_free(&ts_data_fname); buf_free(&ts_index_fname); return r; }
EXPORTED int tls_enabled(void) { const char *val; val = config_getstring(IMAPOPT_TLS_SERVER_CERT); if (!val || !strcasecmp(val, "disabled")) return 0; val = config_getstring(IMAPOPT_TLS_SERVER_KEY); if (!val || !strcasecmp(val, "disabled")) return 0; if (config_getswitch(IMAPOPT_CHATTY)) syslog(LOG_INFO, "TLS is available."); return 1; }
int actions_init(void) { int sieve_usehomedir = 0; sieve_usehomedir = config_getswitch(IMAPOPT_SIEVEUSEHOMEDIR); if (!sieve_usehomedir) { sieve_dir = (char *) config_getstring(IMAPOPT_SIEVEDIR); } else { /* can't use home directories with timsieved */ syslog(LOG_ERR, "can't use home directories"); return TIMSIEVE_FAIL; } return TIMSIEVE_OK; }
/* * Convert 'identifier' into canonical form. * Returns a pointer to a static buffer containing the canonical form * or NULL if 'identifier' is invalid. */ static char *afspts_canonifyid(const char *identifier, size_t len) { static char *retbuf = NULL; char *tmp = NULL; krb5_context context; krb5_principal princ, princ_dummy; char *realm; char *realmbegin; int striprealm = 0; char *identifier2; if(retbuf) free(retbuf); retbuf = NULL; if(!identifier) return NULL; if(!len) len = strlen(identifier); if (strcasecmp(identifier, "anonymous") == 0) return "anonymous"; if (strcasecmp(identifier, "anyone") == 0) return "anyone"; identifier2 = strdup(identifier); if (tmp = strchr(identifier2, '+')) { syslog(LOG_DEBUG, "afspts_canonifyid stripping: %s", identifier2); tmp[0] = 0; syslog(LOG_DEBUG, "afspts_canonifyid stripped: %s", identifier2); } if (krb5_init_context(&context)) { syslog(LOG_ERR, "afspts_canonifyid krb5_init_context failed"); return NULL; } if (krb5_parse_name(context,identifier2,&princ)) { krb5_free_context(context); free(identifier2); syslog(LOG_ERR, "afspts_canonifyid krb5_parse_name failed"); return NULL; } free(identifier2); if(config_getswitch(IMAPOPT_PTSKRB5_STRIP_DEFAULT_REALM)) { /* get local realm */ if (krb5_get_default_realm(context,&realm)) { krb5_free_principal(context,princ); krb5_free_context(context); syslog(LOG_ERR, "afspts_canonifyid krb5_get_default_realm failed"); return NULL; } /* build dummy princ to compare realms */ if (krb5_build_principal(context,&princ_dummy, strlen(realm),realm,"dummy",0)) { krb5_free_principal(context,princ); krb5_free_context(context); free(realm); syslog(LOG_ERR, "afspts_canonifyid krb5_build_principal failed"); return NULL; } /* is this principal local ? */ if (krb5_realm_compare(context,princ,princ_dummy)) { striprealm = 1; } /* done w/ dummy princ free it & realm */ krb5_free_principal(context,princ_dummy); free(realm); } if (config_getswitch(IMAPOPT_PTSKRB5_CONVERT524)) { char nbuf[64], ibuf[64], rbuf[64]; if (krb5_524_conv_principal(context, princ, nbuf, ibuf, rbuf)) { krb5_free_principal(context,princ); krb5_free_context(context); return NULL; } retbuf = xmalloc(3*64 + 3); sprintf(retbuf, "%s%s%s%s%s", nbuf, ibuf[0] ? "." : "", ibuf, rbuf[0] ? "@" : "", rbuf); } else { /* get the text version of princ */ if (krb5_unparse_name(context,princ,&retbuf)) { krb5_free_principal(context,princ); krb5_free_context(context); syslog(LOG_ERR, "afspts_canonifyid krb5_unparse_name failed"); return NULL; } } /* we have the canonical name pointed to by p -- strip realm if local */ realmbegin = strrchr(retbuf, '@'); if(realmbegin) { if(!striprealm) { realm = realmbegin+1; if(is_local_realm(realm)) striprealm = 1; } if(striprealm) { *realmbegin = '\0'; } else { /* Force realm to uppercase */ while(*(++realmbegin)) { *realmbegin = toupper(*realmbegin); } } } krb5_free_principal(context,princ); krb5_free_context(context); return retbuf; }
struct backend *backend_connect(struct backend *ret_backend, const char *server, struct protocol_t *prot, const char *userid, sasl_callback_t *cb, const char **auth_status) { /* need to (re)establish connection to server or create one */ int sock = -1; int r; int err = -1; int ask = 1; /* should we explicitly ask for capabilities? */ struct addrinfo hints, *res0 = NULL, *res; struct sockaddr_un sunsock; char buf[2048]; struct sigaction action; struct backend *ret; char rsessionid[MAX_SESSIONID_SIZE]; if (!ret_backend) { ret = xzmalloc(sizeof(struct backend)); strlcpy(ret->hostname, server, sizeof(ret->hostname)); ret->timeout = NULL; } else ret = ret_backend; if (server[0] == '/') { /* unix socket */ res0 = &hints; memset(res0, 0, sizeof(struct addrinfo)); res0->ai_family = PF_UNIX; res0->ai_socktype = SOCK_STREAM; res0->ai_addr = (struct sockaddr *) &sunsock; res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1; #ifdef SIN6_LEN res0->ai_addrlen += sizeof(sunsock.sun_len); sunsock.sun_len = res0->ai_addrlen; #endif sunsock.sun_family = AF_UNIX; strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path)); /* XXX set that we are preauthed */ /* change hostname to 'config_servername' */ strlcpy(ret->hostname, config_servername, sizeof(ret->hostname)); } else { /* inet socket */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(server, prot->service, &hints, &res0); if (err) { syslog(LOG_ERR, "getaddrinfo(%s) failed: %s", server, gai_strerror(err)); goto error; } } /* Setup timeout */ timedout = 0; action.sa_flags = 0; action.sa_handler = timed_out; sigemptyset(&action.sa_mask); if(sigaction(SIGALRM, &action, NULL) < 0) { syslog(LOG_ERR, "Setting timeout in backend_connect failed: sigaction: %m"); /* continue anyway */ } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; alarm(config_getint(IMAPOPT_CLIENT_TIMEOUT)); if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0) break; if(errno == EINTR && timedout == 1) errno = ETIMEDOUT; close(sock); sock = -1; } /* Remove timeout code */ alarm(0); signal(SIGALRM, SIG_IGN); if (sock < 0) { if (res0 != &hints) freeaddrinfo(res0); syslog(LOG_ERR, "connect(%s) failed: %m", server); goto error; } memcpy(&ret->addr, res->ai_addr, res->ai_addrlen); if (res0 != &hints) freeaddrinfo(res0); ret->in = prot_new(sock, 0); ret->out = prot_new(sock, 1); ret->sock = sock; prot_setflushonread(ret->in, ret->out); ret->prot = prot; /* use literal+ to send literals */ prot_setisclient(ret->in, 1); prot_setisclient(ret->out, 1); if (prot->banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_connect(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); goto error; } } while (strncasecmp(buf, prot->banner.resp, strlen(prot->banner.resp))); strncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if ((server[0] != '/') || (strcmp(prot->sasl_service, "lmtp") && strcmp(prot->sasl_service, "csync"))) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); goto error; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); free(new_mechlist); free(old_mechlist); goto error; } free(new_mechlist); } else if (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && ret && CAPA(ret, CAPA_COMPRESS) && prot->compress_cmd.cmd && do_compress(ret, &prot->compress_cmd)) { syslog(LOG_ERR, "couldn't enable compression on backend server"); goto error; } return ret; error: forget_capabilities(ret); if (ret->in) { prot_free(ret->in); ret->in = NULL; } if (ret->out) { prot_free(ret->out); ret->out = NULL; } if (sock >= 0) close(sock); if (ret->saslconn) { sasl_dispose(&ret->saslconn); ret->saslconn = NULL; } if (!ret_backend) free(ret); return NULL; }
static int user_renamesieve(char *olduser, char *newuser) { char hash, *domain; char oldpath[2048], newpath[2048]; int r; /* oh well */ if(config_getswitch(IMAPOPT_SIEVEUSEHOMEDIR)) return 0; if (config_virtdomains && (domain = strchr(olduser, '@'))) { char d = (char) dir_hash_c(domain+1, config_fulldirhash); *domain = '\0'; /* split user@domain */ hash = (char) dir_hash_c(olduser, config_fulldirhash); snprintf(oldpath, sizeof(oldpath), "%s%s%c/%s/%c/%s", config_getstring(IMAPOPT_SIEVEDIR), FNAME_DOMAINDIR, d, domain+1, hash, olduser); *domain = '@'; /* reassemble user@domain */ } else { hash = (char) dir_hash_c(olduser, config_fulldirhash); snprintf(oldpath, sizeof(oldpath), "%s/%c/%s", config_getstring(IMAPOPT_SIEVEDIR), hash, olduser); } if (config_virtdomains && (domain = strchr(newuser, '@'))) { char d = (char) dir_hash_c(domain+1, config_fulldirhash); *domain = '\0'; /* split user@domain */ hash = (char) dir_hash_c(newuser, config_fulldirhash); snprintf(newpath, sizeof(newpath), "%s%s%c/%s/%c/%s", config_getstring(IMAPOPT_SIEVEDIR), FNAME_DOMAINDIR, d, domain+1, hash, newuser); *domain = '@'; /* reassemble user@domain */ } else { hash = (char) dir_hash_c(newuser, config_fulldirhash); snprintf(newpath, sizeof(newpath), "%s/%c/%s", config_getstring(IMAPOPT_SIEVEDIR), hash, newuser); } /* rename sieve directory * * XXX this doesn't rename sieve scripts */ r = rename(oldpath, newpath); if (r < 0) { if (errno == ENOENT) { syslog(LOG_WARNING, "error renaming %s to %s: %m", oldpath, newpath); /* but maybe the user doesn't have any scripts ? */ r = 0; } else if (errno == EXDEV) { syslog(LOG_ERR, "error renaming %s to %s: different filesystems", oldpath, newpath); /* doh! need to copy entire directory tree */ } else { syslog(LOG_ERR, "error renaming %s to %s: %m", oldpath, newpath); } } return r; }
/* Called before a cyrus application starts (but after command line parameters * are read) */ int cyrus_init(const char *alt_config, const char *ident, unsigned flags) { char *p; const char *val; const char *prefix; int umaskval = 0; if(cyrus_init_run != NOT_RUNNING) { fatal("cyrus_init called twice!", EC_CONFIG); } else { cyrus_init_run = RUNNING; } cyrus_init_nodb = (flags & CYRUSINIT_NODB); initialize_imap_error_table(); initialize_mupd_error_table(); if(!ident) fatal("service name was not specified to cyrus_init", EC_CONFIG); config_ident = ident; /* xxx we lose here since we can't have the prefix until we load the * config file */ openlog(config_ident, LOG_PID, SYSLOG_FACILITY); /* Load configuration file. This will set config_dir when it finds it */ config_read(alt_config); prefix = config_getstring(IMAPOPT_SYSLOG_PREFIX); /* Reopen the log with the new prefix, if needed */ if(prefix) { int size = strlen(prefix) + 1 + strlen(ident) + 1; char *ident_buf = xmalloc(size); strlcpy(ident_buf, prefix, size); strlcat(ident_buf, "/", size); strlcat(ident_buf, ident, size); closelog(); openlog(ident_buf, LOG_PID, SYSLOG_FACILITY); /* don't free the openlog() string! */ } /* Look up default partition */ config_defpartition = config_getstring(IMAPOPT_DEFAULTPARTITION); for (p = (char *)config_defpartition; p && *p; p++) { if (!Uisalnum(*p)) fatal("defaultpartition option contains non-alphanumeric character", EC_CONFIG); if (Uisupper(*p)) *p = tolower((unsigned char) *p); } /* Look up umask */ val = config_getstring(IMAPOPT_UMASK); while (*val) { if (*val >= '0' && *val <= '7') umaskval = umaskval*8 + *val - '0'; val++; } umask(umaskval); config_fulldirhash = config_getswitch(IMAPOPT_FULLDIRHASH); /* look up and canonify the implicit rights of mailbox owners */ config_implicitrights = cyrus_acl_strtomask(config_getstring(IMAPOPT_IMPLICIT_OWNER_RIGHTS)); config_metapartition_files = config_getbitfield(IMAPOPT_METAPARTITION_FILES); if (!cyrus_init_nodb) { /* lookup the database backends */ config_mboxlist_db = cyrusdb_fromname(config_getstring(IMAPOPT_MBOXLIST_DB)); config_quota_db = cyrusdb_fromname(config_getstring(IMAPOPT_QUOTA_DB)); config_subscription_db = cyrusdb_fromname(config_getstring(IMAPOPT_SUBSCRIPTION_DB)); config_annotation_db = cyrusdb_fromname(config_getstring(IMAPOPT_ANNOTATION_DB)); config_seenstate_db = cyrusdb_fromname(config_getstring(IMAPOPT_SEENSTATE_DB)); config_mboxkey_db = cyrusdb_fromname(config_getstring(IMAPOPT_MBOXKEY_DB)); config_duplicate_db = cyrusdb_fromname(config_getstring(IMAPOPT_DUPLICATE_DB)); config_tlscache_db = cyrusdb_fromname(config_getstring(IMAPOPT_TLSCACHE_DB)); config_ptscache_db = cyrusdb_fromname(config_getstring(IMAPOPT_PTSCACHE_DB)); config_statuscache_db = cyrusdb_fromname(config_getstring(IMAPOPT_STATUSCACHE_DB)); config_userdeny_db = cyrusdb_fromname(config_getstring(IMAPOPT_USERDENY_DB)); /* configure libcyrus as needed */ libcyrus_config_setstring(CYRUSOPT_CONFIG_DIR, config_dir); libcyrus_config_setswitch(CYRUSOPT_AUTH_UNIX_GROUP_ENABLE, config_getswitch(IMAPOPT_UNIX_GROUP_ENABLE)); libcyrus_config_setswitch(CYRUSOPT_USERNAME_TOLOWER, config_getswitch(IMAPOPT_USERNAME_TOLOWER)); libcyrus_config_setswitch(CYRUSOPT_SKIPLIST_UNSAFE, config_getswitch(IMAPOPT_SKIPLIST_UNSAFE)); libcyrus_config_setstring(CYRUSOPT_TEMP_PATH, config_getstring(IMAPOPT_TEMP_PATH)); libcyrus_config_setint(CYRUSOPT_PTS_CACHE_TIMEOUT, config_getint(IMAPOPT_PTSCACHE_TIMEOUT)); libcyrus_config_setswitch(CYRUSOPT_FULLDIRHASH, config_getswitch(IMAPOPT_FULLDIRHASH)); libcyrus_config_setstring(CYRUSOPT_PTSCACHE_DB, config_getstring(IMAPOPT_PTSCACHE_DB)); libcyrus_config_setstring(CYRUSOPT_PTSCACHE_DB_PATH, config_getstring(IMAPOPT_PTSCACHE_DB_PATH)); libcyrus_config_setstring(CYRUSOPT_PTLOADER_SOCK, config_getstring(IMAPOPT_PTLOADER_SOCK)); libcyrus_config_setswitch(CYRUSOPT_VIRTDOMAINS, config_getenum(IMAPOPT_VIRTDOMAINS)); libcyrus_config_setint(CYRUSOPT_BERKELEY_CACHESIZE, config_getint(IMAPOPT_BERKELEY_CACHESIZE)); libcyrus_config_setstring(CYRUSOPT_AUTH_MECH, config_getstring(IMAPOPT_AUTH_MECH)); libcyrus_config_setint(CYRUSOPT_BERKELEY_LOCKS_MAX, config_getint(IMAPOPT_BERKELEY_LOCKS_MAX)); libcyrus_config_setint(CYRUSOPT_BERKELEY_TXNS_MAX, config_getint(IMAPOPT_BERKELEY_TXNS_MAX)); libcyrus_config_setstring(CYRUSOPT_DELETERIGHT, config_getstring(IMAPOPT_DELETERIGHT)); libcyrus_config_setstring(CYRUSOPT_SQL_DATABASE, config_getstring(IMAPOPT_SQL_DATABASE)); libcyrus_config_setstring(CYRUSOPT_SQL_ENGINE, config_getstring(IMAPOPT_SQL_ENGINE)); libcyrus_config_setstring(CYRUSOPT_SQL_HOSTNAMES, config_getstring(IMAPOPT_SQL_HOSTNAMES)); libcyrus_config_setstring(CYRUSOPT_SQL_USER, config_getstring(IMAPOPT_SQL_USER)); libcyrus_config_setstring(CYRUSOPT_SQL_PASSWD, config_getstring(IMAPOPT_SQL_PASSWD)); libcyrus_config_setswitch(CYRUSOPT_SQL_USESSL, config_getswitch(IMAPOPT_SQL_USESSL)); /* Not until all configuration parameters are set! */ libcyrus_init(); } return 0; }
static void send_lmtp_error(struct protstream *pout, int r) { switch (r) { case 0: prot_printf(pout, "250 2.1.5 Ok SESSIONID=<%s>\r\n", session_id()); break; case IMAP_IOERROR: prot_printf(pout, "451 4.3.0 System I/O error\r\n"); break; case IMAP_SERVER_UNAVAILABLE: case MUPDATE_NOCONN: case MUPDATE_NOAUTH: case MUPDATE_TIMEOUT: case MUPDATE_PROTOCOL_ERROR: prot_printf(pout, "451 4.4.3 Remote server unavailable\r\n"); break; case IMAP_NOSPACE: prot_printf(pout, "451 4.3.1 cannot create file: out of space\r\n"); break; case IMAP_AGAIN: prot_printf(pout, "451 4.3.0 transient system error\r\n"); break; case IMAP_PERMISSION_DENIED: if (LMTP_LONG_ERROR_MSGS) { prot_printf(pout, "550-You do not have permission to post a message to this mailbox.\r\n" "550-Please contact the owner of this mailbox in order to submit\r\n" "550-your message, or %s if you believe you\r\n" "550-received this message in error.\r\n" "550 5.7.1 Permission denied\r\n", config_getstring(IMAPOPT_POSTMASTER)); } else { prot_printf(pout, "550 5.7.1 Permission denied\r\n"); } break; case IMAP_QUOTA_EXCEEDED: if(config_getswitch(IMAPOPT_LMTP_OVER_QUOTA_PERM_FAILURE)) { /* Not Default - Perm Failure */ prot_printf(pout, "552 5.2.2 Over quota SESSIONID=<%s>\r\n", session_id()); } else { /* Default - Temp Failure */ prot_printf(pout, "452 4.2.2 Over quota SESSIONID=<%s>\r\n", session_id()); } break; case IMAP_MAILBOX_BADFORMAT: case IMAP_MAILBOX_NOTSUPPORTED: prot_printf(pout, "451 4.2.0 Mailbox has an invalid format\r\n"); break; case IMAP_MAILBOX_MOVED: prot_printf(pout, "451 4.2.1 Mailbox Moved\r\n"); break; case IMAP_MESSAGE_CONTAINSNULL: prot_printf(pout, "554 5.6.0 Message contains NUL characters\r\n"); break; case IMAP_MESSAGE_CONTAINSNL: prot_printf(pout, "554 5.6.0 Message contains bare newlines\r\n"); break; case IMAP_MESSAGE_CONTAINS8BIT: prot_printf(pout, "554 5.6.0 Message contains non-ASCII characters in headers\r\n"); break; case IMAP_MESSAGE_BADHEADER: prot_printf(pout, "554 5.6.0 Message contains invalid header\r\n"); break; case IMAP_MESSAGE_NOBLANKLINE: prot_printf(pout, "554 5.6.0 Message has no header/body separator\r\n"); break; case IMAP_MAILBOX_NONEXISTENT: /* XXX Might have been moved to other server */ if (LMTP_LONG_ERROR_MSGS) { prot_printf(pout, "550-Mailbox unknown. Either there is no mailbox associated with this\r\n" "550-name or you do not have authorization to see it.\r\n" "550 5.1.1 User unknown\r\n"); } else { prot_printf(pout, "550 5.1.1 User unknown\r\n"); } break; case IMAP_PROTOCOL_BAD_PARAMETERS: prot_printf(pout, "501 5.5.4 Syntax error in parameters\r\n"); break; case MUPDATE_BADPARAM: default: /* Some error we're not expecting. */ prot_printf(pout, "451 4.3.0 Unexpected internal error\r\n"); break; } }
/* see if 'addr' exists. if so, fill in 'ad' appropriately. on success, return NULL. on failure, return the error. */ static int process_recipient(char *addr, struct namespace *namespace, int ignorequota, int (*verify_user)(const char *, const char *, char *, quota_t, quota_t, struct auth_state *), message_data_t *msg) { char *dest; char *rcpt; int r, sl; address_data_t *ret = (address_data_t *) xmalloc(sizeof(address_data_t)); int forcedowncase = config_getswitch(IMAPOPT_LMTP_DOWNCASE_RCPT); int quoted, detail; assert(addr != NULL && msg != NULL); if (*addr == '<') addr++; dest = rcpt = addr; /* preserve the entire address */ ret->all = xstrdup(addr); sl = strlen(ret->all); if (ret->all[sl-1] == '>') ret->all[sl-1] = '\0'; /* now find just the user */
static int backend_login(struct backend *ret, const char *userid, sasl_callback_t *cb, const char **auth_status, int noauth) { int r = 0; int ask = 1; /* should we explicitly ask for capabilities? */ char buf[2048]; struct protocol_t *prot = ret->prot; if (prot->type != TYPE_STD) return -1; if (prot->u.std.banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_login(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); return -1; } } while (strncasecmp(buf, prot->u.std.banner.resp, strlen(prot->u.std.banner.resp))); xstrncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if (!noauth) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); return -1; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); if (new_mechlist) free(new_mechlist); if (old_mechlist) free(old_mechlist); return -1; } free(new_mechlist); } else if (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { char rsessionid[MAX_SESSIONID_SIZE]; parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "auditlog: proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && CAPA(ret, CAPA_COMPRESS) && prot->u.std.compress_cmd.cmd) { r = do_compress(ret, &prot->u.std.compress_cmd); if (r) { syslog(LOG_NOTICE, "couldn't enable compression on backend server: %s", error_message(r)); r = 0; /* not a fail-level error */ } } return 0; }
/* Returns TRUE if we are done */ int parser(struct protstream *sieved_out, struct protstream *sieved_in) { int token = EOL; const char *error_msg = "Generic Error"; struct buf mechanism_name = BUF_INITIALIZER; struct buf initial_challenge = BUF_INITIALIZER; struct buf sieve_name = BUF_INITIALIZER; struct buf sieve_data = BUF_INITIALIZER; unsigned long num; int ret = FALSE; /* get one token from the lexer */ while(token == EOL) token = timlex(NULL, NULL, sieved_in); if (!authenticated && (token > 255) && (token!=AUTHENTICATE) && (token!=LOGOUT) && (token!=CAPABILITY) && (token!=NOOP) && (token!=CHECKSCRIPT) && (!tls_enabled() || (token!=STARTTLS))) { error_msg = "Authenticate first"; if (token!=EOL) lex_setrecovering(); goto error; } if (verify_only && (token > 255) && (token!=CHECKSCRIPT) && (token!=PUTSCRIPT) && (token!=LOGOUT)) { error_msg = "Script verification only"; if (token!=EOL) lex_setrecovering(); goto error; } switch (token) { case EOF: /* timlex() will return EOF when the remote disconnects badly */ syslog(LOG_WARNING, "Lost connection to client -- exiting"); prot_printf(sieved_out, "BYE \"Shutdown TCP timeout\"\r\n"); ret = TRUE; goto done; break; case AUTHENTICATE: if (!starttls_done && config_getswitch(IMAPOPT_FORCETLSAUTH)) { error_msg = "AUTHENTICATE only available under a layer"; goto error; } if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "SPACE must occur after AUTHENTICATE"; goto error; } if (timlex(&mechanism_name, NULL, sieved_in)!=STRING) { error_msg = "Did not specify mechanism name"; goto error; } token = timlex(NULL, NULL, sieved_in); if (token != EOL) { /* optional client first challenge */ if (token!=SPACE) { error_msg = "Expected SPACE"; goto error; } if (timlex(&initial_challenge, NULL, sieved_in)!=STRING) { error_msg = "Expected string"; goto error; } token = timlex(NULL, NULL, sieved_in); } if (token != EOL) { error_msg = "Expected EOL"; goto error; } if (authenticated) prot_printf(sieved_out, "NO \"Already authenticated\"\r\n"); else if (cmd_authenticate(sieved_out, sieved_in, mechanism_name.s, &initial_challenge, &error_msg)==FALSE) { error_msg = "Authentication Error"; goto error; } #if 0 /* XXX - not implemented in sieveshell*/ /* referral_host is non-null only once we are authenticated */ if(referral_host) goto do_referral; #endif break; case CAPABILITY: if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } if(referral_host) goto do_referral; capabilities(sieved_out, sieved_saslconn, starttls_done, authenticated, sasl_ssf); break; case CHECKSCRIPT: if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "SPACE must occur after CHECKSCRIPT"; goto error; } if (timlex(&sieve_data, NULL, sieved_in)!=STRING) { error_msg = "Expected script content as second parameter"; goto error; } if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } /* f stands for "f"aked name, it could be any valid script name */ buf_reset(&sieve_name); buf_appendcstr(&sieve_name, "f"); putscript(sieved_out, &sieve_name, &sieve_data, /* verify_only */ 1); break; case HAVESPACE: if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "SPACE must occur after HAVESPACE"; goto error; } if (timlex(&sieve_name, NULL, sieved_in)!=STRING) { error_msg = "Did not specify script name"; goto error; } if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "Expected SPACE after SCRIPTNAME"; goto error; } if (timlex(NULL, &num, sieved_in)!=NUMBER) { error_msg = "Expected Number"; goto error; } if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } if(referral_host) goto do_referral; cmd_havespace(sieved_out, &sieve_name, num); break; case LOGOUT: token = timlex(NULL, NULL, sieved_in); /* timlex() will return LOGOUT when the remote disconnects badly */ if (token!=EOL && token!=EOF && token!=LOGOUT) { error_msg = "Garbage after logout command"; goto error; } /* no referral for logout */ cmd_logout(sieved_out, sieved_in); ret = TRUE; goto done; break; case GETSCRIPT: if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "SPACE must occur after GETSCRIPT"; goto error; } if (timlex(&sieve_name, NULL, sieved_in)!=STRING) { error_msg = "Did not specify script name"; goto error; } if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } if(referral_host) goto do_referral; getscript(sieved_out, &sieve_name); break; case PUTSCRIPT: if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "SPACE must occur after PUTSCRIPT"; goto error; } if (timlex(&sieve_name, NULL, sieved_in)!=STRING) { error_msg = "Did not specify script name"; goto error; } if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "Expected SPACE"; goto error; } if (timlex(&sieve_data, NULL, sieved_in)!=STRING) { error_msg = "Did not specify legal script data length"; goto error; } if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } if(referral_host) goto do_referral; putscript(sieved_out, &sieve_name, &sieve_data, verify_only); break; case SETACTIVE: if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "SPACE must occur after SETACTIVE"; goto error; } if (timlex(&sieve_name, NULL, sieved_in)!=STRING) { error_msg = "Did not specify script name"; goto error; } if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } if(referral_host) goto do_referral; setactive(sieved_out, &sieve_name); break; case DELETESCRIPT: if (timlex(NULL, NULL, sieved_in)!=SPACE) { error_msg = "SPACE must occur after DELETESCRIPT"; goto error; } if (timlex(&sieve_name, NULL, sieved_in)!=STRING) { error_msg = "Did not specify script name"; goto error; } if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } if(referral_host) goto do_referral; deletescript(sieved_out, &sieve_name); break; case LISTSCRIPTS: if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } if(referral_host) goto do_referral; listscripts(sieved_out); break; case STARTTLS: if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } /* XXX discard any input pipelined after STARTTLS */ prot_flush(sieved_in); if(referral_host) goto do_referral; cmd_starttls(sieved_out, sieved_in); break; case NOOP: token = timlex(NULL, NULL, sieved_in); if (token != EOL) { /* optional string parameter */ if (token!=SPACE) { error_msg = "Expected SPACE"; goto error; } if (timlex(&sieve_name, NULL, sieved_in)!=STRING) { error_msg = "Expected string"; goto error; } token = timlex(NULL, NULL, sieved_in); } if (token != EOL) { error_msg = "Expected EOL"; goto error; } if (sieve_name.len) { prot_printf(sieved_out, "OK (TAG "); prot_printliteral(sieved_out, sieve_name.s, sieve_name.len); prot_printf(sieved_out, ") \"Done\"\r\n"); } else prot_printf(sieved_out, "OK \"Done\"\r\n"); break; case UNAUTHENTICATE: if (timlex(NULL, NULL, sieved_in)!=EOL) { error_msg = "Expected EOL"; goto error; } cmd_unauthenticate(sieved_out, sieved_in); break; default: error_msg="Expected a command. Got something else."; goto error; break; } done: /* free memory */ buf_free(&mechanism_name); buf_free(&initial_challenge); buf_free(&sieve_name); buf_free(&sieve_data); prot_flush(sieved_out); return ret; error: /* free memory */ buf_free(&mechanism_name); buf_free(&initial_challenge); buf_free(&sieve_name); buf_free(&sieve_data); prot_printf(sieved_out, "NO \"%s\"\r\n",error_msg); prot_flush(sieved_out); return FALSE; do_referral: { char buf[4096]; char *c; /* Truncate the hostname if necessary */ strlcpy(buf, referral_host, sizeof(buf)); c = strchr(buf, '!'); if(c) *c = '\0'; prot_printf(sieved_out, "BYE (REFERRAL \"sieve://%s\") \"Try Remote.\"\r\n", buf); ret = TRUE; goto done; } }
/* Called before a cyrus application starts (but after command line parameters * are read) */ EXPORTED int cyrus_init(const char *alt_config, const char *ident, unsigned flags, int config_need_data) { char *p; const char *val; const char *prefix; int umaskval = 0; int syslog_opts = LOG_PID; const char *facility; if(cyrus_init_run != NOT_RUNNING) { fatal("cyrus_init called twice!", EC_CONFIG); } else { cyrus_init_run = RUNNING; } cyrus_init_nodb = (flags & CYRUSINIT_NODB); #ifdef LOG_PERROR if ((flags & CYRUSINIT_PERROR)) syslog_opts |= LOG_PERROR; #endif initialize_imap_error_table(); initialize_mupd_error_table(); if(!ident) fatal("service name was not specified to cyrus_init", EC_CONFIG); config_ident = ident; /* xxx we lose here since we can't have the prefix until we load the * config file */ openlog(config_ident, syslog_opts, SYSLOG_FACILITY); /* Load configuration file. This will set config_dir when it finds it */ config_read(alt_config, config_need_data); prefix = config_getstring(IMAPOPT_SYSLOG_PREFIX); facility = config_getstring(IMAPOPT_SYSLOG_FACILITY); /* Reopen the log with the new prefix, if needed */ if (prefix || facility) { char *ident_buf; int facnum = facility ? get_facility(facility) : SYSLOG_FACILITY; if (prefix) ident_buf = strconcat(prefix, "/", ident, (char *)NULL); else ident_buf = xstrdup(ident); closelog(); openlog(ident_buf, syslog_opts, facnum); /* don't free the openlog() string! */ } /* allow debug logging */ if (!config_debug) setlogmask(~LOG_MASK(LOG_DEBUG)); /* Look up default partition */ config_defpartition = config_getstring(IMAPOPT_DEFAULTPARTITION); for (p = (char *)config_defpartition; p && *p; p++) { if (!Uisalnum(*p)) fatal("defaultpartition option contains non-alphanumeric character", EC_CONFIG); if (Uisupper(*p)) *p = tolower((unsigned char) *p); } /* Look up umask */ val = config_getstring(IMAPOPT_UMASK); while (*val) { if (*val >= '0' && *val <= '7') umaskval = umaskval*8 + *val - '0'; val++; } umask(umaskval); config_fulldirhash = config_getswitch(IMAPOPT_FULLDIRHASH); /* look up and canonify the implicit rights of mailbox owners */ cyrus_acl_strtomask(config_getstring(IMAPOPT_IMPLICIT_OWNER_RIGHTS), &config_implicitrights); /* XXX and if strtomask fails? */ config_metapartition_files = config_getbitfield(IMAPOPT_METAPARTITION_FILES); val = config_getstring(IMAPOPT_SUPPRESS_CAPABILITIES); if (val) suppressed_capabilities = strarray_split(val, NULL, 0); if (config_getswitch(IMAPOPT_SEARCH_SKIPDIACRIT)) charset_flags |= CHARSET_SKIPDIACRIT; switch (config_getenum(IMAPOPT_SEARCH_WHITESPACE)) { case IMAP_ENUM_SEARCH_WHITESPACE_MERGE: charset_flags |= CHARSET_MERGESPACE; break; case IMAP_ENUM_SEARCH_WHITESPACE_SKIP: charset_flags |= CHARSET_SKIPSPACE; break; default: break; } if (config_getswitch(IMAPOPT_SEARCH_SKIPHTML)) charset_flags |= CHARSET_SKIPHTML; if (config_getswitch(IMAPOPT_RFC2047_UTF8)) charset_flags |= CHARSET_MIME_UTF8; /* Set snippet conversion flags. */ charset_snippet_flags = CHARSET_SNIPPET; if (config_getenum(IMAPOPT_SEARCH_ENGINE) != IMAP_ENUM_SEARCH_ENGINE_XAPIAN) { /* All search engines other than Xapian require escaped HTML */ charset_snippet_flags |= CHARSET_ESCAPEHTML; } if (!cyrus_init_nodb) { /* lookup the database backends */ config_mboxlist_db = config_getstring(IMAPOPT_MBOXLIST_DB); config_quota_db = config_getstring(IMAPOPT_QUOTA_DB); config_subscription_db = config_getstring(IMAPOPT_SUBSCRIPTION_DB); config_annotation_db = config_getstring(IMAPOPT_ANNOTATION_DB); config_seenstate_db = config_getstring(IMAPOPT_SEENSTATE_DB); config_mboxkey_db = config_getstring(IMAPOPT_MBOXKEY_DB); config_duplicate_db = config_getstring(IMAPOPT_DUPLICATE_DB); config_tls_sessions_db = config_getstring(IMAPOPT_TLS_SESSIONS_DB); config_ptscache_db = config_getstring(IMAPOPT_PTSCACHE_DB); config_statuscache_db = config_getstring(IMAPOPT_STATUSCACHE_DB); config_userdeny_db = config_getstring(IMAPOPT_USERDENY_DB); config_zoneinfo_db = config_getstring(IMAPOPT_ZONEINFO_DB); config_conversations_db = config_getstring(IMAPOPT_CONVERSATIONS_DB); config_backup_db = config_getstring(IMAPOPT_BACKUP_DB); /* configure libcyrus as needed */ libcyrus_config_setstring(CYRUSOPT_CONFIG_DIR, config_dir); libcyrus_config_setswitch(CYRUSOPT_AUTH_UNIX_GROUP_ENABLE, config_getswitch(IMAPOPT_UNIX_GROUP_ENABLE)); libcyrus_config_setswitch(CYRUSOPT_USERNAME_TOLOWER, config_getswitch(IMAPOPT_USERNAME_TOLOWER)); libcyrus_config_setswitch(CYRUSOPT_SKIPLIST_UNSAFE, config_getswitch(IMAPOPT_SKIPLIST_UNSAFE)); libcyrus_config_setstring(CYRUSOPT_TEMP_PATH, config_getstring(IMAPOPT_TEMP_PATH)); libcyrus_config_setint(CYRUSOPT_PTS_CACHE_TIMEOUT, config_getint(IMAPOPT_PTSCACHE_TIMEOUT)); libcyrus_config_setswitch(CYRUSOPT_FULLDIRHASH, config_getswitch(IMAPOPT_FULLDIRHASH)); libcyrus_config_setstring(CYRUSOPT_PTSCACHE_DB, config_getstring(IMAPOPT_PTSCACHE_DB)); libcyrus_config_setstring(CYRUSOPT_PTSCACHE_DB_PATH, config_getstring(IMAPOPT_PTSCACHE_DB_PATH)); libcyrus_config_setstring(CYRUSOPT_PTLOADER_SOCK, config_getstring(IMAPOPT_PTLOADER_SOCK)); libcyrus_config_setswitch(CYRUSOPT_VIRTDOMAINS, config_getenum(IMAPOPT_VIRTDOMAINS)); libcyrus_config_setstring(CYRUSOPT_AUTH_MECH, config_getstring(IMAPOPT_AUTH_MECH)); libcyrus_config_setstring(CYRUSOPT_DELETERIGHT, config_getstring(IMAPOPT_DELETERIGHT)); libcyrus_config_setstring(CYRUSOPT_SQL_DATABASE, config_getstring(IMAPOPT_SQL_DATABASE)); libcyrus_config_setstring(CYRUSOPT_SQL_ENGINE, config_getstring(IMAPOPT_SQL_ENGINE)); libcyrus_config_setstring(CYRUSOPT_SQL_HOSTNAMES, config_getstring(IMAPOPT_SQL_HOSTNAMES)); libcyrus_config_setstring(CYRUSOPT_SQL_USER, config_getstring(IMAPOPT_SQL_USER)); libcyrus_config_setstring(CYRUSOPT_SQL_PASSWD, config_getstring(IMAPOPT_SQL_PASSWD)); libcyrus_config_setswitch(CYRUSOPT_SQL_USESSL, config_getswitch(IMAPOPT_SQL_USESSL)); libcyrus_config_setswitch(CYRUSOPT_SKIPLIST_ALWAYS_CHECKPOINT, config_getswitch(IMAPOPT_SKIPLIST_ALWAYS_CHECKPOINT)); /* Not until all configuration parameters are set! */ libcyrus_init(); } return 0; }
/* * Performs a STATUS command - note: state MAY be NULL here. */ int status_lookup(const char *mboxname, const char *userid, unsigned statusitems, struct statusdata *sdata) { struct mailbox *mailbox = NULL; unsigned numrecent = 0; unsigned numunseen = 0; unsigned c_statusitems; int r; /* Check status cache if possible */ if (config_getswitch(IMAPOPT_STATUSCACHE)) { /* Do actual lookup of cache item. */ r = statuscache_lookup(mboxname, userid, statusitems, sdata); /* Seen/recent status uses "push" invalidation events from * seen_db.c. This avoids needing to open cyrus.header to get * the mailbox uniqueid to open the seen db and get the * unseen_mtime and recentuid. */ if (!r) { syslog(LOG_DEBUG, "statuscache, '%s', '%s', '0x%02x', 'yes'", mboxname, userid, statusitems); return 0; } syslog(LOG_DEBUG, "statuscache, '%s', '%s', '0x%02x', 'no'", mboxname, userid, statusitems); } /* Missing or invalid cache entry */ r = mailbox_open_irl(mboxname, &mailbox); if (r) return r; /* We always have message count, uidnext, uidvalidity, and highestmodseq for cache */ c_statusitems = STATUS_MESSAGES | STATUS_UIDNEXT | STATUS_UIDVALIDITY | STATUS_HIGHESTMODSEQ; if (!mailbox->i.exists) { /* no messages, so these two must also be zero */ c_statusitems |= STATUS_RECENT | STATUS_UNSEEN; } else if (statusitems & (STATUS_RECENT | STATUS_UNSEEN)) { /* Read \Seen state */ struct seqset *seq = NULL; uint32_t recno; struct index_record record; int internalseen = mailbox_internal_seen(mailbox, userid); unsigned recentuid; if (internalseen) { recentuid = mailbox->i.recentuid; } else { struct seen *seendb = NULL; struct seendata sd = SEENDATA_INITIALIZER; r = seen_open(userid, SEEN_CREATE, &seendb); if (!r) r = seen_read(seendb, mailbox->uniqueid, &sd); seen_close(&seendb); if (r) goto done; recentuid = sd.lastuid; seq = seqset_parse(sd.seenuids, NULL, recentuid); seen_freedata(&sd); } for (recno = 1; recno <= mailbox->i.num_records; recno++) { if (mailbox_read_index_record(mailbox, recno, &record)) continue; if (record.system_flags & FLAG_EXPUNGED) continue; if (record.uid > recentuid) numrecent++; if (internalseen) { if (!(record.system_flags & FLAG_SEEN)) numunseen++; } else { if (!seqset_ismember(seq, record.uid)) numunseen++; } } /* we've calculated the correct values for both */ c_statusitems |= STATUS_RECENT | STATUS_UNSEEN; } statuscache_fill(sdata, userid, mailbox, c_statusitems, numrecent, numunseen); /* cache the new value while unlocking */ mailbox_unlock_index(mailbox, sdata); done: mailbox_close(&mailbox); return r; }
static void replica_connect(const char *channel) { int wait; struct protoent *proto; sasl_callback_t *cb; int timeout; const char *port, *auth_status = NULL; cb = mysasl_callbacks(NULL, get_config(channel, "sync_authname"), get_config(channel, "sync_realm"), get_config(channel, "sync_password")); /* get the right port */ port = get_config(channel, "sync_port"); if (port) { imap_csync_protocol.service = port; csync_protocol.service = port; } for (wait = 15;; wait *= 2) { sync_backend = backend_connect(sync_backend, servername, &imap_csync_protocol, "", cb, &auth_status, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend) { if (sync_backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ sync_backend->in->userdata = sync_backend->out->userdata = &tagbuf; break; } else { backend_disconnect(sync_backend); sync_backend = NULL; } } sync_backend = backend_connect(sync_backend, servername, &csync_protocol, "", cb, NULL, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend || auth_status || connect_once || wait > 1000) break; fprintf(stderr, "Can not connect to server '%s', retrying in %d seconds\n", servername, wait); sleep(wait); } free_callbacks(cb); cb = NULL; if (!sync_backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); _exit(1); } /* Disable Nagle's Algorithm => increase throughput * * http://en.wikipedia.org/wiki/Nagle's_algorithm */ if (servername[0] != '/') { if (sync_backend->sock >= 0 && (proto = getprotobyname("tcp")) != NULL) { int on = 1; if (setsockopt(sync_backend->sock, proto->p_proto, TCP_NODELAY, (void *) &on, sizeof(on)) != 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_NODELAY): %m"); } /* turn on TCP keepalive if set */ if (config_getswitch(IMAPOPT_TCP_KEEPALIVE)) { int r; int optval = 1; socklen_t optlen = sizeof(optval); struct protoent *proto = getprotobyname("TCP"); r = setsockopt(sync_backend->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(SO_KEEPALIVE): %m"); } #ifdef TCP_KEEPCNT optval = config_getint(IMAPOPT_TCP_KEEPALIVE_CNT); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPCNT, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPCNT): %m"); } } #endif #ifdef TCP_KEEPIDLE optval = config_getint(IMAPOPT_TCP_KEEPALIVE_IDLE); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPIDLE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPIDLE): %m"); } } #endif #ifdef TCP_KEEPINTVL optval = config_getint(IMAPOPT_TCP_KEEPALIVE_INTVL); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPINTVL, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPINTVL): %m"); } } #endif } } else { syslog(LOG_ERR, "unable to getprotobyname(\"tcp\"): %m"); } } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(sync_backend, CAPA_COMPRESS)) { prot_printf(sync_backend->out, "%s\r\n", sync_backend->prot->u.std.compress_cmd.cmd); prot_flush(sync_backend->out); if (sync_parse_response("COMPRESS", sync_backend->in, NULL)) { if (do_compress) fatal("Failed to enable compression, aborting", EC_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(sync_backend->in); prot_setcompress(sync_backend->out); } } else if (do_compress) fatal("Backend does not support compression, aborting", EC_SOFTWARE); #endif /* links to sockets */ sync_in = sync_backend->in; sync_out = sync_backend->out; if (verbose > 1) { prot_setlog(sync_in, fileno(stderr)); prot_setlog(sync_out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(sync_in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(sync_in, 1); prot_setisclient(sync_out, 1); }
/* return malloc'd string containing the address */ static char *parseaddr(char *s) { char *p, *ret; int len; int lmtp_strict_rfc2821 = config_getswitch(IMAPOPT_LMTP_STRICT_RFC2821); p = s; if (*p++ != '<') return 0; /* at-domain-list */ while (*p == '@') { p++; if (*p == '[') { p++; while (Uisdigit(*p) || *p == '.') p++; if (*p++ != ']') return 0; } else { while (Uisalnum(*p) || *p == '.' || *p == '-') p++; } if (*p == ',' && p[1] == '@') p++; else if (*p == ':' && p[1] != '@') p++; else return 0; } /* local-part */ if (*p == '\"') { p++; while (*p && *p != '\"') { if (*p == '\\') { if (!*++p) return 0; } p++; } if (!*p++) return 0; } else { while (*p && *p != '@' && *p != '>') { if (*p == '\\') { if (!*++p) return 0; } else { if (*p & 128 && !lmtp_strict_rfc2821) { /* this prevents us from becoming a backscatter source if our MTA allows 8bit in local-part of adresses. */ *p = 'X'; } if (*p <= ' ' || (*p & 128) || strchr("<>()[]\\,;:\"", *p)) return 0; } p++; } } /* @domain */ if (*p == '@') { p++; if (*p == '[') { p++; while (Uisdigit(*p) || *p == '.') p++; if (*p++ != ']') return 0; } else { while (Uisalnum(*p) || *p == '.' || *p == '-') p++; } } if (*p++ != '>') return 0; if (*p && *p != ' ') return 0; len = p - s; ret = xmalloc(len + 1); memcpy(ret, s, len); ret[len] = '\0'; return ret; }
int main(int argc,char **argv) { int opt; int i; int fflag = 0; int r, code = 0; int do_report = 1; char *alt_config = NULL, *domain = NULL; if ((geteuid()) == 0 && (become_cyrus(/*is_master*/0) != 0)) { fatal("must run as the Cyrus user", EC_USAGE); } while ((opt = getopt(argc, argv, "C:d:fqZ")) != EOF) { switch (opt) { case 'C': /* alt config file */ alt_config = optarg; break; case 'q': do_report = 0; break; case 'd': domain = optarg; break; case 'f': fflag = 1; break; /* deliberately undocumented option for testing */ case 'Z': test_sync_mode = 1; break; default: usage(); } } /* always report if not fixing, otherwise we do nothing */ if (!fflag) do_report = 1; cyrus_init(alt_config, "quota", 0, CONFIG_NEED_PARTITION_DATA); /* Set namespace -- force standard (internal) */ if ((r = mboxname_init_namespace("a_namespace, 1)) != 0) { syslog(LOG_ERR, "%s", error_message(r)); fatal(error_message(r), EC_CONFIG); } if (config_getswitch(IMAPOPT_IMPROVED_MBOXLIST_SORT)) compar = bsearch_compare_mbox; else compar = strcmp; /* * Lock mailbox list to prevent mailbox creation/deletion * during work */ mboxlist_init(0); mboxlist_open(NULL); quotadb_init(0); quotadb_open(NULL); quota_changelock(); if (!r) r = buildquotalist(domain, argv+optind, argc-optind); if (!r && fflag) r = fixquotas(domain, argv+optind, argc-optind); quota_changelockrelease(); if (r) code = convert_code(r); else if (do_report) reportquota(); quotadb_close(); quotadb_done(); mboxlist_close(); mboxlist_done(); /* just for neatness */ for (i = 0; i < quota_num; i++) free(quotaroots[i].name); free(quotaroots); cyrus_done(); return code; }
static int autocreate_sieve(const char *userid, const char *source_script) { /* XXX - this is really ugly, but too much work to tidy up right now -- Bron */ sieve_script_t *s = NULL; bytecode_info_t *bc = NULL; char *err = NULL; FILE *in_stream, *out_fp; int out_fd, in_fd, r, k; int do_compile = 0; const char *compiled_source_script = NULL; const char *sievename = get_script_name(source_script); const char *sieve_script_dir = NULL; char sieve_script_name[MAX_FILENAME]; char sieve_bcscript_name[MAX_FILENAME]; char sieve_default[MAX_FILENAME]; char sieve_tmpname[MAX_FILENAME]; char sieve_bctmpname[MAX_FILENAME]; char sieve_bclink_name[MAX_FILENAME]; char buf[4096]; mode_t oldmask; struct stat statbuf; /* We don't support using the homedirectory, like timsieved */ if (config_getswitch(IMAPOPT_SIEVEUSEHOMEDIR)) { syslog(LOG_WARNING,"autocreate_sieve: autocreate_sieve does not work with sieveusehomedir option in imapd.conf"); return 1; } /* Check if sievedir is defined in imapd.conf */ if(!config_getstring(IMAPOPT_SIEVEDIR)) { syslog(LOG_WARNING, "autocreate_sieve: sievedir option is not defined. Check imapd.conf"); return 1; } /* Check if autocreate_sieve_compiledscript is defined in imapd.conf */ if(!(compiled_source_script = config_getstring(IMAPOPT_AUTOCREATE_SIEVE_SCRIPT_COMPILED))) { syslog(LOG_WARNING, "autocreate_sieve: autocreate_sieve_compiledscript option is not defined. Compiling it"); do_compile = 1; } if (!(sieve_script_dir = user_sieve_path(userid))) { syslog(LOG_WARNING, "autocreate_sieve: unable to determine sieve directory for user %s", userid); return 1; } if(snprintf(sieve_tmpname, MAX_FILENAME, "%s/%s.script.NEW",sieve_script_dir, sievename) >= MAX_FILENAME) { syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid); return 1; } if(snprintf(sieve_bctmpname, MAX_FILENAME, "%s/%s.bc.NEW",sieve_script_dir, sievename) >= MAX_FILENAME) { syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid); return 1; } if(snprintf(sieve_script_name, MAX_FILENAME, "%s/%s.script",sieve_script_dir, sievename) >= MAX_FILENAME) { syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid); return 1; } if(snprintf(sieve_bcscript_name, MAX_FILENAME, "%s/%s.bc",sieve_script_dir, sievename) >= MAX_FILENAME) { syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid); return 1; } if(snprintf(sieve_default, MAX_FILENAME, "%s/%s",sieve_script_dir,"defaultbc") >= MAX_FILENAME) { syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid); return 1; } /* XXX no directory? umm */ if(snprintf(sieve_bclink_name, MAX_FILENAME, "%s.bc", sievename) >= MAX_FILENAME) { syslog(LOG_WARNING, "autocreate_sieve: Invalid sieve path %s, %s, %s", sieve_script_dir, sievename, userid); return 1; } /* Check if a default sieve filter alrady exists */ if(!stat(sieve_default,&statbuf)) { syslog(LOG_WARNING,"autocreate_sieve: Default sieve script already exists"); return 1; } /* Open the source script. if there is a problem with that exit */ in_stream = fopen(source_script, "r"); if(!in_stream) { syslog(LOG_WARNING,"autocreate_sieve: Unable to open sieve script %s. Check permissions",source_script); return 1; } /* * At this point we start the modifications of the filesystem */ /* Create the directory where the sieve scripts will reside */ r = cyrus_mkdir(sieve_bctmpname, 0755); if(r == -1) { /* If this fails we just leave */ fclose(in_stream); return 1; } /* * We open the file that will be used as the bc file. If this file exists, overwrite it * since something bad has happened. We open the file here so that this error checking is * done before we try to open the rest of the files to start copying etc. */ out_fd = open(sieve_bctmpname, O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if(out_fd < 0) { if(errno == EEXIST) { syslog(LOG_WARNING,"autocreate_sieve: File %s already exists. Probaly left over. Ignoring",sieve_bctmpname); } else if (errno == EACCES) { syslog(LOG_WARNING,"autocreate_sieve: No access to create file %s. Check permissions",sieve_bctmpname); fclose(in_stream); return 1; } else { syslog(LOG_WARNING,"autocreate_sieve: Unable to create %s: %m",sieve_bctmpname); fclose(in_stream); return 1; } } if(!do_compile && compiled_source_script && (in_fd = open(compiled_source_script, O_RDONLY)) != -1) { while((r = read(in_fd, buf, sizeof(buf))) > 0) { if((k=write(out_fd, buf,r)) < 0) { syslog(LOG_WARNING, "autocreate_sieve: Error writing to file %s: %m", sieve_bctmpname); close(out_fd); close(in_fd); fclose(in_stream); unlink(sieve_bctmpname); return 1; } } if(r == 0) { /* EOF */ xclose(out_fd); xclose(in_fd); } else if (r < 0) { syslog(LOG_WARNING, "autocreate_sieve: Error reading compiled script file %s: %m. Will try to compile it", compiled_source_script); xclose(in_fd); do_compile = 1; if(lseek(out_fd, 0, SEEK_SET)) { syslog(LOG_WARNING, "autocreate_sieve: Major IO problem (lseek: %m). Aborting"); xclose(out_fd); return 1; } } xclose(in_fd); } else { if(compiled_source_script) syslog(LOG_WARNING,"autocreate_sieve: Problem opening compiled script file: %s. Compiling it", compiled_source_script); do_compile = 1; } /* Because we failed to open a precompiled bc sieve script, we compile one */ if(do_compile) { if(is_script_parsable(in_stream,&err, &s) == TIMSIEVE_FAIL) { if(err && *err) { syslog(LOG_WARNING,"autocreate_sieve: Error while parsing script %s.",err); free(err); } else syslog(LOG_WARNING,"autocreate_sieve: Error while parsing script"); unlink(sieve_bctmpname); fclose(in_stream); close(out_fd); return 1; } /* generate the bytecode */ if(sieve_generate_bytecode(&bc, s) == TIMSIEVE_FAIL) { syslog(LOG_WARNING,"autocreate_sieve: problem compiling sieve script"); /* removing the copied script and cleaning up memory */ unlink(sieve_bctmpname); sieve_script_free(&s); fclose(in_stream); close(out_fd); return 1; } if(sieve_emit_bytecode(out_fd, bc) == TIMSIEVE_FAIL) { syslog(LOG_WARNING,"autocreate_sieve: problem emiting sieve script"); /* removing the copied script and cleaning up memory */ unlink(sieve_bctmpname); sieve_free_bytecode(&bc); sieve_script_free(&s); fclose(in_stream); close(out_fd); return 1; } /* clean up the memory */ sieve_free_bytecode(&bc); sieve_script_free(&s); } xclose(out_fd); rewind(in_stream); /* Copy the initial script */ oldmask = umask(077); if((out_fp = fopen(sieve_tmpname, "w")) == NULL) { syslog(LOG_WARNING,"autocreate_sieve: Unable to open destination sieve script %s: %m", sieve_tmpname); unlink(sieve_bctmpname); umask(oldmask); fclose(in_stream); return 1; } umask(oldmask); while((r = fread(buf,sizeof(char), sizeof(buf), in_stream)) > 0) { if( fwrite(buf,sizeof(char), r, out_fp) != (unsigned)r) { syslog(LOG_WARNING,"autocreate_sieve: Problem writing to sieve script file %s: %m",sieve_tmpname); fclose(out_fp); unlink(sieve_tmpname); unlink(sieve_bctmpname); fclose(in_stream); return 1; } } if(feof(in_stream)) { fclose(out_fp); fclose(in_stream); } else { /* ferror */ fclose(out_fp); unlink(sieve_tmpname); unlink(sieve_bctmpname); fclose(in_stream); return 1; } /* Renaming the necessary stuff */ if(rename(sieve_tmpname, sieve_script_name)) { unlink(sieve_tmpname); unlink(sieve_bctmpname); return 1; } if(rename(sieve_bctmpname, sieve_bcscript_name)) { unlink(sieve_bctmpname); unlink(sieve_bcscript_name); return 1; } /* end now with the symlink */ if(symlink(sieve_bclink_name, sieve_default)) { if(errno != EEXIST) { syslog(LOG_WARNING, "autocreate_sieve: problem making the default link (symlink: %m)."); /* Lets delete the files */ unlink(sieve_script_name); unlink(sieve_bcscript_name); } } /* * If everything has succeeded AND we have compiled the script AND we have requested * to generate the global script so that it is not compiled each time then we create it. */ if(do_compile && config_getswitch(IMAPOPT_AUTOCREATE_SIEVE_SCRIPT_COMPILE)) { if(!compiled_source_script) { syslog(LOG_WARNING, "autocreate_sieve: To save a compiled sieve script, autocreate_sieve_compiledscript must have been defined in imapd.conf"); return 0; } if(snprintf(sieve_tmpname, MAX_FILENAME, "%s.NEW", compiled_source_script) >= MAX_FILENAME) return 0; /* * Copy everything from the newly created bc sieve sieve script. */ if((in_fd = open(sieve_bcscript_name, O_RDONLY))<0) { return 0; } if((out_fd = open(sieve_tmpname, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) { if(errno == EEXIST) { /* Someone is already doing this so just bail out. */ syslog(LOG_WARNING, "autocreate_sieve: %s already exists. Some other instance processing it, or it is left over", sieve_tmpname); close(in_fd); return 0; } else if (errno == EACCES) { syslog(LOG_WARNING,"autocreate_sieve: No access to create file %s. Check permissions",sieve_tmpname); close(in_fd); return 0; } else { syslog(LOG_WARNING,"autocreate_sieve: Unable to create %s: %m",sieve_tmpname); close(in_fd); return 0; } } while((r = read(in_fd, buf, sizeof(buf))) > 0) { if((k = write(out_fd,buf,r)) < 0) { syslog(LOG_WARNING, "autocreate_sieve: Error writing to file: %s: %m", sieve_tmpname); close(out_fd); close(in_fd); unlink(sieve_tmpname); return 0; } } if(r == 0 ) { /*EOF */ xclose(out_fd); xclose(in_fd); } else if (r < 0) { syslog(LOG_WARNING, "autocreate_sieve: Error reading file: %s: %m", sieve_bcscript_name); xclose(out_fd); xclose(in_fd); unlink(sieve_tmpname); return 0; } /* Rename the temporary created sieve script to its final name. */ if(rename(sieve_tmpname, compiled_source_script)) { if(errno != EEXIST) { unlink(sieve_tmpname); unlink(compiled_source_script); } return 0; } syslog(LOG_NOTICE, "autocreate_sieve: Compiled sieve script was successfully saved in %s", compiled_source_script); } return 0; }