Ejemplo n.º 1
0
/* Proxy GETANNOTATION commands to backend */
int annotate_fetch_proxy(const char *server, const char *mbox_pat,
                         const strarray_t *entry_pat,
                         const strarray_t *attribute_pat)
{
    struct backend *be;
    int i;
    char mytag[128];

    assert(server && mbox_pat && entry_pat && attribute_pat);

    be = proxy_findserver(server, &imap_protocol,
                          proxy_userid, &backend_cached,
                          &backend_current, &backend_inbox, imapd_in);
    if (!be) return IMAP_SERVER_UNAVAILABLE;

    /* Send command to remote */
    proxy_gentag(mytag, sizeof(mytag));
    prot_printf(be->out, "%s GETANNOTATION \"%s\" (", mytag, mbox_pat);
    for (i = 0 ; i < entry_pat->count ; i++) {
        prot_printf(be->out, "%s\"%s\"", i ? " " : "", entry_pat->data[i]);
    }
    prot_printf(be->out, ") (");
    for (i = 0 ; i < attribute_pat->count ; i++) {
        prot_printf(be->out, "%s\"%s\"", i ? " " : "", attribute_pat->data[i]);
    }
    prot_printf(be->out, ")\r\n");
    prot_flush(be->out);

    /* Pipe the results.  Note that backend-current may also pipe us other
       messages. */
    pipe_until_tag(be, mytag, 0);

    return 0;
}
Ejemplo n.º 2
0
/* Proxy SETANNOTATION commands to backend */
int annotate_store_proxy(const char *server, const char *mbox_pat,
                         struct entryattlist *entryatts)
{
    struct backend *be;
    struct entryattlist *e;
    struct attvaluelist *av;
    char mytag[128];

    assert(server && mbox_pat && entryatts);

    be = proxy_findserver(server, &imap_protocol,
                          proxy_userid, &backend_cached,
                          &backend_current, &backend_inbox, imapd_in);
    if (!be) return IMAP_SERVER_UNAVAILABLE;

    /* Send command to remote */
    proxy_gentag(mytag, sizeof(mytag));
    prot_printf(be->out, "%s SETANNOTATION \"%s\" (", mytag, mbox_pat);
    for (e = entryatts; e; e = e->next) {
        prot_printf(be->out, "\"%s\" (", e->entry);

        for (av = e->attvalues; av; av = av->next) {
            prot_printf(be->out, "\"%s\" ", av->attrib);
            prot_printmap(be->out, av->value.s, av->value.len);
            prot_printf(be->out, "%s", av->next ? " " : "");
        }
        prot_printf(be->out, ")");
        if (e->next) prot_printf(be->out, " ");
    }
    prot_printf(be->out, ")\r\n");
    prot_flush(be->out);

    /* Pipe the results.  Note that backend-current may also pipe us other
       messages. */
    pipe_until_tag(be, mytag, 0);

    return 0;
}
Ejemplo n.º 3
0
void proxy_copy(const char *tag, char *sequence, char *name, int myrights,
                int usinguid, struct backend *s)
{
    char mytag[128];
    struct d {
        char *idate;
        char *flags;
        unsigned int seqno, uid;
        struct d *next;
    } *head, *p, *q;
    int c;

    /* find out what the flags & internaldate for this message are */
    proxy_gentag(mytag, sizeof(mytag));
    prot_printf(backend_current->out,
                "%s %s %s (Flags Internaldate)\r\n",
                tag, usinguid ? "Uid Fetch" : "Fetch", sequence);
    head = (struct d *) xmalloc(sizeof(struct d));
    head->flags = NULL; head->idate = NULL;
    head->seqno = head->uid = 0;
    head->next = NULL;
    p = head;
    /* read all the responses into the linked list */
    for (/* each FETCH response */;;) {
        unsigned int seqno = 0, uidno = 0;
        char *flags = NULL, *idate = NULL;

        /* read a line */
        c = prot_getc(backend_current->in);
        if (c != '*') break;
        c = prot_getc(backend_current->in);
        if (c != ' ') { /* protocol error */ c = EOF; break; }

        /* check for OK/NO/BAD/BYE response */
        if (!isdigit(c = prot_getc(backend_current->in))) {
            prot_printf(imapd_out, "* %c", c);
            pipe_to_end_of_response(backend_current, 0);
            continue;
        }

        /* read seqno */
        prot_ungetc(c, backend_current->in);
        c = getuint32(backend_current->in, &seqno);
        if (seqno == 0 || c != ' ') {
            /* we suck and won't handle this case */
            c = EOF; break;
        }
        c = chomp(backend_current->in, "fetch (");
        if (c == EOF) {
            c = chomp(backend_current->in, "exists\r");
            if (c == '\n') { /* got EXISTS response */
                prot_printf(imapd_out, "* %d EXISTS\r\n", seqno);
                continue;
            }
        }
        if (c == EOF) {
            /* XXX  the "exists" check above will eat "ex" */
            c = chomp(backend_current->in, "punge\r");
            if (c == '\n') { /* got EXPUNGE response */
                prot_printf(imapd_out, "* %d EXPUNGE\r\n", seqno);
                continue;
            }
        }
        if (c == EOF) {
            c = chomp(backend_current->in, "recent\r");
            if (c == '\n') { /* got RECENT response */
                prot_printf(imapd_out, "* %d RECENT\r\n", seqno);
                continue;
            }
        }
        /* huh, don't get this response */
        if (c == EOF) break;
        for (/* each fetch item */;;) {
            /* looking at the first character in an item */
            switch (c) {
            case 'f': case 'F': /* flags? */
                c = chomp(backend_current->in, "lags");
                if (c != ' ') { c = EOF; }
                else c = prot_getc(backend_current->in);
                if (c != '(') { c = EOF; }
                else {
                    flags = grab(backend_current->in, ')');
                    c = prot_getc(backend_current->in);
                }
                break;
            case 'i': case 'I': /* internaldate? */
                c = chomp(backend_current->in, "nternaldate");
                if (c != ' ') { c = EOF; }
                else c = prot_getc(backend_current->in);
                if (c != '"') { c = EOF; }
                else {
                    idate = grab(backend_current->in, '"');
                    c = prot_getc(backend_current->in);
                }
                break;
            case 'u': case 'U': /* uid */
                c = chomp(backend_current->in, "id");
                if (c != ' ') { c = EOF; }
                else c = getuint32(backend_current->in, &uidno);
                break;
            default: /* hmm, don't like the smell of it */
                c = EOF;
                break;
            }
            /* looking at either SP seperating items or a RPAREN */
            if (c == ' ') { c = prot_getc(backend_current->in); }
            else if (c == ')') break;
            else { c = EOF; break; }
        }
        /* if c == EOF we have either a protocol error or a situation
           we can't handle, and we should die. */
        if (c == ')') c = prot_getc(backend_current->in);
        if (c == '\r') c = prot_getc(backend_current->in);
        if (c != '\n') {
            c = EOF;
            free(flags);
            free(idate);
            break;
        }

        /* if we're missing something, we should echo */
        if (!flags || !idate) {
            char sep = '(';
            prot_printf(imapd_out, "* %d FETCH ", seqno);
            if (uidno) {
                prot_printf(imapd_out, "%cUID %d", sep, uidno);
                sep = ' ';
            }
            if (flags) {
                prot_printf(imapd_out, "%cFLAGS %s", sep, flags);
                sep = ' ';
            }
            if (idate) {
                prot_printf(imapd_out, "%cINTERNALDATE %s", sep, flags);
                sep = ' ';
            }
            prot_printf(imapd_out, ")\r\n");
            if (flags) free(flags);
            if (idate) free(idate);
            continue;
        }

        /* add to p->next */
        p->next = xmalloc(sizeof(struct d));
        p = p->next;
        p->idate = idate;
        p->flags = editflags(flags);
        p->uid = uidno;
        p->seqno = seqno;
        p->next = NULL;
    }
    if (c != EOF) {
        prot_ungetc(c, backend_current->in);

        /* we should be looking at the tag now */
        pipe_until_tag(backend_current, tag, 0);
    }
    if (c == EOF) {
        /* uh oh, we're not happy */
        fatal("Lost connection to selected backend", EC_UNAVAILABLE);
    }

    /* start the append */
    prot_printf(s->out, "%s Append {" SIZE_T_FMT "+}\r\n%s",
                tag, strlen(name), name);
    prot_printf(backend_current->out, "%s %s %s (Rfc822.peek)\r\n",
                mytag, usinguid ? "Uid Fetch" : "Fetch", sequence);
    for (/* each FETCH response */;;) {
        unsigned int seqno = 0, uidno = 0;

        /* read a line */
        c = prot_getc(backend_current->in);
        if (c != '*') break;
        c = prot_getc(backend_current->in);
        if (c != ' ') { /* protocol error */ c = EOF; break; }

        /* check for OK/NO/BAD/BYE response */
        if (!isdigit(c = prot_getc(backend_current->in))) {
            prot_printf(imapd_out, "* %c", c);
            pipe_to_end_of_response(backend_current, 0);
            continue;
        }

        /* read seqno */
        prot_ungetc(c, backend_current->in);
        c = getuint32(backend_current->in, &seqno);
        if (seqno == 0 || c != ' ') {
            /* we suck and won't handle this case */
            c = EOF; break;
        }
        c = chomp(backend_current->in, "fetch (");
        if (c == EOF) { /* not a fetch response */
            c = chomp(backend_current->in, "exists\r");
            if (c == '\n') { /* got EXISTS response */
                prot_printf(imapd_out, "* %d EXISTS\r\n", seqno);
                continue;
            }
        }
        if (c == EOF) { /* not an exists response */
            /* XXX  the "exists" check above will eat "ex" */
            c = chomp(backend_current->in, "punge\r");
            if (c == '\n') { /* got EXPUNGE response */
                prot_printf(imapd_out, "* %d EXPUNGE\r\n", seqno);
                continue;
            }
        }
        if (c == EOF) { /* not an exists response */
            c = chomp(backend_current->in, "recent\r");
            if (c == '\n') { /* got RECENT response */
                prot_printf(imapd_out, "* %d RECENT\r\n", seqno);
                continue;
            }
        }
        if (c == EOF) {
            /* huh, don't get this response */
            break;
        }
        /* find seqno in the list */
        p = head;
        while (p->next && seqno != p->next->seqno) p = p->next;
        if (!p->next) break;
        q = p->next;
        p->next = q->next;
        for (/* each fetch item */;;) {
            int sz = 0;

            switch (c) {
            case 'u': case 'U':
                c = chomp(backend_current->in, "id");
                if (c != ' ') { c = EOF; }
                else c = getuint32(backend_current->in, &uidno);
                break;

            case 'r': case 'R':
                c = chomp(backend_current->in, "fc822");
                if (c == ' ') c = prot_getc(backend_current->in);
                if (c != '{') {
                    /* NIL? */
                    eatline(backend_current->in, c);
                    c = EOF;
                }
                else c = getint32(backend_current->in, &sz);
                if (c == '}') c = prot_getc(backend_current->in);
                if (c == '\r') c = prot_getc(backend_current->in);
                if (c != '\n') c = EOF;

                if (c != EOF) {
                    /* append p to s->out */
                    prot_printf(s->out, " (%s) \"%s\" {%d+}\r\n",
                                q->flags, q->idate, sz);
                    while (sz) {
                        char buf[2048];
                        int j = (sz > (int) sizeof(buf) ?
                                 (int) sizeof(buf) : sz);

                        j = prot_read(backend_current->in, buf, j);
                        if(!j) break;
                        prot_write(s->out, buf, j);
                        sz -= j;
                    }
                    c = prot_getc(backend_current->in);
                }

                break; /* end of case */
            default:
                c = EOF;
                break;
            }
            /* looking at either SP seperating items or a RPAREN */
            if (c == ' ') { c = prot_getc(backend_current->in); }
            else if (c == ')') break;
            else { c = EOF; break; }
        }

        /* if c == EOF we have either a protocol error or a situation
           we can't handle, and we should die. */
        if (c == ')') c = prot_getc(backend_current->in);
        if (c == '\r') c = prot_getc(backend_current->in);
        if (c != '\n') { c = EOF; break; }

        /* free q */
        free(q->idate);
        free(q->flags);
        free(q);
    }
    if (c != EOF) {
        char *appenduid, *b;
        int res;

        /* pushback the first character of the tag we're looking at */
        prot_ungetc(c, backend_current->in);

        /* nothing should be left in the linked list */
        assert(head->next == NULL);

        /* ok, finish the append; we need the UIDVALIDITY and UIDs
           to return as part of our COPYUID response code */
        prot_printf(s->out, "\r\n");

        /* should be looking at 'mytag' on 'backend_current',
           'tag' on 's' */
        pipe_until_tag(backend_current, mytag, 0);
        res = pipe_until_tag(s, tag, 0);

        if (res == PROXY_OK) {
            if (myrights & ACL_READ) {
                appenduid = strchr(s->last_result.s, '[');
                /* skip over APPENDUID */
                if (appenduid) {
                    appenduid += strlen("[appenduid ");
                    b = strchr(appenduid, ']');
                    if (b) *b = '\0';
                    prot_printf(imapd_out, "%s OK [COPYUID %s] %s\r\n", tag,
                                appenduid, error_message(IMAP_OK_COMPLETED));
                }
                else
                    prot_printf(imapd_out, "%s OK %s\r\n", tag, s->last_result.s);
            }
            else {
                prot_printf(imapd_out, "%s OK %s\r\n", tag,
                            error_message(IMAP_OK_COMPLETED));
            }
        } else {
            prot_printf(imapd_out, "%s %s", tag, s->last_result.s);
        }
    } else {
        /* abort the append */
        prot_printf(s->out, " {0+}\r\n\r\n");
        pipe_until_tag(backend_current, mytag, 0);
        pipe_until_tag(s, tag, 0);

        /* report failure */
        prot_printf(imapd_out, "%s NO inter-server COPY failed\r\n", tag);
    }

    /* free dynamic memory */
    while (head) {
        p = head;
        head = head->next;
        if (p->idate) free(p->idate);
        if (p->flags) free(p->flags);
        free(p);
    }
}
Ejemplo n.º 4
0
static void proxy_part_filldata(partlist_t *part_list, int idx)
{
    char mytag[128];
    struct backend *be;
    partitem_t *item = &part_list->items[idx];

    item->id = 0;
    item->available = 0;
    item->total = 0;
    item->quota = 0.;

    syslog(LOG_DEBUG, "checking free space on server '%s'", item->value);

    /* connect to server */
    be = proxy_findserver(item->value, &imap_protocol,
            proxy_userid, &backend_cached,
            &backend_current, &backend_inbox, imapd_in);

    if (be) {
        uint64_t server_available = 0;
        uint64_t server_total = 0;
        const char *annot =
            (part_list->mode == PART_SELECT_MODE_FREESPACE_MOST) ?
            "freespace/total" : "freespace/percent/most";
        struct buf cmd = BUF_INITIALIZER;
        int c;

        /* fetch annotation from remote */
        proxy_gentag(mytag, sizeof(mytag));
        if (CAPA(be, CAPA_METADATA)) {
            buf_printf(&cmd, "METADATA \"\" (\"/shared" IMAP_ANNOT_NS "%s\"",
                       annot);
        }
        else {
            buf_printf(&cmd, "ANNOTATION \"\" \"" IMAP_ANNOT_NS "%s\" "
                       "(\"value.shared\"", annot);
        }
        prot_printf(be->out, "%s GET%s)\r\n", mytag, buf_cstring(&cmd));
        prot_flush(be->out);

        for (/* each annotation response */;;) {
            /* read a line */
            c = prot_getc(be->in);
            if (c != '*') break;
            c = prot_getc(be->in);
            if (c != ' ') { /* protocol error */ c = EOF; break; }

            c = chomp(be->in, buf_cstring(&cmd));
            if (c == ' ') c = prot_getc(be->in);
            if ((c == EOF) || (c != '\"')) {
                /* we don't care about this response */
                eatline(be->in, c);
                continue;
            }

            /* read available */
            c = getuint64(be->in, &server_available);
            if (c != ';') { c = EOF; break; }

            /* read total */
            c = getuint64(be->in, &server_total);
            if (c != '\"') { c = EOF; break; }
            eatline(be->in, c); /* we don't care about the rest of the line */
        }
        buf_free(&cmd);
        if (c != EOF) {
            prot_ungetc(c, be->in);

            /* we should be looking at the tag now */
            eatline(be->in, c);
        }
        if (c == EOF) {
            /* uh oh, we're not happy */
            fatal("Lost connection to backend", EC_UNAVAILABLE);
        }

        /* unique id */
        item->id = idx;
        item->available = server_available;
        item->total = server_total;
    }
}
Ejemplo n.º 5
0
int proxy_catenate_url(struct backend *s, struct imapurl *url, FILE *f,
                       unsigned long *size, const char **parseerr)
{
    char mytag[128];
    int c, r = 0, found = 0;
    unsigned int uidvalidity = 0;

    *size = 0;
    *parseerr = NULL;

    /* select the mailbox (read-only) */
    proxy_gentag(mytag, sizeof(mytag));
    prot_printf(s->out, "%s Examine {" SIZE_T_FMT "+}\r\n%s\r\n",
                mytag, strlen(url->mailbox), url->mailbox);
    for (/* each examine response */;;) {
        /* read a line */
        c = prot_getc(s->in);
        if (c != '*') break;
        c = prot_getc(s->in);
        if (c != ' ') { /* protocol error */ c = EOF; break; }

        c = chomp(s->in, "ok [uidvalidity");
        if (c == EOF) {
            /* we don't care about this response */
            eatline(s->in, c);
            continue;
        }

        /* read uidvalidity */
        c = getuint32(s->in, &uidvalidity);
        if (c != ']') { c = EOF; break; }
        eatline(s->in, c); /* we don't care about the rest of the line */
    }
    if (c != EOF) {
        prot_ungetc(c, s->in);

        /* we should be looking at the tag now */
        eatline(s->in, c);
    }
    if (c == EOF) {
        /* uh oh, we're not happy */
        fatal("Lost connection to backend", EC_UNAVAILABLE);
    }

    if (url->uidvalidity && (uidvalidity != url->uidvalidity)) {
        *parseerr = "Uidvalidity of mailbox has changed";
        r = IMAP_BADURL;
        goto unselect;
    }

    /* fetch the bodypart */
    proxy_gentag(mytag, sizeof(mytag));
    prot_printf(s->out, "%s Uid Fetch %lu Body.Peek[%s]\r\n",
                mytag, url->uid, url->section ? url->section : "");
    for (/* each fetch response */;;) {
        unsigned int seqno;

      next_resp:
        /* read a line */
        c = prot_getc(s->in);
        if (c != '*') break;
        c = prot_getc(s->in);
        if (c != ' ') { /* protocol error */ c = EOF; break; }

        /* read seqno */
        c = getuint32(s->in, &seqno);
        if (seqno == 0 || c != ' ') {
            /* we suck and won't handle this case */
            c = EOF; break;
        }
        c = chomp(s->in, "fetch (");
        if (c == EOF) { /* not a fetch response */
            eatline(s->in, c);
            continue;
        }

        for (/* each fetch item */;;) {
            unsigned uid, sz = 0;

            switch (c) {
            case 'u': case 'U':
                c = chomp(s->in, "id");
                if (c != ' ') { c = EOF; }
                else {
                    c = getuint32(s->in, &uid);
                    if (uid != url->uid) {
                        /* not our response */
                        eatline(s->in, c);
                        goto next_resp;
                    }
                }
                break;

            case 'b': case 'B':
                c = chomp(s->in, "ody[");
                while (c != ']') c = prot_getc(s->in);
                if (c == ']') c = prot_getc(s->in);
                if (c == ' ') c = prot_getc(s->in);
                if (c == '{') {
                    c = getuint32(s->in, &sz);
                    if (c == '}') c = prot_getc(s->in);
                    if (c == '\r') c = prot_getc(s->in);
                    if (c != '\n') c = EOF;
                }
                else if (c == 'n' || c == 'N') {
                    c = chomp(s->in, "il");
                    r = IMAP_BADURL;
                    *parseerr = "No such message part";
                }

                if (c != EOF) {
                    /* catenate to f */
                    found = 1;
                    *size = sz;

                    while (sz) {
                        char buf[2048];
                        int j = (sz > sizeof(buf) ? sizeof(buf) : sz);

                        j = prot_read(s->in, buf, j);
                        if(!j) break;
                        fwrite(buf, j, 1, f);
                        sz -= j;
                    }
                    c = prot_getc(s->in);
                }

                break; /* end of case */
            default:
                /* probably a FLAGS item */
                eatline(s->in, c);
                goto next_resp;
            }
            /* looking at either SP separating items or a RPAREN */
            if (c == ' ') { c = prot_getc(s->in); }
            else if (c == ')') break;
            else { c = EOF; break; }
        }

        /* if c == EOF we have either a protocol error or a situation
           we can't handle, and we should die. */
        if (c == ')') c = prot_getc(s->in);
        if (c == '\r') c = prot_getc(s->in);
        if (c != '\n') { c = EOF; break; }
    }
    if (c != EOF) {
        prot_ungetc(c, s->in);

        /* we should be looking at the tag now */
        eatline(s->in, c);
    }
    if (c == EOF) {
        /* uh oh, we're not happy */
        fatal("Lost connection to backend", EC_UNAVAILABLE);
    }

  unselect:
    /* unselect the mailbox */
    proxy_gentag(mytag, sizeof(mytag));
    prot_printf(s->out, "%s Unselect\r\n", mytag);
    for (/* each unselect response */;;) {
        /* read a line */
        c = prot_getc(s->in);
        if (c != '*') break;
        c = prot_getc(s->in);
        if (c != ' ') { /* protocol error */ c = EOF; break; }

        /* we don't care about this response */
        eatline(s->in, c);
    }
    if (c != EOF) {
        prot_ungetc(c, s->in);

        /* we should be looking at the tag now */
        eatline(s->in, c);
    }
    if (c == EOF) {
        /* uh oh, we're not happy */
        fatal("Lost connection to backend", EC_UNAVAILABLE);
    }

    if (!r && !found) {
        r = IMAP_BADURL;
        *parseerr = "No such message in mailbox";
    }

    return r;
}
Ejemplo n.º 6
0
char *find_free_server(void)
{
    const char *servers = config_getstring(IMAPOPT_SERVERLIST);
    unsigned long max_avail = 0;
    char *server = NULL;

    if (servers) {
	char *tmpbuf, *cur_server, *next_server;
	char mytag[128];
	struct backend *be;

	/* make a working copy of the list */
	cur_server = tmpbuf = xstrdup(servers);

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

	    if (!*cur_server) break;

	    /* find end of server */
	    if ((next_server = strchr(cur_server, ' ')) ||
		(next_server = strchr(cur_server, '\t')))
		*next_server++ = '\0';

	    syslog(LOG_DEBUG, "checking free space on server '%s'", cur_server);

	    /* connect to server */
	    be = proxy_findserver(cur_server, &imap_protocol,
				  proxy_userid, &backend_cached,
				  &backend_current, &backend_inbox, imapd_in);
	    if (be) {
		unsigned avail = 0;
		int c;

		/* fetch annotation from remote */
		proxy_gentag(mytag, sizeof(mytag));
		prot_printf(be->out,
			    "%s GETANNOTATION \"\" "
			    "\"/vendor/cmu/cyrus-imapd/freespace\" "
			    "\"value.shared\"\r\n", mytag);
		prot_flush(be->out);

		for (/* each annotation response */;;) {
		    /* read a line */
		    c = prot_getc(be->in);
		    if (c != '*') break;
		    c = prot_getc(be->in);
		    if (c != ' ') { /* protocol error */ c = EOF; break; }

		    c = chomp(be->in,
			      "ANNOTATION \"\" "
			      "\"/vendor/cmu/cyrus-imapd/freespace\" "
			      "(\"value.shared\" \"");
		    if (c == EOF) {
			/* we don't care about this response */
			eatline(be->in, c);
			continue;
		    }

		    /* read uidvalidity */
		    c = getuint32(be->in, &avail);
		    if (c != '\"') { c = EOF; break; }
		    eatline(be->in, c); /* we don't care about the rest of the line */
		}
		if (c != EOF) {
		    prot_ungetc(c, be->in);

		    /* we should be looking at the tag now */
		    eatline(be->in, c);
		}
		if (c == EOF) {
		    /* uh oh, we're not happy */
		    fatal("Lost connection to backend", EC_UNAVAILABLE);
		}
		if (avail > max_avail) {
		    server = cur_server;
		    max_avail = avail;
		}
	    }

	    /* move to next server */
	    cur_server = next_server;
	}

	if (server) server = xstrdup(server);

	free(tmpbuf);
    }

    return server;
}