int statuscache_lookup(const char *mboxname, const char *userid, unsigned statusitems, struct statusdata *sdata) { size_t keylen, datalen; int r = 0; const char *data = NULL, *dend; char *p, *key = statuscache_buildkey(mboxname, userid, &keylen); unsigned version; /* Don't access DB if it hasn't been opened */ if (!statuscache_dbopen) return IMAP_NO_NOSUCHMSG; memset(sdata, 0, sizeof(struct statusdata)); /* Check if there is an entry in the database */ do { r = cyrusdb_fetch(statuscachedb, key, keylen, &data, &datalen, NULL); } while (r == CYRUSDB_AGAIN); if (r || !data || ((size_t) datalen < sizeof(unsigned))) { return IMAP_NO_NOSUCHMSG; } dend = data + datalen; version = (unsigned) strtoul(data, &p, 10); if (version != (unsigned) STATUSCACHE_VERSION) { /* Wrong version */ return IMAP_NO_NOSUCHMSG; } if (p < dend) sdata->statusitems = strtoul(p, &p, 10); if (p < dend) sdata->messages = strtoul(p, &p, 10); if (p < dend) sdata->recent = strtoul(p, &p, 10); if (p < dend) sdata->uidnext = strtoul(p, &p, 10); if (p < dend) sdata->uidvalidity = strtoul(p, &p, 10); if (p < dend) sdata->unseen = strtoul(p, &p, 10); if (p < dend) sdata->highestmodseq = strtoull(p, &p, 10); /* Sanity check the data */ if (!sdata->statusitems || !sdata->uidnext || !sdata->uidvalidity) { return IMAP_NO_NOSUCHMSG; } if ((sdata->statusitems & statusitems) != statusitems) { /* Don't have all of the requested information */ return IMAP_NO_NOSUCHMSG; } return 0; }
/* * Find the mailbox 'name' 's quotaroot, and return it in 'ret'. * 'ret' must be at least MAX_MAILBOX_NAME. * * returns true if a quotaroot is found, 0 otherwise. */ EXPORTED int quota_findroot(char *ret, size_t retlen, const char *name) { char *tail, *p, *mbox; strlcpy(ret, name, retlen); /* find the start of the unqualified mailbox name */ mbox = (config_virtdomains && (p = strchr(ret, '!'))) ? p+1 : ret; tail = mbox + strlen(mbox); while (cyrusdb_fetch(qdb, ret, strlen(ret), NULL, NULL, NULL)) { tail = strrchr(mbox, '.'); if (!tail) break; *tail = '\0'; } if (tail) return 1; if (mbox == ret) return 0; /* check for a domain quota */ *mbox = '\0'; return (cyrusdb_fetch(qdb, ret, strlen(ret), NULL, NULL, NULL) == 0); }
static int mboxkey_readit(struct mboxkey *mboxkeydb, const char *mailbox, const char **mboxkey, size_t *mboxkeylen, int rw) { int r; const char *data; size_t datalen; unsigned short version, s; assert(mboxkeydb && mailbox); if (rw || mboxkeydb->tid) { r = cyrusdb_fetchlock(mboxkeydb->db, mailbox, strlen(mailbox), &data, &datalen, &mboxkeydb->tid); } else { r = cyrusdb_fetch(mboxkeydb->db, mailbox, strlen(mailbox), &data, &datalen, NULL); } switch (r) { case 0: break; case CYRUSDB_AGAIN: syslog(LOG_DEBUG, "deadlock in mboxkey database for '%s/%s'", mboxkeydb->user, mailbox); return IMAP_AGAIN; break; case CYRUSDB_IOERROR: syslog(LOG_ERR, "DBERROR: error fetching txn %s", cyrusdb_strerror(r)); return IMAP_IOERROR; break; case CYRUSDB_NOTFOUND: *mboxkey = NULL; *mboxkeylen = 0; return 0; break; } /* 'data' is <version><mboxkey> */ memcpy(&s, data, sizeof(s)); version = ntohs(s); assert(version == MBOXKEY_VERSION); *mboxkey = data + sizeof(s); *mboxkeylen = datalen - sizeof(s); return 0; }
static int seen_readit(struct seen *seendb, const char *uniqueid, struct seendata *sd, int rw) { int r; const char *data; size_t datalen; assert(seendb && uniqueid); if (rw || seendb->tid) { r = cyrusdb_fetchlock(seendb->db, uniqueid, strlen(uniqueid), &data, &datalen, &seendb->tid); } else { r = cyrusdb_fetch(seendb->db, uniqueid, strlen(uniqueid), &data, &datalen, NULL); } switch (r) { case 0: break; case CYRUSDB_AGAIN: syslog(LOG_DEBUG, "deadlock in seen database for '%s/%s'", seendb->user, uniqueid); return IMAP_AGAIN; break; case CYRUSDB_NOTFOUND: memset(sd, 0, sizeof(struct seendata)); sd->seenuids = xstrdup(""); return 0; break; default: syslog(LOG_ERR, "DBERROR: error fetching txn %s", cyrusdb_strerror(r)); return IMAP_IOERROR; break; } parse_data(data, datalen, sd); if (sd->seenuids[0] && !imparse_issequence(sd->seenuids)) { syslog(LOG_ERR, "DBERROR: invalid sequence <%s> for %s %s - nuking", sd->seenuids, seendb->user, uniqueid); free(sd->seenuids); sd->seenuids = xstrdup(""); } return 0; }
/* * Read the quota entry 'quota' */ EXPORTED int quota_read(struct quota *quota, struct txn **tid, int wrlock) { int r; size_t qrlen; const char *data; size_t datalen; if (!quota->root || !(qrlen = strlen(quota->root))) return IMAP_QUOTAROOT_NONEXISTENT; if (wrlock) r = cyrusdb_fetchlock(qdb, quota->root, qrlen, &data, &datalen, tid); else r = cyrusdb_fetch(qdb, quota->root, qrlen, &data, &datalen, tid); if (!datalen) /* zero byte file can cause no data to be mapped */ return IMAP_QUOTAROOT_NONEXISTENT; switch (r) { case CYRUSDB_OK: if (!*data) return IMAP_QUOTAROOT_NONEXISTENT; r = quota_parseval(data, datalen, quota, wrlock); if (r) { syslog(LOG_ERR, "DBERROR: error fetching quota " "root=<%s> value=<%s>", quota->root, data); return r; } break; case CYRUSDB_AGAIN: return IMAP_AGAIN; case CYRUSDB_NOTFOUND: return IMAP_QUOTAROOT_NONEXISTENT; } if (r) { syslog(LOG_ERR, "DBERROR: error fetching quota %s: %s", quota->root, cyrusdb_strerror(r)); return IMAP_IOERROR; } return 0; }
EXPORTED time_t duplicate_check(const duplicate_key_t *dkey) { struct buf key = BUF_INITIALIZER; int r; const char *data = NULL; size_t len = 0; time_t mark = 0; if (!duplicate_dbopen) return 0; r = make_key(&key, dkey); if (r) return 0; do { r = cyrusdb_fetch(dupdb, key.s, key.len, &data, &len, NULL); } while (r == CYRUSDB_AGAIN); if (!r && data) { assert((len == sizeof(time_t)) || (len == sizeof(time_t) + sizeof(unsigned long))); /* found the record */ memcpy(&mark, data, sizeof(time_t)); } else if (r != CYRUSDB_OK) { if (r != CYRUSDB_NOTFOUND) { syslog(LOG_ERR, "duplicate_check: error looking up %s/%s/%s: %s", dkey->id, dkey->to, dkey->date, cyrusdb_strerror(r)); } mark = 0; } #if DEBUG syslog(LOG_DEBUG, "duplicate_check: %-40s %-20s %-40s %ld", dkey->id, dkey->to, dkey->date, mark); #endif buf_free(&key); return mark; }
/* * userdeny() checks to see if 'user' is denied access to 'service' * Returns 1 if a matching deny entry exists in DB, otherwise returns 0. */ EXPORTED int userdeny(const char *user, const char *service, char *msgbuf, size_t bufsiz) { int r, ret = 0; /* allow access by default */ const char *data = NULL; size_t datalen; struct buf buf = BUF_INITIALIZER; char *wild = NULL; const char *msg = NULL; tok_t tok; char *pat; int not; if (!denydb) denydb_open(/*create*/0); if (!denydb) return 0; memset(&tok, 0, sizeof(tok)); /* fetch entry for user */ syslog(LOG_DEBUG, "fetching user_deny.db entry for '%s'", user); do { r = cyrusdb_fetch(denydb, user, strlen(user), &data, &datalen, NULL); } while (r == CYRUSDB_AGAIN); /* XXX Should we try to reopen the DB if we get IOERROR? This might be necessary when using SQL backend and we lose the connection. */ if (r || !data || !datalen) { /* ignore non-existent/empty entry, report all other errors */ if (r != CYRUSDB_NOTFOUND) { syslog(LOG_WARNING, "DENYDB_ERROR: error reading entry '%s': %s", user, cyrusdb_strerror(r)); } goto out; } buf_init_ro(&buf, data, datalen); /* parse the data */ r = parse_record(&buf, &wild, &msg); if (r) { syslog(LOG_WARNING, "DENYDB_ERROR: invalid entry for '%s'", user); goto out; } /* scan wildmat right to left for a match against our service */ syslog(LOG_DEBUG, "wild: '%s' service: '%s'", wild, service); tok_initm(&tok, wild, ",", 0); while ((pat = tok_next(&tok))) { /* XXX trim leading & trailing whitespace? */ /* is it a negated pattern? */ not = (*pat == '!'); if (not) ++pat; syslog(LOG_DEBUG, "pat %d:'%s'", not, pat); /* see if pattern matches our service */ if (wildmat(service, pat)) { /* match ==> we're done */ ret = !not; if (msgbuf) strlcpy(msgbuf, msg, bufsiz); break; } } out: tok_fini(&tok); buf_free(&buf); return ret; }
/* Returns 0 on success */ static int ptload(const char *identifier, struct auth_state **state) { struct auth_state *fetched = NULL; size_t id_len; const char *data = NULL; size_t dsize; const char *fname = NULL; char *tofree = NULL; struct db *ptdb; int s; struct sockaddr_un srvaddr; int r, rc=0; static char response[1024]; struct iovec iov[10]; int niov, n; unsigned int start; const char *config_dir = libcyrus_config_getstring(CYRUSOPT_CONFIG_DIR); /* xxx this sucks, but it seems to be the only way to satisfy the linker */ if(the_ptscache_db == NULL) { the_ptscache_db = libcyrus_config_getstring(CYRUSOPT_PTSCACHE_DB); } if(!state || *state) { fatal("bad state pointer passed to ptload()", EC_TEMPFAIL); } fname = libcyrus_config_getstring(CYRUSOPT_PTSCACHE_DB_PATH); if (!fname) { tofree = strconcat(config_dir, PTS_DBFIL, (char *)NULL); fname = tofree; } r = cyrusdb_open(the_ptscache_db, fname, CYRUSDB_CREATE, &ptdb); if (r != 0) { syslog(LOG_ERR, "DBERROR: opening %s: %s", fname, cyrusdb_strerror(ret)); free(tofree); *state = NULL; return -1; } free(tofree); tofree = NULL; id_len = strlen(identifier); if(id_len > PTS_DB_KEYSIZE) { syslog(LOG_ERR, "identifier too long in auth_newstate"); *state = NULL; return -1; } /* fetch the current record for the user */ r = cyrusdb_fetch(ptdb, identifier, id_len, &data, &dsize, NULL); if (r && r != CYRUSDB_NOTFOUND) { syslog(LOG_ERR, "auth_newstate: error fetching record: %s", cyrusdb_strerror(r)); rc = -1; goto done; } /* if it's expired (or nonexistent), * ask the ptloader to reload it and reread it */ fetched = (struct auth_state *) data; if(fetched) { time_t now = time(NULL); int timeout = libcyrus_config_getint(CYRUSOPT_PTS_CACHE_TIMEOUT); syslog(LOG_DEBUG, "ptload(): fetched cache record (%s)" \ "(mark %ld, current %ld, limit %ld)", identifier, fetched->mark, now, now - timeout); if (fetched->mark > (now - timeout)) { /* not expired; let's return it */ goto done; } } syslog(LOG_DEBUG, "ptload(): pinging ptloader"); s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == -1) { syslog(LOG_ERR, "ptload(): unable to create socket for ptloader: %m"); rc = -1; goto done; } fname = libcyrus_config_getstring(CYRUSOPT_PTLOADER_SOCK); if (!fname) { tofree = strconcat(config_dir, PTS_DBSOCKET, (char *)NULL); fname = tofree; } if (strlen(fname) >= sizeof(srvaddr.sun_path)) { syslog(LOG_ERR, "ptload(): socket filename %s too long for " SIZE_T_FMT "-byte buffer", fname, sizeof(srvaddr.sun_path)); rc = -1; goto done; } memset((char *)&srvaddr, 0, sizeof(srvaddr)); srvaddr.sun_family = AF_UNIX; strlcpy(srvaddr.sun_path, fname, sizeof(srvaddr.sun_path)); r = nb_connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr), PT_TIMEOUT_SEC); free(tofree); if (r == -1) { syslog(LOG_ERR, "ptload(): can't connect to ptloader server: %m"); close(s); rc = -1; goto done; } syslog(LOG_DEBUG, "ptload(): connected"); niov = 0; WRITEV_ADD_TO_IOVEC(iov, niov, (char *) &id_len, sizeof(id_len)); WRITEV_ADD_TO_IOVEC(iov, niov, (char *) identifier, id_len); if (timeout_select(s, TS_WRITE, PT_TIMEOUT_SEC) < 0) { syslog(LOG_ERR, "timeoutselect: writing to ptloader %m"); rc = -1; goto done; } retry_writev(s, iov, niov); syslog(LOG_DEBUG, "ptload sent data"); start = 0; while (start < sizeof(response) - 1) { if (timeout_select(s, TS_READ, PT_TIMEOUT_SEC) < 0) { syslog(LOG_ERR, "timeout_select: reading from ptloader: %m"); rc = -1; goto done; } n = read(s, response+start, sizeof(response) - 1 - start); if (n < 1) break; start += n; } response[sizeof(response)-1] = '\0'; close(s); syslog(LOG_DEBUG, "ptload read data back"); if (start <= 1 || strncmp(response, "OK", 2)) { if(start > 1) { syslog(LOG_ERR, "ptload(): bad response from ptloader server: %s", response); } else { syslog(LOG_ERR, "ptload(): empty response from ptloader server"); } rc = -1; goto done; } /* fetch the current record for the user */ r = cyrusdb_fetch(ptdb, identifier, id_len, &data, &dsize, NULL); if (r != 0 || !data) { syslog(LOG_ERR, "ptload(): error fetching record: %s" "(did ptloader add the record?)", cyrusdb_strerror(r)); data = NULL; rc = -1; goto done; } done: /* ok, we got real data, let's use it */ if (data != NULL) { fetched = (struct auth_state *) data; } if (fetched == NULL) { *state = NULL; syslog(LOG_DEBUG, "No data available at all from ptload()"); } else { /* copy it into our structure */ *state = (struct auth_state *)xmalloc(dsize); memcpy(*state, fetched, dsize); syslog(LOG_DEBUG, "ptload returning data"); } /* close and unlock the database */ (cyrusdb_close)(ptdb); return rc; }
int main(int argc, char *argv[]) { int iter; int seed; int i; char *key; char *val; struct db *db; int r; struct txn *txn; const char *data; int datalen; struct timeval t1, t2; int initsize; if (argc > 1) { iter = atoi(argv[1]); } else { printf("%s [iterations] [rndseed] [initsize]\n", argv[0]); printf("if iterations is negative, run forever and report every -iter\n"); exit(1); } TRY(DB->init(".", 0)); if (argc > 2) { srand(atoi(argv[2])); } TRY(cyrusdb_open(DB, "scratch", &db)); if (cyrusdb_consistent) { TRY(cyrusdb_consistent(db)); } if (argc > 3) { initsize = atoi(argv[3]); txn = NULL; for (i = 0; i < initsize; i++) { /* generate a random key */ key = genrand(10 + (rand() % 10)); /* generate a random value */ val = genrand(10 + (rand() % 100)); TRY(cyrusdb_store(db, key, strlen(key), val, strlen(val), &txn)); } TRY(cyrusdb_commit(db, txn)); if (cyrusdb_consistent) { TRY(cyrusdb_consistent(db)); } } printf("starting...\n"); /* repeat for ever if iter < 0 */ for (i = 0; iter > 0 ? (i < iter) : 1; i++) { int oper = rand() % 10; if (i > 0 && iter < 0 && ((i % -iter) == 0)) { do_report(); } switch (oper) { case 0: /* do an ADD */ if (verbose) printf("A"); /* insert it */ gettimeofday(&t1, NULL); /* generate a random key */ key = genrand(10 + (rand() % 10)); /* generate a random value */ val = genrand(10 + (rand() % 100)); txn = NULL; TRY(cyrusdb_store(db, key, strlen(key), val, strlen(val), &txn)); TRY(cyrusdb_commit(db, txn)); gettimeofday(&t2, NULL); ADDDIFF(t_add, t1, t2); c_add++; free(key); free(val); break; case 1: /* do a modify */ if (verbose) printf("M"); gettimeofday(&t1, NULL); /* pick a random victim */ count = 0; victim = NULL; txn = NULL; TRY(cyrusdb_foreach(db, NULL, 0, &countem, NULL, NULL, &txn)); if (count == 0) continue; TRY(cyrusdb_foreach(db, NULL, 0, &findvictim, NULL, NULL, &txn)); assert(victim != NULL); /* generate a random value */ val = genrand(10 + (rand() % 100)); /* do an add */ TRY(cyrusdb_store(db, victim, strlen(victim), val, strlen(val), &txn)); free(val); TRY(cyrusdb_commit(db, txn)); free(victim); victim = NULL; gettimeofday(&t2, NULL); ADDDIFF(t_mod, t1, t2); c_mod++; break; case 2: /* do a delete */ if (verbose) printf("D"); gettimeofday(&t1, NULL); /* pick a random victim */ count = 0; victim = NULL; txn = NULL; TRY(cyrusdb_foreach(db, NULL, 0, &countem, NULL, NULL, &txn)); if (count == 0) continue; TRY(cyrusdb_foreach(db, NULL, 0, &findvictim, NULL, NULL, &txn)); assert(victim != NULL); /* delete it */ TRY(cyrusdb_delete(db, victim, strlen(victim), &txn, 0)); TRY(cyrusdb_commit(db, txn)); free(victim); victim = NULL; gettimeofday(&t2, NULL); ADDDIFF(t_del, t1, t2); c_del++; break; default: /* do a "read" */ if (verbose) printf("R"); gettimeofday(&t1, NULL); /* generate a random key */ key = genrand(10 + (rand() % 10)); txn = NULL; TRY(cyrusdb_fetch(db, key, strlen(key), &data, &datalen, &txn)); TRY(cyrusdb_commit(db, txn)); gettimeofday(&t2, NULL); ADDDIFF(t_find, t1, t2); c_find++; free(key); } fflush(stdout); #if 0 /* run the consistency function, if any */ if (cyrusdb_consistent) { TRY(cyrusdb_consistent(db)); } #endif } TRY(cyrusdb_close(db)); TRY(DB->done()); do_report(); return 0; }