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