Commit message: CVE-2011-3208 - fix buffer overflow in nntpd
CVE: CVE-2011-3208
CWE: CWE-119
Vulnerability description: Stack-based buffer overflow in the split_wildmats function in nntpd.c in nntpd in Cyrus IMAP Server before 2.3.17 and 2.4.x before 2.4.11 allows remote attackers to execute arbitrary code via a crafted NNTP command.

Project: cyrus-imapd

static void cmd_authinfo_sasl(char *cmd, char *mech, char *resp)
{
    int r, sasl_result;
    char *success_data;
    sasl_ssf_t ssf;
    char *ssfmsg = NULL;
    const void *val;
    int failedloginpause;
    struct proc_limits limits;

    /* Conceal initial response in telemetry log */
    if (nntp_logfd != -1 && resp) {
	(void)ftruncate(nntp_logfd,
		  lseek(nntp_logfd, -2, SEEK_CUR) - strlen(resp));
	(void)write(nntp_logfd, "...\r\n", 5);
    }

    if (nntp_userid) {
	prot_printf(nntp_out, "502 Already authenticated\r\n");
	return;
    }

    /* Stop telemetry logging during SASL exchange */
    if (nntp_logfd != -1 && mech) {
	prot_setlog(nntp_in, PROT_NO_FD);
	prot_setlog(nntp_out, PROT_NO_FD);
    }

    if (cmd[0] == 'g') {
	/* AUTHINFO GENERIC */
	if (!mech) {
	    /* If client didn't specify any mech we give them the list */
	    const char *sasllist;
	    int mechnum;

	    prot_printf(nntp_out, "281 List of mechanisms follows\r\n");
      
	    /* CRLF separated, dot terminated */
	    if (sasl_listmech(nntp_saslconn, NULL,
			      "", "\r\n", "\r\n",
			      &sasllist,
			      NULL, &mechnum) == SASL_OK) {
		if (mechnum > 0) {
		    prot_printf(nntp_out, "%s", sasllist);
		}
	    }
      
	    prot_printf(nntp_out, ".\r\n");
	    return;
	}

	r = saslserver(nntp_saslconn, mech, resp, "AUTHINFO GENERIC ", "381 ",
		       "", nntp_in, nntp_out, &sasl_result, &success_data);
    }
    else {
	/* AUTHINFO SASL */
	r = saslserver(nntp_saslconn, mech, resp, "", "383 ", "=",
		       nntp_in, nntp_out, &sasl_result, &success_data);
    }

    /* Restart any telemetry logging */
    prot_setlog(nntp_in, nntp_logfd);
    prot_setlog(nntp_out, nntp_logfd);

    if (r) {
	int code;
	const char *errorstring = NULL;

	switch (r) {
	case IMAP_SASL_CANCEL:
	    prot_printf(nntp_out,
			"481 Client canceled authentication\r\n");
	    break;
	case IMAP_SASL_PROTERR:
	    errorstring = prot_error(nntp_in);

	    prot_printf(nntp_out,
			"482 Error reading client response: %s\r\n",
			errorstring ? errorstring : "");
	    break;
	default: 
	    /* failed authentication */
	    switch (sasl_result) {
	    case SASL_NOMECH:
	    case SASL_TOOWEAK:
		code = 503;
		break;
	    case SASL_ENCRYPT:
		code = 483;
		break;
	    case SASL_BADPROT:
		code = 482;
		break;
	    default:
		code = 481;
	    }

	    syslog(LOG_NOTICE, "badlogin: %s %s [%s]",
		   nntp_clienthost, mech, sasl_errdetail(nntp_saslconn));

	    failedloginpause = config_getint(IMAPOPT_FAILEDLOGINPAUSE);
	    if (failedloginpause != 0) {
	        sleep(failedloginpause);
	    }

	    /* Don't allow user probing */
	    if (sasl_result == SASL_NOUSER) sasl_result = SASL_BADAUTH;

	    errorstring = sasl_errstring(sasl_result, NULL, NULL);
	    if (errorstring) {
		prot_printf(nntp_out, "%d %s\r\n", code, errorstring);
	    } else {
		prot_printf(nntp_out, "%d Error authenticating\r\n", code);
	    }
	}

	reset_saslconn(&nntp_saslconn);
	return;
    }

    /* successful authentication */

    /* get the userid from SASL --- already canonicalized from
     * mysasl_proxy_policy()
     */
    sasl_result = sasl_getprop(nntp_saslconn, SASL_USERNAME, &val);
    if (sasl_result != SASL_OK) {
	prot_printf(nntp_out, "481 weird SASL error %d SASL_USERNAME\r\n", 
		    sasl_result);
	syslog(LOG_ERR, "weird SASL error %d getting SASL_USERNAME", 
	       sasl_result);
	reset_saslconn(&nntp_saslconn);
	return;
    }
    nntp_userid = xstrdup((const char *) val);

    sasl_getprop(nntp_saslconn, SASL_SSF, &val);
    ssf = *((sasl_ssf_t *) val);

    /* really, we should be doing a sasl_getprop on SASL_SSF_EXTERNAL,
       but the current libsasl doesn't allow that. */
    if (nntp_starttls_done) {
	switch(ssf) {
	case 0: ssfmsg = "tls protection"; break;
	case 1: ssfmsg = "tls plus integrity protection"; break;
	default: ssfmsg = "tls plus privacy protection"; break;
	}
    } else {
	switch(ssf) {
	case 0: ssfmsg = "no protection"; break;
	case 1: ssfmsg = "integrity protection"; break;
	default: ssfmsg = "privacy protection"; break;
	}
    }

    limits.procname = "nntpd";
    limits.clienthost = nntp_clienthost;
    limits.userid = nntp_userid;
    if (proc_checklimits(&limits)) {
	const char *sep = "";
	prot_printf(nntp_out,
		    "452 Too many open connections (");
	if (limits.maxhost) {
	    prot_printf(nntp_out, "%s%d of %d from %s", sep,
			limits.host, limits.maxhost, nntp_clienthost);
	    sep = ", ";
	}
	if (limits.maxuser) {
	    prot_printf(nntp_out, "%s%d of %d for %s", sep,
			limits.user, limits.maxuser, nntp_userid);
	}
	prot_printf(nntp_out, ")\r\n");
	reset_saslconn(&nntp_saslconn);
	free(nntp_userid);
	nntp_userid = NULL;
	return;
    }

    syslog(LOG_NOTICE, "login: %s %s %s%s %s", nntp_clienthost, nntp_userid,
	   mech, nntp_starttls_done ? "+TLS" : "", "User logged in");

    proc_register("nntpd", nntp_clienthost, nntp_userid, NULL);

    if (success_data) {
	prot_printf(nntp_out, "283 %s\r\n", success_data);
	free(success_data);
    } else {
	prot_printf(nntp_out, "281 Success (%s)\r\n", ssfmsg);
    }

    prot_setsasl(nntp_in,  nntp_saslconn);
    prot_setsasl(nntp_out, nntp_saslconn);

    /* Close IP-based telemetry log and create new log based on userid */
    if (nntp_logfd != -1) close(nntp_logfd);
    nntp_logfd = telemetry_log(nntp_userid, nntp_in, nntp_out, 0);

    if (ssf) {
	/* close any selected group */
	if (group_state)
	    index_close(&group_state);
	if (backend_current) {
	    proxy_downserver(backend_current);
	    backend_current = NULL;
	}
    }
}

Project: cyrus-imapd

static void parse_groups(const char *groups, message_data_t *msg)
{
    const char *p;
    char *rcpt = NULL;
    char *buf = xstrdup(groups);
    const char sep[] = ", \t";

    for (p = strtok(buf, sep) ; p ; p = strtok(NULL, sep)) {
	/* construct the mailbox name */
	free(rcpt);
	rcpt = strconcat(newsprefix, p, (char *)NULL);

	/* skip mailboxes that we don't serve as newsgroups */
	if (!is_newsgroup(rcpt)) continue;

	/* Only add mailboxes that exist */
	if (!mlookup(rcpt, NULL)) {
	    strarray_appendm(&msg->rcpt, rcpt);
	    rcpt = NULL;
	}
    }
    free(rcpt);
    free(buf);
}

Project: cyrus-imapd

int msg_new(message_data_t **m)
{
    message_data_t *ret = (message_data_t *) xmalloc(sizeof(message_data_t));

    ret->data = NULL;
    ret->f = NULL;
    ret->id = NULL;
    ret->path = NULL;
    ret->control = NULL;
    ret->size = 0;
    strarray_init(&ret->rcpt);
    ret->date = NULL;

    ret->hdrcache = spool_new_hdrcache();

    *m = ret;
    return 0;
}

Project: cyrus-imapd

static void cmdloop(void)
{
    int c, r = 0, mode;
    static struct buf cmd, arg1, arg2, arg3, arg4;
    char *p, *result, buf[1024];
    const char *err;
    uint32_t uid, last;
    struct backend *be;
    char curgroup[MAX_MAILBOX_BUFFER] = "";

    allowanonymous = config_getswitch(IMAPOPT_ALLOWANONYMOUSLOGIN);

    for (;;) {
	/* Flush any buffered output */
	prot_flush(nntp_out);
	if (backend_current) prot_flush(backend_current->out);

	/* Check for shutdown file */
	if (shutdown_file(buf, sizeof(buf)) ||
	    (nntp_userid &&
	     userdeny(nntp_userid, config_ident, buf, sizeof(buf)))) {
	    prot_printf(nntp_out, "400 %s\r\n", buf);
	    shut_down(0);
	}

	signals_poll();

	if (!proxy_check_input(protin, nntp_in, nntp_out,
			       backend_current ? backend_current->in : NULL,
			       NULL, 0)) {
	    /* No input from client */
	    continue;
	}

	if (group_state &&
	    config_getswitch(IMAPOPT_DISCONNECT_ON_VANISHED_MAILBOX)) {
	    if (group_state->mailbox->i.options & OPT_MAILBOX_DELETED) {
		/* Mailbox has been (re)moved */
		syslog(LOG_WARNING,
		       "Newsgroup %s has been (re)moved out from under client",
		       group_state->mailbox->name);
		prot_printf(nntp_out,
			    "400 Newsgroup has been (re)moved\r\n");
		shut_down(0);
	    }
	}

	/* Parse command name */
	c = getword(nntp_in, &cmd);
	if (c == EOF) {
	    if ((err = prot_error(nntp_in)) != NULL
		 && strcmp(err, PROT_EOF_STRING)) {
		syslog(LOG_WARNING, "%s, closing connection", err);
		prot_printf(nntp_out, "400 %s\r\n", err);
	    }
	    return;
	}
	if (!cmd.s[0]) {
	    prot_printf(nntp_out, "501 Empty command\r\n");
	    eatline(nntp_in, c);
	    continue;
	}
	if (Uislower(cmd.s[0])) 
	    cmd.s[0] = toupper((unsigned char) cmd.s[0]);
	for (p = &cmd.s[1]; *p; p++) {
	    if (Uisupper(*p)) *p = tolower((unsigned char) *p);
	}

	/* Ihave/Takethis only allowed for feeders */
	if (!(nntp_capa & MODE_FEED) &&
	    strchr("IT", cmd.s[0])) goto noperm;
    
	/* Body/Date/Group/Newgroups/Newnews/Next/Over/Post/Xhdr/Xover/Xpat
	   only allowed for readers */
	if (!(nntp_capa & MODE_READ) &&
	    strchr("BDGNOPX", cmd.s[0])) goto noperm;
    
	/* Only Authinfo/Capabilities/Check/Head/Help/Ihave/List Active/
	   Mode/Quit/Starttls/Stat/Takethis allowed when not logged in */
	if (!nntp_userid && !allowanonymous &&
	    !strchr("ACHILMQST", cmd.s[0])) goto nologin;

	/* In case a [LIST]GROUP fails or
	   a retrieval by msgid makes us switch groups */
	strcpy(curgroup, group_state ? group_state->mailbox->name : "");

	switch (cmd.s[0]) {
	case 'A':
	    if (!strcmp(cmd.s, "Authinfo")) {
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* subcommand */
		if (c == EOF) goto missingargs;

		lcase(arg1.s);

		if (!strcmp(arg1.s, "user") || !strcmp(arg1.s, "pass")) {
		    if (c != ' ') goto missingargs;
		    c = getuserpass(nntp_in, &arg2); /* user/pass */
		    if (c == EOF) goto missingargs;

		    if (c == '\r') c = prot_getc(nntp_in);
		    if (c != '\n') goto extraargs;

		    if (arg1.s[0] == 'u')
			cmd_authinfo_user(arg2.s);
		    else
			cmd_authinfo_pass(arg2.s);
		}
		else if (!strcmp(arg1.s, "sasl") || !strcmp(arg1.s, "generic")) {
		    arg2.len = arg3.len = 0;

		    /* mech name required for SASL but not GENERIC */
		    if ((arg1.s[0] == 's') && (c != ' ')) goto missingargs;

		    if (c == ' ') {
			c = getword(nntp_in, &arg2); /* mech name */
			if (c == EOF) goto missingargs;

			if (c == ' ') {
			    c = getword(nntp_in, &arg3); /* init response */
			    if (c == EOF) goto missingargs;
			}
		    }

		    if (c == '\r') c = prot_getc(nntp_in);
		    if (c != '\n') goto extraargs;

		    cmd_authinfo_sasl(arg1.s, arg2.len ? arg2.s : NULL,
				      arg3.len ? arg3.s : NULL);
		}
		else
		    prot_printf(nntp_out,
				"501 Unrecognized AUTHINFO command\r\n");
	    }
	    else if (!(nntp_capa & MODE_READ)) goto noperm;
	    else if (!nntp_userid && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Article")) {
		char *msgid;

		mode = ARTICLE_ALL;

	      article:
		if (arg1.s) *arg1.s = 0;

		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* number/msgid (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (parserange(arg1.s, &uid, NULL, &msgid, &be) != -1) {
		    if (be) {
			if (arg1.s && *arg1.s)
			    prot_printf(be->out, "%s %s\r\n", cmd.s, arg1.s);
			else
			    prot_printf(be->out, "%s\r\n", cmd.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "22", 2) &&
				mode != ARTICLE_STAT) {
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_article(mode, msgid, uid);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	case 'B':
	    if (!strcmp(cmd.s, "Body")) {
		mode = ARTICLE_BODY;
		goto article;
	    }
	    else goto badcmd;
	    break;

	case 'C':
	    if (!strcmp(cmd.s, "Capabilities")) {
		arg1.len = 0;

		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* keyword (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_capabilities(arg1.s);
	    }
	    else if (!(nntp_capa & MODE_FEED)) goto noperm;
	    else if (!strcmp(cmd.s, "Check")) {
		mode = POST_CHECK;
		goto ihave;
	    }
	    else goto badcmd;
	    break;

	case 'D':
	    if (!strcmp(cmd.s, "Date")) {
		time_t now = time(NULL);
		struct tm *my_tm = gmtime(&now);
		char buf[15];

		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", my_tm);
		prot_printf(nntp_out, "111 %s\r\n", buf);
	    }
	    else goto badcmd;
	    break;

	case 'G':
	    if (!strcmp(cmd.s, "Group")) {
		arg2.len = 0; /* GROUP command (no range) */

	      group:
#define LISTGROUP (arg2.len)

		if (!LISTGROUP && c != ' ') goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* group */
		    if (c == EOF) goto missingargs;
		}
		if (LISTGROUP && c == ' ') {
		    c = getword(nntp_in, &arg2); /* range (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		be = backend_current;
		if (arg1.len &&
		    (!is_newsgroup(arg1.s) ||
		     (r = open_group(arg1.s, 0, &be, NULL)))) goto nogroup;
		else if (be) {
		    prot_printf(be->out, "%s", cmd.s);
		    if (arg1.len) {
			prot_printf(be->out, " %s", arg1.s);
			  if (LISTGROUP) prot_printf(be->out, " %s", arg2.s);
		    }
		    prot_printf(be->out, "\r\n");

		    r = read_response(be, 0, &result);
		    if (r) goto nogroup;

		    prot_printf(nntp_out, "%s", result);

		    if (!strncmp(result, "211", 3)) {
			if (LISTGROUP) pipe_to_end_of_response(be, 0);

			if (backend_current && backend_current != be) {
			    /* remove backend_current from the protgroup */
			    protgroup_delete(protin, backend_current->in);
			}
			backend_current = be;

			/* add backend_current to the protgroup */
			protgroup_insert(protin, backend_current->in);
		    }
		}
		else if (!group_state) goto noopengroup;
		else if (LISTGROUP &&
			 parserange(arg2.s, &uid, &last, NULL, NULL) != 0) {
		    /* parserange() will handle error code -- do nothing */
		}
		else {
		    if (backend_current) {
			/* remove backend_current from the protgroup */
			protgroup_delete(protin, backend_current->in);
		    }
		    backend_current = NULL;

		    nntp_exists = group_state->exists;
		    nntp_current = nntp_exists > 0;

		    prot_printf(nntp_out, "211 %u %lu %lu %s\r\n",
				nntp_exists,
				nntp_exists ? index_getuid(group_state, 1) :
				group_state->last_uid+1,
				nntp_exists ? index_getuid(group_state, nntp_exists) :
				group_state->last_uid,
				group_state->mailbox->name + strlen(newsprefix));

		    if (LISTGROUP) {
			int msgno, last_msgno;

			msgno = index_finduid(group_state, uid);
			if (!msgno || index_getuid(group_state, msgno) != uid) {
			    msgno++;
			}
			last_msgno = index_finduid(group_state, last);

			for (; msgno <= last_msgno; msgno++) {
			    prot_printf(nntp_out, "%u\r\n",
					index_getuid(group_state, msgno));
			}
			prot_printf(nntp_out, ".\r\n");
		    }
		}
#undef LISTGROUP
	    }
	    else goto badcmd;
	    break;

	case 'H':
	    if (!strcmp(cmd.s, "Head")) {
		mode = ARTICLE_HEAD;
		goto article;
	    }
	    else if (!strcmp(cmd.s, "Help")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_help();
	    }
	    else if (!(nntp_capa & MODE_READ)) goto noperm;
	    else if (!nntp_userid && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Hdr")) {
		char *msgid;

	      hdr:
		if (arg2.s) *arg2.s = 0;

		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* header */
		if (c == EOF) goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg2); /* range (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (parserange(arg2.s, &uid, &last, &msgid, &be) != -1) {
		    if (be) {
			if (arg2.s && *arg2.s)
			    prot_printf(be->out, "%s %s %s\r\n",
					cmd.s, arg1.s, arg2.s);
			else
			    prot_printf(be->out, "%s %s\r\n", cmd.s, arg1.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "22", 2)) { /* 221 or 225 */
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_hdr(cmd.s, arg1.s, NULL, msgid, uid, last);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	case 'I':
	    if (!strcmp(cmd.s, "Ihave")) {
		mode = POST_IHAVE;

	      ihave:
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* msgid */
		if (c == EOF) goto missingargs;
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_post(arg1.s, mode);
	    }
	    else goto badcmd;
	    break;

	case 'L':
	    if (!strcmp(cmd.s, "List")) {
		arg1.len = arg2.len = 0;
		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* subcommand (optional) */
		    if (c == EOF) goto missingargs;
		    if (c == ' ') {
			c = getword(nntp_in, &arg2); /* argument (optional) */
			if (c == EOF) goto missingargs;
		    }
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_list(arg1.len ? arg1.s : NULL, arg2.len ? arg2.s : NULL);

		goto prevgroup;  /* In case we did LIST [ACTIVE] */
	    }
	    else if (!(nntp_capa & MODE_READ)) goto noperm;
	    else if (!nntp_userid && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Last")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (backend_current) {
		    prot_printf(backend_current->out, "LAST\r\n");
		}
		else if (!group_state) goto noopengroup;
		else if (!nntp_current) goto nocurrent;
		else if (nntp_current == 1) {
		    prot_printf(nntp_out,
				"422 No previous article in this group\r\n");
		}
		else {
		    char *msgid = index_get_msgid(group_state, --nntp_current);

		    prot_printf(nntp_out, "223 %u %s\r\n",
				index_getuid(group_state, nntp_current),
				msgid ? msgid : "<0>");

		    if (msgid) free(msgid);
		}
	    }
	    else if (!strcmp(cmd.s, "Listgroup")) {
		arg1.len = 0;   	   /* group is optional */
		buf_setcstr(&arg2, "1-");  /* default range is all */
		buf_cstring(&arg2);	   /* appends a '\0' */
		goto group;
	    }
	    else goto badcmd;
	    break;

	case 'M':
	    if (!strcmp(cmd.s, "Mode")) {
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* mode */
		if (c == EOF) goto missingargs;
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_mode(arg1.s);
	    }
	    else goto badcmd;
	    break;

	case 'N':
	    if (!strcmp(cmd.s, "Newgroups")) {
		time_t tstamp;

		arg3.len = 0;
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* date */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg2); /* time */
		if (c == EOF) goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg3); /* "GMT" (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if ((tstamp = parse_datetime(arg1.s, arg2.s,
					     arg3.len ? arg3.s : NULL)) < 0)
		    goto baddatetime;

		cmd_newgroups(tstamp);
	    }
	    else if (!strcmp(cmd.s, "Newnews")) {
		time_t tstamp;

		if (!config_getswitch(IMAPOPT_ALLOWNEWNEWS))
		    goto cmddisabled;

		arg4.len = 0;
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* wildmat */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg2); /* date */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg3); /* time */
		if (c == EOF) goto missingargs;
		if (c == ' ') {
		    c = getword(nntp_in, &arg4); /* "GMT" (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if ((tstamp = parse_datetime(arg2.s, arg3.s,
					     arg4.len ? arg4.s : NULL)) < 0)
		    goto baddatetime;

		cmd_newnews(arg1.s, tstamp);
	    }
	    else if (!strcmp(cmd.s, "Next")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (backend_current) {
		    prot_printf(backend_current->out, "NEXT\r\n");
		}
		else if (!group_state) goto noopengroup;
		else if (!nntp_current) goto nocurrent;
		else if (nntp_current == nntp_exists) {
		    prot_printf(nntp_out,
				"421 No next article in this group\r\n");
		}
		else {
		    char *msgid = index_get_msgid(group_state, ++nntp_current);

		    prot_printf(nntp_out, "223 %u %s\r\n",
				index_getuid(group_state, nntp_current),
				msgid ? msgid : "<0>");

		    if (msgid) free(msgid);
		}
	    }
	    else goto badcmd;
	    break;

	case 'O':
	    if (!strcmp(cmd.s, "Over")) {
		char *msgid;

	      over:
		if (arg1.s) *arg1.s = 0;

		if (c == ' ') {
		    c = getword(nntp_in, &arg1); /* range/msgid (optional) */
		    if (c == EOF) goto missingargs;
		}
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		msgid = NULL;
		if (parserange(arg1.s, &uid, &last,
			       /* XOVER doesn't accept message-id */
			       (cmd.s[0] == 'X' ? NULL : &msgid), &be) != -1) {
		    if (be) {
			if (arg1.s && *arg1.s)
			    prot_printf(be->out, "%s %s\r\n", cmd.s, arg1.s);
			else
			    prot_printf(be->out, "%s\r\n", cmd.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "224", 3)) {
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_over(msgid, uid, last);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	case 'P':
	    if (!strcmp(cmd.s, "Post")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		cmd_post(NULL, POST_POST);
	    }
	    else goto badcmd;
	    break;

	case 'Q':
	    if (!strcmp(cmd.s, "Quit")) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		prot_printf(nntp_out, "205 Connection closing\r\n");
		return;
	    }
	    else goto badcmd;
	    break;

	case 'S':
	    if (!strcmp(cmd.s, "Starttls") && tls_enabled()) {
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		/* XXX  discard any input pipelined after STARTTLS */
		prot_flush(nntp_in);

		cmd_starttls(0);
	    }
	    else if (!strcmp(cmd.s, "Stat")) {
		mode = ARTICLE_STAT;
		goto article;
	    }
	    else if (!nntp_userid && !allowanonymous) goto nologin;
	    else if (!strcmp(cmd.s, "Slave")) {	
		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		prot_printf(nntp_out, "202 Slave status noted\r\n");
	    }
	    else goto badcmd;
	    break;

	case 'T':
	    if (!strcmp(cmd.s, "Takethis")) {
		mode = POST_TAKETHIS;
		goto ihave;
	    }
	    else goto badcmd;
	    break;

	case 'X':
	    if (!strcmp(cmd.s, "Xhdr")) {
		goto hdr;
	    }
	    else if (!strcmp(cmd.s, "Xover")) {
		goto over;
	    }
	    else if (!strcmp(cmd.s, "Xpat")) {
		char *msgid;

		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg1); /* header */
		if (c != ' ') goto missingargs;

		/* gobble extra whitespace (hack for Mozilla) */
		while ((c = prot_getc(nntp_in)) == ' ');
		prot_ungetc(c, nntp_in);

		c = getword(nntp_in, &arg2); /* range */
		if (c != ' ') goto missingargs;
		c = getword(nntp_in, &arg3); /* wildmat */
		if (c == EOF) goto missingargs;

		/* XXX per RFC 2980, we can have multiple patterns */

		if (c == '\r') c = prot_getc(nntp_in);
		if (c != '\n') goto extraargs;

		if (parserange(arg2.s, &uid, &last, &msgid, &be) != -1) {
		    if (be) {
			prot_printf(be->out, "%s %s %s %s\r\n",
				    cmd.s, arg1.s, arg2.s, arg3.s);

			if (be != backend_current) {
			    r = read_response(be, 0, &result);
			    if (r) goto noopengroup;

			    prot_printf(nntp_out, "%s", result);
			    if (!strncmp(result, "221", 3)) {
				pipe_to_end_of_response(be, 0);
			    }
			}
		    }
		    else
			cmd_hdr(cmd.s, arg1.s, arg3.s, msgid, uid, last);
		}

		if (msgid) goto prevgroup;
	    }
	    else goto badcmd;
	    break;

	default:
	  badcmd:
	    prot_printf(nntp_out, "500 Unrecognized command\r\n");
	    eatline(nntp_in, c);
	}

	continue;

      noperm:
	prot_printf(nntp_out, "502 Permission denied\r\n");
	eatline(nntp_in, c);
	continue;

      nologin:
	prot_printf(nntp_out, "480 Authentication required\r\n");
	eatline(nntp_in, c);
	continue;

      cmddisabled:
	prot_printf(nntp_out, "503 \"%s\" disabled\r\n", cmd.s);
	eatline(nntp_in, c);
	continue;

      extraargs:
	prot_printf(nntp_out, "501 Unexpected extra argument\r\n");
	eatline(nntp_in, c);
	continue;

      missingargs:
	prot_printf(nntp_out, "501 Missing argument\r\n");
	eatline(nntp_in, c);
	continue;

      baddatetime:
	prot_printf(nntp_out, "501 Bad date/time\r\n");
	continue;

      nogroup:
	prot_printf(nntp_out, "411 No such newsgroup (%s)\r\n",
		    error_message(r));

      prevgroup:
	/* Return to previously selected group */
	if (*curgroup &&
	    (!group_state || strcmp(curgroup, group_state->mailbox->name))) {
	    open_group(curgroup, 1, NULL, NULL);
	}

	continue;

      noopengroup:
	prot_printf(nntp_out, "412 No newsgroup selected\r\n");
	continue;

      nocurrent:
	prot_printf(nntp_out, "420 Current article number is invalid\r\n");
	continue;
    }
}

Project: cyrus-imapd

static void feedpeer(char *peer, message_data_t *msg)
{
    char *user, *pass, *host, *port, *wild, *path, *s;
    int oldform = 0;
    struct wildmat *wmat = NULL, *w;
    int len, err, n, feed = 1;
    struct addrinfo hints, *res, *res0;
    int sock = -1;
    struct protstream *pin, *pout;
    char buf[4096];
    int body = 0, skip;

    /* parse the peer */
    user = pass = host = port = wild = NULL;
    if ((wild = strrchr(peer, '/')))
	*wild++ = '\0';
    else if ((wild = strrchr(peer, ':')) &&
	     strcspn(wild, "!*?,.") != strlen(wild)) {
	*wild++ = '\0';
	host = peer;
	oldform = 1;
    }
    if (!oldform) {
	if ((host = strchr(peer, '@'))) {
	    *host++ = '\0';
	    user = peer;
	    if ((pass = strchr(user, ':'))) *pass++ = '\0';
	}
	else
	    host = peer;

	if ((port = strchr(host, ':'))) *port++ = '\0';
    }

    /* check path to see if this message came through our peer */
    len = strlen(host);
    path = msg->path;
    while (path && (s = strchr(path, '!'))) {
	if ((s - path) == len && !strncmp(path, host, len)) {
	    return;
	}
	path = s + 1;
    }

    /* check newsgroups against wildmat to see if we should feed it */
    if (wild && *wild) {
	wmat = split_wildmats(wild);

	feed = 0;
	for (n = 0; n < msg->rcpt.count; n++) {
	    /* see if the newsgroup matches one of our wildmats */
	    w = wmat;
	    while (w->pat &&
		   wildmat(msg->rcpt.data[n], w->pat) != 1) {
		w++;
	    }

	    if (w->pat) {
		/* we have a match, check to see what kind of match */
		if (!w->not) {
		    /* positive match, ok to feed, keep checking */
		    feed = 1;
		}
		else if (w->not < 0) {
		    /* absolute negative match, do not feed */
		    feed = 0;
		    break;
		}
		else {
		    /* negative match, keep checking */
		}
	    }
	    else {
		/* no match, keep checking */
	    }
	}

	free_wildmats(wmat);
    }

    if (!feed) return;
    
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;
    if (!port || !*port) port = "119";
    if ((err = getaddrinfo(host, port, &hints, &res0)) != 0) {
	syslog(LOG_ERR, "getaddrinfo(%s, %s) failed: %m", host, port);
	return;
    }

    for (res = res0; res; res = res->ai_next) {
	if ((sock = socket(res->ai_family, res->ai_socktype,
			   res->ai_protocol)) < 0)
	    continue;
	if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0)
	    break;
	close(sock);
	sock = -1;
    }
    freeaddrinfo(res0);
    if(sock < 0) {
	syslog(LOG_ERR, "connect(%s:%s) failed: %m", host, port);
	return;
    }
    
    pin = prot_new(sock, 0);
    pout = prot_new(sock, 1);
    prot_setflushonread(pin, pout);

    /* read the initial greeting */
    if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("200", buf, 3)) {
	syslog(LOG_ERR, "peer doesn't allow posting");
	goto quit;
    }

    if (user) {
	/* change to reader mode - not always necessary, so ignore result */
	prot_printf(pout, "MODE READER\r\n");
	prot_fgets(buf, sizeof(buf), pin);

	if (*user) {
	    /* authenticate to peer */
	    /* XXX this should be modified to support SASL and STARTTLS */

	    prot_printf(pout, "AUTHINFO USER %s\r\n", user);
	    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 (!pass) {
		    syslog(LOG_ERR, "need password for AUTHINFO PASS");
		    goto quit;
		}

		prot_printf(pout, "AUTHINFO PASS %s\r\n", pass);
		if (!prot_fgets(buf, sizeof(buf), pin)) {
		    syslog(LOG_ERR, "AUTHINFO PASS terminated abnormally");
		    goto quit;
		}
	    }

	    if (strncmp("281", buf, 3)) {
		/* auth failed */
		syslog(LOG_ERR, "authentication failed");
		goto quit;
	    }
	}

	/* tell the peer we want to post */
	prot_printf(pout, "POST\r\n");
	prot_flush(pout);

	if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("340", buf, 3)) {
	    syslog(LOG_ERR, "peer doesn't allow posting");
	    goto quit;
	}
    }
    else {
	/* tell the peer about our new article */
	prot_printf(pout, "IHAVE %s\r\n", msg->id);
	prot_flush(pout);

	if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("335", buf, 3)) {
	    syslog(LOG_ERR, "peer doesn't want article %s", msg->id);
	    goto quit;
	}
    }

    /* send the article */
    rewind(msg->f);
    while (fgets(buf, sizeof(buf), msg->f)) {
	if (!body && buf[0] == '\r' && buf[1] == '\n') {
	    /* blank line between header and body */
	    body = 1;
	}

	skip = 0;
	if (!body) {
	    if (!strncasecmp(buf, "Reply-To:", 9)) {
		/* strip any post addresses, skip if becomes empty */
		if (!strip_post_addresses(buf+9)) skip = 1;
	    }
	}

	if (!skip && buf[0] == '.') prot_putc('.', pout);
	do {
	    if (!skip) prot_printf(pout, "%s", buf);
	} while (buf[strlen(buf)-1] != '\n' &&
		 fgets(buf, sizeof(buf), msg->f));
    }

    /* Protect against messages not ending in CRLF */
    if (buf[strlen(buf)-1] != '\n') prot_printf(pout, "\r\n");

    prot_printf(pout, ".\r\n");

    if (!prot_fgets(buf, sizeof(buf), pin) || strncmp("2", buf, 1)) {
	syslog(LOG_ERR, "article %s transfer to peer failed", msg->id);
    }

  quit:
    prot_printf(pout, "QUIT\r\n");
    prot_flush(pout);

    prot_fgets(buf, sizeof(buf), pin);

    /* Flush the incoming buffer */
    prot_NONBLOCK(pin);
    prot_fill(pin);

    /* close/free socket & prot layer */
    close(sock);
    
    prot_free(pin);
    prot_free(pout);

    return;
}

Project: cyrus-imapd

static void cmd_post(char *msgid, int mode)
{
    char *mboxname;
    FILE *f = NULL;
    message_data_t *msg;
    int r = 0;

    /* check if we want this article */
    if (msgid && my_find_msgid(msgid, &mboxname, NULL)) {
	/* already have it */
	syslog(LOG_INFO,
	       "dupelim: news article id %s already present in mailbox %s",
	       msgid, mboxname);
	r = NNTP_DONT_SEND;
    }

    if (mode != POST_TAKETHIS) {
	if (r) {
	    prot_printf(nntp_out, "%u %s Do not send article\r\n",
			post_codes[mode].no, msgid ? msgid : "");
	    return;
	}
	else {
	    prot_printf(nntp_out, "%u %s Send article\r\n",
			post_codes[mode].cont, msgid ? msgid : "");
	    if (mode == POST_CHECK) return;
	}
    }

    /* get a spool file (if needed) */
    if (!r) {
	f = tmpfile();
	if (!f) r = IMAP_IOERROR;
    }

    if (f) {
	msg_new(&msg);

	/* spool the article */
	r = savemsg(msg, f);

	/* deliver the article */
	if (!r) r = deliver(msg);

	if (!r) {
	    prot_printf(nntp_out, "%u %s Article received ok\r\n",
			post_codes[mode].ok, msg->id ? msg->id : "");
#if 0  /* XXX  Need to review control message auth/authz and implementation */
	    /* process control messages */
	    if (msg->control && !config_mupdate_server) {
		int r1 = 0;

		/* XXX check PGP signature */
		if (!strncmp(msg->control, "newgroup", 8))
		    r1 = newgroup(msg);
		else if (!strncmp(msg->control, "rmgroup", 7))
		    r1 = rmgroup(msg);
		else if (!strncmp(msg->control, "mvgroup", 7))
		    r1 = mvgroup(msg);
		else if (!strncmp(msg->control, "cancel", 6))
		    r1 = cancel(msg);
		else
		    r1 = NNTP_UNKNOWN_CONTROLMSG;

		if (r1)
		    syslog(LOG_WARNING, "control message '%s' failed: %s",
			   msg->control, error_message(r1));
		else {
		    syslog(LOG_INFO, "control message '%s' succeeded",
			   msg->control);
		}
	    }
#endif
	    if (msg->id) {
		const char *peers = config_getstring(IMAPOPT_NEWSPEER);

		/* send the article upstream */
		if (peers) {
		    char *tmpbuf, *cur_peer, *next_peer;

		    /* make a working copy of the peers */
		    cur_peer = tmpbuf = xstrdup(peers);

		    while (cur_peer) {
			/* eat any leading whitespace */
			while (Uisspace(*cur_peer)) cur_peer++;

			/* find end of peer */
			if ((next_peer = strchr(cur_peer, ' ')) ||
			    (next_peer = strchr(cur_peer, '\t')))
			    *next_peer++ = '\0';

			/* feed the article to this peer */
			feedpeer(cur_peer, msg);

			/* move to next peer */
			cur_peer = next_peer;
		    }

		    free(tmpbuf);
		}

		/* gateway news to mail */
		news2mail(msg);
	    }
	}

	msg_free(msg); /* does fclose() */
	if (stage) append_removestage(stage);
	stage = NULL;
    }
    else {
	/* flush the article from the stream */
	spool_copy_msg(nntp_in, NULL);
    }

    if (r) {
	prot_printf(nntp_out, "%u %s Failed receiving article (%s)\r\n",
		    post_codes[mode].fail, msgid ? msgid : "",
		    error_message(r));
    }

    prot_flush(nntp_out);
}

Project: cyrus-imapd

static void cmd_list(char *arg1, char *arg2)
{
    if (!arg1)
	arg1 = "active";
    else
	lcase(arg1);

    if (!strcmp(arg1, "active")) {
	char pattern[MAX_MAILBOX_BUFFER];
	struct list_rock lrock;
	struct enum_rock erock;

	if (!arg2) arg2 = "*";

	erock.cmd = "ACTIVE";
	erock.wild = xstrdup(arg2); /* make a copy before we munge it */

	lrock.proc = do_active;
	lrock.wild = split_wildmats(arg2); /* split the list of wildmats */

	/* xxx better way to determine a size for this table? */
	construct_hash_table(&lrock.server_table, 10, 1);

	prot_printf(nntp_out, "215 List of newsgroups follows:\r\n");

	strcpy(pattern, newsprefix);
	strcat(pattern, "*");
	list_cb(NULL, 0, 0, NULL);
	mboxlist_findall(NULL, pattern, 0, nntp_userid, nntp_authstate,
			 list_cb, &lrock);

	/* proxy to the backends */
	hash_enumerate(&lrock.server_table, list_proxy, &erock);

	prot_printf(nntp_out, ".\r\n");

	/* free the hash table */
	free_hash_table(&lrock.server_table, NULL);

	/* free the wildmats */
	free_wildmats(lrock.wild);
	free(erock.wild);

	if (group_state)
	    index_close(&group_state);
    }
    else if (!(nntp_capa & MODE_READ)) {
	prot_printf(nntp_out, "502 Permission denied\r\n");
	return;
    }
    else if (!nntp_userid && !allowanonymous) {
	prot_printf(nntp_out, "480 Authentication required\r\n");
	return;
    }
    else if (!strcmp(arg1, "headers")) {
	if (arg2 && strcmp(arg2, "msgid") && strcmp(arg2, "range")) {
	    prot_printf(nntp_out, "501 Unexpected extra argument\r\n");
	    return;
	}

	prot_printf(nntp_out, "215 Header and metadata list follows:\r\n");
	prot_printf(nntp_out, ":\r\n"); /* all headers */
	prot_printf(nntp_out, ":bytes\r\n");
	prot_printf(nntp_out, ":lines\r\n");
	prot_printf(nntp_out, ".\r\n");
    }
    else if (!strcmp(arg1, "newsgroups")) {
	char pattern[MAX_MAILBOX_BUFFER];
	struct list_rock lrock;
	struct enum_rock erock;

	if (!arg2) arg2 = "*";

	erock.cmd = "NEWSGROUPS";
	erock.wild = xstrdup(arg2); /* make a copy before we munge it */

	lrock.proc = do_newsgroups;
	lrock.wild = split_wildmats(arg2); /* split the list of wildmats */

	/* xxx better way to determine a size for this table? */
	construct_hash_table(&lrock.server_table, 10, 1);

	prot_printf(nntp_out, "215 List of newsgroups follows:\r\n");

	strcpy(pattern, newsprefix);
	strcat(pattern, "*");
	list_cb(NULL, 0, 0, NULL);
	mboxlist_findall(NULL, pattern, 0, nntp_userid, nntp_authstate,
			 list_cb, &lrock);

	/* proxy to the backends */
	hash_enumerate(&lrock.server_table, list_proxy, &erock);

	strcpy(pattern, newsprefix);
	strcat(pattern, "*");
	annotatemore_findall(pattern, 0, "/comment",
			     newsgroups_cb, lrock.wild);

	prot_printf(nntp_out, ".\r\n");

	/* free the hash table */
	free_hash_table(&lrock.server_table, NULL);

	/* free the wildmats */
	free_wildmats(lrock.wild);
	free(erock.wild);
    }
    else if (!strcmp(arg1, "overview.fmt")) {
	if (arg2) {
	    prot_printf(nntp_out, "501 Unexpected extra argument\r\n");
	    return;
	}

	prot_printf(nntp_out, "215 Order of overview fields follows:\r\n");
	prot_printf(nntp_out, "Subject:\r\n");
	prot_printf(nntp_out, "From:\r\n");
	prot_printf(nntp_out, "Date:\r\n");
	prot_printf(nntp_out, "Message-ID:\r\n");
	prot_printf(nntp_out, "References:\r\n");
	if (did_capabilities) {
	    /* new OVER format */
	    prot_printf(nntp_out, ":bytes\r\n");
	    prot_printf(nntp_out, ":lines\r\n");
	} else {
	    /* old XOVER format */
	    prot_printf(nntp_out, "Bytes:\r\n");
	    prot_printf(nntp_out, "Lines:\r\n");
	}
	prot_printf(nntp_out, "Xref:full\r\n");
	prot_printf(nntp_out, ".\r\n");
    }
    else if (!strcmp(arg1, "active.times") || !strcmp(arg1, "distributions") ||
	     !strcmp(arg1, "distrib.pats")) {
	prot_printf(nntp_out, "503 Unsupported LIST command\r\n");
    }
    else {
	prot_printf(nntp_out, "501 Unrecognized LIST command\r\n");
    }
    prot_flush(nntp_out);
}

Project: cyrus-imapd

static int deliver(message_data_t *msg)
{
    int n, r = 0, myrights;
    char *rcpt = NULL;
    unsigned long uid;
    struct body *body = NULL;
    struct dest *dlist = NULL;
    duplicate_key_t dkey = {msg->id, NULL, msg->date};

    /* check ACLs of all mailboxes */
    for (n = 0; n < msg->rcpt.count; n++) {
	struct mboxlist_entry *mbentry = NULL;
	rcpt = msg->rcpt.data[n];
	dkey.to = rcpt;

	/* look it up */
	r = mlookup(rcpt, &mbentry);
	if (r) return IMAP_MAILBOX_NONEXISTENT;

	if (!(mbentry->acl && (myrights = cyrus_acl_myrights(nntp_authstate, mbentry->acl)) &&
	      (myrights & ACL_POST))) {
	    mboxlist_entry_free(&mbentry);
	    return IMAP_PERMISSION_DENIED;
	}

	if (mbentry->server) {
	    /* remote group */
	    proxy_adddest(&dlist, NULL, 0, mbentry->server, "");
	}
	else {
	    /* local group */
	    struct appendstate as;

	    if (msg->id && 
		duplicate_check(&dkey)) {
		/* duplicate message */
		duplicate_log(&dkey, "nntp delivery");
		continue;
	    }

	    r = append_setup(&as, rcpt, nntp_userid, nntp_authstate,
			     ACL_POST, 0, NULL, 0);

	    if (!r) {
		prot_rewind(msg->data);
		if (stage) {
		    r = append_fromstage(&as, &body, stage, 0,
					 NULL, !singleinstance,
					 /*annotations*/NULL);
		} else {
		    /* XXX should never get here */
		    r = append_fromstream(&as, &body, msg->data, msg->size, 0,
					  (const char **) NULL, 0);
		}
		if (r || ( msg->id && duplicate_check(&dkey) ) ) {    
		    append_abort(&as);
                   
		    if (!r) {
			/* duplicate message */
			duplicate_log(&dkey, "nntp delivery");
			continue;
		    }            
		}                
		else {           
		    r = append_commit(&as, NULL, &uid, NULL, NULL);
		}
	    }

	    if (!r && msg->id)
		duplicate_mark(&dkey, time(NULL), uid);

	    if (r) {
		mboxlist_entry_free(&mbentry);
		return r;
	    }
	}
	mboxlist_entry_free(&mbentry);
    }

    if (body) {
	message_free_body(body);
	free(body);
    }

    if (dlist) {
	struct dest *d;

	/* run the txns */
	r = deliver_remote(msg, dlist);

	/* free the destination list */
	d = dlist;
	while (d) {
	    struct dest *nextd = d->next;
	    free(d);
	    d = nextd;
	}
    }

    return r;
}

Project: cyrus-imapd

static void cmd_authinfo_pass(char *pass)
{
    int failedloginpause;
    /* Conceal password in telemetry log */
    if (nntp_logfd != -1 && pass) {
	(void)ftruncate(nntp_logfd,
		  lseek(nntp_logfd, -2, SEEK_CUR) - strlen(pass));
	(void)write(nntp_logfd, "...\r\n", 5);
    }

    if (nntp_authstate) {
	prot_printf(nntp_out, "502 Already authenticated\r\n");
	return;
    }

    if (!nntp_userid) {
	prot_printf(nntp_out, "482 Must give AUTHINFO USER command first\r\n");
	return;
    }

    if (!strcmp(nntp_userid, "anonymous")) {
	if (allowanonymous) {
	    pass = beautify_string(pass);
	    if (strlen(pass) > 500) pass[500] = '\0';
	    syslog(LOG_NOTICE, "login: %s anonymous %s",
		   nntp_clienthost, pass);
	}
	else {
	    syslog(LOG_NOTICE, "badlogin: %s anonymous login refused",
		   nntp_clienthost);
	    prot_printf(nntp_out, "481 Invalid login\r\n");
	    return;
	}
    }
    else if (sasl_checkpass(nntp_saslconn,
			    nntp_userid,
			    strlen(nntp_userid),
			    pass,
			    strlen(pass))!=SASL_OK) { 
	syslog(LOG_NOTICE, "badlogin: %s plaintext %s %s",
	       nntp_clienthost, nntp_userid, sasl_errdetail(nntp_saslconn));
	failedloginpause = config_getint(IMAPOPT_FAILEDLOGINPAUSE);
	if (failedloginpause != 0) {
	    sleep(failedloginpause);
	}
	prot_printf(nntp_out, "481 Invalid login\r\n");
	free(nntp_userid);
	nntp_userid = 0;

	return;
    }
    else {
	syslog(LOG_NOTICE, "login: %s %s plaintext%s %s", nntp_clienthost,
	       nntp_userid, nntp_starttls_done ? "+TLS" : "",
	       "User logged in");

	prot_printf(nntp_out, "281 User logged in\r\n");

	nntp_authstate = auth_newstate(nntp_userid);

	/* Close IP-based telemetry log and create new log based on userid */
	if (nntp_logfd != -1) close(nntp_logfd);
	nntp_logfd = telemetry_log(nntp_userid, nntp_in, nntp_out, 0);
    }
}

Project: cyrus-imapd

static int do_active(char *name, void *rock)
{
    struct list_rock *lrock = (struct list_rock *) rock;
    int r, postable;
    struct backend *be;

    /* open the group */
    r = open_group(name, 1, &be, &postable);
    if (r) {
	/* can't open group, skip it */
    }
    else if (be) {
	if (!hash_lookup(be->hostname, &lrock->server_table)) {
	    /* add this server to our table */
	    hash_insert(be->hostname, (void *)0xDEADBEEF, &lrock->server_table);
	}
    }
    else {
	prot_printf(nntp_out, "%s %u %u %c\r\n", name+strlen(newsprefix),
		    group_state->exists ? index_getuid(group_state, group_state->exists) :
		    group_state->mailbox->i.last_uid,
		    group_state->exists ? index_getuid(group_state, 1) :
		    group_state->mailbox->i.last_uid+1,
		    postable ? 'y' : 'n');
	index_close(&group_state);
    }

    return 0;
}

Project: cyrus-imapd

static int do_newsgroups(char *name, void *rock)
{
    struct list_rock *lrock = (struct list_rock *) rock;
    struct mboxlist_entry *mbentry = NULL;
    int r;

    r = mlookup(name, &mbentry);

    if (r || !mbentry->acl ||
	!(cyrus_acl_myrights(nntp_authstate, mbentry->acl) && ACL_LOOKUP)) {
	mboxlist_entry_free(&mbentry);
	return 0;
    }

    if (mbentry->server) {
	/* remote group */
	if (!hash_lookup(mbentry->server, &lrock->server_table)) {
	    /* add this server to our table */
	    hash_insert(mbentry->server, (void *)0xDEADBEEF, &lrock->server_table);
	}
	mboxlist_entry_free(&mbentry);
    }
    else {
	/* local group */
	mboxlist_entry_free(&mbentry);
	return CYRUSDB_DONE;
    }

    return 0;
}

Project: cyrus-imapd

static void cmd_authinfo_user(char *user)
{
    const char *p;

    if (nntp_authstate) {
	prot_printf(nntp_out, "502 Already authenticated\r\n");
	return;
    }

    /* possibly disallow USER */
    if (!(nntp_starttls_done || (extprops_ssf > 1) ||
	  config_getswitch(IMAPOPT_ALLOWPLAINTEXT))) {
	prot_printf(nntp_out,
		    "483 AUTHINFO USER command only available under a layer\r\n");
	return;
    }

    if (nntp_userid) {
	free(nntp_userid);
	nntp_userid = NULL;
    }

    if (!(p = canonify_userid(user, NULL, NULL))) {
	prot_printf(nntp_out, "481 Invalid user\r\n");
	syslog(LOG_NOTICE,
	       "badlogin: %s plaintext %s invalid user",
	       nntp_clienthost, beautify_string(user));
    }
    else {
	nntp_userid = xstrdup(p);
	prot_printf(nntp_out, "381 Give AUTHINFO PASS command\r\n");
    }
}

Project: cyrus-imapd

static int savemsg(message_data_t *m, FILE *f)
{
    struct stat sbuf;
    const char **body, **groups;
    int r, i;
    time_t now = time(NULL);
    static int post_count = 0;
    FILE *stagef = NULL;
    const char *skipheaders[] = {
	"Path",		/* need to prepend our servername */
	"Xref",		/* need to remove (generated on the fly) */
	"Reply-To",	/* need to add "post" email addresses */
	NULL
    };

    m->f = f;

    /* fill the cache */
    r = spool_fill_hdrcache(nntp_in, f, m->hdrcache, skipheaders);
    if (r) {
	/* got a bad header */

	/* flush the remaining output */
	spool_copy_msg(nntp_in, NULL);
	return r;
    }

    /* now, using our header cache, fill in the data that we want */

    /* get path */
    if ((body = spool_getheader(m->hdrcache, "path")) != NULL) {
	/* prepend to the cached path */
	m->path = strconcat(config_servername, "!", body[0], (char *)NULL);
	spool_replace_header(xstrdup("Path"), xstrdup(m->path), m->hdrcache);
    } else {
	/* no path, create one */
	m->path = strconcat(config_servername, "!",
			    nntp_userid ? nntp_userid : "anonymous",
			    (char *)NULL);
	spool_cache_header(xstrdup("Path"), xstrdup(m->path), m->hdrcache);
    }
    fprintf(f, "Path: %s\r\n", m->path);

    /* get message-id */
    if ((body = spool_getheader(m->hdrcache, "message-id")) != NULL) {
	m->id = xstrdup(body[0]);
    } else {
	/* no message-id, create one */
	pid_t p = getpid();

	m->id = xmalloc(40 + strlen(config_servername));
	sprintf(m->id, "<cmu-nntpd-%d-%d-%d@%s>", p, (int) now, 
		post_count++, config_servername);
	fprintf(f, "Message-ID: %s\r\n", m->id);
	spool_cache_header(xstrdup("Message-ID"), xstrdup(m->id), m->hdrcache);
    }

    /* get date */
    if ((body = spool_getheader(m->hdrcache, "date")) == NULL) {
	/* no date, create one */
	char datestr[RFC822_DATETIME_MAX+1];

	time_to_rfc822(now, datestr, sizeof(datestr));
	m->date = xstrdup(datestr);
	fprintf(f, "Date: %s\r\n", datestr);
	spool_cache_header(xstrdup("Date"), xstrdup(datestr), m->hdrcache);
    }
    else {
	m->date = xstrdup(body[0]);
    }

    /* get control */
    if ((body = spool_getheader(m->hdrcache, "control")) != NULL) {
	size_t len;
	char *s;

	m->control = xstrdup(body[0]);

	/* create a recipient for the appropriate pseudo newsgroup */
	len = strcspn(m->control, " \t\r\n");
	s = xmalloc(strlen(newsprefix) + 8 + len + 1);
	sprintf(s, "%scontrol.%.*s", newsprefix, (int) len, m->control);

	strarray_appendm(&m->rcpt, s);
    } else {
	m->control = NULL;	/* no control */

	/* get newsgroups */
	if ((groups = spool_getheader(m->hdrcache, "newsgroups")) != NULL) {
	    /* parse newsgroups and create recipients */
	    parse_groups(groups[0], m);
	    if (!m->rcpt.count) {
		r = IMAP_MAILBOX_NONEXISTENT; /* no newsgroups that we serve */
	    }
	    if (!r) {
		const char *newspostuser = config_getstring(IMAPOPT_NEWSPOSTUSER);
		/* get reply-to */
		body = spool_getheader(m->hdrcache, "reply-to");

		/* add Reply-To: header */
		if (body || newspostuser) {
		    const char **postto, *p;
		    int fold = 0;
		    const char *sep = "";
		    char *replyto;
		    size_t n;
		    struct buf buf = BUF_INITIALIZER;

		    if (newspostuser) {
			/* add "post" email addresses based on newsgroup */

			/* determine which groups header to use */
			postto = spool_getheader(m->hdrcache, "followup-to");
			if (!postto) postto = groups;

			if (body) {
			    /* append to the cached header */
			    buf_appendcstr(&buf, body[0]);
			    fold = buf.len+1;
			    sep = ", ";
			}
			for (p = postto[0];; p += n) {
			    /* skip whitespace */
			    while (p && *p &&
				   (Uisspace(*p) || *p == ',')) p++;
			    if (!p || !*p) break;

			    /* find end of group name */
			    n = strcspn(p, ", \t");

			    /* add the post address */
			    buf_printf(&buf, "%s%s+%.*s",
					 sep, newspostuser, (int) n, p);

			    sep = ", ";
			}
			replyto = buf_release(&buf);
			if (body) {
			    /* replace the existing header */
			    spool_replace_header(xstrdup("Reply-To"), replyto,
					         m->hdrcache);
			} else {
			    /* add the new header to the cache */
			    spool_cache_header(xstrdup("Reply-To"), replyto,
					       m->hdrcache);
			}
		    } else {
			/* no newspostuser, use original replyto */
			replyto = (char *) body[0];
		    }

		    /* add the header to the file */
		    fprintf(f, "Reply-To: ");
		    if (fold)
			fprintf(f, "%.*s\r\n\t", fold, replyto);
		    fprintf(f, "%s\r\n", replyto+fold);
		}
	    }
	} else {
	    r = NNTP_NO_NEWSGROUPS;		/* no newsgroups header */
	}

	if (r) {
	    /* error getting newsgroups */

	    /* flush the remaining output */
	    spool_copy_msg(nntp_in, NULL);
	    return r;
	}
    }

    fflush(f);
    if (ferror(f)) {
	return IMAP_IOERROR;
    }

    if (fstat(fileno(f), &sbuf) == -1) {
	return IMAP_IOERROR;
    }

    /* spool to the stage of one of the recipients */
    for (i = 0; !stagef && (i < m->rcpt.count); i++) {
	stagef = append_newstage(m->rcpt.data[i], now, 0, &stage);
    }

    if (stagef) {
	const char *base = 0;
	unsigned long size = 0;
	int n;

	/* copy the header from our tmpfile to the stage */
	map_refresh(fileno(f), 1, &base, &size, sbuf.st_size, "tmp", 0);
	n = retry_write(fileno(stagef), base, size);
	map_free(&base, &size);

	if (n == -1) {
	    /* close and remove the stage */
	    fclose(stagef);
	    append_removestage(stage);
	    stage = NULL;
	    return IMAP_IOERROR;
	}
	else {
	    /* close the tmpfile and use the stage */
	    fclose(f);
	    m->f = f = stagef;
	}
    }
    /* else this is probably a remote group, so use the tmpfile */

    r = spool_copy_msg(nntp_in, f);

    if (r) return r;

    fflush(f);
    if (ferror(f)) {
	return IMAP_IOERROR;
    }

    if (fstat(fileno(f), &sbuf) == -1) {
	return IMAP_IOERROR;
    }
    m->size = sbuf.st_size;
    m->data = prot_new(fileno(f), 0);

    return 0;
}

Project: cyrus-imapd

static int mlookup(const char *name, struct mboxlist_entry **mbentryptr)
{
    struct mboxlist_entry *mbentry = NULL;
    int r;
    
    r = mboxlist_lookup(name, &mbentry, NULL);
    if (r == IMAP_MAILBOX_NONEXISTENT && config_mupdate_server) {
	kick_mupdate();
	mboxlist_entry_free(&mbentry);
	r = mboxlist_lookup(name, &mbentry, NULL);
    }

    if (mbentryptr && !r) *mbentryptr = mbentry;
    else mboxlist_entry_free(&mbentry);

    return r;
}

Project: cyrus-imapd

void msg_free(message_data_t *m)
{
    if (m->data)
	prot_free(m->data);
    if (m->f)
	fclose(m->f);
    free(m->id);
    free(m->path);
    free(m->control);
    free(m->date);

    strarray_fini(&m->rcpt);

    spool_free_hdrcache(m->hdrcache);

    free(m);
}

Project: cyrus-imapd

static int parserange(char *str, uint32_t *uid, uint32_t *last,
		      char **msgid, struct backend **ret)
{
    const char *p = NULL;
    char *mboxname;
    int r = 0;

    *uid = 0;
    if (last) *last = 0;
    if (msgid) *msgid = NULL;
    if (ret) *ret = NULL;

    if (!str || !*str) {
	/* no argument, use current article */
	if (backend_current) {
	    if (ret) *ret = backend_current;
	}
	else if (!group_state) goto noopengroup;
	else if (!nntp_current) goto nocurrent;
	else {
	    *uid = index_getuid(group_state, nntp_current);
	    if (last) *last = *uid;
	}
    }
    else if (*str == '<') {
	/* message-id, find server and/or mailbox */
	if (!msgid) goto badrange;
	if (!my_find_msgid(str, &mboxname, uid)) goto nomsgid;

	*msgid = str;

	/* open group if its different from our current one */
	if (!group_state || strcmp(mboxname, group_state->mailbox->name)) {
	    if ((r = open_group(mboxname, 1, ret, NULL))) goto nomsgid;
	}
    }
    else if (backend_current)
	*ret = backend_current;
    else if (!group_state) goto noopengroup;
    else if (parseuint32(str, &p, uid) || uid == 0) goto badrange;
    else if (p && *p) {
	/* extra stuff, check for range */
	if (!last || (*p != '-')) goto badrange;
	if (*++p) {
	    if (parseuint32(p, NULL, last))
		*last = 0;
	}
	else
	    *last = UINT32_MAX;  /* open range -> use highest possible UID */
    }

    if (last && !*last) *last = *uid;

    return 0;

  noopengroup:
    prot_printf(nntp_out, "412 No newsgroup selected\r\n");
    return -1;

  nocurrent:
    prot_printf(nntp_out, "420 Current article number is invalid\r\n");
    return -1;

  nomsgid:
    prot_printf(nntp_out, "430 No article found with that message-id");
    if (r) prot_printf(nntp_out, " (%s)", error_message(r));
    prot_printf(nntp_out, "\r\n");
    return -1;

  badrange:
    prot_printf(nntp_out, "501 Bad message-id, message number, or range\r\n");
    return -1;
}

Project: cyrus-imapd

static int list_cb(char *name, int matchlen,
		   int maycreate __attribute__((unused)), void *rock)
{
    static char lastname[MAX_MAILBOX_BUFFER];
    struct list_rock *lrock = (struct list_rock *) rock;
    struct wildmat *wild;

    /* We have to reset the initial state.
     * Handle it as a dirty hack.
     */
    if (!name) {
	lastname[0] = '\0';
	return 0;
    }

    /* skip mailboxes that we don't want to serve as newsgroups */
    if (!is_newsgroup(name)) return 0;

    /* don't repeat */
    if (matchlen == (int) strlen(lastname) &&
	!strncmp(name, lastname, matchlen)) return 0;

    strncpy(lastname, name, matchlen);
    lastname[matchlen] = '\0';

    /* see if the mailbox matches one of our specified wildmats */
    wild = lrock->wild;
    while (wild->pat && wildmat(name, wild->pat) != 1) wild++;

    /* if we don't have a match, or its a negative match, skip it */
    if (!wild->pat || wild->not) return 0;

    return lrock->proc(name, lrock);
}

Project: cyrus-imapd

void list_proxy(const char *server, void *data __attribute__((unused)), void *rock)
{
    struct enum_rock *erock = (struct enum_rock *) rock;
    struct backend *be;
    int r;
    char *result;

    be = proxy_findserver(server, &nntp_protocol,
			  nntp_userid ? nntp_userid : "anonymous",
			  &backend_cached, &backend_current, NULL, nntp_in);
    if (!be) return;

    prot_printf(be->out, "LIST %s %s\r\n", erock->cmd, erock->wild);

    r = read_response(be, 0, &result);
    if (!r && !strncmp(result, "215 ", 4)) {
	while (!(r = read_response(be, 0, &result)) && result[0] != '.') {
	    prot_printf(nntp_out, "%s", result);
	}
    }
}

Project: cyrus-imapd

static int open_group(char *name, int has_prefix, struct backend **ret,
		      int *postable /* used for LIST ACTIVE only */)
{
    char mailboxname[MAX_MAILBOX_BUFFER];
    int r = 0;
    struct mboxlist_entry *mbentry = NULL;
    struct backend *backend_next = NULL;

    /* close local group */
    if (group_state) 
	index_close(&group_state);

    if (!has_prefix) {
	snprintf(mailboxname, sizeof(mailboxname), "%s%s", newsprefix, name);
	name = mailboxname;
    }

    if (!r) r = mlookup(name, &mbentry);

    if (!r && mbentry->acl) {
	int myrights = cyrus_acl_myrights(nntp_authstate, mbentry->acl);

	if (postable) *postable = myrights & ACL_POST;
	if (!postable && /* allow limited 'r' for LIST ACTIVE */
	    !(myrights & ACL_READ)) {
	    r = (myrights & ACL_LOOKUP) ?
		IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
	}
    }

    if (r) {
	mboxlist_entry_free(&mbentry);
	return r;
    }

    if (mbentry->server) {
	/* remote group */
	backend_next = proxy_findserver(mbentry->server, &nntp_protocol,
					nntp_userid ? nntp_userid : "anonymous",
					&backend_cached, &backend_current,
					NULL, nntp_in);
	mboxlist_entry_free(&mbentry);
	if (!backend_next) return IMAP_SERVER_UNAVAILABLE;

	*ret = backend_next;
    }
    else {
	/* local group */
	struct index_init init;

	mboxlist_entry_free(&mbentry);
	memset(&init, 0, sizeof(struct index_init));
	init.userid = nntp_userid;
	init.authstate = nntp_authstate;
	r = index_open(name, &init, &group_state);
	if (r) return r;

	if (ret) *ret = NULL;
    }

    syslog(LOG_DEBUG, "open: user %s opened %s",
	   nntp_userid ? nntp_userid : "anonymous", name);

    return 0;
}

Project: cyrus-imapd

static void news2mail(message_data_t *msg)
{
    struct buf attrib = BUF_INITIALIZER;
    int n, r;
    FILE *sm;
    static strarray_t smbuf = STRARRAY_INITIALIZER;
    static int smbuf_basic_count;
    int sm_stat;
    pid_t sm_pid;
    char buf[4096], to[1024] = "";

    if (!smbuf.count) {
	strarray_append(&smbuf, "sendmail");
	strarray_append(&smbuf, "-i");		/* ignore dots */
	strarray_append(&smbuf, "-f");
	strarray_append(&smbuf, "<>");
	strarray_append(&smbuf, "--");
	smbuf_basic_count = smbuf.count;
    }

    for (n = 0; n < msg->rcpt.count ; n++) {
	/* see if we want to send this to a mailing list */
	buf_free(&attrib);
	r = annotatemore_lookup(msg->rcpt.data[n],
				"/vendor/cmu/cyrus-imapd/news2mail", "",
				&attrib);
	if (r) continue;

	/* add the email address to our argv[] and to our To: header */
	if (attrib.s) {
	    strarray_append(&smbuf, buf_cstring(&attrib));

	    if (to[0]) strlcat(to, ", ", sizeof(to));
	    strlcat(to, buf_cstring(&attrib), sizeof(to));
	}
    }
    buf_free(&attrib);

    /* send the message */
    if (smbuf.count > smbuf_basic_count) {
	sm_pid = open_sendmail((const char **)smbuf.data, &sm);

	if (!sm)
	    syslog(LOG_ERR, "news2mail: could not spawn sendmail process");
	else {
	    int body = 0, skip, found_to = 0;

	    rewind(msg->f);

	    while (fgets(buf, sizeof(buf), msg->f)) {
		if (!body && buf[0] == '\r' && buf[1] == '\n') {
		    /* blank line between header and body */
		    body = 1;

		    /* insert a To: header if the message doesn't have one */
		    if (!found_to) fprintf(sm, "To: %s\r\n", to);
		}

		skip = 0;
		if (!body) {
		    /* munge various news-specific headers */
		    if (!strncasecmp(buf, "Newsgroups:", 11)) {
			/* rename Newsgroups: to X-Newsgroups: */
			fprintf(sm, "X-");
		    } else if (!strncasecmp(buf, "Xref:", 5) ||
			       !strncasecmp(buf, "Path:", 5) ||
			       !strncasecmp(buf, "NNTP-Posting-", 13)) {
			/* skip these (for now) */
			skip = 1;
		    } else if (!strncasecmp(buf, "To:", 3)) {
			/* insert our mailing list RCPTs first, and then
			   fold the header to accomodate the original RCPTs */
			fprintf(sm, "To: %s,\r\n", to);
			/* overwrite the original "To:" with spaces */
			memset(buf, ' ', 3);
			found_to = 1;
		    } else if (!strncasecmp(buf, "Reply-To:", 9)) {
			/* strip any post addresses, skip if becomes empty */
			if (!strip_post_addresses(buf+9)) skip = 1;
		    }
		}

		do {
		    if (!skip) fprintf(sm, "%s", buf);
		} while (buf[strlen(buf)-1] != '\n' &&
			 fgets(buf, sizeof(buf), msg->f));
	    }

	    /* Protect against messages not ending in CRLF */
	    if (buf[strlen(buf)-1] != '\n') fprintf(sm, "\r\n");

	    fclose(sm);
	    while (waitpid(sm_pid, &sm_stat, 0) < 0);

	    if (sm_stat) /* sendmail exit value */
		syslog(LOG_ERR, "news2mail failed: %s",
		       sendmail_errstr(sm_stat));
	}

	/* free the RCPTs */
	strarray_truncate(&smbuf, smbuf_basic_count);
    }

    return;
}

Project: cyrus-imapd

static int my_find_msgid(char *msgid, char **mailbox, uint32_t *uid)
{
    struct findrock frock = { NULL, 0 };

    duplicate_find(msgid, &find_cb, &frock);

    if (!frock.mailbox) return 0;

    if (mailbox) {
	if (!frock.mailbox[0]) return 0;
	*mailbox = (char *) frock.mailbox;
    }
    if (uid) {
	if (!frock.uid) return 0;
	*uid = frock.uid;
    }

    return 1;
}

Project: cyrus-imapd

int service_init(int argc __attribute__((unused)),
		 char **argv __attribute__((unused)),
		 char **envp __attribute__((unused)))
{
    int opt;
    const char *prefix;

    initialize_nntp_error_table();

    if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
    setproctitle_init(argc, argv, envp);

    /* set signal handlers */
    signals_set_shutdown(&shut_down);
    signal(SIGPIPE, SIG_IGN);

    /* load the SASL plugins */
    global_sasl_init(1, 1, mysasl_cb);

    if ((prefix = config_getstring(IMAPOPT_NEWSPREFIX)))
	snprintf(newsprefix, sizeof(newsprefix), "%s.", prefix);

    newsgroups = split_wildmats((char *) config_getstring(IMAPOPT_NEWSGROUPS));

    /* initialize duplicate delivery database */
    if (duplicate_init(NULL, 0) != 0) {
	syslog(LOG_ERR, 
	       "unable to init duplicate delivery database\n");
	fatal("unable to init duplicate delivery database", EC_SOFTWARE);
    }

    /* open the mboxlist, we'll need it for real work */
    mboxlist_init(0);
    mboxlist_open(NULL);

    /* open the quota db, we'll need it for expunge */
    quotadb_init(0);
    quotadb_open(NULL);

    /* open the user deny db */
    denydb_init(0);
    denydb_open(NULL);

    /* setup for sending IMAP IDLE notifications */
    idle_enabled();

    while ((opt = getopt(argc, argv, "srfp:")) != EOF) {
	switch(opt) {
	case 's': /* nntps (do starttls right away) */
	    nntps = 1;
	    if (!tls_enabled()) {
		syslog(LOG_ERR, "nntps: required OpenSSL options not present");
		fatal("nntps: required OpenSSL options not present",
		      EC_CONFIG);
	    }
	    break;

	case 'r': /* enter reader-only mode */
	    nntp_capa = MODE_READ;
	    break;

	case 'f': /* enter feeder-only mode */
	    nntp_capa = MODE_FEED;
	    break;

	case 'p': /* external protection */
	    extprops_ssf = atoi(optarg);
	    break;

	default:
	    usage();
	}
    }

    /* Initialize the annotatemore extention */
    annotatemore_init(NULL, NULL);
    annotatemore_open();

    newsmaster = (char *) config_getstring(IMAPOPT_NEWSMASTER);
    newsmaster_authstate = auth_newstate(newsmaster);

    singleinstance = config_getswitch(IMAPOPT_SINGLEINSTANCESTORE);

    /* Create a protgroup for input from the client and selected backend */
    protin = protgroup_new(2);

    return 0;
}

Project: cyrus-imapd

int newsgroups_cb(const char *mailbox,
		  uint32_t uid __attribute__((unused)),
		  const char *entry __attribute__((unused)),
		  const char *userid,
		  const struct buf *value, void *rock)
{
    struct wildmat *wild = (struct wildmat *) rock;

    /* skip personal mailboxes */
    if ((!strncasecmp(mailbox, "INBOX", 5) &&
	 (!mailbox[5] || mailbox[5] == '.')) ||
	!strncmp(mailbox, "user.", 5))
	return 0;

    /* see if the mailbox matches one of our wildmats */
    while (wild->pat && wildmat(mailbox, wild->pat) != 1) wild++;

    /* if we don't have a match, or its a negative match, skip it */
    if (!wild->pat || wild->not) return 0;

    /* we only care about shared /comment */
    if (userid[0]) return 0;

    prot_printf(nntp_out, "%s\t%s\r\n", mailbox+strlen(newsprefix),
		value->s);

    return 0;
}