EXPORTED void duplicate_mark(const duplicate_key_t *dkey, time_t mark, unsigned long uid) { struct buf key = BUF_INITIALIZER; char data[100]; int r; if (!duplicate_dbopen) return; r = make_key(&key, dkey); if (r) return; memcpy(data, &mark, sizeof(mark)); memcpy(data + sizeof(mark), &uid, sizeof(uid)); do { r = cyrusdb_store(dupdb, key.s, key.len, data, sizeof(mark)+sizeof(uid), NULL); } while (r == CYRUSDB_AGAIN); #if DEBUG syslog(LOG_DEBUG, "duplicate_mark: %-40s %-20s %-40s %ld %lu", dkey->id, dkey->to, dkey->date, mark, uid); #endif buf_free(&key); }
static int statuscache_update_txn(const char *mboxname, struct statusdata *sdata, struct txn **tidptr) { char data[250]; /* enough room for 11*(UULONG + SP) */ size_t keylen, datalen; char *key = statuscache_buildkey(mboxname, sdata->userid, &keylen); int r; /* Don't access DB if it hasn't been opened */ if (!statuscache_dbopen) return 0; /* The trailing whitespace is necessary because we * use non-length-based functions to parse the values. * Any non-digit char would be fine, but whitespace * looks less ugly in dbtool output */ datalen = snprintf(data, sizeof(data), "%u %u %u %u %u %u %u " MODSEQ_FMT " ", STATUSCACHE_VERSION, sdata->statusitems, sdata->messages, sdata->recent, sdata->uidnext, sdata->uidvalidity, sdata->unseen, sdata->highestmodseq); r = cyrusdb_store(statuscachedb, key, keylen, data, datalen, tidptr); if (r != CYRUSDB_OK) { syslog(LOG_ERR, "DBERROR: error updating database: %s (%s)", mboxname, cyrusdb_strerror(r)); } return r; }
/* Copy keys from tmp file to tgt file. * * XXX We currently have nothing to compare against. */ static int mboxkey_merge_cb(void *rockp, const char *key, size_t keylen, const char *tmpdata, size_t tmpdatalen) { int r; struct mboxkey_merge_rock *rockdata = (struct mboxkey_merge_rock *)rockp; struct db *tgtdb = rockdata->db; const char *tgtdata; size_t tgtdatalen; if (!tgtdb) return IMAP_INTERNAL; r = cyrusdb_fetchlock(tgtdb, key, keylen, &tgtdata, &tgtdatalen, &(rockdata->tid)); if(!r && tgtdata) { unsigned short version, s; const char *tmp = tmpdata, *tgt = tgtdata; /* get version */ memcpy(&s, tgt, sizeof(s)); version = ntohs(s); assert(version == MBOXKEY_VERSION); /* get version */ memcpy(&s, tmp, sizeof(s)); version = ntohs(s); assert(version == MBOXKEY_VERSION); } return cyrusdb_store(tgtdb, key, keylen, tmpdata, tmpdatalen, &(rockdata->tid)); }
/* * Write out the quota entry 'quota' */ EXPORTED int quota_write(struct quota *quota, struct txn **tid) { int r; int qrlen; int res; struct buf buf = BUF_INITIALIZER; struct dlist *dl = NULL; if (!quota->root) return IMAP_QUOTAROOT_NONEXISTENT; qrlen = strlen(quota->root); if (!qrlen) return IMAP_QUOTAROOT_NONEXISTENT; dl = dlist_newkvlist(NULL, NULL); for (res = 0; res < QUOTA_NUMRESOURCES; res++) { struct dlist *item = dlist_newlist(dl, quota_db_names[res]); dlist_setnum64(item, NULL, quota->useds[res]); if (quota->limits[res] != QUOTA_UNLIMITED) dlist_setnum64(item, NULL, quota->limits[res]); } if (quota->scanmbox) { struct dlist *scan = dlist_newkvlist(dl, "SCAN"); dlist_setatom(scan, "MBOX", quota->scanmbox); for (res = 0; res < QUOTA_NUMRESOURCES; res++) dlist_setnum64(scan, quota_db_names[res], quota->scanuseds[res]); } dlist_printbuf(dl, 0, &buf); r = cyrusdb_store(qdb, quota->root, qrlen, buf.s, buf.len, tid); switch (r) { case CYRUSDB_OK: r = 0; break; case CYRUSDB_AGAIN: r = IMAP_AGAIN; break; default: syslog(LOG_ERR, "DBERROR: error storing %s: %s", quota->root, cyrusdb_strerror(r)); r = IMAP_IOERROR; break; } dlist_free(&dl); buf_free(&buf); return r; }
/* * Add an entry to the deny DB. Message 'msg' may be NULL, resulting * in a default message being used. Service name 'service' may be NULL, * resulting in all services being blocked for the user. The username * 'user' is a required argument. Returns an IMAP error code or 0 on * success. */ EXPORTED int denydb_set(const char *user, const char *service, const char *msg) { struct txn *txn = NULL; struct buf data = BUF_INITIALIZER; int r = 0; if (!denydb) { r = IMAP_INTERNAL; goto out; } if (!service) service = "*"; if (!user || strchr(service, '\t')) { /* the service field may not contain a TAB, it's the field separator */ r = IMAP_INVALID_IDENTIFIER; goto out; } /* compose the record */ buf_printf(&data, "%u\t", USERDENY_VERSION); buf_appendcstr(&data, service); buf_putc(&data, '\t'); buf_appendcstr(&data, (msg ? msg : default_message)); /* write the record */ do { r = cyrusdb_store(denydb, user, strlen(user), data.s, data.len, &txn); } while (r == CYRUSDB_AGAIN); if (r) { syslog(LOG_ERR, "IOERROR: couldn't store denydb record for %s: %s", user, cyrusdb_strerror(r)); r = IMAP_IOERROR; } out: if (txn) { if (r) cyrusdb_abort(denydb, txn); else cyrusdb_commit(denydb, txn); } buf_free(&data); return r; }
int mboxkey_write(struct mboxkey *mboxkeydb, const char *mailbox, const char *mboxkey, size_t mboxkeylen) { int r; assert(mboxkeydb && mailbox); /* assert(mboxkeydb->tid);*/ if (MBOXKEY_DEBUG) { syslog(LOG_DEBUG, "mboxkey_db: mboxkey_write(%s, %s, %s)", mboxkeydb->user, mailbox, mboxkey ? "KEY" : "NIL"); } if (!mboxkey) { r = cyrusdb_delete(mboxkeydb->db, mailbox, strlen(mailbox), &mboxkeydb->tid, 1); } else { unsigned short version = MBOXKEY_VERSION, s; int datalen = sizeof(s) + mboxkeylen; char *data = xmalloc(datalen); s = htons(version); memcpy(data, &s, sizeof(s)); memcpy(data+sizeof(s), mboxkey, mboxkeylen); r = cyrusdb_store(mboxkeydb->db, mailbox, strlen(mailbox), data, datalen, &mboxkeydb->tid); free(data); } switch (r) { case CYRUSDB_OK: break; case CYRUSDB_IOERROR: r = IMAP_AGAIN; break; default: syslog(LOG_ERR, "DBERROR: error updating database: %s", cyrusdb_strerror(r)); r = IMAP_IOERROR; break; } return r; }
int seen_write(struct seen *seendb, const char *uniqueid, struct seendata *sd) { int sz = strlen(sd->seenuids) + 50; char *data = xmalloc(sz); int datalen; int r; assert(seendb && uniqueid); if (SEEN_DEBUG) { syslog(LOG_DEBUG, "seen_db: seen_write %s (%s)", seendb->user, uniqueid); } snprintf(data, sz, "%d %lu %u %lu %s", SEEN_VERSION, sd->lastread, sd->lastuid, sd->lastchange, sd->seenuids); datalen = strlen(data); r = cyrusdb_store(seendb->db, uniqueid, strlen(uniqueid), data, datalen, &seendb->tid); switch (r) { case CYRUSDB_OK: break; case CYRUSDB_IOERROR: r = IMAP_AGAIN; break; default: syslog(LOG_ERR, "DBERROR: error updating database: %s", cyrusdb_strerror(r)); r = IMAP_IOERROR; break; } free(data); sync_log_seen(seendb->user, uniqueid); return r; }
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; }
int main(int argc, char *argv[]) { extern char *optarg; int opt; char *alt_config = NULL, *port = "119"; const char *peer = NULL, *server = "localhost", *wildmat = "*"; char *authname = NULL, *password = NULL; int psock = -1, ssock = -1; struct protstream *pin, *pout, *sin, *sout; char buf[BUFFERSIZE]; char sfile[1024] = ""; int fd = -1, i, offered, rejected, accepted, failed; time_t stamp; strarray_t resp = STRARRAY_INITIALIZER; int newnews = 1; char *datefmt = "%y%m%d %H%M%S"; if ((geteuid()) == 0 && (become_cyrus(/*is_master*/0) != 0)) { fatal("must run as the Cyrus user", EC_USAGE); } while ((opt = getopt(argc, argv, "C:s:w:f:a:p:ny")) != EOF) { switch (opt) { case 'C': /* alt config file */ alt_config = optarg; break; case 's': /* server */ server = xstrdup(optarg); if ((port = strchr(server, ':'))) *port++ = '\0'; else port = "119"; break; case 'w': /* wildmat */ wildmat = optarg; break; case 'f': /* timestamp file */ snprintf(sfile, sizeof(sfile), "%s", optarg); break; case 'a': /* authname */ authname = optarg; break; case 'p': /* password */ password = optarg; break; case 'n': /* no newnews */ newnews = 0; break; case 'y': /* newsserver is y2k compliant */ datefmt = "%Y%m%d %H%M%S"; break; default: usage(); /* NOTREACHED */ } } if (argc - optind < 1) { usage(); /* NOTREACHED */ } peer = argv[optind++]; cyrus_init(alt_config, "fetchnews", 0, 0); /* connect to the peer */ /* xxx configurable port number? */ if ((psock = init_net(peer, "119", &pin, &pout)) < 0) { fprintf(stderr, "connection to %s failed\n", peer); cyrus_done(); exit(-1); } /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("20", buf, 2)) { syslog(LOG_ERR, "peer not available"); goto quit; } if (authname) { /* authenticate to peer */ /* XXX this should be modified to support SASL and STARTTLS */ prot_printf(pout, "AUTHINFO USER %s\r\n", authname); if (!prot_fgets(buf, sizeof(buf), pin)) { syslog(LOG_ERR, "AUTHINFO USER terminated abnormally"); goto quit; } else if (!strncmp("381", buf, 3)) { /* password required */ if (!password) password = cyrus_getpass("Please enter the password: "******"failed to get password\n"); goto quit; } prot_printf(pout, "AUTHINFO PASS %s\r\n", password); if (!prot_fgets(buf, sizeof(buf), pin)) { syslog(LOG_ERR, "AUTHINFO PASS terminated abnormally"); goto quit; } } if (strncmp("281", buf, 3)) { /* auth failed */ goto quit; } } /* change to reader mode - not always necessary, so ignore result */ prot_printf(pout, "MODE READER\r\n"); prot_fgets(buf, sizeof(buf), pin); if (newnews) { struct tm ctime, *ptime; /* fetch the server's current time */ prot_printf(pout, "DATE\r\n"); if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("111 ", buf, 4)) { syslog(LOG_ERR, "error fetching DATE"); goto quit; } /* parse and normalize the server time */ memset(&ctime, 0, sizeof(struct tm)); sscanf(buf+4, "%4d%02d%02d%02d%02d%02d", &ctime.tm_year, &ctime.tm_mon, &ctime.tm_mday, &ctime.tm_hour, &ctime.tm_min, &ctime.tm_sec); ctime.tm_year -= 1900; ctime.tm_mon--; ctime.tm_isdst = -1; /* read the previous timestamp */ if (!sfile[0]) { char oldfile[1024]; snprintf(sfile, sizeof(sfile), "%s/fetchnews.stamp", config_dir); /* upgrade from the old stamp filename to the new */ snprintf(oldfile, sizeof(oldfile), "%s/newsstamp", config_dir); rename(oldfile, sfile); } if ((fd = open(sfile, O_RDWR | O_CREAT, 0644)) == -1) { syslog(LOG_ERR, "cannot open %s", sfile); goto quit; } if (lock_nonblocking(fd) == -1) { syslog(LOG_ERR, "cannot lock %s: %m", sfile); goto quit; } if (read(fd, &stamp, sizeof(stamp)) < (int) sizeof(stamp)) { /* XXX do something better here */ stamp = 0; } /* ask for new articles */ if (stamp) stamp -= 180; /* adjust back 3 minutes */ ptime = gmtime(&stamp); ptime->tm_isdst = -1; strftime(buf, sizeof(buf), datefmt, ptime); prot_printf(pout, "NEWNEWS %s %s GMT\r\n", wildmat, buf); if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("230", buf, 3)) { syslog(LOG_ERR, "peer doesn't support NEWNEWS"); newnews = 0; } /* prepare server's current time as new timestamp */ stamp = mktime(&ctime); /* adjust for local timezone XXX We need to do this because we use gmtime() above. We can't change this, otherwise we'd be incompatible with an old localtime timestamp. */ stamp += gmtoff_of(&ctime, stamp); } if (!newnews) { prot_printf(pout, "LIST ACTIVE %s\r\n", wildmat); if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("215", buf, 3)) { syslog(LOG_ERR, "peer doesn't support LIST ACTIVE"); goto quit; } } /* process the NEWNEWS/LIST ACTIVE list */ while (prot_fgets(buf, sizeof(buf), pin)) { if (buf[0] == '.') break; strarray_append(&resp, buf); } if (buf[0] != '.') { syslog(LOG_ERR, "%s terminated abnormally", newnews ? "NEWNEWS" : "LIST ACTIVE"); goto quit; } if (!resp.count) { /* nothing matches our wildmat */ goto quit; } /* connect to the server */ if ((ssock = init_net(server, port, &sin, &sout)) < 0) { fprintf(stderr, "connection to %s failed\n", server); goto quit; } /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), sin) || strncmp("20", buf, 2)) { syslog(LOG_ERR, "server not available"); goto quit; } /* fetch and store articles */ offered = rejected = accepted = failed = 0; if (newnews) { /* response is a list of msgids */ for (i = 0; i < resp.count; i++) { /* find the end of the msgid */ *(strrchr(resp.data[i], '>') + 1) = '\0'; offered++; if (fetch(resp.data[i], 1, pin, pout, sin, sout, &rejected, &accepted, &failed)) { goto quit; } } /* write the current timestamp */ lseek(fd, 0, SEEK_SET); if (write(fd, &stamp, sizeof(stamp)) < (int) sizeof(stamp)) syslog(LOG_ERR, "error writing %s", sfile); lock_unlock(fd); close(fd); } else { char group[BUFFERSIZE], msgid[BUFFERSIZE], lastbuf[50]; const char *data; unsigned long low, high, last, cur; int start; size_t datalen; struct txn *tid = NULL; newsrc_init(NULL, 0); /* * response is a list of groups. * select each group, and STAT each article we haven't seen yet. */ for (i = 0; i < resp.count; i++) { /* parse the LIST ACTIVE response */ sscanf(resp.data[i], "%s %lu %lu", group, &high, &low); last = 0; if (!cyrusdb_fetchlock(newsrc_db, group, strlen(group), &data, &datalen, &tid)) { last = strtoul(data, NULL, 10); } if (high <= last) continue; /* select the group */ prot_printf(pout, "GROUP %s\r\n", group); if (!prot_fgets(buf, sizeof(buf), pin)) { syslog(LOG_ERR, "GROUP terminated abnormally"); continue; } else if (strncmp("211", buf, 3)) break; for (start = 1, cur = low > last ? low : ++last;; cur++) { if (start) { /* STAT the first article we haven't seen */ prot_printf(pout, "STAT %lu\r\n", cur); } else { /* continue with the NEXT article */ prot_printf(pout, "NEXT\r\n"); } if (!prot_fgets(buf, sizeof(buf), pin)) { syslog(LOG_ERR, "STAT/NEXT terminated abnormally"); cur--; break; } if (!strncmp("223", buf, 3)) { /* parse the STAT/NEXT response */ sscanf(buf, "223 %lu %s", &cur, msgid); /* find the end of the msgid */ *(strrchr(msgid, '>') + 1) = '\0'; if (fetch(msgid, 0, pin, pout, sin, sout, &rejected, &accepted, &failed)) { cur--; break; } offered++; start = 0; } /* have we reached the highwater mark? */ if (cur >= high) break; } snprintf(lastbuf, sizeof(lastbuf), "%lu", cur); cyrusdb_store(newsrc_db, group, strlen(group), lastbuf, strlen(lastbuf)+1, &tid); } if (tid) cyrusdb_commit(newsrc_db, tid); newsrc_done(); } syslog(LOG_NOTICE, "fetchnews: %s offered %d; %s rejected %d, accepted %d, failed %d", peer, offered, server, rejected, accepted, failed); quit: if (psock >= 0) { prot_printf(pout, "QUIT\r\n"); prot_flush(pout); /* Flush the incoming buffer */ prot_NONBLOCK(pin); prot_fill(pin); /* close/free socket & prot layer */ close(psock); prot_free(pin); prot_free(pout); } if (ssock >= 0) { prot_printf(sout, "QUIT\r\n"); prot_flush(sout); /* Flush the incoming buffer */ prot_NONBLOCK(sin); prot_fill(sin); /* close/free socket & prot layer */ close(psock); prot_free(sin); prot_free(sout); } cyrus_done(); return 0; }