Beispiel #1
0
static ssize_t recv_cb(wslay_event_context_ptr ev,
                       uint8_t *buf, size_t len,
                       int flags __attribute__((unused)),
                       void *user_data)
{
    struct transaction_t *txn = (struct transaction_t *) user_data;
    struct protstream *pin = txn->conn->pin;
    ssize_t n;

    if (txn->conn->sess_ctx) {
        pin = ((struct ws_context *) txn->ws_ctx)->h2_pin;
    }

    n = prot_read(pin, (char *) buf, len);
    if (!n) {
        /* No data */
        if (pin->eof && !pin->fixedsize)
            wslay_event_set_error(ev, WSLAY_ERR_NO_MORE_MSG);
        else if (pin->error)
            wslay_event_set_error(ev, WSLAY_ERR_CALLBACK_FAILURE);
        else
            wslay_event_set_error(ev, WSLAY_ERR_WOULDBLOCK);

        n = -1;
    }

    syslog(LOG_DEBUG,
           "ws_recv_cb(%zu): n = %zd, eof = %d, err = '%s', errno = %m",
           len, n, pin->eof, pin->error ? pin->error : "");

    return n;
}
Beispiel #2
0
static int send_rejection(const char *origid,
			  const char *rejto,
			  const char *origreceip, 
			  const char *mailreceip, 
			  const char *reason, 
			  struct protstream *file)
{
    FILE *sm;
    const char *smbuf[10];
    char buf[8192], *namebuf;
    int i, sm_stat;
    time_t t;
    char datestr[RFC822_DATETIME_MAX+1];
    pid_t sm_pid, p;
    duplicate_key_t dkey = DUPLICATE_INITIALIZER;

    smbuf[0] = "sendmail";
    smbuf[1] = "-i";		/* ignore dots */
    smbuf[2] = "-f";
    smbuf[3] = "<>";
    smbuf[4] = "--";
    smbuf[5] = rejto;
    smbuf[6] = NULL;
    sm_pid = open_sendmail(smbuf, &sm);
    if (sm == NULL) {
	return -1;
    }

    t = time(NULL);
    p = getpid();
    snprintf(buf, sizeof(buf), "<cmu-sieve-%d-%d-%d@%s>", (int) p, (int) t, 
	     global_outgoing_count++, config_servername);
    
    namebuf = make_sieve_db(mailreceip);

    time_to_rfc822(t, datestr, sizeof(datestr));

    dkey.id = buf;
    dkey.to = namebuf;
    dkey.date = datestr;
    duplicate_mark(&dkey, t, 0);

    fprintf(sm, "Message-ID: %s\r\n", buf);
    fprintf(sm, "Date: %s\r\n", datestr);

    fprintf(sm, "X-Sieve: %s\r\n", SIEVE_VERSION);
    fprintf(sm, "From: Mail Sieve Subsystem <%s>\r\n",
	    config_getstring(IMAPOPT_POSTMASTER));
    fprintf(sm, "To: <%s>\r\n", rejto);
    fprintf(sm, "MIME-Version: 1.0\r\n");
    fprintf(sm, "Content-Type: "
	    "multipart/report; report-type=disposition-notification;"
	    "\r\n\tboundary=\"%d/%s\"\r\n", (int) p, config_servername);
    fprintf(sm, "Subject: Automatically rejected mail\r\n");
    fprintf(sm, "Auto-Submitted: auto-replied (rejected)\r\n");
    fprintf(sm, "\r\nThis is a MIME-encapsulated message\r\n\r\n");

    /* this is the human readable status report */
    fprintf(sm, "--%d/%s\r\n", (int) p, config_servername);
    fprintf(sm, "Content-Type: text/plain; charset=utf-8\r\n");
    fprintf(sm, "Content-Disposition: inline\r\n");
    fprintf(sm, "Content-Transfer-Encoding: 8bit\r\n\r\n");

    fprintf(sm, "Your message was automatically rejected by Sieve, a mail\r\n"
	    "filtering language.\r\n\r\n");
    fprintf(sm, "The following reason was given:\r\n%s\r\n\r\n", reason);

    /* this is the MDN status report */
    fprintf(sm, "--%d/%s\r\n"
	    "Content-Type: message/disposition-notification\r\n\r\n",
	    (int) p, config_servername);
    fprintf(sm, "Reporting-UA: %s; Cyrus %s/%s\r\n",
	    config_servername, cyrus_version(), SIEVE_VERSION);
    if (origreceip)
	fprintf(sm, "Original-Recipient: rfc822; %s\r\n", origreceip);
    fprintf(sm, "Final-Recipient: rfc822; %s\r\n", mailreceip);
    if (origid)
	fprintf(sm, "Original-Message-ID: %s\r\n", origid);
    fprintf(sm, "Disposition: "
	    "automatic-action/MDN-sent-automatically; deleted\r\n");
    fprintf(sm, "\r\n");

    /* this is the original message */
    fprintf(sm, "--%d/%s\r\nContent-Type: message/rfc822\r\n\r\n",
	    (int) p, config_servername);
    prot_rewind(file);
    while ((i = prot_read(file, buf, sizeof(buf))) > 0) {
	fwrite(buf, i, 1, sm);
    }
    fprintf(sm, "\r\n\r\n");
    fprintf(sm, "--%d/%s--\r\n", (int) p, config_servername);

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

    return sm_stat;	/* sendmail exit value */
}
Beispiel #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);
    }
}
Beispiel #4
0
/* copy our current input to 's' until we hit a true EOL.

   'optimistic_literal' is how happy we should be about assuming
   that a command will go through by converting synchronizing literals of
   size less than optimistic_literal to nonsync

   returns 0 on success, <0 on big failure, >0 on full command not sent */
int pipe_command(struct backend *s, int optimistic_literal)
{
    char buf[2048];
    char eol[128];
    int sl;

    s->timeout->mark = time(NULL) + IDLE_TIMEOUT;

    eol[0] = '\0';

    /* again, the complication here are literals */
    for (;;) {
        if (!prot_fgets(buf, sizeof(buf), imapd_in)) {
            /* uh oh */
            return -1;
        }

        sl = strlen(buf);

        if (sl == (sizeof(buf) - 1) && buf[sl-1] != '\n') {
            /* only got part of a line */
            strcpy(eol, buf + sl - 64);

            /* and write this out, except for what we've saved */
            prot_write(s->out, buf, sl - 64);
            continue;
        } else {
            int i, nonsynch = 0, islit = 0, litlen = 0;

            if (sl < 64) {
                strcat(eol, buf);
            } else {
                /* write out what we have, and copy the last 64 characters
                   to eol */
                prot_printf(s->out, "%s", eol);
                prot_write(s->out, buf, sl - 64);
                strcpy(eol, buf + sl - 64);
            }

            /* now determine if eol has a literal in it */
            i = strlen(eol);
            if (i >= 4 &&
                eol[i-1] == '\n' && eol[i-2] == '\r' && eol[i-3] == '}') {
                /* possible literal */
                i -= 4;
                if (eol[i] == '+') {
                    nonsynch = 1;
                    i--;
                }
                while (i > 0 && eol[i] != '{' && Uisdigit(eol[i])) {
                    i--;
                }
                if (eol[i] == '{') {
                    islit = 1;
                    litlen = atoi(eol + i + 1);
                }
            }

            if (islit) {
                if (nonsynch) {
                    prot_write(s->out, eol, strlen(eol));
                } else if (!nonsynch && (litlen <= optimistic_literal)) {
                    prot_printf(imapd_out, "+ i am an optimist\r\n");
                    prot_write(s->out, eol, strlen(eol) - 3);
                    /* need to insert a + to turn it into a nonsynch */
                    prot_printf(s->out, "+}\r\n");
                } else {
                    /* we do a standard synchronizing literal */
                    prot_write(s->out, eol, strlen(eol));
                    /* but here the game gets tricky... */
                    prot_fgets(buf, sizeof(buf), s->in);
                    /* but for now we cheat */
                    prot_write(imapd_out, buf, strlen(buf));
                    if (buf[0] != '+' && buf[1] != ' ') {
                        /* char *p = strchr(buf, ' '); */
                        /* strncpy(s->last_result, p + 1, LAST_RESULT_LEN);*/

                        /* stop sending command now */
                        return 1;
                    }
                }

                /* gobble literal and sent it onward */
                while (litlen > 0) {
                    int j = (litlen > (int) sizeof(buf) ?
                             (int) sizeof(buf) : litlen);

                    j = prot_read(imapd_in, buf, j);
                    if(!j) {
                        /* EOF or other error */
                        return -1;
                    }
                    prot_write(s->out, buf, j);
                    litlen -= j;
                }

                eol[0] = '\0';

                /* have to keep going for the send of the command */
                continue;
            } else {
                /* no literal, so we're done! */
                prot_write(s->out, eol, strlen(eol));

                return 0;
            }
        }
    }
}
Beispiel #5
0
/* pipe_response() reads from 's->in' until either the tagged response
   starting with 'tag' appears, or if 'tag' is NULL, to the end of the
   current line.  If 'include_last' is set, the last/tagged line is included
   in the output, otherwise the last/tagged line is stored in 's->last_result'.
   In either case, the result of the tagged command is returned.

   's->last_result' assumes that tagged responses don't contain literals.
   Unfortunately, the IMAP grammar allows them

   force_notfatal says to not fatal() if we lose connection to backend_current
   even though it is in 95% of the cases, a good idea...
*/
static int pipe_response(struct backend *s, const char *tag, int include_last,
                         int force_notfatal)
{
    char buf[2048];
    char eol[128];
    unsigned sl;
    int cont = 0, last = !tag, r = PROXY_OK;
    size_t taglen = 0;

    s->timeout->mark = time(NULL) + IDLE_TIMEOUT;

    if (tag) {
        taglen = strlen(tag);
        if(taglen >= sizeof(buf) + 1) {
            fatal("tag too large",EC_TEMPFAIL);
        }
    }

    buf_reset(&s->last_result);

    /* the only complication here are literals */
    do {
        /* if 'cont' is set, we're looking at the continuation to a very
           long line.
           if 'last' is set, we've seen the tag we're looking for, we're
           just reading the end of the line. */
        if (!cont) eol[0] = '\0';

        if (!prot_fgets(buf, sizeof(buf), s->in)) {
            /* uh oh */
            if(s == backend_current && !force_notfatal)
                fatal("Lost connection to selected backend", EC_UNAVAILABLE);
            proxy_downserver(s);
            return PROXY_NOCONNECTION;
        }

        sl = strlen(buf);

        if (tag) {
            /* Check for the tagged line */
            if (!cont && buf[taglen] == ' ' && !strncmp(tag, buf, taglen)) {

                switch (buf[taglen + 1]) {
                case 'O': case 'o':
                    r = PROXY_OK;
                    break;
                case 'N': case 'n':
                    r = PROXY_NO;
                    break;
                case 'B': case 'b':
                    r = PROXY_BAD;
                    break;
                default: /* huh? no result? */
                    if(s == backend_current && !force_notfatal)
                        fatal("Lost connection to selected backend",
                              EC_UNAVAILABLE);
                    proxy_downserver(s);
                    r = PROXY_NOCONNECTION;
                    break;
                }

                last = 1;
            }

            if (last && !include_last) {
                /* Store the tagged line */
                buf_appendcstr(&s->last_result, buf+taglen+1);
                buf_cstring(&s->last_result);
            }
        }

        if (sl == (sizeof(buf) - 1) && buf[sl-1] != '\n') {
            /* only got part of a line */
            /* we save the last 64 characters in case it has important
               literal information */
            strcpy(eol, buf + sl - 64);

            /* write out this part, but we have to keep reading until we
               hit the end of the line */
            if (!last || include_last) prot_write(imapd_out, buf, sl);
            cont = 1;
            continue;
        } else {                /* we got the end of the line */
            int i;
            int litlen = 0, islit = 0;

            if (!last || include_last) prot_write(imapd_out, buf, sl);

            /* now we have to see if this line ends with a literal */
            if (sl < 64) {
                strcat(eol, buf);
            } else {
                strcat(eol, buf + sl - 63);
            }

            /* eol now contains the last characters from the line; we want
               to see if we've hit a literal */
            i = strlen(eol);
            if (i >= 4 &&
                eol[i-1] == '\n' && eol[i-2] == '\r' && eol[i-3] == '}') {
                /* possible literal */
                i -= 4;
                while (i > 0 && eol[i] != '{' && Uisdigit(eol[i])) {
                    i--;
                }
                if (eol[i] == '{') {
                    islit = 1;
                    litlen = atoi(eol + i + 1);
                }
            }

            /* copy the literal over */
            if (islit) {
                while (litlen > 0) {
                    int j = (litlen > (int) sizeof(buf) ?
                             (int) sizeof(buf) : litlen);

                    j = prot_read(s->in, buf, j);
                    if(!j) {
                        /* EOF or other error */
                        return -1;
                    }
                    if (!last || include_last) prot_write(imapd_out, buf, j);
                    litlen -= j;
                }

                /* none of our saved information has any relevance now */
                eol[0] = '\0';

                /* have to keep going for the end of the line */
                cont = 1;
                continue;
            }
        }

        /* ok, let's read another line */
        cont = 0;

    } while (!last || cont);

    return r;
}
Beispiel #6
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;
}
Beispiel #7
0
static int reservefile(struct protstream *in, const char *part,
                       struct message_guid *guid, unsigned long size,
                       const char **fname)
{
    FILE *file;
    char buf[8192+1];
    int r = 0;

    /* XXX - write to a temporary file then move in to place! */
    *fname = dlist_reserve_path(part, /*isarchive*/0, guid);

    /* remove any duplicates if they're still here */
    unlink(*fname);

    file = fopen(*fname, "w+");
    if (!file) {
        syslog(LOG_ERR,
               "IOERROR: failed to upload file %s", message_guid_encode(guid));
        r = IMAP_IOERROR;
        /* Note: we still read the file's data from the wire,
         * to avoid losing protocol sync */
    }

    /* XXX - calculate sha1 on the fly? */
    while (size) {
        size_t n = prot_read(in, buf, size > 8192 ? 8192 : size);
        if (!n) {
            syslog(LOG_ERR,
                "IOERROR: reading message: unexpected end of file");
            r = IMAP_IOERROR;
            break;
        }
        size -= n;
        if (fwrite(buf, 1, n, file) != n) {
            syslog(LOG_ERR, "IOERROR: writing to file '%s': %m", *fname);
            r = IMAP_IOERROR;
            break;
        }
    }

    if (r)
        goto error;

    /* Make sure that message flushed to disk just incase mmap has problems */
    fflush(file);
    if (ferror(file)) {
        r = IMAP_IOERROR;
        goto error;
    }

    if (fsync(fileno(file)) < 0) {
        syslog(LOG_ERR, "IOERROR: fsyncing file '%s': %m", *fname);
        r = IMAP_IOERROR;
        goto error;
    }

    fclose(file);

    return 0;

error:
    if (file) {
        fclose(file);
        unlink(*fname);
        *fname = NULL;
    }
    return r;
}