Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
/* 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));
}
Ejemplo n.º 4
0
/*
 * 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;
}
Ejemplo n.º 5
0
/*
 * 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;
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
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;
}
Ejemplo n.º 9
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;
}