/* 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)); }
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; }
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; }