Commit message: imapd: check for isadmin BEFORE parsing sync lines
CVE: CVE-2017-12843
CWE: CWE-20
Vulnerability description: Cyrus IMAP before 3.0.3 allows remote authenticated users to write to arbitrary files via a crafted (1) SYNCAPPLY, (2) SYNCGET or (3) SYNCRESTORE command.

Project: cyrus-imapd
File: imapd.c

static void _addsubs(struct list_rock *rock)
{
    if (!rock->subs) return;
    if (!rock->last_mbentry) return;
    int i;
    const char *last_name = rock->last_mbentry->name;
    int namelen = strlen(last_name);
    for (i = 0; i < rock->subs->count; i++) {
        const char *name = strarray_nth(rock->subs, i);
        if (strncmp(last_name, name, namelen))
            continue;
        else if (!name[namelen]) {
            if ((rock->last_attributes & MBOX_ATTRIBUTE_NONEXISTENT))
                rock->last_attributes |= MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED;
            else
                rock->last_attributes |= MBOX_ATTRIBUTE_SUBSCRIBED;
        }
        else if (name[namelen] == '.')
            rock->last_attributes |= MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED;
    }
}

Project: cyrus-imapd
File: imapd.c

static int append_catenate(FILE *f, const char *cur_name, unsigned *totalsize,
                           int *binary, const char **parseerr, const char **url)
{
    int c, r = 0;
    static struct buf arg;

    do {
        c = getword(imapd_in, &arg);
        if (c != ' ') {
            *parseerr = "Missing message part data in Append command";
            return IMAP_PROTOCOL_ERROR;
        }

        if (!strcasecmp(arg.s, "TEXT")) {
            int r1 = catenate_text(f, totalsize, binary, parseerr);
            if (r1) return r1;

            /* if we see a SP, we're trying to catenate more than one part */

            /* Parse newline terminating command */
            c = prot_getc(imapd_in);
        }
        else if (!strcasecmp(arg.s, "URL")) {
            c = getastring(imapd_in, imapd_out, &arg);
            if (c != ' ' && c != ')') {
                *parseerr = "Missing URL in Append command";
                return IMAP_PROTOCOL_ERROR;
            }

            if (!r) {
                r = catenate_url(arg.s, cur_name, f, totalsize, parseerr);
                if (r) {
                    *url = arg.s;
                    return r;
                }
            }
        }
        else {
            *parseerr = "Invalid message part type in Append command";
            return IMAP_PROTOCOL_ERROR;
        }

        fflush(f);
    } while (c == ' ');

    if (c != ')') {
        *parseerr = "Missing space or ) after catenate list in Append command";
        return IMAP_PROTOCOL_ERROR;
    }

    if (ferror(f) || fsync(fileno(f))) {
        syslog(LOG_ERR, "IOERROR: writing message: %m");
        return IMAP_IOERROR;
    }

    return r;
}

Project: cyrus-imapd
File: imapd.c

static int backend_version(struct backend *be)
{
    const char *minor;

    /* IMPORTANT:
     *
     * When adding checks for new versions, you must also backport these
     * checks to previous versions (especially 2.4 and 2.5).
     *
     * Otherwise, old versions will be unable to recognise the new version,
     * assume it is ancient, and downgrade the index to the oldest version
     * supported (version 6, prior to v2.3).
     */

    /* It's like looking in the mirror and not suffering from schizophrenia */
    if (strstr(be->banner, cyrus_version())) {
        return MAILBOX_MINOR_VERSION;
    }

    /* master branch? */
    if (strstr(be->banner, "Cyrus IMAP 3.0")) {
        return 13;
    }

    /* version 2.5 is 13 */
    if (strstr(be->banner, "Cyrus IMAP 2.5.")
     || strstr(be->banner, "Cyrus IMAP Murder 2.5.")
     || strstr(be->banner, "git2.5.")) {
        return 13;
    }

    /* version 2.4 was all 12 */
    if (strstr(be->banner, "v2.4.") || strstr(be->banner, "git2.4.")) {
        return 12;
    }

    minor = strstr(be->banner, "v2.3.");
    if (!minor) return 6;

    /* at least version 2.3.10 */
    if (minor[1] != ' ') {
        return 10;
    }
    /* single digit version, figure out which */
    switch (minor[0]) {
    case '0':
    case '1':
    case '2':
    case '3':
        return 7;
        break;

    case '4':
    case '5':
    case '6':
        return 8;
        break;

    case '7':
    case '8':
    case '9':
        return 9;
        break;
    }

    /* fallthrough, shouldn't happen */
    return 6;
}

Project: cyrus-imapd
File: imapd.c

static void clear_id() {
    if (imapd_id.params) {
        freeattvalues(imapd_id.params);
    }
    memset(&imapd_id, 0, sizeof(struct id_data));
}

Project: cyrus-imapd
File: imapd.c

static void cmd_append(char *tag, char *name, const char *cur_name)
{
    int c;
    static struct buf arg;
    time_t now = time(NULL);
    quota_t qdiffs[QUOTA_NUMRESOURCES] = QUOTA_DIFFS_INITIALIZER;
    unsigned size;
    int sync_seen = 0;
    int r;
    int i;
    struct appendstate appendstate;
    unsigned long uidvalidity = 0;
    long doappenduid = 0;
    const char *parseerr = NULL, *url = NULL;
    struct appendstage *curstage;
    mbentry_t *mbentry = NULL;

    /* See if we can append */
    char *intname = mboxname_from_external(name, &imapd_namespace, imapd_userid);
    r = mlookup(tag, name, intname, &mbentry);

    if (!r && (mbentry->mbtype & MBTYPE_REMOTE)) {
        /* remote mailbox */
        struct backend *s = NULL;

        if (supports_referrals) {
            imapd_refer(tag, mbentry->server, name);
            /* Eat the argument */
            eatline(imapd_in, prot_getc(imapd_in));
            mboxlist_entry_free(&mbentry);
            free(intname);
            return;
        }

        s = proxy_findserver(mbentry->server, &imap_protocol,
                             proxy_userid, &backend_cached,
                             &backend_current, &backend_inbox, imapd_in);
        if (!s) r = IMAP_SERVER_UNAVAILABLE;

        mboxlist_entry_free(&mbentry);

        imapd_check(s, 0);

        if (!r) {
            int is_active = 1;
            s->context = (void*) &is_active;
            if (imapd_index) {
                const char *mboxname = index_mboxname(imapd_index);
                prot_printf(s->out, "%s Localappend {" SIZE_T_FMT "+}\r\n%s"
                            " {" SIZE_T_FMT "+}\r\n%s ",
                            tag, strlen(name), name,
                            strlen(mboxname), mboxname);
            } else {
                prot_printf(s->out, "%s Localappend {" SIZE_T_FMT "+}\r\n%s"
                            " \"\" ", tag, strlen(name), name);
            }
            if (!(r = pipe_command(s, 16384))) {
                pipe_including_tag(s, tag, 0);
            }
            s->context = NULL;
        } else {
            eatline(imapd_in, prot_getc(imapd_in));
        }

        if (r) {
            prot_printf(imapd_out, "%s NO %s\r\n", tag,
                        prot_error(imapd_in) ? prot_error(imapd_in) :
                        error_message(r));
        }
        free(intname);

        return;
    }

    mboxlist_entry_free(&mbentry);

    /* local mailbox */
    if (!r) {
        qdiffs[QUOTA_MESSAGE] = 1;
        r = append_check(intname, imapd_authstate, ACL_INSERT, ignorequota ? NULL : qdiffs);
    }
    if (r) {
        eatline(imapd_in, ' ');
        prot_printf(imapd_out, "%s NO %s%s\r\n",
                    tag,
                    (r == IMAP_MAILBOX_NONEXISTENT &&
                     mboxlist_createmailboxcheck(intname, 0, 0,
                                                 imapd_userisadmin,
                                                 imapd_userid, imapd_authstate,
                                                 NULL, NULL, 0) == 0)
                    ? "[TRYCREATE] " : "", error_message(r));
        free(intname);
        return;
    }

    c = ' '; /* just parsed a space */
    /* we loop, to support MULTIAPPEND */
    while (!r && c == ' ') {
        curstage = xzmalloc(sizeof(*curstage));
        ptrarray_push(&stages, curstage);

        /* now parsing "append-opts" in the ABNF */

        /* Parse flags */
        c = getword(imapd_in, &arg);
        if  (c == '(' && !arg.s[0]) {
            strarray_init(&curstage->flags);
            do {
                c = getword(imapd_in, &arg);
                if (!curstage->flags.count && !arg.s[0] && c == ')') break; /* empty list */
                if (!isokflag(arg.s, &sync_seen)) {
                    parseerr = "Invalid flag in Append command";
                    r = IMAP_PROTOCOL_ERROR;
                    goto done;
                }
                strarray_append(&curstage->flags, arg.s);
            } while (c == ' ');
            if (c != ')') {
                parseerr =
                    "Missing space or ) after flag name in Append command";
                r = IMAP_PROTOCOL_ERROR;
                goto done;
            }
            c = prot_getc(imapd_in);
            if (c != ' ') {
                parseerr = "Missing space after flag list in Append command";
                r = IMAP_PROTOCOL_ERROR;
                goto done;
            }
            c = getword(imapd_in, &arg);
        }

        /* Parse internaldate */
        if (c == '\"' && !arg.s[0]) {
            prot_ungetc(c, imapd_in);
            c = getdatetime(&(curstage->internaldate));
            if (c != ' ') {
                parseerr = "Invalid date-time in Append command";
                r = IMAP_PROTOCOL_ERROR;
                goto done;
            }
            c = getword(imapd_in, &arg);
        }

        /* try to parse a sequence of "append-ext" */
        for (;;) {
            if (!strcasecmp(arg.s, "ANNOTATION")) {
                /* RFC5257 */
                if (c != ' ') {
                    parseerr = "Missing annotation data in Append command";
                    r = IMAP_PROTOCOL_ERROR;
                    goto done;
                }
                c = parse_annotate_store_data(tag,
                                              /*permessage_flag*/1,
                                              &curstage->annotations);
                if (c == EOF) {
                    eatline(imapd_in, c);
                    goto cleanup;
                }
                qdiffs[QUOTA_ANNOTSTORAGE] += sizeentryatts(curstage->annotations);
                c = getword(imapd_in, &arg);
            }
            else
                break;  /* not a known extension keyword */
        }

        /* Stage the message */
        curstage->f = append_newstage(intname, now, stages.count, &(curstage->stage));
        if (!curstage->f) {
            r = IMAP_IOERROR;
            goto done;
        }

        /* now parsing "append-data" in the ABNF */

        if (!strcasecmp(arg.s, "CATENATE")) {
            if (c != ' ' || (c = prot_getc(imapd_in) != '(')) {
                parseerr = "Missing message part(s) in Append command";
                r = IMAP_PROTOCOL_ERROR;
                goto done;
            }

            /* Catenate the message part(s) to stage */
            size = 0;
            r = append_catenate(curstage->f, cur_name, &size,
                                &(curstage->binary), &parseerr, &url);
            if (r) goto done;
        }
        else {
            /* Read size from literal */
            r = getliteralsize(arg.s, c, &size, &(curstage->binary), &parseerr);
            if (!r && size == 0) r = IMAP_ZERO_LENGTH_LITERAL;
            if (r) goto done;

            /* Copy message to stage */
            r = message_copy_strict(imapd_in, curstage->f, size, curstage->binary);
        }
        qdiffs[QUOTA_STORAGE] += size;
        /* If this is a non-BINARY message, close the stage file.
         * Otherwise, leave it open so we can encode the binary parts.
         *
         * XXX  For BINARY MULTIAPPEND, we may have to close the stage files
         * anyways to avoid too many open files.
         */
        if (!curstage->binary) {
            fclose(curstage->f);
            curstage->f = NULL;
        }

        /* if we see a SP, we're trying to append more than one message */

        /* Parse newline terminating command */
        c = prot_getc(imapd_in);
    }

 done:
    if (r) {
        eatline(imapd_in, c);
    } else {
        /* we should be looking at the end of the line */
        if (c == '\r') c = prot_getc(imapd_in);
        if (c != '\n') {
            parseerr = "junk after literal";
            r = IMAP_PROTOCOL_ERROR;
            eatline(imapd_in, c);
        }
    }

    /* Append from the stage(s) */
    if (!r) {
        qdiffs[QUOTA_MESSAGE] = stages.count;
        r = append_setup(&appendstate, intname,
                         imapd_userid, imapd_authstate, ACL_INSERT,
                         ignorequota ? NULL : qdiffs, &imapd_namespace,
                         (imapd_userisadmin || imapd_userisproxyadmin),
                         EVENT_MESSAGE_APPEND);
    }
    if (!r) {
        struct body *body;

        doappenduid = (appendstate.myrights & ACL_READ);
        uidvalidity = append_uidvalidity(&appendstate);

        for (i = 0; !r && i < stages.count ; i++) {
            curstage = stages.data[i];
            body = NULL;
            if (curstage->binary) {
                r = message_parse_binary_file(curstage->f, &body);
                fclose(curstage->f);
                curstage->f = NULL;
            }
            if (!r) {
                r = append_fromstage(&appendstate, &body, curstage->stage,
                                     curstage->internaldate,
                                     &curstage->flags, 0,
                                     curstage->annotations);
            }
            if (body) {
                /* Note: either the calls to message_parse_binary_file()
                 * or append_fromstage() above, may create a body.  */
                message_free_body(body);
                free(body);
                body = NULL;
            }
        }

        if (!r) {
            r = append_commit(&appendstate);
        } else {
            append_abort(&appendstate);
        }
    }

    imapd_check(NULL, 1);

    if (r == IMAP_PROTOCOL_ERROR && parseerr) {
        prot_printf(imapd_out, "%s BAD %s\r\n", tag, parseerr);
    } else if (r == IMAP_BADURL) {
        prot_printf(imapd_out, "%s NO [BADURL \"%s\"] %s\r\n",
                    tag, url, parseerr);
    } else if (r) {
        prot_printf(imapd_out, "%s NO %s%s\r\n",
                    tag,
                    (r == IMAP_MAILBOX_NONEXISTENT &&
                     mboxlist_createmailboxcheck(intname, 0, 0,
                                                 imapd_userisadmin,
                                                 imapd_userid, imapd_authstate,
                                                 NULL, NULL, 0) == 0)
                    ? "[TRYCREATE] " : r == IMAP_MESSAGE_TOO_LARGE
                    ? "[TOOBIG]" : "", error_message(r));
    } else if (doappenduid) {
        /* is this a space seperated list or sequence list? */
        prot_printf(imapd_out, "%s OK [APPENDUID %lu ", tag, uidvalidity);
        if (appendstate.nummsg == 1) {
            prot_printf(imapd_out, "%u", appendstate.baseuid);
        } else {
            prot_printf(imapd_out, "%u:%u", appendstate.baseuid,
                        appendstate.baseuid + appendstate.nummsg - 1);
        }
        prot_printf(imapd_out, "] %s\r\n", error_message(IMAP_OK_COMPLETED));
    } else {
        prot_printf(imapd_out, "%s OK %s\r\n", tag,
                    error_message(IMAP_OK_COMPLETED));
    }

cleanup:
    /* Cleanup the stage(s) */
    while ((curstage = ptrarray_pop(&stages))) {
        if (curstage->f != NULL) fclose(curstage->f);
        append_removestage(curstage->stage);
        strarray_fini(&curstage->flags);
        freeentryatts(curstage->annotations);
        free(curstage);
    }
    free(intname);
    ptrarray_fini(&stages);
}

Project: cyrus-imapd
File: imapd.c

static void cmd_compress(char *tag, char *alg)
{
    if (imapd_compress_done) {
        prot_printf(imapd_out,
                    "%s BAD [COMPRESSIONACTIVE] DEFLATE active via COMPRESS\r\n",
                    tag);
    }
#if defined(HAVE_SSL) && (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
    else if (imapd_tls_comp) {
        prot_printf(imapd_out,
                    "%s NO [COMPRESSIONACTIVE] %s active via TLS\r\n",
                    tag, SSL_COMP_get_name(imapd_tls_comp));
    }
#endif // defined(HAVE_SSL) && (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
    else if (strcasecmp(alg, "DEFLATE")) {
        prot_printf(imapd_out,
                    "%s NO Unknown COMPRESS algorithm: %s\r\n", tag, alg);
    }
    else if (ZLIB_VERSION[0] != zlibVersion()[0]) {
        prot_printf(imapd_out,
                    "%s NO Error initializing %s (incompatible zlib version)\r\n",
                    tag, alg);
    }
    else {
        prot_printf(imapd_out,
                    "%s OK %s active\r\n", tag, alg);

        /* enable (de)compression for the prot layer */
        prot_setcompress(imapd_in);
        prot_setcompress(imapd_out);

        imapd_compress_done = 1;
    }
}

Project: cyrus-imapd
File: imapd.c

static void cmd_expunge(char *tag, char *sequence)
{
    modseq_t old;
    modseq_t new;
    int r = 0;

    if (backend_current) {
        /* remote mailbox */
        if (sequence) {
            prot_printf(backend_current->out, "%s UID Expunge %s\r\n", tag,
                        sequence);
        } else {
            prot_printf(backend_current->out, "%s Expunge\r\n", tag);
        }
        pipe_including_tag(backend_current, tag, 0);
        return;
    }

    /* local mailbox */
    if (!index_hasrights(imapd_index, ACL_EXPUNGE))
        r = IMAP_PERMISSION_DENIED;

    old = index_highestmodseq(imapd_index);

    if (!r) r = index_expunge(imapd_index, sequence, 1);
    /* tell expunges */
    if (!r) index_tellchanges(imapd_index, 1, sequence ? 1 : 0, 0);

    if (r) {
        prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
        return;
    }

    new = index_highestmodseq(imapd_index);

    prot_printf(imapd_out, "%s OK ", tag);
    if (new > old)
        prot_printf(imapd_out, "[HIGHESTMODSEQ " MODSEQ_FMT "] ", new);
    prot_printf(imapd_out, "%s\r\n", error_message(IMAP_OK_COMPLETED));
}

Project: cyrus-imapd

static void cmd_getmetadata(const char *tag)
{
    int c, r = 0;
    strarray_t lists[3] = { STRARRAY_INITIALIZER,
                            STRARRAY_INITIALIZER,
                            STRARRAY_INITIALIZER };
    int is_list[3] = { 1, 1, 1 };
    int nlists = 0;
    strarray_t *options = NULL;
    strarray_t *mboxes = NULL;
    strarray_t *entries = NULL;
    strarray_t newe = STRARRAY_INITIALIZER;
    strarray_t newa = STRARRAY_INITIALIZER;
    struct buf arg1 = BUF_INITIALIZER;
    int mbox_is_pattern = 0;
    struct getmetadata_options opts = OPTS_INITIALIZER;
    annotate_state_t *astate = NULL;

    while (nlists < 3)
    {
        c = parse_metadata_string_or_list(tag, &lists[nlists], &is_list[nlists]);
        nlists++;
        if (c == '\r' || c == EOF)
            break;
    }

    /* check for CRLF */
    if (c == '\r') {

        c = prot_getc(imapd_in);

        if (c != '\n') {
            prot_printf(imapd_out,
                        "%s BAD Unexpected extra arguments to Getannotation\r\n",
                        tag);
            eatline(imapd_in, c);
            goto freeargs;
        }
    } else {
        eatline(imapd_in, c);
    }

    /*
     * We have three strings or lists of strings.  Now to figure out
     * what's what.  We have two complicating factors.  First, due to
     * a erratum in RFC5464 and our earlier misreading of the document,
     * we historically supported specifying the options *after* the
     * mailbox name.  Second, we have for a few months now supported
     * a non-standard extension where a list of mailbox names could
     * be supplied instead of just a single one.  So we have to apply
     * some rules.  We support the following syntaxes:
     *
     * --- no options
     * mailbox entry
     * mailbox (entries)
     * (mailboxes) entry
     * (mailboxes) (entries)
     *
     * --- options in the correct place (per the ABNF in RFC5464)
     * (options) mailbox entry
     * (options) mailbox (entries)
     * (options) (mailboxes) entry
     * (options) (mailboxes) (entries)
     *
     * --- options in the wrong place (per the examples in RFC5464)
     * mailbox (options) entry
     * mailbox (options) (entries)
     * (mailboxes) (options) entry
     * (mailboxes) (options) (entries)
     */
    if (nlists < 2)
        goto missingargs;
    entries = &lists[nlists-1];     /* entries always last */
    if (nlists == 2) {
        /* no options */
        mboxes = &lists[0];
        mbox_is_pattern = is_list[0];
    }
    if (nlists == 3) {
        /* options, either before or after */
        int r0 = (parse_getmetadata_options(&lists[0], NULL) > 0);
        int r1 = (parse_getmetadata_options(&lists[1], NULL) > 0);
        switch ((r1<<1)|r0) {
        case 0:
            /* neither are valid options */
            goto missingargs;
        case 1:
            /* (options) (mailboxes) */
            options = &lists[0];
            mboxes = &lists[1];
            mbox_is_pattern = is_list[1];
            break;
        case 2:
            /* (mailboxes) (options) */
            mboxes = &lists[0];
            mbox_is_pattern = is_list[0];
            options = &lists[1];
            break;
        case 3:
            /* both appear like valid options */
            prot_printf(imapd_out,
                        "%s BAD Too many option lists for Getmetadata\r\n",
                        tag);
            eatline(imapd_in, c);
            goto freeargs;
        }
    }

    if (options) parse_getmetadata_options(options, &opts);

    if (_metadata_to_annotate(entries, &newa, &newe, tag, opts.depth))
        goto freeargs;

    astate = annotate_state_new();
    annotate_state_set_auth(astate,
                            imapd_userisadmin || imapd_userisproxyadmin,
                            imapd_userid, imapd_authstate);
    if (!mboxes->count || !strcmpsafe(mboxes->data[0], NULL)) {
        r = annotate_state_set_server(astate);
        if (!r)
            r = annotate_state_fetch(astate, &newe, &newa,
                                     getmetadata_response, &opts);
    }
    else {
        struct annot_fetch_rock arock;
        arock.entries = &newe;
        arock.attribs = &newa;
        arock.callback = getmetadata_response;
        arock.cbrock = &opts;
        if (mbox_is_pattern)
            r = apply_mailbox_pattern(astate, mboxes->data[0], annot_fetch_cb, &arock);
        else
            r = apply_mailbox_array(astate, mboxes, annot_fetch_cb, &arock);
    }
    /* we didn't write anything */
    annotate_state_abort(&astate);

    getmetadata_response(NULL, 0, NULL, NULL, &opts);

    imapd_check(NULL, 0);

    if (r) {
        prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
    } else if (opts.maxsize && opts.biggest > opts.maxsize) {
        prot_printf(imapd_out, "%s OK [METADATA LONGENTRIES %u] %s\r\n",
                    tag, (unsigned)opts.biggest, error_message(IMAP_OK_COMPLETED));
    } else {
        prot_printf(imapd_out, "%s OK %s\r\n",
                    tag, error_message(IMAP_OK_COMPLETED));
    }

freeargs:
    strarray_fini(&lists[0]);
    strarray_fini(&lists[1]);
    strarray_fini(&lists[2]);
    strarray_fini(&newe);
    strarray_fini(&newa);
    buf_free(&arg1);
    return;

missingargs:
    prot_printf(imapd_out, "%s BAD Missing arguments to Getmetadata\r\n", tag);
    eatline(imapd_in, c);
    goto freeargs;
}

Project: cyrus-imapd
File: imapd.c

static void cmd_list(char *tag, struct listargs *listargs)
{
    clock_t start = clock();
    char mytime[100];

    if (listargs->sel & LIST_SEL_REMOTE) {
        if (!config_getswitch(IMAPOPT_PROXYD_DISABLE_MAILBOX_REFERRALS)) {
            supports_referrals = !disable_referrals;
        }
    }

    list_callback_calls = 0;

    if (listargs->pat.count && !*(listargs->pat.data[0]) && (listargs->cmd == LIST_CMD_LIST)) {
        /* special case: query top-level hierarchy separator */
        prot_printf(imapd_out, "* LIST (\\Noselect) \"%c\" \"\"\r\n",
                    imapd_namespace.hier_sep);
    } else if (listargs->pat.count && !*(listargs->pat.data[0]) && (listargs->cmd == LIST_CMD_XLIST)) {
        /* special case: query top-level hierarchy separator */
        prot_printf(imapd_out, "* XLIST (\\Noselect) \"%c\" \"\"\r\n",
                    imapd_namespace.hier_sep);
    } else if ((listargs->cmd == LIST_CMD_LSUB) &&
               (backend_inbox || (backend_inbox = proxy_findinboxserver(imapd_userid)))) {
        /* remote inbox */
        if (list_data_remote(backend_inbox, tag, listargs, NULL))
            return;
    } else {
        list_data(listargs);
    }

    strarray_fini(&listargs->pat);
    strarray_fini(&listargs->metaitems);

    imapd_check((listargs->sel & LIST_SEL_SUBSCRIBED) ?  NULL : backend_inbox, 0);

    snprintf(mytime, sizeof(mytime), "%2.3f",
             (clock() - start) / (double) CLOCKS_PER_SEC);
    if ((listargs->sel & LIST_SEL_METADATA) && listargs->metaopts.maxsize &&
        listargs->metaopts.biggest > listargs->metaopts.maxsize) {
        prot_printf(imapd_out, "%s OK [METADATA LONGENTRIES %u] %s\r\n", tag,
                    (unsigned)listargs->metaopts.biggest, error_message(IMAP_OK_COMPLETED));
    }
    else {
        prot_printf(imapd_out, "%s OK %s (%s secs", tag,
                    error_message(IMAP_OK_COMPLETED), mytime);
        if (list_callback_calls)
            prot_printf(imapd_out, " %u calls", list_callback_calls);
        prot_printf(imapd_out, ")\r\n");
    }

    if (global_conversations) {
        conversations_abort(&global_conversations);
        global_conversations = NULL;
    }
}

Project: cyrus-imapd
File: imapd.c

static void cmd_setannotation(const char *tag, char *mboxpat)
{
    int c, r = 0;
    struct entryattlist *entryatts = NULL;
    annotate_state_t *astate = NULL;

    c = parse_annotate_store_data(tag, 0, &entryatts);
    if (c == EOF) {
        eatline(imapd_in, c);
        goto freeargs;
    }

    /* check for CRLF */
    if (c == '\r') c = prot_getc(imapd_in);
    if (c != '\n') {
        prot_printf(imapd_out,
                    "%s BAD Unexpected extra arguments to Setannotation\r\n",
                    tag);
        eatline(imapd_in, c);
        goto freeargs;
    }

    astate = annotate_state_new();
    annotate_state_set_auth(astate, imapd_userisadmin,
                            imapd_userid, imapd_authstate);
    if (!r) {
        if (!*mboxpat) {
            r = annotate_state_set_server(astate);
            if (!r)
                r = annotate_state_store(astate, entryatts);
        }
        else {
            struct annot_store_rock arock;
            arock.entryatts = entryatts;
            r = apply_mailbox_pattern(astate, mboxpat, annot_store_cb, &arock);
        }
    }
    if (!r)
        annotate_state_commit(&astate);
    else
        annotate_state_abort(&astate);

    imapd_check(NULL, 0);

    if (r) {
        prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
    } else {
        prot_printf(imapd_out, "%s OK %s\r\n", tag,
                    error_message(IMAP_OK_COMPLETED));
    }

  freeargs:
    if (entryatts) freeentryatts(entryatts);
}

Project: cyrus-imapd
File: imapd.c

static int getdatetime(time_t *date)
{
    int c;
    int r;
    int i = 0;
    char buf[RFC3501_DATETIME_MAX+1];

    c = prot_getc(imapd_in);
    if (c != '\"')
        goto baddate;
    while ((c = prot_getc(imapd_in)) != '\"') {
        if (i >= RFC3501_DATETIME_MAX)
            goto baddate;
        buf[i++] = c;
    }
    buf[i] = '\0';

    r = time_from_rfc3501(buf, date);
    if (r < 0)
        goto baddate;

    c = prot_getc(imapd_in);
    return c;

 baddate:
    prot_ungetc(c, imapd_in);
    return EOF;
}

Project: cyrus-imapd
File: imapd.c

static void list_data(struct listargs *listargs)
{
    canonical_list_patterns(listargs->ref, &listargs->pat);

    /* Check to see if we should only list the personal namespace */
    if (!(listargs->cmd == LIST_CMD_EXTENDED)
            && !strcmp(listargs->pat.data[0], "*")
            && config_getswitch(IMAPOPT_FOOLSTUPIDCLIENTS)) {
        strarray_set(&listargs->pat, 0, "INBOX*");
    }

    if ((listargs->ret & LIST_RET_SUBSCRIBED) &&
        (backend_inbox || (backend_inbox = proxy_findinboxserver(imapd_userid)))) {
        list_data_remotesubscriptions(listargs);
    }
    else if (listargs->sel & LIST_SEL_RECURSIVEMATCH) {
        list_data_recursivematch(listargs);
    }
    else {
        struct list_rock rock;
        memset(&rock, 0, sizeof(struct list_rock));
        rock.listargs = listargs;

        if (listargs->sel & LIST_SEL_SUBSCRIBED) {
            mboxlist_findsubmulti(&imapd_namespace, &listargs->pat,
                                  imapd_userisadmin, imapd_userid,
                                  imapd_authstate, subscribed_cb, &rock, 1);
        }
        else {
            if (config_mupdate_server) {
                /* In case we proxy to backends due to select/return criteria */
                construct_hash_table(&rock.server_table, 10, 1);
            }

            /* XXX: is there a cheaper way to figure out \Subscribed? */
            if (listargs->ret & LIST_RET_SUBSCRIBED) {
                rock.subs = mboxlist_sublist(imapd_userid);
            }

            mboxlist_findallmulti(&imapd_namespace, &listargs->pat,
                                  imapd_userisadmin, imapd_userid,
                                  imapd_authstate, list_cb, &rock);

            if (rock.subs) strarray_free(rock.subs);
            if (rock.server_table.size)
                free_hash_table(&rock.server_table, NULL);
        }

        if (rock.last_name) free(rock.last_name);
    }
}

Project: cyrus-imapd
File: imapd.c

static int list_data_remote(struct backend *be, char *tag,
                            struct listargs *listargs, strarray_t *subs)
{
    if ((listargs->cmd == LIST_CMD_EXTENDED) &&
        !CAPA(be, CAPA_LISTEXTENDED)) {
        /* client wants to use extended list command but backend doesn't
         * support it */
        prot_printf(imapd_out,
                    "%s NO Backend server does not support LIST-EXTENDED\r\n",
                    tag);
        return IMAP_MAILBOX_NOTSUPPORTED;
    }

    /* print tag, command and list selection options */
    if (listargs->cmd == LIST_CMD_LSUB) {
        prot_printf(be->out, "%s Lsub ", tag);
    } else if (listargs->cmd == LIST_CMD_XLIST) {
        prot_printf(be->out, "%s Xlist ", tag);
    } else {
        prot_printf(be->out, "%s List ", tag);

        uint32_t select_mask = listargs->sel;

        if (be != backend_inbox) {
            /* don't send subscribed selection options to non-Inbox backend */
            select_mask &= ~(LIST_SEL_SUBSCRIBED | LIST_SEL_RECURSIVEMATCH);
        }

        /* print list selection options */
        if (select_mask) {
            const char *select_opts[] = {
                /* XXX  MUST be in same order as LIST_SEL_* bitmask */
                "subscribed", "remote", "recursivematch",
                "special-use", "vendor.cmu-dav", "metadata", NULL
            };
            char c = '(';
            int i;

            for (i = 0; select_opts[i]; i++) {
                unsigned opt = (1 << i);

                if (!(select_mask & opt)) continue;

                prot_printf(be->out, "%c%s", c, select_opts[i]);
                c = ' ';

                if (opt == LIST_SEL_METADATA) {
                    /* print metadata options */
                    prot_puts(be->out, " (depth ");
                    if (listargs->metaopts.depth < 0) {
                        prot_puts(be->out, "infinity");
                    }
                    else {
                        prot_printf(be->out, "%d",
                                    listargs->metaopts.depth);
                    }
                    if (listargs->metaopts.maxsize) {
                        prot_printf(be->out, " maxsize %zu",
                                    listargs->metaopts.maxsize);
                    }
                    (void)prot_putc(')', be->out);
                }
            }
            prot_puts(be->out, ") ");
        }
    }

    /* print reference argument */
    prot_printf(be->out,
                "{%tu+}\r\n%s ", strlen(listargs->ref), listargs->ref);

    /* print mailbox pattern(s) */
    if (listargs->pat.count > 1) {
        char **p;
        char c = '(';

        for (p = listargs->pat.data ; *p ; p++) {
            prot_printf(be->out,
                        "%c{%tu+}\r\n%s", c, strlen(*p), *p);
            c = ' ';
        }
        (void)prot_putc(')', be->out);
    } else {
        prot_printf(be->out, "{%tu+}\r\n%s",
                    strlen(listargs->pat.data[0]), listargs->pat.data[0]);
    }

    /* print list return options */
    if (listargs->ret && listargs->cmd == LIST_CMD_EXTENDED) {
        const char *return_opts[] = {
            /* XXX  MUST be in same order as LIST_RET_* bitmask */
            "subscribed", "children", "special-use",
            "status ", "myrights", "metadata ", NULL
        };
        char c = '(';
        int i, j;

        prot_puts(be->out, " return ");
        for (i = 0; return_opts[i]; i++) {
            unsigned opt = (1 << i);

            if (!(listargs->ret & opt)) continue;

            prot_printf(be->out, "%c%s", c, return_opts[i]);
            c = ' ';

            if (opt == LIST_RET_STATUS) {
                /* print status items */
                const char *status_items[] = {
                    /* XXX  MUST be in same order as STATUS_* bitmask */
                    "messages", "recent", "uidnext", "uidvalidity", "unseen",
                    "highestmodseq", "xconvexists", "xconvunseen",
                    "xconvmodseq", NULL
                };

                c = '(';
                for (j = 0; status_items[j]; j++) {
                    if (!(listargs->statusitems & (1 << j))) continue;

                    prot_printf(be->out, "%c%s", c, status_items[j]);
                    c = ' ';
                }
                (void)prot_putc(')', be->out);
            }
            else if (opt == LIST_RET_METADATA) {
                /* print metadata items */
                int n = strarray_size(&listargs->metaitems);

                c = '(';
                for (j = 0; j < n; j++) {
                    prot_printf(be->out, "%c\"%s\"", c,
                                strarray_nth(&listargs->metaitems, j));
                    c = ' ';
                }
                (void)prot_putc(')', be->out);
            }
        }
        (void)prot_putc(')', be->out);
    }

    prot_printf(be->out, "\r\n");
    pipe_lsub(be, imapd_userid, tag, 0, listargs, subs);

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static void list_data_remotesubscriptions(struct listargs *listargs)
{
    /* Need to fetch subscription list from backend_inbox */
    struct list_rock rock;
    char mytag[128];

    memset(&rock, 0, sizeof(struct list_rock));
    rock.listargs = listargs;
    rock.subs = strarray_new();
    construct_hash_table(&rock.server_table, 10, 1);

    proxy_gentag(mytag, sizeof(mytag));

    if ((listargs->sel & LIST_SEL_SUBSCRIBED) &&
        !(listargs->sel & (LIST_SEL_SPECIALUSE | LIST_SEL_METADATA))) {
        /* Subscriptions are the only selection criteria.

           Send client request as-is to backend_inbox.
           Responses will be piped to the client as we build subs list.
        */
        list_data_remote(backend_inbox, mytag, listargs, rock.subs);

        /* Don't proxy to backend_inbox again */
        hash_insert(backend_inbox->hostname,
                    (void *)0xDEADBEEF, &rock.server_table);
    }
    else {
        /* Multiple selection criteria or need to return subscription info.

           Just fetch subscriptions without piping responses to the client.
           If we send entire client request, subscribed mailboxes on
           non-Inbox servers might be filtered out due to lack of metadata
           to meet the selection criteria.

           Note that we end up sending two requests to backend_inbox,
           but there doesn't appear to be any way around this.
        */
        struct listargs myargs;

        memcpy(&myargs, listargs, sizeof(struct listargs));
        myargs.sel = LIST_SEL_SUBSCRIBED;
        myargs.ret = 0;

        list_data_remote(backend_inbox, mytag, &myargs, rock.subs);
    }

    /* find */
    mboxlist_findallmulti(&imapd_namespace, &listargs->pat,
                          imapd_userisadmin, imapd_userid,
                          imapd_authstate, list_cb, &rock);

    strarray_free(rock.subs);
    free_hash_table(&rock.server_table, NULL);
    if (rock.last_name) free(rock.last_name);
}

Project: cyrus-imapd
File: imapd.c

static int list_entry_comparator(const void *p1, const void *p2) {
    const struct list_entry *e1 = (struct list_entry *)p1;
    const struct list_entry *e2 = (struct list_entry *)p2;

    return bsearch_compare_mbox(e1->name, e2->name);
}

Project: cyrus-imapd
File: imapd.c

static int parse_annotate_fetch_data(const char *tag,
                                     int permessage_flag,
                                     strarray_t *entries,
                                     strarray_t *attribs)
{
    int c;
    static struct buf arg;

    c = prot_getc(imapd_in);
    if (c == EOF) {
        prot_printf(imapd_out,
                    "%s BAD Missing annotation entry\r\n", tag);
        goto baddata;
    }
    else if (c == '(') {
        /* entry list */
        do {
            if (permessage_flag)
                c = getastring(imapd_in, imapd_out, &arg);
            else
                c = getqstring(imapd_in, imapd_out, &arg);
            if (c == EOF) {
                prot_printf(imapd_out,
                            "%s BAD Missing annotation entry\r\n", tag);
                goto baddata;
            }

            /* add the entry to the list */
            strarray_append(entries, arg.s);

        } while (c == ' ');

        if (c != ')') {
            prot_printf(imapd_out,
                        "%s BAD Missing close paren in annotation entry list \r\n",
                        tag);
            goto baddata;
        }

        c = prot_getc(imapd_in);
    }
    else {
        /* single entry -- add it to the list */
        prot_ungetc(c, imapd_in);
        if (permessage_flag)
            c = getastring(imapd_in, imapd_out, &arg);
        else
            c = getqstring(imapd_in, imapd_out, &arg);
        if (c == EOF) {
            prot_printf(imapd_out,
                        "%s BAD Missing annotation entry\r\n", tag);
            goto baddata;
        }

        strarray_append(entries, arg.s);
    }

    if (c != ' ' || (c = prot_getc(imapd_in)) == EOF) {
        prot_printf(imapd_out,
                    "%s BAD Missing annotation attribute(s)\r\n", tag);
        goto baddata;
    }

    if (c == '(') {
        /* attrib list */
        do {
            if (permessage_flag)
                c = getastring(imapd_in, imapd_out, &arg);
            else
                c = getqstring(imapd_in, imapd_out, &arg);
            if (c == EOF) {
                prot_printf(imapd_out,
                            "%s BAD Missing annotation attribute(s)\r\n", tag);
                goto baddata;
            }

            /* add the attrib to the list */
            strarray_append(attribs, arg.s);

        } while (c == ' ');

        if (c != ')') {
            prot_printf(imapd_out,
                        "%s BAD Missing close paren in "
                        "annotation attribute list\r\n", tag);
            goto baddata;
        }

        c = prot_getc(imapd_in);
    }
    else {
        /* single attrib */
        prot_ungetc(c, imapd_in);
        if (permessage_flag)
            c = getastring(imapd_in, imapd_out, &arg);
        else
            c = getqstring(imapd_in, imapd_out, &arg);
        if (c == EOF) {
            prot_printf(imapd_out,
                        "%s BAD Missing annotation attribute\r\n", tag);
            goto baddata;
        }

        strarray_append(attribs, arg.s);
   }

    return c;

  baddata:
    if (c != EOF) prot_ungetc(c, imapd_in);
    return EOF;
}

Project: cyrus-imapd
File: imapd.c

static void printmetadata(const mbentry_t *mbentry,
                          const strarray_t *entries,
                          struct getmetadata_options *opts)
{
    annotate_state_t *astate = annotate_state_new();
    strarray_t newa = STRARRAY_INITIALIZER;
    strarray_t newe = STRARRAY_INITIALIZER;
    annotate_state_set_auth(astate,
                            imapd_userisadmin || imapd_userisproxyadmin,
                            imapd_userid, imapd_authstate);
    int r = annotate_state_set_mailbox_mbe(astate, mbentry);
    if (r) goto done;
    r = _metadata_to_annotate(entries, &newa, &newe, NULL, opts->depth);
    if (r) goto done;

    annotate_state_fetch(astate, &newe, &newa, getmetadata_response, opts);
    getmetadata_response(NULL, 0, NULL, NULL, opts);

done:
    annotate_state_abort(&astate);
}

Project: cyrus-imapd
File: imapd.c

void shut_down(int code)
{
    int i;
    int bytes_in = 0;
    int bytes_out = 0;

    in_shutdown = 1;

    proc_cleanup();

    i = 0;
    while (backend_cached && backend_cached[i]) {
        proxy_downserver(backend_cached[i]);
        if (backend_cached[i]->last_result.s) {
            free(backend_cached[i]->last_result.s);
        }
        free(backend_cached[i]);
        i++;
    }
    if (backend_cached) free(backend_cached);
    if (mupdate_h) mupdate_disconnect(&mupdate_h);

    if (idling)
        idle_stop(index_mboxname(imapd_index));

    if (imapd_index) index_close(&imapd_index);

    sync_log_done();
    seen_done();
    mboxkey_done();
    mboxlist_close();
    mboxlist_done();

    quotadb_close();
    quotadb_done();

    denydb_close();
    denydb_done();

    annotatemore_close();
    annotate_done();

    idle_done();

    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
        statuscache_close();
        statuscache_done();
    }

    partlist_local_done();

    if (imapd_in) {
        /* Flush the incoming buffer */
        prot_NONBLOCK(imapd_in);
        prot_fill(imapd_in);
        bytes_in = prot_bytes_in(imapd_in);
        prot_free(imapd_in);
    }

    if (imapd_out) {
        /* Flush the outgoing buffer */
        prot_flush(imapd_out);
        bytes_out = prot_bytes_out(imapd_out);
        prot_free(imapd_out);

        /* one less active connection */
        snmp_increment(ACTIVE_CONNECTIONS, -1);
    }

    if (config_auditlog)
        syslog(LOG_NOTICE, "auditlog: traffic sessionid=<%s> bytes_in=<%d> bytes_out=<%d>",
                           session_id(), bytes_in, bytes_out);

    if (protin) protgroup_free(protin);

#ifdef HAVE_SSL
    tls_shutdown_serverengine();
#endif

    cyrus_done();

    exit(code);
}