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 fetchargs_fini (struct fetchargs *fa)
{
    section_list_free(fa->binsections);
    section_list_free(fa->sizesections);
    section_list_free(fa->bodysections);
    freefieldlist(fa->fsections);
    strarray_fini(&fa->headers);
    strarray_fini(&fa->headers_not);
    strarray_fini(&fa->entries);
    strarray_fini(&fa->attribs);

    memset(fa, 0, sizeof(struct fetchargs));
}

Project: cyrus-imapd
File: imapd.c

static void free_snippetargs(struct snippetargs **sap)
{
    while (*sap) {
        struct snippetargs *sa = *sap;
        *sap = sa->next;
        free(sa->mboxname);
        free(sa->uids.data);
        free(sa);
    }
}

Project: cyrus-imapd
File: imapd.c

static void free_windowargs(struct windowargs *wa)
{
    if (!wa)
        return;
    free(wa->anchorfolder);
    free(wa);
}

Project: cyrus-imapd
File: imapd.c

static void freefieldlist(struct fieldlist *l)
{
    struct fieldlist *n;

    while (l) {
        n = l->next;
        free(l->section);
        strarray_free(l->fields);
        free(l->trail);
        if (l->rock) free(l->rock);
        free((char *)l);
        l = n;
    }
}

Project: cyrus-imapd
File: imapd.c

static int get_snippetargs(struct snippetargs **sap)
{
    int c;
    struct snippetargs **prevp = sap;
    struct snippetargs *sa = NULL;
    struct buf arg = BUF_INITIALIZER;
    uint32_t uid;
    char *intname = NULL;

    c = prot_getc(imapd_in);
    if (c != '(') goto syntax_error;

    for (;;) {
        c = prot_getc(imapd_in);
        if (c == ')') break;
        if (c != '(') goto syntax_error;

        c = getastring(imapd_in, imapd_out, &arg);
        if (c != ' ') goto syntax_error;

        intname = mboxname_from_external(buf_cstring(&arg), &imapd_namespace, imapd_userid);

        /* allocate a new snippetargs */
        sa = xzmalloc(sizeof(struct snippetargs));
        sa->mboxname = xstrdup(intname);
        /* append to the list */
        *prevp = sa;
        prevp = &sa->next;

        c = getuint32(imapd_in, &sa->uidvalidity);
        if (c != ' ') goto syntax_error;

        c = prot_getc(imapd_in);
        if (c != '(') break;
        for (;;) {
            c = getuint32(imapd_in, &uid);
            if (c != ' ' && c != ')') goto syntax_error;
            if (sa->uids.count + 1 > sa->uids.alloc) {
                sa->uids.alloc += 64;
                sa->uids.data = xrealloc(sa->uids.data,
                                         sizeof(uint32_t) * sa->uids.alloc);
            }
            sa->uids.data[sa->uids.count++] = uid;
            if (c == ')') break;
        }

        c = prot_getc(imapd_in);
        if (c != ')') goto syntax_error;
    }

    c = prot_getc(imapd_in);
    if (c != ' ') goto syntax_error;

out:
    free(intname);
    buf_free(&arg);
    return c;

syntax_error:
    free_snippetargs(sap);
    c = EOF;
    goto out;
}

Project: cyrus-imapd
File: imapd.c

static void getannotation_response(const char *mboxname,
                                   uint32_t uid
                                        __attribute__((unused)),
                                   const char *entry,
                                   struct attvaluelist *attvalues,
                                   void *rock __attribute__((unused)))
{
    int sep = '(';
    struct attvaluelist *l;
    char *extname = *mboxname ?
        mboxname_to_external(mboxname, &imapd_namespace, imapd_userid) :
        xstrdup("");  /* server annotation */

    prot_printf(imapd_out, "* ANNOTATION ");
    prot_printastring(imapd_out, extname);
    prot_putc(' ', imapd_out);
    prot_printstring(imapd_out, entry);
    prot_putc(' ', imapd_out);
    for (l = attvalues ; l ; l = l->next) {
        prot_putc(sep, imapd_out);
        sep = ' ';
        prot_printstring(imapd_out, l->attrib);
        prot_putc(' ',  imapd_out);
        prot_printmap(imapd_out, l->value.s, l->value.len);
    }
    prot_printf(imapd_out, ")\r\n");
    free(extname);
}

Project: cyrus-imapd
File: imapd.c

static void getlistargs(char *tag, struct listargs *listargs)
{
    static struct buf reference, buf;
    int c;

    /* Check for and parse LIST-EXTENDED selection options */
    c = prot_getc(imapd_in);
    if (c == '(') {
        listargs->cmd = LIST_CMD_EXTENDED;
        c = getlistselopts(tag, listargs);
        if (c == EOF) {
            eatline(imapd_in, c);
            return;
        }
    }
    else
        prot_ungetc(c, imapd_in);

    if (!strcmpsafe(imapd_magicplus, "+")) listargs->sel |= LIST_SEL_SUBSCRIBED;
    else if (!strcasecmpsafe(imapd_magicplus, "+dav")) listargs->sel |= LIST_SEL_DAV;

    /* Read in reference name */
    c = getastring(imapd_in, imapd_out, &reference);
    if (c == EOF && !*reference.s) {
        prot_printf(imapd_out,
                    "%s BAD Missing required argument to List: reference name\r\n",
                    tag);
        eatline(imapd_in, c);
        return;
    }
    listargs->ref = reference.s;

    if (c != ' ') {
        prot_printf(imapd_out,
                    "%s BAD Missing required argument to List: mailbox pattern\r\n", tag);
        eatline(imapd_in, c);
        return;
    }

    /* Read in mailbox pattern(s) */
    c = prot_getc(imapd_in);
    if (c == '(') {
        listargs->cmd = LIST_CMD_EXTENDED;
        for (;;) {
            c = getastring(imapd_in, imapd_out, &buf);
            if (*buf.s)
                strarray_append(&listargs->pat, buf.s);
            if (c != ' ') break;
        }
        if (c != ')') {
            prot_printf(imapd_out,
                        "%s BAD Invalid syntax in List command\r\n", tag);
            eatline(imapd_in, c);
            goto freeargs;
        }
        c = prot_getc(imapd_in);
    }
    else {
        prot_ungetc(c, imapd_in);
        c = getastring(imapd_in, imapd_out, &buf);
        if (c == EOF) {
            prot_printf(imapd_out,
                        "%s BAD Missing required argument to List: mailbox pattern\r\n",
                        tag);
            eatline(imapd_in, c);
            goto freeargs;
        }
        strarray_append(&listargs->pat, buf.s);
    }

    /* Check for and parse LIST-EXTENDED return options */
    if (c == ' ') {
        listargs->cmd = LIST_CMD_EXTENDED;
        c = getlistretopts(tag, listargs);
        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 List\r\n", tag);
        eatline(imapd_in, c);
        goto freeargs;
    }

#ifdef USE_AUTOCREATE
    autocreate_inbox();
#endif // USE_AUTOCREATE

    return;

  freeargs:
    strarray_fini(&listargs->pat);
    strarray_fini(&listargs->metaitems);
    return;
}

Project: cyrus-imapd
File: imapd.c

static int getlistretopts(char *tag, struct listargs *args)
{
    static struct buf buf;
    int c;

    c = getword(imapd_in, &buf);
    if (!*buf.s) {
        prot_printf(imapd_out,
                    "%s BAD Invalid syntax in List command\r\n", tag);
        return EOF;
    }
    lcase(buf.s);
    if (strcasecmp(buf.s, "return")) {
        prot_printf(imapd_out,
                    "%s BAD Unexpected extra argument to List: \"%s\"\r\n",
                    tag, buf.s);
        return EOF;
    }

    if (c != ' ' || (c = prot_getc(imapd_in)) != '(') {
        prot_printf(imapd_out,
                    "%s BAD Missing return argument list\r\n", tag);
        return EOF;
    }

    if ( (c = prot_getc(imapd_in)) == ')')
        return prot_getc(imapd_in);
    else
        prot_ungetc(c, imapd_in);

    for (;;) {
        c = getword(imapd_in, &buf);

        if (!*buf.s) {
            prot_printf(imapd_out,
                        "%s BAD Invalid syntax in List command\r\n", tag);
            return EOF;
        }

        lcase(buf.s);

        if (!strcmp(buf.s, "subscribed"))
            args->ret |= LIST_RET_SUBSCRIBED;
        else if (!strcmp(buf.s, "children"))
            args->ret |= LIST_RET_CHILDREN;
        else if (!strcmp(buf.s, "myrights"))
            args->ret |= LIST_RET_MYRIGHTS;
        else if (!strcmp(buf.s, "special-use"))
            args->ret |= LIST_RET_SPECIALUSE;
        else if (!strcmp(buf.s, "status")) {
            const char *errstr = "Bad status string";
            args->ret |= LIST_RET_STATUS;
            c = parse_statusitems(&args->statusitems, &errstr);
            if (c == EOF) {
                prot_printf(imapd_out, "%s BAD %s", tag, errstr);
                return EOF;
            }
        }
        else if (!strcmp(buf.s, "metadata")) {
            args->ret |= LIST_RET_METADATA;
            /* outputs the error for us */
            c = parse_metadata_string_or_list(tag, &args->metaitems, NULL);
            if (c == EOF) return EOF;
        }
        else {
            prot_printf(imapd_out,
                        "%s BAD Invalid List return option \"%s\"\r\n",
                        tag, buf.s);
            return EOF;
        }

        if (c != ' ') break;
    }

    if (c != ')') {
        prot_printf(imapd_out,
                    "%s BAD Missing close parenthesis for List return options\r\n", tag);
        return EOF;
    }

    return prot_getc(imapd_in);
}

Project: cyrus-imapd
File: imapd.c

static int getlistselopts(char *tag, struct listargs *args)
{
    int c;
    static struct buf buf;

    if ( (c = prot_getc(imapd_in)) == ')')
        return prot_getc(imapd_in);
    else
        prot_ungetc(c, imapd_in);

    for (;;) {
        c = getword(imapd_in, &buf);

        if (!*buf.s) {
            prot_printf(imapd_out,
                        "%s BAD Invalid syntax in List command\r\n",
                        tag);
            return EOF;
        }

        lcase(buf.s);

        if (!strcmp(buf.s, "subscribed")) {
            args->sel |= LIST_SEL_SUBSCRIBED;
            args->ret |= LIST_RET_SUBSCRIBED;
        } else if (!strcmp(buf.s, "vendor.cmu-dav")) {
            args->sel |= LIST_SEL_DAV;
        } else if (!strcmp(buf.s, "remote")) {
            args->sel |= LIST_SEL_REMOTE;
        } else if (!strcmp(buf.s, "recursivematch")) {
            args->sel |= LIST_SEL_RECURSIVEMATCH;
        } else if (!strcmp(buf.s, "special-use")) {
            args->sel |= LIST_SEL_SPECIALUSE;
            args->ret |= LIST_RET_SPECIALUSE;
        } else if (!strcmp(buf.s, "metadata")) {
            struct getmetadata_options opts = OPTS_INITIALIZER;
            args->sel |= LIST_SEL_METADATA;
            args->ret |= LIST_RET_METADATA;

            strarray_t options = STRARRAY_INITIALIZER;
            c = parse_metadata_string_or_list(tag, &options, NULL);
            parse_getmetadata_options(&options, &opts);
            args->metaopts = opts;
            strarray_fini(&options);
            if (c == EOF) return EOF;
        } else {
            prot_printf(imapd_out,
                        "%s BAD Invalid List selection option \"%s\"\r\n",
                        tag, buf.s);
            return EOF;
        }

        if (c != ' ') break;
    }

    if (c != ')') {
        prot_printf(imapd_out,
                    "%s BAD Missing close parenthesis for List selection options\r\n", tag);
        return EOF;
    }

    if (args->sel & list_select_mod_opts
            && ! (args->sel & list_select_base_opts)) {
        prot_printf(imapd_out,
                    "%s BAD Invalid combination of selection options\r\n",
                    tag);
        return EOF;
    }

    return prot_getc(imapd_in);
}

Project: cyrus-imapd
File: imapd.c

static int getliteralsize(const char *p, int c,
                          unsigned *size, int *binary, const char **parseerr)

{
    int isnowait = 0;
    uint32_t num;

    /* Check for literal8 */
    if (*p == '~') {
        p++;
        *binary = 1;
    }

    /* check for start of literal */
    if (*p != '{') {
        *parseerr = "Missing required argument to Append command";
        return IMAP_PROTOCOL_ERROR;
    }

    /* Read size from literal */
    if (parseuint32(p+1, &p, &num)) {
        *parseerr = "Literal size not a number";
        return IMAP_PROTOCOL_ERROR;
    }

    if (*p == '+') {
        isnowait++;
        p++;
    }

    if (c == '\r') {
        c = prot_getc(imapd_in);
    }

    if (*p != '}' || p[1] || c != '\n') {
        *parseerr = "Invalid literal in Append command";
        return IMAP_PROTOCOL_ERROR;
    }

    if (!isnowait) {
        /* Tell client to send the message */
        prot_printf(imapd_out, "+ go ahead\r\n");
        prot_flush(imapd_out);
    }

    *size = num;

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static void getmetadata_response(const char *mboxname,
                                 uint32_t uid __attribute__((unused)),
                                 const char *entry,
                                 struct attvaluelist *attvalues,
                                 void *rock)
{
    struct getmetadata_options *opts = (struct getmetadata_options *)rock;

    if (strcmpsafe(mboxname, opts->lastname) || !entry) {
        if (opts->items.count) {
            char *extname = NULL;
            size_t i;
            if (opts->lastname)
                extname = mboxname_to_external(opts->lastname, &imapd_namespace, imapd_userid);
            else
                extname = xstrdup("");
            prot_printf(imapd_out, "* METADATA ");
            prot_printastring(imapd_out, extname);
            prot_putc(' ', imapd_out);
            for (i = 0; i + 1 < opts->items.count; i+=2) {
                prot_putc(i ? ' ' : '(', imapd_out);
                const struct buf *key = bufarray_nth(&opts->items, i);
                prot_printmap(imapd_out, key->s, key->len);
                prot_putc(' ', imapd_out);
                const struct buf *val = bufarray_nth(&opts->items, i+1);
                prot_printmap(imapd_out, val->s, val->len);
            }
            prot_printf(imapd_out, ")\r\n");
            free(extname);
        }
        free(opts->lastname);
        opts->lastname = xstrdupnull(mboxname);
        bufarray_fini(&opts->items);
    }

    struct attvaluelist *l;
    struct buf buf = BUF_INITIALIZER;

    for (l = attvalues ; l ; l = l->next) {
        /* size check */
        if (opts->maxsize && l->value.len >= opts->maxsize) {
            if (l->value.len > opts->biggest) opts->biggest = l->value.len;
            continue;
        }
        /* check if it's a value we print... */
        buf_reset(&buf);
        if (!strcmp(l->attrib, "value.shared"))
            buf_appendcstr(&buf, "/shared");
        else if (!strcmp(l->attrib, "value.priv"))
            buf_appendcstr(&buf, "/private");
        else
            continue;
        buf_appendcstr(&buf, entry);

        bufarray_append(&opts->items, &buf);
        bufarray_append(&opts->items, &l->value);
    }
    buf_free(&buf);
}

Project: cyrus-imapd
File: imapd.c

static int getresult(struct protstream *p, const char *tag)
{
    char buf[4096];
    char *str = (char *) buf;

    while(1) {
        if (!prot_fgets(str, sizeof(buf), p)) {
            return IMAP_SERVER_UNAVAILABLE;
        }
        if (!strncmp(str, tag, strlen(tag))) {
            str += strlen(tag);
            if(!*str) {
                /* We got a tag, but no response */
                return IMAP_SERVER_UNAVAILABLE;
            }
            str++;
            if (!strncasecmp(str, "OK ", 3)) { return 0; }
            if (!strncasecmp(str, "NO ", 3)) { return IMAP_REMOTE_DENIED; }
            return IMAP_SERVER_UNAVAILABLE; /* huh? */
        }
        /* skip this line, we don't really care */
    }
}

Project: cyrus-imapd
File: imapd.c

static int getsortcriteria(char *tag, struct sortcrit **sortcrit)
{
    int c;
    static struct buf criteria;
    int nsort, n;
    int hasconv = config_getswitch(IMAPOPT_CONVERSATIONS);

    *sortcrit = NULL;

    c = prot_getc(imapd_in);
    if (c != '(') goto missingcrit;

    c = getword(imapd_in, &criteria);
    if (criteria.s[0] == '\0') goto missingcrit;

    nsort = 0;
    n = 0;
    for (;;) {
        if (n >= nsort - 1) {   /* leave room for implicit criterion */
            /* (Re)allocate an array for sort criteria */
            nsort += SORTGROWSIZE;
            *sortcrit =
                (struct sortcrit *) xrealloc(*sortcrit,
                                             nsort * sizeof(struct sortcrit));
            /* Zero out the newly added sortcrit */
            memset((*sortcrit)+n, 0, SORTGROWSIZE * sizeof(struct sortcrit));
        }

        lcase(criteria.s);
        if (!strcmp(criteria.s, "reverse")) {
            (*sortcrit)[n].flags |= SORT_REVERSE;
            goto nextcrit;
        }
        else if (!strcmp(criteria.s, "arrival"))
            (*sortcrit)[n].key = SORT_ARRIVAL;
        else if (!strcmp(criteria.s, "cc"))
            (*sortcrit)[n].key = SORT_CC;
        else if (!strcmp(criteria.s, "date"))
            (*sortcrit)[n].key = SORT_DATE;
        else if (!strcmp(criteria.s, "displayfrom"))
            (*sortcrit)[n].key = SORT_DISPLAYFROM;
        else if (!strcmp(criteria.s, "displayto"))
            (*sortcrit)[n].key = SORT_DISPLAYTO;
        else if (!strcmp(criteria.s, "from"))
            (*sortcrit)[n].key = SORT_FROM;
        else if (!strcmp(criteria.s, "size"))
            (*sortcrit)[n].key = SORT_SIZE;
        else if (!strcmp(criteria.s, "subject"))
            (*sortcrit)[n].key = SORT_SUBJECT;
        else if (!strcmp(criteria.s, "to"))
            (*sortcrit)[n].key = SORT_TO;
        else if (!strcmp(criteria.s, "annotation")) {
            const char *userid = NULL;

            (*sortcrit)[n].key = SORT_ANNOTATION;
            if (c != ' ') goto missingarg;
            c = getastring(imapd_in, imapd_out, &criteria);
            if (c != ' ') goto missingarg;
            (*sortcrit)[n].args.annot.entry = xstrdup(criteria.s);
            c = getastring(imapd_in, imapd_out, &criteria);
            if (c == EOF) goto missingarg;
            if (!strcmp(criteria.s, "value.shared"))
                userid = "";
            else if (!strcmp(criteria.s, "value.priv"))
                userid = imapd_userid;
            else
                goto missingarg;
            (*sortcrit)[n].args.annot.userid = xstrdup(userid);
        }
        else if (!strcmp(criteria.s, "modseq"))
            (*sortcrit)[n].key = SORT_MODSEQ;
        else if (!strcmp(criteria.s, "uid"))
            (*sortcrit)[n].key = SORT_UID;
        else if (!strcmp(criteria.s, "hasflag")) {
            (*sortcrit)[n].key = SORT_HASFLAG;
            if (c != ' ') goto missingarg;
            c = getastring(imapd_in, imapd_out, &criteria);
            if (c == EOF) goto missingarg;
            (*sortcrit)[n].args.flag.name = xstrdup(criteria.s);
        }
        else if (hasconv && !strcmp(criteria.s, "convmodseq"))
            (*sortcrit)[n].key = SORT_CONVMODSEQ;
        else if (hasconv && !strcmp(criteria.s, "convexists"))
            (*sortcrit)[n].key = SORT_CONVEXISTS;
        else if (hasconv && !strcmp(criteria.s, "convsize"))
            (*sortcrit)[n].key = SORT_CONVSIZE;
        else if (hasconv && !strcmp(criteria.s, "hasconvflag")) {
            (*sortcrit)[n].key = SORT_HASCONVFLAG;
            if (c != ' ') goto missingarg;
            c = getastring(imapd_in, imapd_out, &criteria);
            if (c == EOF) goto missingarg;
            (*sortcrit)[n].args.flag.name = xstrdup(criteria.s);
        }
        else if (!strcmp(criteria.s, "folder"))
            (*sortcrit)[n].key = SORT_FOLDER;
        else if (!strcmp(criteria.s, "relevancy"))
            (*sortcrit)[n].key = SORT_RELEVANCY;
        else if (!strcmp(criteria.s, "spamscore"))
            (*sortcrit)[n].key = SORT_SPAMSCORE;
        else {
            prot_printf(imapd_out, "%s BAD Invalid Sort criterion %s\r\n",
                        tag, criteria.s);
            if (c != EOF) prot_ungetc(c, imapd_in);
            return EOF;
        }

        n++;

 nextcrit:
        if (c == ' ') c = getword(imapd_in, &criteria);
        else break;
    }

    if ((*sortcrit)[n].flags & SORT_REVERSE  && !(*sortcrit)[n].key) {
        prot_printf(imapd_out,
                    "%s BAD Missing Sort criterion to reverse\r\n", tag);
        if (c != EOF) prot_ungetc(c, imapd_in);
        return EOF;
    }

    if (c != ')') {
        prot_printf(imapd_out,
                    "%s BAD Missing close parenthesis in Sort\r\n", tag);
        if (c != EOF) prot_ungetc(c, imapd_in);
        return EOF;
    }

    /* Terminate the list with the implicit sort criterion */
    (*sortcrit)[n++].key = SORT_SEQUENCE;

    c = prot_getc(imapd_in);

    return c;

 missingcrit:
    prot_printf(imapd_out, "%s BAD Missing Sort criteria\r\n", tag);
    if (c != EOF) prot_ungetc(c, imapd_in);
    return EOF;
 missingarg:
    prot_printf(imapd_out, "%s BAD Missing argument to Sort criterion %s\r\n",
                tag, criteria.s);
    if (c != EOF) prot_ungetc(c, imapd_in);
    return EOF;
}

Project: cyrus-imapd
File: imapd.c

static int imapd_canon_user(sasl_conn_t *conn, void *context,
                            const char *user, unsigned ulen,
                            unsigned flags, const char *user_realm,
                            char *out, unsigned out_max, unsigned *out_ulen)
{
    char userbuf[MAX_MAILBOX_BUFFER], *p;
    size_t n;
    int r;

    if (!ulen) ulen = strlen(user);

    if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) {
        /* make a working copy of the auth[z]id */
        if (ulen >= MAX_MAILBOX_BUFFER) {
            sasl_seterror(conn, 0, "buffer overflow while canonicalizing");
            return SASL_BUFOVER;
        }
        memcpy(userbuf, user, ulen);
        userbuf[ulen] = '\0';
        user = userbuf;

        /* See if we're using the magic plus */
        if ((p = strchr(userbuf, '+'))) {
            n = config_virtdomains ? strcspn(p, "@") : strlen(p);

            if (flags & SASL_CU_AUTHZID) {
                /* make a copy of the magic plus */
                if (imapd_magicplus) free(imapd_magicplus);
                imapd_magicplus = xstrndup(p, n);
            }

            /* strip the magic plus from the auth[z]id */
            memmove(p, p+n, strlen(p+n)+1);
            ulen -= n;
        }
    }

    r = mysasl_canon_user(conn, context, user, ulen, flags, user_realm,
                          out, out_max, out_ulen);

    if (!r && imapd_magicplus && flags == SASL_CU_AUTHZID) {
        /* If we're only doing the authzid, put back the magic plus
           in case its used in the challenge/response calculation */
        n = strlen(imapd_magicplus);
        if (*out_ulen + n > out_max) {
            sasl_seterror(conn, 0, "buffer overflow while canonicalizing");
            r = SASL_BUFOVER;
        }
        else {
            p = (config_virtdomains && (p = strchr(out, '@'))) ?
                p : out + *out_ulen;
            memmove(p+n, p, strlen(p)+1);
            memcpy(p, imapd_magicplus, n);
            *out_ulen += n;
        }
    }

    return r;
}

Project: cyrus-imapd
File: imapd.c

static void imapd_check(struct backend *be, int usinguid)
{
    if (backend_current && backend_current != be) {
        /* remote mailbox */
        char mytag[128];

        proxy_gentag(mytag, sizeof(mytag));

        prot_printf(backend_current->out, "%s Noop\r\n", mytag);
        pipe_until_tag(backend_current, mytag, 0);
    }
    else {
        /* local mailbox */
        index_check(imapd_index, usinguid, 0);
    }
}

Project: cyrus-imapd
File: imapd.c

static int imapd_proxy_policy(sasl_conn_t *conn,
                              void *context,
                              const char *requested_user, unsigned rlen,
                              const char *auth_identity, unsigned alen,
                              const char *def_realm,
                              unsigned urlen,
                              struct propctx *propctx)
{
    char userbuf[MAX_MAILBOX_BUFFER];

    if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) {
        size_t n;
        char *p;

        /* make a working copy of the authzid */
        if (!rlen) rlen = strlen(requested_user);
        if (rlen >= MAX_MAILBOX_BUFFER) {
            sasl_seterror(conn, 0, "buffer overflow while proxying");
            return SASL_BUFOVER;
        }
        memcpy(userbuf, requested_user, rlen);
        userbuf[rlen] = '\0';
        requested_user = userbuf;

        /* See if we're using the magic plus */
        if ((p = strchr(userbuf, '+'))) {
            n = config_virtdomains ? strcspn(p, "@") : strlen(p);

            /* strip the magic plus from the authzid */
            memmove(p, p+n, strlen(p+n)+1);
            rlen -= n;
        }
    }

    return mysasl_proxy_policy(conn, context, requested_user, rlen,
                               auth_identity, alen, def_realm, urlen, propctx);
}

Project: cyrus-imapd
File: imapd.c

static void imapd_refer(const char *tag,
                        const char *server,
                        const char *mailbox)
{
    struct imapurl imapurl;
    char url[MAX_MAILBOX_PATH+1];

    memset(&imapurl, 0, sizeof(struct imapurl));
    imapurl.server = server;
    imapurl.mailbox = mailbox;
    imapurl.auth = !strcmp(imapd_userid, "anonymous") ? "anonymous" : "*";

    imapurl_toURL(url, &imapurl);

    prot_printf(imapd_out, "%s NO [REFERRAL %s] Remote mailbox.\r\n",
                tag, url);

    free(imapurl.freeme);
}

Project: cyrus-imapd
File: imapd.c

static void imapd_reset(void)
{
    int i;
    int bytes_in = 0;
    int bytes_out = 0;

    proc_cleanup();

    /* close backend connections */
    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);
    backend_cached = NULL;
    backend_inbox = backend_current = NULL;
    if (mupdate_h) mupdate_disconnect(&mupdate_h);
    mupdate_h = NULL;
    proxy_cmdcnt = 0;
    disable_referrals = 0;
    supports_referrals = 0;

    if (imapd_index) index_close(&imapd_index);

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

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

    imapd_in = imapd_out = NULL;

    if (protin) protgroup_reset(protin);

#ifdef HAVE_SSL
    if (tls_conn) {
        if (tls_reset_servertls(&tls_conn) == -1) {
            fatal("tls_reset() failed", EC_TEMPFAIL);
        }
        tls_conn = NULL;
    }
#endif

    cyrus_reset_stdio();

    imapd_clienthost = "[local]";
    if (imapd_logfd != -1) {
        close(imapd_logfd);
        imapd_logfd = -1;
    }
    if (imapd_userid != NULL) {
        free(imapd_userid);
        imapd_userid = NULL;
    }
    if (proxy_userid != NULL) {
        free(proxy_userid);
        proxy_userid = NULL;
    }
    if (imapd_magicplus != NULL) {
        free(imapd_magicplus);
        imapd_magicplus = NULL;
    }
    if (imapd_authstate) {
        auth_freestate(imapd_authstate);
        imapd_authstate = NULL;
    }
    imapd_userisadmin = 0;
    imapd_userisproxyadmin = 0;
    client_capa = 0;
    if (imapd_saslconn) {
        sasl_dispose(&imapd_saslconn);
        free(imapd_saslconn);
        imapd_saslconn = NULL;
    }
    imapd_compress_done = 0;
    imapd_tls_comp = NULL;
    imapd_starttls_done = 0;
    plaintextloginalert = NULL;

    if(saslprops.iplocalport) {
        free(saslprops.iplocalport);
        saslprops.iplocalport = NULL;
    }
    if(saslprops.ipremoteport) {
        free(saslprops.ipremoteport);
        saslprops.ipremoteport = NULL;
    }
    if(saslprops.authid) {
        free(saslprops.authid);
        saslprops.authid = NULL;
    }
    saslprops.ssf = 0;

    clear_id();
}

Project: cyrus-imapd
File: imapd.c

static int imapd_sasl_log(void *context __attribute__((unused)),
                          int level, const char *message)
{
    int syslog_level = LOG_INFO;

    switch (level) {
    case SASL_LOG_ERR:
    case SASL_LOG_FAIL:
        syslog_level = LOG_ERR;
        break;
    case SASL_LOG_WARN:
        syslog_level = LOG_WARNING;
        break;
    case SASL_LOG_DEBUG:
    case SASL_LOG_TRACE:
    case SASL_LOG_PASS:
        syslog_level = LOG_DEBUG;
        break;
    }

    syslog(syslog_level, "SASL %s", message);
    return SASL_OK;
}

Project: cyrus-imapd
File: imapd.c

static int imapd_statusdata(const char *mailboxname, unsigned statusitems,
                            struct statusdata *sd)
{
    int r;
    struct conversations_state *state = NULL;

    if (!(statusitems & STATUS_CONVITEMS)) goto nonconv;
    statusitems &= ~STATUS_CONVITEMS; /* strip them for the regular lookup */

    /* use the existing state if possible */
    state = conversations_get_mbox(mailboxname);

    /* otherwise fetch a new one! */
    if (!state) {
        if (global_conversations) {
            conversations_abort(&global_conversations);
            global_conversations = NULL;
        }
        r = conversations_open_mbox(mailboxname, &state);
        if (r) {
            /* maybe the mailbox doesn't even have conversations - just ignore */
            goto nonconv;
        }
        global_conversations = state;
    }

    r = conversation_getstatus(state, mailboxname, &sd->xconv);
    if (r) return r;

nonconv:
    /* use the index status if we can so we get the 'alive' Recent count */
    if (!strcmpsafe(mailboxname, index_mboxname(imapd_index)) && imapd_index->mailbox)
        return index_status(imapd_index, sd);

    /* fall back to generic lookup */
    return status_lookup(mailboxname, imapd_userid, statusitems, sd);
}

Project: cyrus-imapd
File: imapd.c

static int isokflag(char *s, int *isseen)
{
    if (s[0] == '\\') {
        lcase(s);
        if (!strcmp(s, "\\seen")) {
            *isseen = 1;
            return 1;
        }
        if (!strcmp(s, "\\answered")) return 1;
        if (!strcmp(s, "\\flagged")) return 1;
        if (!strcmp(s, "\\draft")) return 1;
        if (!strcmp(s, "\\deleted")) return 1;

        /* uh oh, system flag i don't recognize */
        return 0;
    } else {
        /* valid user flag? */
        return imparse_isatom(s);
    }
}

Project: cyrus-imapd
File: imapd.c

static int list_cb(struct findall_data *data, void *rockp)
{
    struct list_rock *rock = (struct list_rock *)rockp;
    if (!data) {
        if (!(rock->last_attributes & MBOX_ATTRIBUTE_HASCHILDREN))
            rock->last_attributes |= MBOX_ATTRIBUTE_HASNOCHILDREN;
        perform_output(NULL, NULL, rock);
        return 0;
    }
    size_t last_len = (rock->last_name ? strlen(rock->last_name) : 0);
    const char *extname = data->extname;
    int last_name_is_ancestor =
        rock->last_name
        && strlen(extname) >= last_len
        && extname[last_len] == imapd_namespace.hier_sep
        && !memcmp(rock->last_name, extname, last_len);

    list_callback_calls++;

    /* list_response will calculate haschildren/hasnochildren flags later
     * if they're required but not yet set, but it's a little cheaper to
     * precalculate them now while we're iterating the mailboxes anyway.
     */
    if (last_name_is_ancestor || (rock->last_name && !data->mbname && !strcmp(rock->last_name, extname)))
        rock->last_attributes |= MBOX_ATTRIBUTE_HASCHILDREN;

    else if (!(rock->last_attributes & MBOX_ATTRIBUTE_HASCHILDREN))
        rock->last_attributes |= MBOX_ATTRIBUTE_HASNOCHILDREN;

    if (!perform_output(data->extname, data->mbentry, rock))
        return 0;

    if (!data->mbname)
        rock->last_attributes |= MBOX_ATTRIBUTE_HASCHILDREN | MBOX_ATTRIBUTE_NONEXISTENT;

    else if (data->mb_category == MBNAME_ALTINBOX)
        rock->last_attributes |= MBOX_ATTRIBUTE_NOINFERIORS;

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static void list_data_recursivematch(struct listargs *listargs) {
    struct list_rock_recursivematch rock;

    rock.count = 0;
    rock.listargs = listargs;
    construct_hash_table(&rock.table, 100, 1);

    /* find */
    mboxlist_findsubmulti(&imapd_namespace, &listargs->pat, imapd_userisadmin, imapd_userid,
                          imapd_authstate, recursivematch_cb, &rock, 1);

    if (rock.count) {
        int i;
        int entries = rock.count;

        /* sort */
        rock.array = xmalloc(entries * (sizeof(struct list_entry)));
        hash_enumerate(&rock.table, copy_to_array, &rock);
        qsort(rock.array, entries, sizeof(struct list_entry),
              list_entry_comparator);
        assert(rock.count == 0);

        /* print */
        for (i = 0; i < entries; i++) {
            if (!rock.array[i].name) continue;
            mbentry_t *mbentry = NULL;
            mboxlist_lookup(rock.array[i].name, &mbentry, NULL);
            list_response(rock.array[i].name,
                          mbentry,
                          rock.array[i].attributes,
                          rock.listargs);
            mboxlist_entry_free(&mbentry);
        }

        free(rock.array);
    }

    free_hash_table(&rock.table, free);
}

Project: cyrus-imapd
File: imapd.c

static void list_response(const char *extname, const mbentry_t *mbentry,
                          uint32_t attributes, struct listargs *listargs)
{
    int r;
    struct statusdata sdata = STATUSDATA_INIT;
    struct buf specialuse = BUF_INITIALIZER;

    if ((attributes & MBOX_ATTRIBUTE_NONEXISTENT)) {
        if (!(listargs->cmd == LIST_CMD_EXTENDED)) {
            attributes |= MBOX_ATTRIBUTE_NOSELECT;
            attributes &= ~MBOX_ATTRIBUTE_NONEXISTENT;
        }
    }

    else if (listargs->scan) {
        /* SCAN mailbox for content */

        if (!strcmpsafe(mbentry->name, index_mboxname(imapd_index))) {
            /* currently selected mailbox */
            if (!index_scan(imapd_index, listargs->scan))
                return; /* no matching messages */
        }
        else {
            /* other local mailbox */
            struct index_state *state;
            struct index_init init;
            int doclose = 0;

            memset(&init, 0, sizeof(struct index_init));
            init.userid = imapd_userid;
            init.authstate = imapd_authstate;
            init.out = imapd_out;

            r = index_open(mbentry->name, &init, &state);

            if (!r)
                doclose = 1;

            if (!r && index_hasrights(state, ACL_READ)) {
                r = (imapd_userisadmin || index_hasrights(state, ACL_LOOKUP)) ?
                    IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
            }

            if (!r) {
                if (!index_scan(state, listargs->scan)) {
                    r = -1;  /* no matching messages */
                }
            }

            if (doclose) index_close(&state);

            if (r) return;
        }
    }

    /* figure out \Has(No)Children if necessary
       This is mainly used for LIST (SUBSCRIBED) RETURN (CHILDREN)
    */
    uint32_t have_childinfo =
        MBOX_ATTRIBUTE_HASCHILDREN | MBOX_ATTRIBUTE_HASNOCHILDREN;
    if ((listargs->ret & LIST_RET_CHILDREN) && !(attributes & have_childinfo)) {
        if (imapd_namespace.isalt && !strcmp(extname, "INBOX")) {
            /* don't look inside INBOX under altnamespace, its children aren't children */
        }
        else {
            char *intname = NULL, *freeme = NULL;

            /* if we got here via subscribed_cb, mbentry isn't set */
            if (mbentry)
                intname = mbentry->name;
            else
                intname = freeme = mboxname_from_external(extname, &imapd_namespace, imapd_userid);

            mboxlist_mboxtree(intname, set_haschildren, &attributes, MBOXTREE_SKIP_ROOT);
            if (freeme) free(freeme);
        }

        if (!(attributes & MBOX_ATTRIBUTE_HASCHILDREN))
            attributes |= MBOX_ATTRIBUTE_HASNOCHILDREN;
    }

    if (attributes & (MBOX_ATTRIBUTE_NONEXISTENT | MBOX_ATTRIBUTE_NOSELECT)) {
        int keep = 0;
        /* extended get told everything */
        if (listargs->cmd == LIST_CMD_EXTENDED) {
            keep = 1;
        }
        /* we have to mention this, it has children */
        if (listargs->cmd == LIST_CMD_LSUB) {
            /* subscribed children need a mention */
            if (attributes & MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED)
                keep = 1;
            /* if mupdate is configured we can't drop out, we might
             * be a backend and need to report folders that don't
             * exist on this backend - this is awful and complex
             * and brittle and should be changed */
            if (config_mupdate_server)
                keep = 1;
        }
        else if (attributes & MBOX_ATTRIBUTE_HASCHILDREN)
            keep = 1;

        if (!keep) return;
    }

    if (listargs->cmd == LIST_CMD_LSUB) {
        /* \Noselect has a special second meaning with (R)LSUB */
        if ( !(attributes & MBOX_ATTRIBUTE_SUBSCRIBED)
             && attributes & MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED)
            attributes |= MBOX_ATTRIBUTE_NOSELECT | MBOX_ATTRIBUTE_HASCHILDREN;
        attributes &= ~MBOX_ATTRIBUTE_SUBSCRIBED;
    }

    /* As CHILDINFO extended data item is not allowed if the
     * RECURSIVEMATCH selection option is not specified */
    if (!(listargs->sel & LIST_SEL_RECURSIVEMATCH)) {
        attributes &= ~MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED;
    }

    /* no inferiors means no children (this basically means the INBOX
     * in alt namespace mode */
    if (attributes & MBOX_ATTRIBUTE_NOINFERIORS)
        attributes &= ~MBOX_ATTRIBUTE_HASCHILDREN;

    /* you can't have both!  If it's had children, it has children */
    if (attributes & MBOX_ATTRIBUTE_HASCHILDREN)
        attributes &= ~MBOX_ATTRIBUTE_HASNOCHILDREN;

    /* remove redundant flags */
    if (listargs->cmd == LIST_CMD_EXTENDED) {
        /* \NoInferiors implies \HasNoChildren */
        if (attributes & MBOX_ATTRIBUTE_NOINFERIORS)
            attributes &= ~MBOX_ATTRIBUTE_HASNOCHILDREN;
        /* \NonExistent implies \Noselect */
        if (attributes & MBOX_ATTRIBUTE_NONEXISTENT)
            attributes &= ~MBOX_ATTRIBUTE_NOSELECT;
    }

    if (config_getswitch(IMAPOPT_SPECIALUSEALWAYS) ||
        listargs->cmd == LIST_CMD_XLIST ||
        listargs->ret & LIST_RET_SPECIALUSE) {
        specialuse_flags(mbentry, &specialuse, listargs->cmd == LIST_CMD_XLIST);
    }

    if (listargs->sel & LIST_SEL_SPECIALUSE) {
        /* check that this IS a specialuse folder */
        if (!buf_len(&specialuse)) return;
    }

    /* can we read the status data ? */
    if ((listargs->ret & LIST_RET_STATUS) && mbentry) {
        r = imapd_statusdata(mbentry->name, listargs->statusitems, &sdata);
        if (r) {
            /* RFC 5819: the STATUS response MUST NOT be returned and the
             * LIST response MUST include the \NoSelect attribute. */
            attributes |= MBOX_ATTRIBUTE_NOSELECT;
        }
    }

    print_listresponse(listargs->cmd, extname,
                       imapd_namespace.hier_sep, attributes, &specialuse);
    buf_free(&specialuse);

    if ((listargs->ret & LIST_RET_STATUS) &&
        !(attributes & MBOX_ATTRIBUTE_NOSELECT)) {
        /* output the status line now, per rfc 5819 */
        if (mbentry) print_statusline(extname, listargs->statusitems, &sdata);
    }

    if ((listargs->ret & LIST_RET_MYRIGHTS) &&
        !(attributes & MBOX_ATTRIBUTE_NOSELECT)) {
        if (mbentry) printmyrights(extname, mbentry);
    }

    if ((listargs->ret & LIST_RET_METADATA) &&
        !(attributes & MBOX_ATTRIBUTE_NOSELECT)) {
        if (mbentry)
            printmetadata(mbentry, &listargs->metaitems, &listargs->metaopts);
    }
}

Project: cyrus-imapd
File: imapd.c

static int mlookup(const char *tag, const char *ext_name,
            const char *name, mbentry_t **mbentryptr)
{
    int r;
    mbentry_t *mbentry = NULL;

    r = mboxlist_lookup(name, &mbentry, NULL);
    if ((r == IMAP_MAILBOX_NONEXISTENT || (!r && (mbentry->mbtype & MBTYPE_RESERVE))) &&
        config_mupdate_server) {
        /* It is not currently active, make sure we have the most recent
         * copy of the database */
        kick_mupdate();
        mboxlist_entry_free(&mbentry);
        r = mboxlist_lookup(name, &mbentry, NULL);
    }

    if (r) goto done;

    if (mbentry->mbtype & MBTYPE_RESERVE) {
        r = IMAP_MAILBOX_RESERVED;
    }
    else if (mbentry->mbtype & MBTYPE_DELETED) {
        r = IMAP_MAILBOX_NONEXISTENT;
    }
    else if (mbentry->mbtype & MBTYPE_MOVING) {
        /* do we have rights on the mailbox? */
        if (!imapd_userisadmin &&
           (!mbentry->acl || !(cyrus_acl_myrights(imapd_authstate, mbentry->acl) & ACL_LOOKUP))) {
            r = IMAP_MAILBOX_NONEXISTENT;
        } else if (tag && ext_name && mbentry->server) {
            imapd_refer(tag, mbentry->server, ext_name);
            r = IMAP_MAILBOX_MOVED;
        } else if (config_mupdate_server) {
            r = IMAP_SERVER_UNAVAILABLE;
        } else {
            r = IMAP_MAILBOX_NOTSUPPORTED;
        }
    }

done:
    if (r) mboxlist_entry_free(&mbentry);
    else if (mbentryptr) *mbentryptr = mbentry;
    else mboxlist_entry_free(&mbentry); /* we don't actually want it! */

    return r;
}

Project: cyrus-imapd
File: imapd.c

static void motd_file(void)
{
    char *filename = NULL;
    int fd = -1;
    struct protstream *motd_in = NULL;
    char buf[MAX_MAILBOX_PATH+1];
    char *p;

    filename = strconcat(config_dir, "/msg/motd", (char *)NULL);
    fd = open(filename, O_RDONLY, 0);
    if (fd < 0)
        goto out;

    motd_in = prot_new(fd, 0);

    prot_fgets(buf, sizeof(buf), motd_in);
    if ((p = strchr(buf, '\r'))!=NULL) *p = 0;
    if ((p = strchr(buf, '\n'))!=NULL) *p = 0;

    for (p = buf; *p == '['; p++); /* can't have [ be first char, sigh */
    prot_printf(imapd_out, "* OK [ALERT] %s\r\n", p);

out:
    if (motd_in)
        prot_free(motd_in);
    if (fd >= 0)
        close(fd);
    free(filename);
}

Project: cyrus-imapd
File: imapd.c

static int namespacedata(struct findall_data *data, void *rock)
{
    if (!data) return 0;
    int* sawone = (int*) rock;

    switch (data->mb_category) {
    case MBNAME_INBOX:
    case MBNAME_ALTINBOX:
    case MBNAME_ALTPREFIX:
        sawone[NAMESPACE_INBOX] = 1;
        break;

    case MBNAME_OTHERUSER:
        sawone[NAMESPACE_USER] = 1;
        break;

    case MBNAME_SHARED:
        sawone[NAMESPACE_SHARED] = 1;
        break;
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int parse_annotate_store_data(const char *tag,
                                     int permessage_flag,
                                     struct entryattlist **entryatts)
{
    int c, islist = 0;
    static struct buf entry, attrib, value;
    struct attvaluelist *attvalues = NULL;

    *entryatts = NULL;

    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 */
        islist = 1;
    }
    else if (permessage_flag) {
        prot_printf(imapd_out,
                    "%s BAD Missing paren for annotation entry\r\n", tag);
        goto baddata;
    }
    else {
        /* single entry -- put the char back */
        prot_ungetc(c, imapd_in);
    }

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

        /* parse att-value list */
        if (c != ' ' || (c = prot_getc(imapd_in)) != '(') {
            prot_printf(imapd_out,
                        "%s BAD Missing annotation attribute-values list\r\n",
                        tag);
            goto baddata;
        }

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

            /* get value */
            if (c != ' ') {
                prot_printf(imapd_out,
                            "%s BAD Missing annotation value\r\n", tag);
                goto baddata;
            }
            c = getbnstring(imapd_in, imapd_out, &value);
            if (c == EOF) {
                prot_printf(imapd_out,
                            "%s BAD Missing annotation value\r\n", tag);
                goto baddata;
            }

            /* add the attrib-value pair to the list */
            appendattvalue(&attvalues, attrib.s, &value);

        } while (c == ' ');

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

        /* add the entry to the list */
        appendentryatt(entryatts, entry.s, attvalues);
        attvalues = NULL;

        c = prot_getc(imapd_in);

    } while (c == ' ');

    if (islist) {
        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);
    }

    return c;

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

Project: cyrus-imapd
File: imapd.c

static int parse_fetch_args(const char *tag, const char *cmd,
                            int allow_vanished,
                            struct fetchargs *fa)
{
    static struct buf fetchatt, fieldname;
    int c;
    int inlist = 0;
    char *p, *section;
    struct octetinfo oi;
    strarray_t *newfields = strarray_new();

    c = getword(imapd_in, &fetchatt);
    if (c == '(' && !fetchatt.s[0]) {
        inlist = 1;
        c = getword(imapd_in, &fetchatt);
    }
    for (;;) {
        ucase(fetchatt.s);
        switch (fetchatt.s[0]) {
        case 'A':
            if (!inlist && !strcmp(fetchatt.s, "ALL")) {
                fa->fetchitems |= FETCH_ALL;
            }
            else if (!strcmp(fetchatt.s, "ANNOTATION")) {
                fa->fetchitems |= FETCH_ANNOTATION;
                if (c != ' ')
                    goto badannotation;
                c = prot_getc(imapd_in);
                if (c != '(')
                    goto badannotation;
                c = parse_annotate_fetch_data(tag,
                                              /*permessage_flag*/1,
                                              &fa->entries,
                                              &fa->attribs);
                if (c == EOF) {
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                if (c != ')') {
badannotation:
                    prot_printf(imapd_out, "%s BAD invalid Annotation\r\n", tag);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                c = prot_getc(imapd_in);
            }
            else goto badatt;
            break;

        case 'B':
            if (!strncmp(fetchatt.s, "BINARY[", 7) ||
                !strncmp(fetchatt.s, "BINARY.PEEK[", 12) ||
                !strncmp(fetchatt.s, "BINARY.SIZE[", 12)) {
                int binsize = 0;

                p = section = fetchatt.s + 7;
                if (!strncmp(p, "PEEK[", 5)) {
                    p = section += 5;
                }
                else if (!strncmp(p, "SIZE[", 5)) {
                    p = section += 5;
                    binsize = 1;
                }
                else {
                    fa->fetchitems |= FETCH_SETSEEN;
                }
                while (Uisdigit(*p) || *p == '.') {
                    if (*p == '.' && !Uisdigit(p[-1])) break;
                    /* Part number cannot begin with '0' */
                    if (*p == '0' && !Uisdigit(p[-1])) break;
                    p++;
                }

                if (*p != ']') {
                    prot_printf(imapd_out, "%s BAD Invalid binary section\r\n", tag);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                p++;

                if (!binsize) PARSE_PARTIAL(oi.start_octet, oi.octet_count);

                if (*p) {
                    prot_printf(imapd_out, "%s BAD Junk after binary section\r\n", tag);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                if (binsize)
                    section_list_append(&fa->sizesections, section, &oi);
                else
                    section_list_append(&fa->binsections, section, &oi);
            }
            else if (!strcmp(fetchatt.s, "BODY")) {
                fa->fetchitems |= FETCH_BODY;
            }
            else if (!strcmp(fetchatt.s, "BODYSTRUCTURE")) {
                fa->fetchitems |= FETCH_BODYSTRUCTURE;
            }
            else if (!strncmp(fetchatt.s, "BODY[", 5) ||
                     !strncmp(fetchatt.s, "BODY.PEEK[", 10)) {
                p = section = fetchatt.s + 5;
                if (!strncmp(p, "PEEK[", 5)) {
                    p = section += 5;
                }
                else {
                    fa->fetchitems |= FETCH_SETSEEN;
                }
                while (Uisdigit(*p) || *p == '.') {
                    if (*p == '.' && !Uisdigit(p[-1])) break;
                    /* Obsolete section 0 can only occur before close brace */
                    if (*p == '0' && !Uisdigit(p[-1]) && p[1] != ']') break;
                    p++;
                }

                if (*p == 'H' && !strncmp(p, "HEADER.FIELDS", 13) &&
                    (p == section || p[-1] == '.') &&
                    (p[13] == '\0' || !strcmp(p+13, ".NOT"))) {

                    /*
                     * If not top-level or a HEADER.FIELDS.NOT, can't pull
                     * the headers out of the cache.
                     */
                    if (p != section || p[13] != '\0') {
                        fa->cache_atleast = BIT32_MAX;
                    }

                    if (c != ' ') {
                        prot_printf(imapd_out,
                                    "%s BAD Missing required argument to %s %s\r\n",
                                    tag, cmd, fetchatt.s);
                        eatline(imapd_in, c);
                        goto freeargs;
                    }
                    c = prot_getc(imapd_in);
                    if (c != '(') {
                        prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n",
                                    tag, cmd, fetchatt.s);
                        eatline(imapd_in, c);
                        goto freeargs;
                    }
                    do {
                        c = getastring(imapd_in, imapd_out, &fieldname);
                        for (p = fieldname.s; *p; p++) {
                            if (*p <= ' ' || *p & 0x80 || *p == ':') break;
                        }
                        if (*p || !*fieldname.s) {
                            prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n",
                                        tag, cmd, fetchatt.s);
                            eatline(imapd_in, c);
                            goto freeargs;
                        }
                        strarray_append(newfields, fieldname.s);
                        if (fa->cache_atleast < BIT32_MAX) {
                            bit32 this_ver =
                                mailbox_cached_header(fieldname.s);
                            if(this_ver > fa->cache_atleast)
                                fa->cache_atleast = this_ver;
                        }
                    } while (c == ' ');
                    if (c != ')') {
                        prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
                                    tag, cmd, fetchatt.s);
                        eatline(imapd_in, c);
                        goto freeargs;
                    }

                    /* Grab/parse the ]<x.y> part */
                    c = getword(imapd_in, &fieldname);
                    p = fieldname.s;
                    if (*p++ != ']') {
                        prot_printf(imapd_out, "%s BAD Missing required close bracket after %s %s\r\n",
                                    tag, cmd, fetchatt.s);
                        eatline(imapd_in, c);
                        goto freeargs;
                    }

                    PARSE_PARTIAL(oi.start_octet, oi.octet_count);

                    if (*p) {
                        prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
                        eatline(imapd_in, c);
                        goto freeargs;
                    }
                    appendfieldlist(&fa->fsections,
                                    section, newfields, fieldname.s,
                                    &oi, sizeof(oi));
                    /* old 'newfields' is managed by the fieldlist now */
                    newfields = strarray_new();
                    break;
                }

                switch (*p) {
                case 'H':
                    if (p != section && p[-1] != '.') break;
                    if (!strncmp(p, "HEADER]", 7)) p += 6;
                    break;

                case 'M':
                    if (!strncmp(p-1, ".MIME]", 6)) p += 4;
                    break;

                case 'T':
                    if (p != section && p[-1] != '.') break;
                    if (!strncmp(p, "TEXT]", 5)) p += 4;
                    break;
                }

                if (*p != ']') {
                    prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                p++;

                PARSE_PARTIAL(oi.start_octet, oi.octet_count);

                if (*p) {
                    prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                section_list_append(&fa->bodysections, section, &oi);
            }
            else goto badatt;
            break;

        case 'C':
            if (!strcmp(fetchatt.s, "CID") &&
                config_getswitch(IMAPOPT_CONVERSATIONS)) {
                fa->fetchitems |= FETCH_CID;
            }
            else goto badatt;
            break;

        case 'D':
            if (!strcmp(fetchatt.s, "DIGEST.SHA1")) {
                fa->fetchitems |= FETCH_GUID;
            }
            else goto badatt;
            break;

        case 'E':
            if (!strcmp(fetchatt.s, "ENVELOPE")) {
                fa->fetchitems |= FETCH_ENVELOPE;
            }
            else goto badatt;
            break;

        case 'F':
            if (!inlist && !strcmp(fetchatt.s, "FAST")) {
                fa->fetchitems |= FETCH_FAST;
            }
            else if (!inlist && !strcmp(fetchatt.s, "FULL")) {
                fa->fetchitems |= FETCH_FULL;
            }
            else if (!strcmp(fetchatt.s, "FLAGS")) {
                fa->fetchitems |= FETCH_FLAGS;
            }
            else if (!strcmp(fetchatt.s, "FOLDER")) {
                fa->fetchitems |= FETCH_FOLDER;
            }
            else goto badatt;
            break;

        case 'I':
            if (!strcmp(fetchatt.s, "INTERNALDATE")) {
                fa->fetchitems |= FETCH_INTERNALDATE;
            }
            else goto badatt;
            break;

        case 'M':
            if (!strcmp(fetchatt.s, "MODSEQ")) {
                fa->fetchitems |= FETCH_MODSEQ;
            }
            else goto badatt;
            break;

        case 'R':
            if (!strcmp(fetchatt.s, "RFC822")) {
                fa->fetchitems |= FETCH_RFC822|FETCH_SETSEEN;
            }
            else if (!strcmp(fetchatt.s, "RFC822.HEADER")) {
                fa->fetchitems |= FETCH_HEADER;
            }
            else if (!strcmp(fetchatt.s, "RFC822.PEEK")) {
                fa->fetchitems |= FETCH_RFC822;
            }
            else if (!strcmp(fetchatt.s, "RFC822.SIZE")) {
                fa->fetchitems |= FETCH_SIZE;
            }
            else if (!strcmp(fetchatt.s, "RFC822.TEXT")) {
                fa->fetchitems |= FETCH_TEXT|FETCH_SETSEEN;
            }
            else if (!strcmp(fetchatt.s, "RFC822.SHA1")) {
                fa->fetchitems |= FETCH_SHA1;
            }
            else if (!strcmp(fetchatt.s, "RFC822.FILESIZE")) {
                fa->fetchitems |= FETCH_FILESIZE;
            }
            else if (!strcmp(fetchatt.s, "RFC822.TEXT.PEEK")) {
                fa->fetchitems |= FETCH_TEXT;
            }
            else if (!strcmp(fetchatt.s, "RFC822.HEADER.LINES") ||
                     !strcmp(fetchatt.s, "RFC822.HEADER.LINES.NOT")) {
                if (c != ' ') {
                    prot_printf(imapd_out, "%s BAD Missing required argument to %s %s\r\n",
                           tag, cmd, fetchatt.s);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                c = prot_getc(imapd_in);
                if (c != '(') {
                    prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n",
                           tag, cmd, fetchatt.s);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                do {
                    c = getastring(imapd_in, imapd_out, &fieldname);
                    for (p = fieldname.s; *p; p++) {
                        if (*p <= ' ' || *p & 0x80 || *p == ':') break;
                    }
                    if (*p || !*fieldname.s) {
                        prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n",
                               tag, cmd, fetchatt.s);
                        eatline(imapd_in, c);
                        goto freeargs;
                    }
                    lcase(fieldname.s);;
                    /* 19 is magic number -- length of
                     * "RFC822.HEADERS.NOT" */
                    strarray_append(strlen(fetchatt.s) == 19 ?
                                  &fa->headers : &fa->headers_not,
                                  fieldname.s);
                    if (strlen(fetchatt.s) != 19) {
                        fa->cache_atleast = BIT32_MAX;
                    }
                    if (fa->cache_atleast < BIT32_MAX) {
                        bit32 this_ver =
                            mailbox_cached_header(fieldname.s);
                        if(this_ver > fa->cache_atleast)
                            fa->cache_atleast = this_ver;
                   }
                } while (c == ' ');
                if (c != ')') {
                    prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
                           tag, cmd, fetchatt.s);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                c = prot_getc(imapd_in);
            }
            else goto badatt;
            break;

        case 'U':
            if (!strcmp(fetchatt.s, "UID")) {
                fa->fetchitems |= FETCH_UID;
            }
            else if (!strcmp(fetchatt.s, "UIDVALIDITY")) {
                fa->fetchitems |= FETCH_UIDVALIDITY;
            }
            else goto badatt;
            break;

        default:
        badatt:
            prot_printf(imapd_out, "%s BAD Invalid %s attribute %s\r\n", tag, cmd, fetchatt.s);
            eatline(imapd_in, c);
            goto freeargs;
        }

        if (inlist && c == ' ') c = getword(imapd_in, &fetchatt);
        else break;
    }

    if (inlist && c == ')') {
        inlist = 0;
        c = prot_getc(imapd_in);
    }
    if (inlist) {
        prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n",
                    tag, cmd);
        eatline(imapd_in, c);
        goto freeargs;
    }

    if (c == ' ') {
        /* Grab/parse the modifier(s) */
        c = prot_getc(imapd_in);
        if (c != '(') {
            prot_printf(imapd_out,
                        "%s BAD Missing required open parenthesis in %s modifiers\r\n",
                        tag, cmd);
            eatline(imapd_in, c);
            goto freeargs;
        }
        do {
            c = getword(imapd_in, &fetchatt);
            ucase(fetchatt.s);
            if (!strcmp(fetchatt.s, "CHANGEDSINCE")) {
                if (c != ' ') {
                    prot_printf(imapd_out,
                                "%s BAD Missing required argument to %s %s\r\n",
                                tag, cmd, fetchatt.s);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                c = getmodseq(imapd_in, &fa->changedsince);
                if (c == EOF) {
                    prot_printf(imapd_out,
                                "%s BAD Invalid argument to %s %s\r\n",
                                tag, cmd, fetchatt.s);
                    eatline(imapd_in, c);
                    goto freeargs;
                }
                fa->fetchitems |= FETCH_MODSEQ;
            }
            else if (allow_vanished &&
                     !strcmp(fetchatt.s, "VANISHED")) {
                fa->vanished = 1;
            }
            else {
                prot_printf(imapd_out, "%s BAD Invalid %s modifier %s\r\n",
                            tag, cmd, fetchatt.s);
                eatline(imapd_in, c);
                goto freeargs;
            }
        } while (c == ' ');
        if (c != ')') {
            prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n",
                        tag, cmd);
            eatline(imapd_in, c);
            goto freeargs;
        }
        c = prot_getc(imapd_in);
    }

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

    if (!fa->fetchitems && !fa->bodysections && !fa->fsections &&
        !fa->binsections && !fa->sizesections &&
        !fa->headers.count && !fa->headers_not.count) {
        prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd);
        goto freeargs;
    }

    if (fa->vanished && !fa->changedsince) {
        prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd);
        goto freeargs;
    }

    if (fa->fetchitems & FETCH_MODSEQ) {
        if (!(client_capa & CAPA_CONDSTORE)) {
            client_capa |= CAPA_CONDSTORE;
            if (imapd_index)
                prot_printf(imapd_out, "* OK [HIGHESTMODSEQ " MODSEQ_FMT "]  \r\n",
                        index_highestmodseq(imapd_index));
        }
    }

    if (fa->fetchitems & (FETCH_ANNOTATION|FETCH_FOLDER)) {
        fa->namespace = &imapd_namespace;
        fa->userid = imapd_userid;
    }
    if (fa->fetchitems & FETCH_ANNOTATION) {
        fa->isadmin = imapd_userisadmin || imapd_userisproxyadmin;
        fa->authstate = imapd_authstate;
    }

    strarray_free(newfields);
    return 0;

freeargs:
    strarray_free(newfields);
    return IMAP_PROTOCOL_BAD_PARAMETERS;
}

Project: cyrus-imapd
File: imapd.c

static int parse_getmetadata_options(const strarray_t *sa,
                                     struct getmetadata_options *opts)
{
    int i;
    int n = 0;
    struct getmetadata_options dummy = OPTS_INITIALIZER;

    if (!opts) opts = &dummy;

    for (i = 0 ; i < sa->count ; i+=2) {
        const char *option = sa->data[i];
        const char *value = sa->data[i+1];
        if (!value)
            return -1;
        if (!strcasecmp(option, "MAXSIZE")) {
            char *end = NULL;
            /* we add one so that it's "less than" maxsize
             * and zero works but is still true */
            opts->maxsize = strtoul(value, &end, 10) + 1;
            if (!end || *end || end == value)
                return -1;
            n++;
        }
        else if (!strcasecmp(option, "DEPTH")) {
            if (!strcmp(value, "0"))
                opts->depth = 0;
            else if (!strcmp(value, "1"))
                opts->depth = 1;
            else if (!strcasecmp(value, "infinity"))
                opts->depth = -1;
            else
                return -1;
            n++;
        }
        else {
            return 0;
        }
    }

    return n;
}

Project: cyrus-imapd
File: imapd.c

static int parse_metadata_store_data(const char *tag,
                                     struct entryattlist **entryatts)
{
    int c;
    const char *name;
    const char *att;
    static struct buf entry, value;
    struct attvaluelist *attvalues = NULL;
    struct entryattlist *entryp;
    int need_add;

    *entryatts = NULL;

    c = prot_getc(imapd_in);
    if (c != '(') {
        prot_printf(imapd_out,
                    "%s BAD Missing metadata entry list\r\n", tag);
        goto baddata;
    }

    do {
        /* get entry */
        c = getastring(imapd_in, imapd_out, &entry);
        if (c != ' ') {
            prot_printf(imapd_out,
                        "%s BAD Missing metadata entry\r\n", tag);
            goto baddata;
        }
        lcase(entry.s);

        /* get value */
        c = getbnstring(imapd_in, imapd_out, &value);
        if (c == EOF) {
            prot_printf(imapd_out,
                        "%s BAD Missing metadata value\r\n", tag);
            goto baddata;
        }

        if (!strncmp(entry.s, "/private", 8) &&
            (entry.s[8] == '\0' || entry.s[8] == '/')) {
            att = "value.priv";
            name = entry.s + 8;
        }
        else if (!strncmp(entry.s, "/shared", 7) &&
                 (entry.s[7] == '\0' || entry.s[7] == '/')) {
            att = "value.shared";
            name = entry.s + 7;
        }
        else {
            prot_printf(imapd_out,
                        "%s BAD entry must begin with /shared or /private\r\n",
                        tag);
            goto baddata;
        }

        need_add = 1;
        for (entryp = *entryatts; entryp; entryp = entryp->next) {
            if (strcmp(entryp->entry, name)) continue;
            /* it's a match, have to append! */
            appendattvalue(&entryp->attvalues, att, &value);
            need_add = 0;
            break;
        }
        if (need_add) {
            appendattvalue(&attvalues, att, &value);
            appendentryatt(entryatts, name, attvalues);
            attvalues = NULL;
        }
    } 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);

    return c;

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

Project: cyrus-imapd

static int parse_metadata_string_or_list(const char *tag,
                                         strarray_t *entries,
                                         int *is_list)
{
    int c;
    static struct buf arg;

    if (is_list) *is_list = 1;

    c = prot_getc(imapd_in);
    if (c == EOF) {
        prot_printf(imapd_out,
                    "%s BAD Missing metadata entry\r\n", tag);
        goto baddata;
    }
    else if (c == '\r') {
        return c;
    }
    else if (c == '(') {
        /* entry list */
        do {
            c = getastring(imapd_in, imapd_out, &arg);
            if (c == EOF) {
                prot_printf(imapd_out,
                            "%s BAD Missing metadata 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 metadata entry list \r\n",
                        tag);
            goto baddata;
        }

        if (is_list) *is_list = 0;

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

        strarray_append(entries, arg.s);

        if (!strchr(arg.s, '*') && !strchr(arg.s, '%')) {
            if (is_list) *is_list = 0;
        }
    }

    if (c == ' ' || c == '\r' || c == ')') return c;

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

Project: cyrus-imapd
File: imapd.c

static int parse_statusitems(unsigned *statusitemsp, const char **errstr)
{
    static struct buf arg;
    unsigned statusitems = 0;
    int c;
    int hasconv = config_getswitch(IMAPOPT_CONVERSATIONS);

    c = prot_getc(imapd_in);
    if (c != '(') return EOF;

    c = getword(imapd_in, &arg);
    if (arg.s[0] == '\0') return EOF;
    for (;;) {
        lcase(arg.s);
        if (!strcmp(arg.s, "messages")) {
            statusitems |= STATUS_MESSAGES;
        }
        else if (!strcmp(arg.s, "recent")) {
            statusitems |= STATUS_RECENT;
        }
        else if (!strcmp(arg.s, "uidnext")) {
            statusitems |= STATUS_UIDNEXT;
        }
        else if (!strcmp(arg.s, "uidvalidity")) {
            statusitems |= STATUS_UIDVALIDITY;
        }
        else if (!strcmp(arg.s, "unseen")) {
            statusitems |= STATUS_UNSEEN;
        }
        else if (!strcmp(arg.s, "highestmodseq")) {
            statusitems |= STATUS_HIGHESTMODSEQ;
        }
        else if (hasconv && !strcmp(arg.s, "xconvexists")) {
            statusitems |= STATUS_XCONVEXISTS;
        }
        else if (hasconv && !strcmp(arg.s, "xconvunseen")) {
            statusitems |= STATUS_XCONVUNSEEN;
        }
        else if (hasconv && !strcmp(arg.s, "xconvmodseq")) {
            statusitems |= STATUS_XCONVMODSEQ;
        }
        else {
            static char buf[200];
            snprintf(buf, 200, "Invalid Status attributes %s", arg.s);
            *errstr = buf;
            return EOF;
        }

        if (c == ' ') c = getword(imapd_in, &arg);
        else break;
    }

    if (c != ')') {
        *errstr = "Missing close parenthesis in Status";
        return EOF;
    }
    c = prot_getc(imapd_in);

    /* success */
    *statusitemsp = statusitems;
    return c;
}

Project: cyrus-imapd
File: imapd.c

static int parse_windowargs(const char *tag,
                            struct windowargs **wa,
                            int updates)
{
    struct windowargs windowargs;
    struct buf arg = BUF_INITIALIZER;
    struct buf ext_folder = BUF_INITIALIZER;
    int c;

    memset(&windowargs, 0, sizeof(windowargs));

    c = prot_getc(imapd_in);
    if (c == EOF)
        goto out;
    if (c != '(') {
        /* no window args at all */
        prot_ungetc(c, imapd_in);
        goto out;
    }

    for (;;)
    {
        c = prot_getc(imapd_in);
        if (c == EOF)
            goto out;
        if (c == ')')
            break;  /* end of window args */

        prot_ungetc(c, imapd_in);
        c = getword(imapd_in, &arg);
        if (!arg.len)
            goto syntax_error;

        if (!strcasecmp(arg.s, "CONVERSATIONS"))
            windowargs.conversations = 1;
        else if (!strcasecmp(arg.s, "POSITION")) {
            if (updates)
                goto syntax_error;
            if (c != ' ')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (c != '(')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.position);
            if (c != ' ')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.limit);
            if (c != ')')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (windowargs.position == 0)
                goto syntax_error;
        }
        else if (!strcasecmp(arg.s, "ANCHOR")) {
            if (updates)
                goto syntax_error;
            if (c != ' ')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (c != '(')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.anchor);
            if (c != ' ')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.offset);
            if (c != ' ')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.limit);
            if (c != ')')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (windowargs.anchor == 0)
                goto syntax_error;
        }
        else if (!strcasecmp(arg.s, "MULTIANCHOR")) {
            if (updates)
                goto syntax_error;
            if (c != ' ')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (c != '(')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.anchor);
            if (c != ' ')
                goto syntax_error;
            c = getastring(imapd_in, imapd_out, &ext_folder);
            if (c != ' ')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.offset);
            if (c != ' ')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.limit);
            if (c != ')')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (windowargs.anchor == 0)
                goto syntax_error;
        }
        else if (!strcasecmp(arg.s, "CHANGEDSINCE")) {
            if (!updates)
                goto syntax_error;
            windowargs.changedsince = 1;
            if (c != ' ')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (c != '(')
                goto syntax_error;
            c = getmodseq(imapd_in, &windowargs.modseq);
            if (c != ' ')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.uidnext);
            if (c != ')')
                goto syntax_error;
            c = prot_getc(imapd_in);
        } else if (!strcasecmp(arg.s, "UPTO")) {
            if (!updates)
                goto syntax_error;
            if (c != ' ')
                goto syntax_error;
            c = prot_getc(imapd_in);
            if (c != '(')
                goto syntax_error;
            c = getuint32(imapd_in, &windowargs.upto);
            if (c != ')')
                goto syntax_error;
            c = prot_getc(imapd_in);

            if (windowargs.upto == 0)
                goto syntax_error;
        }
        else
            goto syntax_error;

        if (c == ')')
            break;
        if (c != ' ')
            goto syntax_error;
    }

    c = prot_getc(imapd_in);
    if (c != ' ')
        goto syntax_error;

out:
    /* these two are mutually exclusive */
    if (windowargs.anchor && windowargs.position)
        goto syntax_error;
    /* changedsince is mandatory for XCONVUPDATES
     * and illegal for XCONVSORT */
    if (!!updates != windowargs.changedsince)
        goto syntax_error;

    if (ext_folder.len) {
        windowargs.anchorfolder = mboxname_from_external(buf_cstring(&ext_folder),
                                                         &imapd_namespace,
                                                         imapd_userid);
    }

    *wa = xmemdup(&windowargs, sizeof(windowargs));
    buf_free(&ext_folder);
    buf_free(&arg);
    return c;

syntax_error:
    free(windowargs.anchorfolder);
    buf_free(&ext_folder);
    prot_printf(imapd_out, "%s BAD Syntax error in window arguments\r\n", tag);
    return EOF;
}

Project: cyrus-imapd
File: imapd.c

static int parsecreateargs(struct dlist **extargs)
{
    int c;
    static struct buf arg, val;
    struct dlist *res;
    struct dlist *sub;
    char *p;
    const char *name;

    res = dlist_newkvlist(NULL, "CREATE");

    c = prot_getc(imapd_in);
    if (c == '(') {
        /* new style RFC4466 arguments */
        do {
            c = getword(imapd_in, &arg);
            name = ucase(arg.s);
            if (c != ' ') goto fail;
            c = prot_getc(imapd_in);
            if (c == '(') {
                /* fun - more lists! */
                sub = dlist_newlist(res, name);
                do {
                    c = getword(imapd_in, &val);
                    dlist_setatom(sub, name, val.s);
                } while (c == ' ');
                if (c != ')') goto fail;
                c = prot_getc(imapd_in);
            }
            else {
                prot_ungetc(c, imapd_in);
                c = getword(imapd_in, &val);
                dlist_setatom(res, name, val.s);
            }
        } while (c == ' ');
        if (c != ')') goto fail;
        c = prot_getc(imapd_in);
    }
    else {
        prot_ungetc(c, imapd_in);
        c = getword(imapd_in, &arg);
        if (c == EOF) goto fail;
        p = strchr(arg.s, '!');
        if (p) {
            /* with a server */
            *p = '\0';
            dlist_setatom(res, "SERVER", arg.s);
            dlist_setatom(res, "PARTITION", p+1);
        }
        else {
            dlist_setatom(res, "PARTITION", arg.s);
        }
    }

    *extargs = res;
    return c;

 fail:
    dlist_free(&res);
    return EOF;
}

Project: cyrus-imapd
File: imapd.c

partition_list_add(char *name, struct partition_list *pl)
{
    struct partition_list *p;

    /* Is name already on list? */
    for (p=pl; p; p = p->next) {
        if (!strcmp(p->name, name))
            return(pl);
    }

    /* Add entry to start of list and return new list */
    p = xzmalloc(sizeof(struct partition_list));
    p->next = pl;
    p->name = xstrdup(name);

    return(p);
}

Project: cyrus-imapd
File: imapd.c

partition_list_free(struct partition_list *current)
{
    while (current) {
        struct partition_list *next = current->next;

        free(current->name);
        free(current);

        current = next;
    }
}

Project: cyrus-imapd
File: imapd.c

static int perform_output(const char *extname, const mbentry_t *mbentry, struct list_rock *rock)
{
    /* skip non-responsive mailboxes early, so they don't break sub folder detection */
    if (mbentry && !imapd_userisadmin) {
        if (mbentry->mbtype == MBTYPE_NETNEWS) return 0;
        if (!(rock->listargs->sel & LIST_SEL_DAV)) {
            if (mboxname_iscalendarmailbox(mbentry->name, mbentry->mbtype)) return 0;
            if (mboxname_isaddressbookmailbox(mbentry->name, mbentry->mbtype)) return 0;
            if (mboxname_isdavdrivemailbox(mbentry->name, mbentry->mbtype)) return 0;
            if (mboxname_isdavnotificationsmailbox(mbentry->name, mbentry->mbtype)) return 0;
        }
    }

    if (mbentry && (mbentry->mbtype & MBTYPE_REMOTE)) {
        struct listargs *listargs = rock->listargs;

        if (hash_lookup(mbentry->server, &rock->server_table)) {
            /* already proxied to this backend server */
            return 0;
        }
        if (listargs->scan ||
            (listargs->ret &
             (LIST_RET_SPECIALUSE | LIST_RET_STATUS | LIST_RET_METADATA))) {
            /* remote mailbox that we need to fetch metadata from */
            struct backend *s;

            hash_insert(mbentry->server,
                        (void *)0xDEADBEEF, &rock->server_table);
            s = proxy_findserver(mbentry->server, &imap_protocol,
                                 proxy_userid, &backend_cached,
                                 &backend_current, &backend_inbox, imapd_in);
            if (s) {
                char mytag[128];

                proxy_gentag(mytag, sizeof(mytag));

                if (listargs->scan) {
                    /* Send SCAN command to backend */
                    prot_printf(s->out, "%s Scan {%tu+}\r\n%s {%tu+}\r\n%s"
                                " {%tu+}\r\n%s\r\n",
                                mytag, strlen(listargs->ref), listargs->ref,
                                strlen(listargs->pat.data[0]),
                                listargs->pat.data[0],
                                strlen(listargs->scan), listargs->scan);

                    pipe_until_tag(s, mytag, 0);
                }
                else {
                    /* Send LIST command to backend */
                    list_data_remote(s, mytag, listargs, rock->subs);
                }
            }

            return 0;
        }
    }

    if (rock->last_name) {
        if (extname) {
            /* same again */
            if (!strcmp(rock->last_name, extname)) return 0;
            size_t extlen = strlen(extname);
            if (extlen < strlen(rock->last_name)
             && rock->last_name[extlen] == imapd_namespace.hier_sep
             && !strncmp(rock->last_name, extname, extlen))
                return 0; /* skip duplicate or reversed calls */
        }
        _addsubs(rock);
        /* check if we need to filter out this mailbox */
        if (!(rock->listargs->sel & LIST_SEL_SUBSCRIBED) ||
            (rock->last_attributes &
             (MBOX_ATTRIBUTE_SUBSCRIBED | MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED))) {
            list_response(rock->last_name, rock->last_mbentry,
                          rock->last_attributes, rock->listargs);
        }
        free(rock->last_name);
        rock->last_name = NULL;
        mboxlist_entry_free(&rock->last_mbentry);
    }

    if (extname) {
        rock->last_name = xstrdup(extname);
        if (mbentry) rock->last_mbentry = mboxlist_entry_copy(mbentry);
    }

    rock->last_attributes = 0;
    rock->last_category = 0;

    return 1;
}

Project: cyrus-imapd
File: imapd.c

static void print_quota_limits(struct protstream *o, const struct quota *q)
{
    int res;
    const char *sep = "";

    prot_putc('(', o);
    for (res = 0 ; res < QUOTA_NUMRESOURCES ; res++) {
        if (q->limits[res] >= 0) {
            prot_printf(o, "%s%s " QUOTA_T_FMT,
                        sep, quota_names[res],
                        q->limits[res]);
            sep = " ";
        }
    }
    prot_putc(')', o);
}

Project: cyrus-imapd
File: imapd.c

static void print_quota_used(struct protstream *o, const struct quota *q)
{
    int res;
    const char *sep = "";

    prot_putc('(', o);
    for (res = 0 ; res < QUOTA_NUMRESOURCES ; res++) {
        if (q->limits[res] >= 0) {
            prot_printf(o, "%s%s " QUOTA_T_FMT " " QUOTA_T_FMT,
                        sep, quota_names[res],
                        q->useds[res]/quota_units[res],
                        q->limits[res]);
            sep = " ";
        }
    }
    prot_putc(')', o);
}

Project: cyrus-imapd
File: imapd.c

static int print_statusline(const char *extname, unsigned statusitems,
                            struct statusdata *sd)
{
    int sepchar;

    prot_printf(imapd_out, "* STATUS ");
    prot_printastring(imapd_out, extname);
    prot_printf(imapd_out, " ");
    sepchar = '(';

    if (statusitems & STATUS_MESSAGES) {
        prot_printf(imapd_out, "%cMESSAGES %u", sepchar, sd->messages);
        sepchar = ' ';
    }
    if (statusitems & STATUS_RECENT) {
        prot_printf(imapd_out, "%cRECENT %u", sepchar, sd->recent);
        sepchar = ' ';
    }
    if (statusitems & STATUS_UIDNEXT) {
        prot_printf(imapd_out, "%cUIDNEXT %u", sepchar, sd->uidnext);
        sepchar = ' ';
    }
    if (statusitems & STATUS_UIDVALIDITY) {
        prot_printf(imapd_out, "%cUIDVALIDITY %u", sepchar, sd->uidvalidity);
        sepchar = ' ';
    }
    if (statusitems & STATUS_UNSEEN) {
        prot_printf(imapd_out, "%cUNSEEN %u", sepchar, sd->unseen);
        sepchar = ' ';
    }
    if (statusitems & STATUS_HIGHESTMODSEQ) {
        prot_printf(imapd_out, "%cHIGHESTMODSEQ " MODSEQ_FMT,
                    sepchar, sd->highestmodseq);
        sepchar = ' ';
    }
    if (statusitems & STATUS_XCONVEXISTS) {
        prot_printf(imapd_out, "%cXCONVEXISTS %u", sepchar, sd->xconv.exists);
        sepchar = ' ';
    }
    if (statusitems & STATUS_XCONVUNSEEN) {
        prot_printf(imapd_out, "%cXCONVUNSEEN %u", sepchar, sd->xconv.unseen);
        sepchar = ' ';
    }
    if (statusitems & STATUS_XCONVMODSEQ) {
        prot_printf(imapd_out, "%cXCONVMODSEQ " MODSEQ_FMT, sepchar, sd->xconv.modseq);
        sepchar = ' ';
    }

    prot_printf(imapd_out, ")\r\n");

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int printmyrights(const char *extname, const mbentry_t *mbentry)
{
    int rights = 0;
    char str[ACL_MAXSTR];

    rights = cyrus_acl_myrights(imapd_authstate, mbentry->acl);

    /* Add in implicit rights */
    if (imapd_userisadmin) {
        rights |= ACL_LOOKUP|ACL_ADMIN;
    }
    else if (mboxname_userownsmailbox(imapd_userid, mbentry->name)) {
        rights |= config_implicitrights;
    }

    if (!(rights & (ACL_LOOKUP|ACL_READ|ACL_INSERT|ACL_CREATE|ACL_DELETEMBOX|ACL_ADMIN))) {
        return IMAP_MAILBOX_NONEXISTENT;
    }

    prot_printf(imapd_out, "* MYRIGHTS ");
    prot_printastring(imapd_out, extname);
    prot_printf(imapd_out, " ");
    prot_printastring(imapd_out, cyrus_acl_masktostr(rights, str));
    prot_printf(imapd_out, "\r\n");

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int quota_cb(const mbentry_t *mbentry, void *rock)
{
    const char *servername = (const char *)rock;
    int r;

    if (strcmp(servername, mbentry->server)) {
        /* Not on same server as the root */
        r = IMAP_NOT_SINGULAR_ROOT;
    } else {
        r = PROXY_OK;
    }

    return r;
}

Project: cyrus-imapd
File: imapd.c

static int recursivematch_cb(struct findall_data *data, void *rockp)
{
    if (!data) return 0;

    struct list_rock_recursivematch *rock = (struct list_rock_recursivematch *)rockp;
    list_callback_calls++;

    const char *extname = data->extname;

    /* skip non-responsive mailboxes early, so they don't break sub folder detection */
    if (!(imapd_userisadmin || (rock->listargs->sel & LIST_SEL_DAV))) {
        mbname_t *mbname = (mbname_t *) data->mbname;
        const char *intname;
        int r;

        if (!mbname) {
            mbname = mbname_from_extname(extname, &imapd_namespace, imapd_userid);
        }

        intname = mbname_intname(mbname);
        r = mboxname_iscalendarmailbox(intname, 0) ||
            mboxname_isaddressbookmailbox(intname, 0) ||
            mboxname_isdavdrivemailbox(intname, 0) ||
            mboxname_isdavnotificationsmailbox(intname, 0);

        if (!data->mbname) mbname_free(&mbname);

        if (r) return 0;
    }

    uint32_t *list_info = hash_lookup(extname, &rock->table);
    if (!list_info) {
        list_info = xzmalloc(sizeof(uint32_t));
        hash_insert(extname, list_info, &rock->table);
        rock->count++;
    }

    if (data->mbname) { /* exact match */
        *list_info |= MBOX_ATTRIBUTE_SUBSCRIBED;
    }
    else {
        *list_info |= MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED | MBOX_ATTRIBUTE_HASCHILDREN;
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int renmbox(const mbentry_t *mbentry, void *rock)
{
    struct renrock *text = (struct renrock *)rock;
    char *oldextname = NULL, *newextname = NULL;
    int r = 0;
    uint32_t uidvalidity = mbentry->uidvalidity;

    if((text->nl + strlen(mbentry->name + text->ol)) >= MAX_MAILBOX_BUFFER)
        goto done;

    strcpy(text->newmailboxname + text->nl, mbentry->name + text->ol);

    /* check if a previous deleted mailbox existed */
    mbentry_t *newmbentry = NULL;
    r = mboxlist_lookup_allow_all(text->newmailboxname, &newmbentry, NULL);
    /* XXX - otherwise we should probably reject now, but meh, save it for
     * a real cleanup */
    if (!r && newmbentry->mbtype == MBTYPE_DELETED) {
        /* changing the unique id since last time? */
        if (strcmpsafe(mbentry->uniqueid, newmbentry->uniqueid)) {
            /* then the UIDVALIDITY must be higher than before */
            if (uidvalidity <= newmbentry->uidvalidity)
                uidvalidity = newmbentry->uidvalidity+1;
        }
    }
    mboxlist_entry_free(&newmbentry);


    /* don't notify implied rename in mailbox hierarchy */
    r = mboxlist_renamemailbox(mbentry->name, text->newmailboxname,
                               text->partition, uidvalidity,
                               1, imapd_userid, imapd_authstate, NULL, 0, 0,
                               text->rename_user);

    oldextname =
        mboxname_to_external(mbentry->name, &imapd_namespace, imapd_userid);
    newextname =
        mboxname_to_external(text->newmailboxname, &imapd_namespace, imapd_userid);

    if(r) {
        prot_printf(imapd_out, "* NO rename %s %s: %s\r\n",
                    oldextname, newextname, error_message(r));
        if (!RENAME_STOP_ON_ERROR) r = 0;
    } else {
        /* If we're renaming a user, change quotaroot and ACL */
        if (text->rename_user) {
            user_copyquotaroot(mbentry->name, text->newmailboxname);
            user_renameacl(text->namespace, text->newmailboxname,
                           text->olduser, text->newuser);
        }


        prot_printf(imapd_out, "* OK rename %s %s\r\n",
                    oldextname, newextname);
    }

done:
    prot_flush(imapd_out);
    free(oldextname);
    free(newextname);

    return r;
}

Project: cyrus-imapd
File: imapd.c

static int reset_saslconn(sasl_conn_t **conn)
{
    int ret;
    sasl_security_properties_t *secprops = NULL;

    sasl_dispose(conn);
    /* do initialization typical of service_main */
    ret = sasl_server_new("imap", config_servername,
                          NULL, NULL, NULL,
                          NULL, 0, conn);
    if(ret != SASL_OK) return ret;

    if(saslprops.ipremoteport)
        ret = sasl_setprop(*conn, SASL_IPREMOTEPORT,
                           saslprops.ipremoteport);
    if(ret != SASL_OK) return ret;

    if(saslprops.iplocalport)
        ret = sasl_setprop(*conn, SASL_IPLOCALPORT,
                           saslprops.iplocalport);
    if(ret != SASL_OK) return ret;

    secprops = mysasl_secprops(0);
    ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
    if(ret != SASL_OK) return ret;
    /* end of service_main initialization excepting SSF */

    /* If we have TLS/SSL info, set it */
    if(saslprops.ssf) {
        ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &saslprops.ssf);
    } else {
        ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &extprops_ssf);
    }
    if(ret != SASL_OK) return ret;

    if(saslprops.authid) {
        ret = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, saslprops.authid);
        if(ret != SASL_OK) return ret;
    }
    /* End TLS/SSL Info */

    return SASL_OK;
}

Project: cyrus-imapd
File: imapd.c

static void section_list_append(struct section **l,
                                const char *name,
                                const struct octetinfo *oi)
{
    struct section **tail = l;

    while (*tail) tail = &(*tail)->next;

    *tail = xzmalloc(sizeof(struct section));
    (*tail)->name = xstrdup(name);
    (*tail)->octetinfo = *oi;
    (*tail)->next = NULL;
}

Project: cyrus-imapd
File: imapd.c

static void section_list_free(struct section *l)
{
    struct section *n;

    while (l) {
        n = l->next;
        free(l->name);
        free(l);
        l = n;
    }
}

Project: cyrus-imapd
File: imapd.c

void service_abort(int error)
{
    shut_down(error);
}

Project: cyrus-imapd
File: imapd.c

int service_init(int argc, char **argv, char **envp)
{
    int opt, events;

    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);

    /* 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 real work */
    quotadb_init(0);
    quotadb_open(NULL);

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

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

    /* setup for mailbox event notifications */
    events = mboxevent_init();
    apns_enabled =
      (events & EVENT_APPLEPUSHSERVICE) && config_getstring(IMAPOPT_APS_TOPIC);

    search_attr_init();

    /* create connection to the SNMP listener, if available. */
    snmp_connect(); /* ignore return code */
    snmp_set_str(SERVER_NAME_VERSION,cyrus_version());

    while ((opt = getopt(argc, argv, "Np:sq")) != EOF) {
        switch (opt) {
        case 's': /* imaps (do starttls right away) */
            imaps = 1;
            if (!tls_enabled()) {
                syslog(LOG_ERR, "imaps: required OpenSSL options not present");
                fatal("imaps: required OpenSSL options not present",
                      EC_CONFIG);
            }
            break;
        case 'p': /* external protection */
            extprops_ssf = atoi(optarg);
            break;
        case 'N': /* bypass SASL password check.  Not recommended unless
                   * you know what you're doing! */
            nosaslpasswdcheck = 1;
            break;
        case 'q': /* don't enforce quotas */
            ignorequota = 1;
            break;
        default:
            break;
        }
    }

    /* Initialize the annotatemore extention */
    if (config_mupdate_server)
        annotate_init(annotate_fetch_proxy, annotate_store_proxy);
    else
        annotate_init(NULL, NULL);
    annotatemore_open();

    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
        statuscache_open();
    }

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

    return 0;
}

Project: cyrus-imapd

int service_main(int argc, char **argv, char **envp __attribute__((unused)))
#else
int service_main(int argc __attribute__((unused)),
                 char **argv __attribute__((unused)),
                 char **envp __attribute__((unused)))
#endif
{
    sasl_security_properties_t *secprops = NULL;
    const char *localip, *remoteip;
    struct mboxevent *mboxevent = NULL;
    struct io_count *io_count_start = NULL;
    struct io_count *io_count_stop = NULL;

    if (config_iolog) {
        io_count_start = xmalloc (sizeof (struct io_count));
        io_count_stop = xmalloc (sizeof (struct io_count));
        read_io_count(io_count_start);
    }

    session_new_id();

    signals_poll();

#ifdef ID_SAVE_CMDLINE
    /* get command line args for use in ID before getopt mangles them */
    id_getcmdline(argc, argv);
#endif

    sync_log_init();

    imapd_in = prot_new(0, 0);
    imapd_out = prot_new(1, 1);
    protgroup_insert(protin, imapd_in);

    /* Find out name of client host */
    imapd_clienthost = get_clienthost(0, &localip, &remoteip);

    /* create the SASL connection */
    if (sasl_server_new("imap", config_servername,
                        NULL, NULL, NULL, NULL, 0,
                        &imapd_saslconn) != SASL_OK) {
        fatal("SASL failed initializing: sasl_server_new()", EC_TEMPFAIL);
    }

    secprops = mysasl_secprops(0);
    if (sasl_setprop(imapd_saslconn, SASL_SEC_PROPS, secprops) != SASL_OK)
        fatal("Failed to set SASL property", EC_TEMPFAIL);
    if (sasl_setprop(imapd_saslconn, SASL_SSF_EXTERNAL, &extprops_ssf) != SASL_OK)
        fatal("Failed to set SASL property", EC_TEMPFAIL);

    if (localip && remoteip) {
        sasl_setprop(imapd_saslconn, SASL_IPREMOTEPORT, remoteip);
        saslprops.ipremoteport = xstrdup(remoteip);
        sasl_setprop(imapd_saslconn, SASL_IPLOCALPORT, localip);
        saslprops.iplocalport = xstrdup(localip);
    }

    imapd_tls_required = config_getswitch(IMAPOPT_TLS_REQUIRED);

    proc_register(config_ident, imapd_clienthost, NULL, NULL, NULL);

    /* Set inactivity timer */
    imapd_timeout = config_getint(IMAPOPT_TIMEOUT);
    if (imapd_timeout < 30) imapd_timeout = 30;
    imapd_timeout *= 60;
    prot_settimeout(imapd_in, imapd_timeout);
    prot_setflushonread(imapd_in, imapd_out);

    /* we were connected on imaps port so we should do
       TLS negotiation immediately */
    if (imaps == 1) cmd_starttls(NULL, 1);

    snmp_increment(TOTAL_CONNECTIONS, 1);
    snmp_increment(ACTIVE_CONNECTIONS, 1);

    /* Setup a default namespace until replaced after authentication. */
    mboxname_init_namespace(&imapd_namespace, /*isadmin*/1);
    mboxevent_setnamespace(&imapd_namespace);

    cmdloop();

    /* LOGOUT executed */
    prot_flush(imapd_out);
    snmp_increment(ACTIVE_CONNECTIONS, -1);

    /* send a Logout event notification */
    if ((mboxevent = mboxevent_new(EVENT_LOGOUT))) {
        mboxevent_set_access(mboxevent, saslprops.iplocalport,
                             saslprops.ipremoteport, imapd_userid, NULL, 1);

        mboxevent_notify(&mboxevent);
        mboxevent_free(&mboxevent);
    }

    /* cleanup */
    imapd_reset();

    if (config_iolog) {
        read_io_count(io_count_stop);
        syslog(LOG_INFO,
               "IMAP session stats : I/O read : %d bytes : I/O write : %d bytes",
                io_count_stop->io_read_count - io_count_start->io_read_count,
                io_count_stop->io_write_count - io_count_start->io_write_count);
        free (io_count_start);
        free (io_count_stop);
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int set_haschildren(const mbentry_t *mbentry __attribute__((unused)),
                           void *rock)
{
    uint32_t *attributes = (uint32_t *)rock;
    list_callback_calls++;
    *attributes |= MBOX_ATTRIBUTE_HASCHILDREN;
    return CYRUSDB_DONE;
}

Project: cyrus-imapd
File: imapd.c

static void specialuse_flags(const mbentry_t *mbentry, struct buf *attrib,
                             int isxlist)
{
    if (!mbentry) return;

    char *inbox = mboxname_user_mbox(imapd_userid, NULL);
    int inboxlen = strlen(inbox);

    /* doesn't match inbox, not xlistable */
    if (strncmp(mbentry->name, inbox, inboxlen)) {
        free(inbox);
        return;
    }

    /* inbox - only print if command is XLIST */
    if (mbentry->name[inboxlen] == '\0') {
        if (isxlist) buf_init_ro_cstr(attrib, "\\Inbox");
    }
    /* subdir */
    else if (mbentry->name[inboxlen] == '.') {
        /* check if there's a special use flag set */
        annotatemore_lookup(mbentry->name, "/specialuse", imapd_userid, attrib);
    }
    free(inbox);
    /* otherwise it's actually another user who matches for
     * the substr.  Ok to just print nothing */
}

Project: cyrus-imapd
File: imapd.c

static int subscribed_cb(struct findall_data *data, void *rockp)
{
    struct list_rock *rock = (struct list_rock *)rockp;
    if (!data) {
        perform_output(NULL, NULL, rock);
        return 0;
    }
    size_t last_len = (rock->last_name ? strlen(rock->last_name) : 0);
    const char *extname = data->extname;
    int last_name_is_ancestor =
        rock->last_name
        && strlen(extname) >= last_len
        && extname[last_len] == imapd_namespace.hier_sep
        && !memcmp(rock->last_name, extname, last_len);

    list_callback_calls++;

    if (last_name_is_ancestor ||
        (rock->last_name && !data->mbname && !strcmp(rock->last_name, extname)))
        rock->last_attributes |= MBOX_ATTRIBUTE_HASCHILDREN;

    if (data->mbname) { /* exact match */
        mbentry_t *mbentry = NULL;
        mboxlist_lookup(mbname_intname(data->mbname), &mbentry, NULL);
        perform_output(extname, mbentry, rock);
        mboxlist_entry_free(&mbentry);

        rock->last_attributes |= MBOX_ATTRIBUTE_SUBSCRIBED;
        if (mboxlist_lookup(mbname_intname(data->mbname), NULL, NULL))
            rock->last_attributes |= MBOX_ATTRIBUTE_NONEXISTENT;
        if (data->mb_category == MBNAME_ALTINBOX)
            rock->last_attributes |= MBOX_ATTRIBUTE_NOINFERIORS;
    }
    else if (rock->listargs->cmd == LIST_CMD_LSUB) {
        /* special case: for LSUB,
         * mailbox names that match the pattern but aren't subscribed
         * must also be returned if they have a child mailbox that is
         * subscribed */
        perform_output(extname, data->mbentry, rock);
        rock->last_attributes |= MBOX_ATTRIBUTE_CHILDINFO_SUBSCRIBED;
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int sync_mailbox(struct mailbox *mailbox,
                        struct sync_folder_list *replica_folders,
                        const char *topart,
                        struct backend *be)
{
    int r = 0;
    struct sync_folder_list *master_folders = NULL;
    struct sync_reserve_list *reserve_guids = NULL;
    struct sync_msgid_list *part_list;
    struct sync_reserve *reserve;
    struct sync_folder *mfolder, *rfolder;
    struct sync_annot_list *annots = NULL;
    modseq_t xconvmodseq = 0;
    unsigned flags = SYNC_FLAG_LOGGING | SYNC_FLAG_LOCALONLY;

    if (!topart) topart = mailbox->part;
    reserve_guids = sync_reserve_list_create(SYNC_MSGID_LIST_HASH_SIZE);
    part_list = sync_reserve_partlist(reserve_guids, topart);

    /* always send mailbox annotations */
    r = read_annotations(mailbox, NULL, &annots);
    if (r) {
        syslog(LOG_ERR, "sync_mailbox(): read annotations failed: %s '%s'",
               mailbox->name, error_message(r));
        goto cleanup;
    }

    /* xconvmodseq */
    if (mailbox_has_conversations(mailbox)) {
        r = mailbox_get_xconvmodseq(mailbox, &xconvmodseq);
        if (r) {
            syslog(LOG_ERR, "sync_mailbox(): mailbox get xconvmodseq failed: %s '%s'",
                mailbox->name, error_message(r));
            goto cleanup;
        }
    }

    master_folders = sync_folder_list_create();
    sync_folder_list_add(master_folders,
                         mailbox->uniqueid, mailbox->name,
                         mailbox->mbtype,
                         mailbox->part,
                         mailbox->acl,
                         mailbox->i.options,
                         mailbox->i.uidvalidity,
                         mailbox->i.last_uid,
                         mailbox->i.highestmodseq,
                         mailbox->i.synccrcs,
                         mailbox->i.recentuid,
                         mailbox->i.recenttime,
                         mailbox->i.pop3_last_login,
                         mailbox->i.pop3_show_after,
                         annots,
                         xconvmodseq,
                         /* ispartial */0);
    annots = NULL; /* list took ownership */

    mfolder = master_folders->head;
    /* when mfolder->mailbox is set, sync_update_mailbox will use it rather
     * than obtaining its own (short-lived) locks */
    mfolder->mailbox = mailbox;

    uint32_t fromuid = 0;
    rfolder = sync_folder_lookup(replica_folders, mfolder->uniqueid);
    if (rfolder) {
        rfolder->mark = 1;

        /* does it need a rename? */
        if (strcmp(mfolder->name, rfolder->name) ||
            strcmp(topart, rfolder->part)) {
            /* bail and retry */
            syslog(LOG_NOTICE,
                   "XFER: rename %s!%s -> %s!%s during final sync"
                   " - must try XFER again",
                   mfolder->name, mfolder->part, rfolder->name, rfolder->part);
            r = IMAP_AGAIN;
            goto cleanup;
        }
        fromuid = rfolder->last_uid;
    }
    sync_find_reserve_messages(mailbox, fromuid, mailbox->i.last_uid, part_list);

    reserve = reserve_guids->head;
    r = sync_reserve_partition(reserve->part, replica_folders,
                               reserve->list, be);
    if (r) {
        syslog(LOG_ERR, "sync_mailbox(): reserve partition failed: %s '%s'",
               mfolder->name, error_message(r));
        goto cleanup;
    }

    r = sync_update_mailbox(mfolder, rfolder, topart, reserve_guids, be, flags);
    if (r) {
        syslog(LOG_ERR, "sync_mailbox(): update failed: %s '%s'",
                mfolder->name, error_message(r));
    }

  cleanup:
    sync_reserve_list_free(&reserve_guids);
    sync_folder_list_free(&master_folders);
    sync_annot_list_free(&annots);

    return r;
}

Project: cyrus-imapd
File: imapd.c

static int trashacl(struct protstream *pin, struct protstream *pout,
                    char *mailbox)
{
    int i=0, j=0;
    char tagbuf[128];
    int c;              /* getword() returns an int */
    struct buf cmd, tmp, user;
    int r = 0;

    memset(&cmd, 0, sizeof(struct buf));
    memset(&tmp, 0, sizeof(struct buf));
    memset(&user, 0, sizeof(struct buf));

    prot_printf(pout, "ACL0 GETACL {" SIZE_T_FMT "+}\r\n%s\r\n",
                strlen(mailbox), mailbox);

    while(1) {
        c = prot_getc(pin);
        if (c != '*') {
            prot_ungetc(c, pin);
            r = getresult(pin, "ACL0");
            break;
        }

        c = prot_getc(pin);  /* skip SP */
        c = getword(pin, &cmd);
        if (c == EOF) {
            r = IMAP_SERVER_UNAVAILABLE;
            break;
        }

        if (!strncmp(cmd.s, "ACL", 3)) {
            while(c != '\n') {
                /* An ACL response, we should send a DELETEACL command */
                c = getastring(pin, pout, &tmp);
                if (c == EOF) {
                    r = IMAP_SERVER_UNAVAILABLE;
                    goto cleanup;
                }

                if(c == '\r') {
                    c = prot_getc(pin);
                    if(c != '\n') {
                        r = IMAP_SERVER_UNAVAILABLE;
                        goto cleanup;
                    }
                }
                if(c == '\n') break;  /* end of * ACL */

                c = getastring(pin, pout, &user);
                if (c == EOF) {
                    r = IMAP_SERVER_UNAVAILABLE;
                    goto cleanup;
                }

                snprintf(tagbuf, sizeof(tagbuf), "ACL%d", ++i);

                prot_printf(pout, "%s DELETEACL {" SIZE_T_FMT "+}\r\n%s"
                            " {" SIZE_T_FMT "+}\r\n%s\r\n",
                            tagbuf, strlen(mailbox), mailbox,
                            strlen(user.s), user.s);
                if(c == '\r') {
                    c = prot_getc(pin);
                    if(c != '\n') {
                        r = IMAP_SERVER_UNAVAILABLE;
                        goto cleanup;
                    }
                }
                /* if the next character is \n, we'll exit the loop */
            }
        }
        else {
            /* skip this line, we don't really care */
            eatline(pin, c);
        }
    }

    cleanup:

    /* Now cleanup after all the DELETEACL commands */
    if(!r) {
        while(j < i) {
            snprintf(tagbuf, sizeof(tagbuf), "ACL%d", ++j);
            r = getresult(pin, tagbuf);
            if (r) break;
        }
    }

    if(r) eatline(pin, c);

    buf_free(&user);
    buf_free(&tmp);
    buf_free(&cmd);

    return r;
}

Project: cyrus-imapd
File: imapd.c

static void warn_about_quota(const char *quotaroot)
{
    time_t now = time(NULL);
    struct quota q;
    int res;
    int r;
    int thresholds[QUOTA_NUMRESOURCES];
    int pc_threshold = config_getint(IMAPOPT_QUOTAWARN);
    int pc_usage;
    struct buf msg = BUF_INITIALIZER;
    static char lastqr[MAX_MAILBOX_PATH+1] = "";
    static time_t nextalert = 0;

    if (!quotaroot || !*quotaroot)
        return;     /* no quota, nothing to do */

    /* rate limit checks and warnings to every 10 min */
    if (!strcmp(quotaroot, lastqr) && now < nextalert)
        return;
    strlcpy(lastqr, quotaroot, sizeof(lastqr));
    nextalert = now + 600;

    quota_init(&q, quotaroot);
    r = quota_read(&q, NULL, 0);
    if (r)
        goto out;           /* failed to read */

    memset(thresholds, 0, sizeof(thresholds));
    thresholds[QUOTA_STORAGE] = config_getint(IMAPOPT_QUOTAWARNKB);
    thresholds[QUOTA_MESSAGE] = config_getint(IMAPOPT_QUOTAWARNMSG);
    thresholds[QUOTA_ANNOTSTORAGE] = config_getint(IMAPOPT_QUOTAWARNKB);

    for (res = 0 ; res < QUOTA_NUMRESOURCES ; res++) {
        if (q.limits[res] < 0)
            continue;       /* this resource is unlimited */
        buf_reset(&msg);

        if (thresholds[res] <= 0 ||
            thresholds[res] >= q.limits[res] ||
            q.useds[res] > ((quota_t) (q.limits[res] - thresholds[res])) * quota_units[res]) {

            pc_usage = (int)(((double) q.useds[res] * 100.0) /
                             (double) ((quota_t) q.limits[res] * quota_units[res]));

            if (q.useds[res] > (quota_t) q.limits[res] * quota_units[res])
                buf_printf(&msg, error_message(IMAP_NO_OVERQUOTA),
                           quota_names[res]);
            else if (pc_usage > pc_threshold)
                buf_printf(&msg, error_message(IMAP_NO_CLOSEQUOTA),
                           pc_usage, quota_names[res]);
        }

        if (msg.len)
            prot_printf(imapd_out, "* NO [ALERT] %s\r\n", buf_cstring(&msg));
    }

    buf_reset(&msg);
out:
    quota_free(&q);
}

Project: cyrus-imapd
File: imapd.c

static int xbackup_addmbox(struct findall_data *data, void *rock)
{
    if (!data) return 0;
    ptrarray_t *list = (ptrarray_t *) rock;

    if (!data->mbname) {
        /* No partial matches */ /* FIXME ??? */
        return 0;
    }

    /* Only add shared mailboxes or user INBOXes */
    if (!mbname_localpart(data->mbname) ||
        (!mbname_isdeleted(data->mbname) &&
         !strarray_size(mbname_boxes(data->mbname)))) {

        ptrarray_append(list, mbname_dup(data->mbname));
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int xconvfetch_lookup(struct conversations_state *statep,
                             conversation_id_t cid,
                             modseq_t ifchangedsince,
                             hash_table *wanted_cids,
                             strarray_t *folder_list)
{
    const char *key = conversation_id_encode(cid);
    conversation_t *conv = NULL;
    conv_folder_t *folder;
    int r;

    r = conversation_load(statep, cid, &conv);
    if (r) return r;

    if (!conv)
        goto out;

    if (!conv->exists)
        goto out;

    /* output the metadata for this conversation */
    {
        struct dlist *dl = dlist_newlist(NULL, "");
        dlist_setatom(dl, "", "MODSEQ");
        do_one_xconvmeta(statep, cid, conv, dl);
        dlist_free(&dl);
    }

    if (ifchangedsince >= conv->modseq)
        goto out;

    hash_insert(key, (void *)1, wanted_cids);

    for (folder = conv->folders; folder; folder = folder->next) {
        /* no contents */
        if (!folder->exists)
            continue;

        /* finally, something worth looking at */
        strarray_add(folder_list, strarray_nth(statep->folder_names, folder->number));
    }

out:
    conversation_free(conv);
    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_addmbox(struct findall_data *data, void *rock)
{
    if (!data) return 0;
    struct xfer_list *list = (struct xfer_list *) rock;

    if (!data->mbentry) {
        /* No partial matches */
        return 0;
    }

    if (list->part && strcmp(data->mbentry->partition, list->part)) {
        /* Not on specified partition */
        return 0;
    }

    /* Only add shared mailboxes, targeted user submailboxes, or user INBOXes */
    if (!mbname_localpart(data->mbname) || list->allow_usersubs ||
        (!mbname_isdeleted(data->mbname) && !strarray_size(mbname_boxes(data->mbname)))) {
        const char *extname = mbname_extname(data->mbname, list->ns, list->userid);
        struct xfer_item *mbox = xzmalloc(sizeof(struct xfer_item));

        mbox->mbentry = mboxlist_entry_copy(data->mbentry);
        strncpy(mbox->extname, extname, sizeof(mbox->extname));
        if (mbname_localpart(data->mbname) && !list->allow_usersubs) {
            /* User INBOX */
            mbox->state = XFER_MOVING_USER;
        }

        /* Add link on to the list (reverse order) */
        mbox->next = list->mboxes;
        list->mboxes = mbox;
    }

    return 0;
}

Project: cyrus-imapd

static int xfer_addusermbox(const mbentry_t *mbentry, void *rock)
{
    struct xfer_header *xfer = (struct xfer_header *)rock;

    /* Skip remote mailbox */
    if (mbentry->mbtype & MBTYPE_REMOTE)
        return 0;

    struct xfer_item *item = xzmalloc(sizeof(struct xfer_item));

    int r = mboxlist_lookup(mbentry->name, &item->mbentry, 0);
    if (r) return r;
    char *extname = mboxname_to_external(item->mbentry->name, &imapd_namespace, imapd_userid);
    strncpy(item->extname, extname, sizeof(item->extname));
    free(extname);
    item->mailbox = NULL;
    item->state = 0;

    /* and link on to the list (reverse order) */
    item->next = xfer->items;
    xfer->items = item;

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_backport_seen_item(struct xfer_item *item,
                                   struct seen *seendb)
{
    struct mailbox *mailbox = item->mailbox;
    struct seqset *outlist = NULL;
    struct seendata sd = SEENDATA_INITIALIZER;
    int r;

    outlist = seqset_init(mailbox->i.last_uid, SEQ_MERGE);

    struct mailbox_iter *iter = mailbox_iter_init(mailbox, 0, ITER_SKIP_EXPUNGED);

    const message_t *msg;
    while ((msg = mailbox_iter_step(iter))) {
        const struct index_record *record = msg_record(msg);
        if (record->system_flags & FLAG_SEEN)
            seqset_add(outlist, record->uid, 1);
        else
            seqset_add(outlist, record->uid, 0);
    }

    mailbox_iter_done(&iter);

    sd.lastread = mailbox->i.recenttime;
    sd.lastuid = mailbox->i.recentuid;
    sd.lastchange = mailbox->i.last_appenddate;
    sd.seenuids = seqset_cstring(outlist);
    if (!sd.seenuids) sd.seenuids = xstrdup("");

    r = seen_write(seendb, mailbox->uniqueid, &sd);

    seen_freedata(&sd);
    seqset_free(outlist);

    return r;
}

Project: cyrus-imapd
File: imapd.c

static void xfer_cleanup(struct xfer_header *xfer)
{
    struct xfer_item *item, *next;

    /* remove items */
    item = xfer->items;
    while (item) {
        next = item->next;
        mboxlist_entry_free(&item->mbentry);
        free(item);
        item = next;
    }
    xfer->items = NULL;

    free(xfer->topart);
    free(xfer->userid);
    xfer->topart = xfer->userid = NULL;

    seen_close(&xfer->seendb);
    xfer->seendb = NULL;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_deactivate(struct xfer_header *xfer)
{
    struct xfer_item *item;
    int r;

    syslog(LOG_INFO, "XFER: deactivating mailboxes");

    /* Step 3: mupdate.DEACTIVATE(mailbox, newserver) */
    for (item = xfer->items; item; item = item->next) {
        r = xfer_mupdate(0, item->mbentry->name, item->mbentry->partition,
                         config_servername, item->mbentry->acl);
        if (r) {
            syslog(LOG_ERR,
                   "Could not move mailbox: %s, MUPDATE DEACTIVATE failed",
                   item->mbentry->name);
            return r;
        }

        item->state = XFER_DEACTIVATED;
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_delete(struct xfer_header *xfer)
{
    mbentry_t *newentry = NULL;
    struct xfer_item *item;
    int r;

    syslog(LOG_INFO, "XFER: deleting mailboxes on source");

    /* 7) local delete of mailbox
     * & remove local "remote" mailboxlist entry */
    for (item = xfer->items; item; item = item->next) {
        /* Set mailbox as DELETED on local server
           (need to also reset to local partition,
           otherwise mailbox can not be opened for deletion) */
        /* XXX - this code is awful... need a sane way to manage mbentries */
        newentry = mboxlist_entry_create();
        newentry->name = xstrdupnull(item->mbentry->name);
        newentry->acl = xstrdupnull(item->mbentry->acl);
        newentry->server = xstrdupnull(item->mbentry->server);
        newentry->partition = xstrdupnull(item->mbentry->partition);
        newentry->mbtype = item->mbentry->mbtype|MBTYPE_DELETED;
        r = mboxlist_update(newentry, 1);
        mboxlist_entry_free(&newentry);

        if (r) {
            syslog(LOG_ERR,
                   "Could not move mailbox: %s, mboxlist_update failed (%s)",
                   item->mbentry->name, error_message(r));
        }

        /* Note that we do not check the ACL, and we don't update MUPDATE */
        /* note also that we need to remember to let proxyadmins do this */
        /* On a unified system, the subsequent MUPDATE PUSH on the remote
           should repopulate the local mboxlist entry */
        r = mboxlist_deletemailbox(item->mbentry->name,
                                   imapd_userisadmin || imapd_userisproxyadmin,
                                   imapd_userid, imapd_authstate, NULL, 0, 1, 0);
        if (r) {
            syslog(LOG_ERR,
                   "Could not delete local mailbox during move of %s",
                   item->mbentry->name);
            /* can't abort now! */
        }
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static void xfer_done(struct xfer_header **xferptr)
{
    struct xfer_header *xfer = *xferptr;

    syslog(LOG_INFO, "XFER: disconnecting from servers");

    free(xfer->toserver);

    buf_free(&xfer->tagbuf);

    xfer_cleanup(xfer);

    free(xfer);

    *xferptr = NULL;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_finalsync(struct xfer_header *xfer)
{
    struct sync_name_list *master_quotaroots = sync_name_list_create();
    struct sync_folder_list *replica_folders = sync_folder_list_create();
    struct sync_folder *rfolder;
    struct sync_name_list *replica_subs = NULL;
    struct sync_sieve_list *replica_sieve = NULL;
    struct sync_seen_list *replica_seen = NULL;
    struct sync_quota_list *replica_quota = sync_quota_list_create();
    const char *cmd;
    struct dlist *kl = NULL;
    struct xfer_item *item;
    struct mailbox *mailbox = NULL;
    mbentry_t newentry;
    unsigned flags = SYNC_FLAG_LOGGING | SYNC_FLAG_LOCALONLY;
    int r;

    if (xfer->userid) {
        syslog(LOG_INFO, "XFER: final sync of user %s", xfer->userid);

        replica_subs = sync_name_list_create();
        replica_sieve = sync_sieve_list_create();
        replica_seen = sync_seen_list_create();

        cmd = "USER";
        kl = dlist_setatom(NULL, cmd, xfer->userid);
    }
    else {
        syslog(LOG_INFO, "XFER: final sync of mailbox %s",
               xfer->items->mbentry->name);

        cmd = "MAILBOXES";
        kl = dlist_newlist(NULL, cmd);
        dlist_setatom(kl, "MBOXNAME", xfer->items->mbentry->name);
    }

    sync_send_lookup(kl, xfer->be->out);
    dlist_free(&kl);

    r = sync_response_parse(xfer->be->in, cmd, replica_folders, replica_subs,
                            replica_sieve, replica_seen, replica_quota);

    if (r) goto done;

    for (item = xfer->items; item; item = item->next) {
        r = mailbox_open_iwl(item->mbentry->name, &mailbox);
        if (!r) r = sync_mailbox_version_check(&mailbox);
        if (r) {
            syslog(LOG_ERR,
                   "Failed to open mailbox %s for xfer_final_sync() %s",
                   item->mbentry->name, error_message(r));
            goto done;
        }

        /* Open cyrus.annotations before we set mailbox to MOVING and
           change its location to destination server and partition */
        r = mailbox_get_annotate_state(mailbox, ANNOTATE_ANY_UID, NULL);
        if (r) {
            syslog(LOG_ERR,
                   "Failed to get annotate state for mailbox %s"
                   " for xfer_final_sync() %s",
                   mailbox->name, error_message(r));
            mailbox_close(&mailbox);
            goto done;
        }

        /* Step 3.5: Set mailbox as MOVING on local server */
        /* XXX - this code is awful... need a sane way to manage mbentries */
        newentry.name = item->mbentry->name;
        newentry.acl = item->mbentry->acl;
        newentry.uniqueid = item->mbentry->uniqueid;
        newentry.uidvalidity = item->mbentry->uidvalidity;
        newentry.mbtype = item->mbentry->mbtype|MBTYPE_MOVING;
        newentry.server = xfer->toserver;
        newentry.partition = xfer->topart;
        r = mboxlist_update(&newentry, 1);

        if (r) {
            syslog(LOG_ERR,
                   "Could not move mailbox: %s, mboxlist_update() failed %s",
                   item->mbentry->name, error_message(r));
        }
        else item->state = XFER_LOCAL_MOVING;

        /* Step 4: Sync local -> remote */
        if (!r) {
            r = sync_mailbox(mailbox, replica_folders, xfer->topart, xfer->be);
            if (r) {
                syslog(LOG_ERR,
                       "Could not move mailbox: %s, sync_mailbox() failed %s",
                       item->mbentry->name, error_message(r));
            }
            else {
                if (mailbox->quotaroot &&
                    !sync_name_lookup(master_quotaroots, mailbox->quotaroot)) {
                    sync_name_list_add(master_quotaroots, mailbox->quotaroot);
                }

                r = sync_do_annotation(mailbox->name, xfer->be, flags);
                if (r) {
                    syslog(LOG_ERR, "Could not move mailbox: %s,"
                           " sync_do_annotation() failed %s",
                           item->mbentry->name, error_message(r));
                }
            }
        }

        mailbox_close(&mailbox);

        if (r) goto done;

        item->state = XFER_UNDUMPED;
    }

    /* Delete folders on replica which no longer exist on master */
    for (rfolder = replica_folders->head; rfolder; rfolder = rfolder->next) {
        if (rfolder->mark) continue;

        r = sync_folder_delete(rfolder->name, xfer->be, flags);
        if (r) {
            syslog(LOG_ERR, "sync_folder_delete(): failed: %s '%s'",
                   rfolder->name, error_message(r));
            goto done;
        }
    }

    /* Handle any mailbox/user metadata */
    r = sync_do_user_quota(master_quotaroots, replica_quota, xfer->be, flags);
    if (!r && xfer->userid) {
        r = sync_do_user_seen(xfer->userid, replica_seen, xfer->be, flags);
        if (!r) r = sync_do_user_sub(xfer->userid, replica_subs,
                                     xfer->be, flags);
        if (!r) r = sync_do_user_sieve(xfer->userid, replica_sieve,
                                       xfer->be, flags);
    }

  done:
    sync_name_list_free(&master_quotaroots);
    sync_folder_list_free(&replica_folders);
    if (replica_subs) sync_name_list_free(&replica_subs);
    if (replica_sieve) sync_sieve_list_free(&replica_sieve);
    if (replica_seen) sync_seen_list_free(&replica_seen);
    if (replica_quota) sync_quota_list_free(&replica_quota);

    return r;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_init(const char *toserver, struct xfer_header **xferptr)
{
    struct xfer_header *xfer = xzmalloc(sizeof(struct xfer_header));
    int r;

    syslog(LOG_INFO, "XFER: connecting to server '%s'", toserver);

    /* Get a connection to the remote backend */
    xfer->be = proxy_findserver(toserver, &imap_protocol, "", &backend_cached,
                                NULL, NULL, imapd_in);
    if (!xfer->be) {
        syslog(LOG_ERR, "Failed to connect to server '%s'", toserver);
        r = IMAP_SERVER_UNAVAILABLE;
        goto fail;
    }

    xfer->remoteversion = backend_version(xfer->be);
    if (xfer->be->capability & CAPA_REPLICATION) {
        syslog(LOG_INFO, "XFER: destination supports replication");
        xfer->use_replication = 1;

        /* attach our IMAP tag buffer to our protstreams as userdata */
        xfer->be->in->userdata = xfer->be->out->userdata = &xfer->tagbuf;
    }

    xfer->toserver = xstrdup(toserver);
    xfer->topart = NULL;
    xfer->seendb = NULL;

    /* connect to mupdate server if configured */
    if (config_mupdate_server && !mupdate_h) {
        syslog(LOG_INFO, "XFER: connecting to mupdate '%s'",
               config_mupdate_server);

        r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
        if (r) {
            syslog(LOG_INFO, "Failed to connect to mupdate '%s'",
                   config_mupdate_server);
            goto fail;
        }
    }

    *xferptr = xfer;
    return 0;

fail:
    xfer_done(&xfer);
    return r;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_initialsync(struct xfer_header *xfer)
{
    unsigned flags = SYNC_FLAG_LOGGING | SYNC_FLAG_LOCALONLY;
    int r;

    if (xfer->userid) {
        struct xfer_item *item, *next;

        syslog(LOG_INFO, "XFER: initial sync of user %s", xfer->userid);

        r = sync_do_user(xfer->userid, xfer->topart, xfer->be,
                         /*channelp*/NULL, flags);
        if (r) return r;

        /* User moves may take a while, do another non-blocking sync */
        syslog(LOG_INFO, "XFER: second sync of user %s", xfer->userid);

        r = sync_do_user(xfer->userid, xfer->topart, xfer->be,
                         /*channelp*/NULL, flags);
        if (r) return r;

        /* User may have renamed/deleted a mailbox while syncing,
           recreate the submailboxes list */
        for (item = xfer->items; item; item = next) {
            next = item->next;
            mboxlist_entry_free(&item->mbentry);
            free(item);
        }
        xfer->items = NULL;

        r = mboxlist_usermboxtree(xfer->userid, xfer_addusermbox,
                                  xfer, MBOXTREE_DELETED);
    }
    else {
        struct sync_name_list *mboxname_list = sync_name_list_create();

        syslog(LOG_INFO, "XFER: initial sync of mailbox %s",
               xfer->items->mbentry->name);

        sync_name_list_add(mboxname_list, xfer->items->mbentry->name);
        r = sync_do_mailboxes(mboxname_list, xfer->topart, xfer->be,
                              /*channelp*/NULL, flags);
        sync_name_list_free(&mboxname_list);
    }

    return r;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_localcreate(struct xfer_header *xfer)
{
    struct xfer_item *item;
    int r;

    syslog(LOG_INFO, "XFER: creating mailboxes on destination");

    for (item = xfer->items; item; item = item->next) {
        if (xfer->topart) {
            /* need to send partition as an atom */
            prot_printf(xfer->be->out, "LC1 LOCALCREATE {" SIZE_T_FMT "+}\r\n%s %s\r\n",
                        strlen(item->extname), item->extname, xfer->topart);
        } else {
            prot_printf(xfer->be->out, "LC1 LOCALCREATE {" SIZE_T_FMT "+}\r\n%s\r\n",
                        strlen(item->extname), item->extname);
        }
        r = getresult(xfer->be->in, "LC1");
        if (r) {
            syslog(LOG_ERR, "Could not move mailbox: %s, LOCALCREATE failed",
                   item->mbentry->name);
            return r;
        }

        item->state = XFER_REMOTE_CREATED;
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_mupdate(int isactivate,
                        const char *mboxname, const char *part,
                        const char *servername, const char *acl)
{
    char buf[MAX_PARTITION_LEN+HOSTNAME_SIZE+2];
    int retry = 0;
    int r = 0;

    /* no mupdate handle */
    if (!mupdate_h) return 0;

    snprintf(buf, sizeof(buf), "%s!%s", servername, part);

retry:
    /* make the change */
    if (isactivate)
        r = mupdate_activate(mupdate_h, mboxname, buf, acl);
    else
        r = mupdate_deactivate(mupdate_h, mboxname, buf);

    if (r && !retry) {
        syslog(LOG_INFO, "MUPDATE: lost connection, retrying");
        mupdate_disconnect(&mupdate_h);
        r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
        if (r) {
            syslog(LOG_INFO, "Failed to connect to mupdate '%s'",
                   config_mupdate_server);
        }
        else {
            retry = 1;
            goto retry;
        }
    }

    return r;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_reactivate(struct xfer_header *xfer)
{
    struct xfer_item *item;
    int r;

    syslog(LOG_INFO, "XFER: reactivating mailboxes");

    if (!mupdate_h) return 0;

    /* 6.5) Kick remote server to correct mupdate entry */
    for (item = xfer->items; item; item = item->next) {
        prot_printf(xfer->be->out, "MP1 MUPDATEPUSH {" SIZE_T_FMT "+}\r\n%s\r\n",
                    strlen(item->extname), item->extname);
        r = getresult(xfer->be->in, "MP1");
        if (r) {
            syslog(LOG_ERR, "MUPDATE: can't activate mailbox entry '%s': %s",
                   item->mbentry->name, error_message(r));
        }
    }

    return 0;
}

Project: cyrus-imapd
File: imapd.c

static void xfer_recover(struct xfer_header *xfer)
{
    struct xfer_item *item;
    int r;

    syslog(LOG_INFO, "XFER: recovering");

    /* Backout any changes - we stop on first untouched mailbox */
    for (item = xfer->items; item && item->state; item = item->next) {
        switch (item->state) {
        case XFER_UNDUMPED:
        case XFER_LOCAL_MOVING:
            /* Unset mailbox as MOVING on local server */
            r = mboxlist_update(item->mbentry, 1);

            if (r) {
                syslog(LOG_ERR,
                       "Could not back out MOVING flag during move of %s (%s)",
                       item->mbentry->name, error_message(r));
            }

        case XFER_REMOTE_CREATED:
            if (!xfer->use_replication) {
                /* Delete remote mailbox */
                prot_printf(xfer->be->out,
                            "LD1 LOCALDELETE {" SIZE_T_FMT "+}\r\n%s\r\n",
                            strlen(item->extname), item->extname);
                r = getresult(xfer->be->in, "LD1");
                if (r) {
                    syslog(LOG_ERR,
                        "Could not back out remote mailbox during move of %s (%s)",
                        item->mbentry->name, error_message(r));
                }
            }

        case XFER_DEACTIVATED:
            /* Tell murder it's back here and active */
            r = xfer_mupdate(1, item->mbentry->name, item->mbentry->partition,
                             config_servername, item->mbentry->acl);
            if (r) {
                syslog(LOG_ERR,
                       "Could not back out mupdate during move of %s (%s)",
                       item->mbentry->name, error_message(r));
            }
        }
    }
}

Project: cyrus-imapd
File: imapd.c

static int xfer_setquotaroot(struct xfer_header *xfer, const char *mboxname)
{
    struct quota q;
    int r;

    syslog(LOG_INFO, "XFER: setting quota root %s", mboxname);

    quota_init(&q, mboxname);
    r = quota_read(&q, NULL, 0);
    if (r == IMAP_QUOTAROOT_NONEXISTENT) return 0;
    if (r) return r;

    /* note use of + to force the setting of a nonexistant
     * quotaroot */
    char *extname = mboxname_to_external(mboxname, &imapd_namespace, imapd_userid);
    prot_printf(xfer->be->out, "Q01 SETQUOTA {" SIZE_T_FMT "+}\r\n+%s ",
                strlen(extname)+1, extname);
    free(extname);
    print_quota_limits(xfer->be->out, &q);
    prot_printf(xfer->be->out, "\r\n");
    quota_free(&q);

    r = getresult(xfer->be->in, "Q01");
    if (r) syslog(LOG_ERR,
                  "Could not move mailbox: %s, " \
                  "failed setting initial quota root\r\n",
                  mboxname);
    return r;
}

Project: cyrus-imapd
File: imapd.c

static int xfer_undump(struct xfer_header *xfer)
{
    struct xfer_item *item;
    int r;
    mbentry_t *newentry;
    struct mailbox *mailbox = NULL;

    syslog(LOG_INFO, "XFER: dumping mailboxes to destination");

    for (item = xfer->items; item; item = item->next) {
        r = mailbox_open_irl(item->mbentry->name, &mailbox);
        if (r) {
            syslog(LOG_ERR,
                   "Failed to open mailbox %s for dump_mailbox() %s",
                   item->mbentry->name, error_message(r));
            return r;
        }

        /* Step 3.5: Set mailbox as MOVING on local server */
        /* XXX - this code is awful... need a sane way to manage mbentries */
        newentry = mboxlist_entry_create();
        newentry->name = xstrdupnull(item->mbentry->name);
        newentry->acl = xstrdupnull(item->mbentry->acl);
        newentry->server = xstrdupnull(xfer->toserver);
        newentry->partition = xstrdupnull(xfer->topart);
        newentry->mbtype = item->mbentry->mbtype|MBTYPE_MOVING;
        r = mboxlist_update(newentry, 1);
        mboxlist_entry_free(&newentry);

        if (r) {
            syslog(LOG_ERR,
                   "Could not move mailbox: %s, mboxlist_update() failed %s",
                   item->mbentry->name, error_message(r));
        }
        else item->state = XFER_LOCAL_MOVING;

        if (!r && xfer->seendb) {
            /* Backport the user's seendb on-the-fly */
            item->mailbox = mailbox;
            r = xfer_backport_seen_item(item, xfer->seendb);
            if (r) syslog(LOG_WARNING,
                          "Failed to backport seen state for mailbox '%s'",
                          item->mbentry->name);

            /* Need to close seendb before dumping Inbox (last item) */
            if (!item->next) seen_close(&xfer->seendb);
        }

        /* Step 4: Dump local -> remote */
        if (!r) {
            prot_printf(xfer->be->out, "D01 UNDUMP {" SIZE_T_FMT "+}\r\n%s ",
                        strlen(item->extname), item->extname);

            r = dump_mailbox(NULL, mailbox, 0, xfer->remoteversion,
                             xfer->be->in, xfer->be->out, imapd_authstate);
            if (r) {
                syslog(LOG_ERR,
                       "Could not move mailbox: %s, dump_mailbox() failed %s",
                       item->mbentry->name, error_message(r));
            }
        }

        mailbox_close(&mailbox);

        if (r) return r;

        r = getresult(xfer->be->in, "D01");
        if (r) {
            syslog(LOG_ERR, "Could not move mailbox: %s, UNDUMP failed %s",
                   item->mbentry->name, error_message(r));
            return r;
        }

        /* Step 5: Set ACL on remote */
        r = trashacl(xfer->be->in, xfer->be->out,
                     item->extname);
        if (r) {
            syslog(LOG_ERR, "Could not clear remote acl on %s",
                   item->mbentry->name);
            return r;
        }

        r = dumpacl(xfer->be->in, xfer->be->out,
                    item->extname, item->mbentry->acl);
        if (r) {
            syslog(LOG_ERR, "Could not set remote acl on %s",
                   item->mbentry->name);
            return r;
        }

        item->state = XFER_UNDUMPED;
    }

    return 0;
}