#include <config.h>
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/un.h>
#include "acl.h"
#include "annotate.h"
#include "backend.h"
#include "exitcodes.h"
#include "global.h"
#include "imap_err.h"
#include "imap_proxy.h"
#include "proxy.h"
#include "mboxname.h"
#include "mupdate-client.h"
#include "prot.h"
#include "xmalloc.h"
extern unsigned int proxy_cmdcnt;
extern struct protstream *imapd_in, *imapd_out;
extern struct backend *backend_inbox, *backend_current, **backend_cached;
extern char *imapd_userid, *proxy_userid;
extern struct namespace imapd_namespace;
extern void printstring(const char *s);
extern void printastring(const char *s);
extern int mlookup(const char *tag, const char *ext_name,
const char *name, int *flags, char **pathp, char **mpathp,
char **partp, char **aclp, struct txn **tid) ;
void proxy_gentag(char *tag, size_t len)
{
snprintf(tag, len, "PROXY%d", proxy_cmdcnt++);
}
struct backend *proxy_findinboxserver(void)
{
char inbox[MAX_MAILBOX_NAME+1];
int r, mbtype;
char *server = NULL;
struct backend *s = NULL;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX",
imapd_userid, inbox);
if(!r) {
r = mlookup(NULL, NULL, inbox, &mbtype, NULL, NULL, &server, NULL, NULL);
if (!r && (mbtype & MBTYPE_REMOTE)) {
s = proxy_findserver(server, &protocol[PROTOCOL_IMAP],
proxy_userid, &backend_cached,
&backend_current, &backend_inbox, imapd_in);
}
}
return s;
}
static int pipe_response(struct backend *s, const char *tag, int include_tag,
int force_notfatal)
{
char buf[2048];
char eol[128];
int sl;
int cont = 0, last = !tag, r = PROXY_OK;
size_t taglen;
s->timeout->mark = time(NULL) + IDLE_TIMEOUT;
if (tag) {
taglen = strlen(tag);
if(taglen >= sizeof(buf)) {
fatal("tag too large",EC_TEMPFAIL);
}
}
s->last_result.len = 0;
do {
if (!cont) eol[0] = '\0';
if (!prot_fgets(buf, sizeof(buf), s->in)) {
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) {
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:
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_tag) {
if (sl > s->last_result.alloc - s->last_result.len) {
s->last_result.alloc =
(s->last_result.alloc == 0) ? sizeof(buf) :
s->last_result.alloc * 2;
s->last_result.s = xrealloc(s->last_result.s,
s->last_result.alloc+1);
}
strcpy(s->last_result.s + s->last_result.len, buf + taglen + 1);
s->last_result.len += sl - taglen - 1;
}
}
if (sl == (sizeof(buf) - 1) && buf[sl-1] != '\n') {
strcpy(eol, buf + sl - 64);
if (!last || include_tag) prot_write(imapd_out, buf, sl);
cont = 1;
continue;
} else {
int i;
int litlen = 0, islit = 0;
if (!last || include_tag) prot_write(imapd_out, buf, sl);
if (sl < 64) {
strcat(eol, buf);
} else {
strcat(eol, buf + sl - 63);
}
i = strlen(eol);
if (i >= 4 &&
eol[i-1] == '\n' && eol[i-2] == '\r' && eol[i-3] == '}') {
i -= 4;
while (i > 0 && eol[i] != '{' && isdigit((int) eol[i])) {
i--;
}
if (eol[i] == '{') {
islit = 1;
litlen = atoi(eol + i + 1);
}
}
if (islit) {
while (litlen > 0) {
int j = (litlen > sizeof(buf) ? sizeof(buf) : litlen);
j = prot_read(s->in, buf, j);
if(!j) {
return -1;
}
if (!last || include_tag) prot_write(imapd_out, buf, j);
litlen -= j;
}
eol[0] = '\0';
cont = 1;
continue;
}
}
cont = 0;
} while (!last || cont);
return r;
}
int pipe_until_tag(struct backend *s, const char *tag, int force_notfatal)
{
return pipe_response(s, tag, 0, force_notfatal);
}
int pipe_including_tag(struct backend *s, const char *tag, int force_notfatal) {
int r;
r = pipe_response(s, tag, 1, force_notfatal);
if (r == PROXY_NOCONNECTION) {
prot_printf(imapd_out, "%s NO %s\r\n", tag,
error_message(IMAP_SERVER_UNAVAILABLE));
}
return r;
}
static int pipe_to_end_of_response(struct backend *s, int force_notfatal)
{
return pipe_response(s, NULL, 0, force_notfatal);
}
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';
for (;;) {
if (!prot_fgets(buf, sizeof(buf), imapd_in)) {
return -1;
}
sl = strlen(buf);
if (sl == (sizeof(buf) - 1) && buf[sl-1] != '\n') {
strcpy(eol, buf + sl - 64);
prot_write(s->out, buf, sl - 64);
continue;
} else {
int i, nonsynch = 0, islit = 0, litlen = 0;
if (sl < 64) {
strcat(eol, buf);
} else {
prot_printf(s->out, "%s", eol);
prot_write(s->out, buf, sl - 64);
strcpy(eol, buf + sl - 64);
}
i = strlen(eol);
if (i >= 4 &&
eol[i-1] == '\n' && eol[i-2] == '\r' && eol[i-3] == '}') {
i -= 4;
if (eol[i] == '+') {
nonsynch = 1;
i--;
}
while (i > 0 && eol[i] != '{' && isdigit((int) 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);
prot_printf(s->out, "+}\r\n");
} else {
prot_write(s->out, eol, strlen(eol));
prot_fgets(buf, sizeof(buf), s->in);
prot_write(imapd_out, buf, strlen(buf));
if (buf[0] != '+' && buf[1] != ' ') {
return 1;
}
}
while (litlen > 0) {
int j = (litlen > sizeof(buf) ? sizeof(buf) : litlen);
j = prot_read(imapd_in, buf, j);
if(!j) {
return -1;
}
prot_write(s->out, buf, j);
litlen -= j;
}
eol[0] = '\0';
continue;
} else {
prot_write(s->out, eol, strlen(eol));
return 0;
}
}
}
}
int pipe_lsub(struct backend *s, const char *tag,
int force_notfatal, const char *resp)
{
int taglen = strlen(tag);
int c;
int r = PROXY_OK;
int exist_r;
char mailboxname[MAX_MAILBOX_PATH + 1];
static struct buf tagb, cmd, sep, name;
int cur_flags_size = 64;
char *flags = xmalloc(cur_flags_size);
const char *end_strip_flags[] = { " \\NonExistent)", "\\NonExistent)",
NULL };
const char *mid_strip_flags[] = { "\\NonExistent ",
NULL
};
assert(s);
assert(s->timeout);
s->timeout->mark = time(NULL) + IDLE_TIMEOUT;
while(1) {
c = getword(s->in, &tagb);
if(c == EOF) {
if(s == backend_current && !force_notfatal)
fatal("Lost connection to selected backend", EC_UNAVAILABLE);
proxy_downserver(s);
free(flags);
return PROXY_NOCONNECTION;
}
if(!strncmp(tag, tagb.s, taglen)) {
char buf[2048];
if(!prot_fgets(buf, sizeof(buf), s->in)) {
if(s == backend_current && !force_notfatal)
fatal("Lost connection to selected backend",
EC_UNAVAILABLE);
proxy_downserver(s);
free(flags);
return PROXY_NOCONNECTION;
}
if (s->last_result.alloc == 0) {
s->last_result.alloc = sizeof(buf);
s->last_result.s = xmalloc(s->last_result.alloc+1);
}
strcpy(s->last_result.s, buf);
s->last_result.len = strlen(buf);
switch (buf[0]) {
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:
if(s == backend_current && !force_notfatal)
fatal("Lost connection to selected backend",
EC_UNAVAILABLE);
proxy_downserver(s);
r = PROXY_NOCONNECTION;
break;
}
break;
}
c = getword(s->in, &cmd);
if(c == EOF) {
if(s == backend_current && !force_notfatal)
fatal("Lost connection to selected backend", EC_UNAVAILABLE);
proxy_downserver(s);
free(flags);
return PROXY_NOCONNECTION;
}
if(strncasecmp("LSUB", cmd.s, 4) && strncasecmp("LIST", cmd.s, 4)) {
prot_printf(imapd_out, "%s %s ", tagb.s, cmd.s);
r = pipe_to_end_of_response(s, force_notfatal);
if(r != PROXY_OK) {
free(flags);
return r;
}
} else {
int i = 0;
char *p;
c = prot_getc(s->in);
while(c != ')' && c != EOF) {
flags[i++] = c;
if(i == cur_flags_size) {
cur_flags_size *= 2;
flags = xrealloc(flags, cur_flags_size);
}
c = prot_getc(s->in);
}
if(c != EOF) {
flags[i++] = ')';
if(i == cur_flags_size) {
cur_flags_size *= 2;
flags = xrealloc(flags, cur_flags_size);
}
flags[i] = '\0';
c = prot_getc(s->in);
}
if(c != ' ') {
if(s == backend_current && !force_notfatal)
fatal("Bad LSUB response from selected backend",
EC_UNAVAILABLE);
proxy_downserver(s);
free(flags);
return PROXY_NOCONNECTION;
}
for(i=0; end_strip_flags[i]; i++) {
p = strstr(flags, end_strip_flags[i]);
if(p) {
*p = ')';
*(p+1) = '\0';
}
}
for(i=0; mid_strip_flags[i]; i++) {
int mid_strip_len = strlen(mid_strip_flags[i]);
p = strstr(flags, mid_strip_flags[i]);
while(p) {
strcpy(p, p + mid_strip_len);
p = strstr(flags, mid_strip_flags[i]);
}
}
c = getastring(s->in, s->out, &sep);
if(c != ' ') {
if(s == backend_current && !force_notfatal)
fatal("Bad LSUB response from selected backend",
EC_UNAVAILABLE);
proxy_downserver(s);
free(flags);
return PROXY_NOCONNECTION;
}
c = getastring(s->in, s->out, &name);
if(c == '\r') c = prot_getc(s->in);
if(c != '\n') {
if(s == backend_current && !force_notfatal)
fatal("Bad LSUB response from selected backend",
EC_UNAVAILABLE);
proxy_downserver(s);
free(flags);
return PROXY_NOCONNECTION;
}
exist_r = 1;
r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace,
name.s,
imapd_userid,
mailboxname);
if (!r) {
int mbtype;
exist_r = mboxlist_detail(mailboxname, &mbtype,
NULL, NULL, NULL, NULL, NULL);
if(!exist_r && (mbtype & MBTYPE_RESERVE))
exist_r = IMAP_MAILBOX_RESERVED;
} else {
syslog(LOG_ERR, "could not convert %s to internal form",
name.s);
continue;
}
if(resp[0] == 'L') {
if(!exist_r) {
prot_printf(imapd_out, "* %s %s \"%s\" ",
resp, flags, sep.s);
} else {
prot_printf(imapd_out, "* %s (\\Noselect) \"%s\" ",
resp, sep.s);
}
printstring(name.s);
prot_printf(imapd_out, "\r\n");
} else if(resp[0] == 'M' && !exist_r) {
prot_printf(imapd_out, "* %s ", resp);
printastring(name.s);
prot_printf(imapd_out, "\r\n");
}
}
}
free(flags);
return r;
}
static int chomp(struct protstream *p, char *s)
{
int c = prot_getc(p);
while (*s) {
if (tolower(c) != tolower(*s)) { break; }
s++;
c = prot_getc(p);
}
if (*s) {
if (c != EOF) prot_ungetc(c, p);
c = EOF;
}
return c;
}
#define BUFGROWSIZE 100
static char *grab(struct protstream *p, char end)
{
int alloc = BUFGROWSIZE, cur = 0;
int c = -1;
char *ret = (char *) xmalloc(alloc);
ret[0] = '\0';
while ((c = prot_getc(p)) != end) {
if (c == EOF) break;
if (cur == alloc - 1) {
alloc += BUFGROWSIZE;
ret = xrealloc(ret, alloc);
}
ret[cur++] = c;
}
if (cur) ret[cur] = '\0';
return ret;
}
static char *editflags(char *flags)
{
char *p;
p = flags;
while ((p = strchr(p, '\\')) != NULL) {
if (!strncasecmp(p + 1, "recent", 6)) {
if (p[7] == ' ') {
char *q;
q = p + 8;
while (*q) {
*p++ = *q++;
}
*p = '\0';
} else if (p[7] == '\0') {
*p = '\0';
} else {
p++;
}
} else {
p++;
}
}
return flags;
}
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;
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;
for (;;) {
unsigned int seqno = 0, uidno = 0;
char *flags = NULL, *idate = NULL;
c = prot_getc(backend_current->in);
if (c != '*') break;
c = prot_getc(backend_current->in);
if (c != ' ') { c = EOF; break; }
seqno = 0;
while (isdigit(c = prot_getc(backend_current->in))) {
seqno *= 10;
seqno += c - '0';
}
if (seqno == 0 || c != ' ') {
c = EOF; break;
}
c = chomp(backend_current->in, "fetch (");
if (c == EOF) {
c = chomp(backend_current->in, "exists\r");
if (c == '\n') {
prot_printf(imapd_out, "* %d EXISTS\r\n", seqno);
continue;
}
}
if (c == EOF) {
c = chomp(backend_current->in, "punge\r");
if (c == '\n') {
prot_printf(imapd_out, "* %d EXPUNGE\r\n", seqno);
continue;
}
}
if (c == EOF) {
c = chomp(backend_current->in, "recent\r");
if (c == '\n') {
prot_printf(imapd_out, "* %d RECENT\r\n", seqno);
continue;
}
}
if (c == EOF) break;
for (;;) {
switch (c) {
case 'f': case 'F':
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':
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':
c = chomp(backend_current->in, "id");
if (c != ' ') { c = EOF; }
else {
uidno = 0;
while (isdigit(c = prot_getc(backend_current->in))) {
uidno *= 10;
uidno += c - '0';
}
}
break;
default:
c = EOF;
break;
}
if (c == ' ') { c = prot_getc(backend_current->in); }
else if (c == ')') break;
else { c = EOF; break; }
}
if (c == ')') c = prot_getc(backend_current->in);
if (c == '\r') c = prot_getc(backend_current->in);
if (c != '\n') { c = EOF; break; }
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");
continue;
}
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);
pipe_until_tag(backend_current, tag, 0);
}
if (c == EOF) {
fatal("Lost connection to selected backend", EC_UNAVAILABLE);
}
prot_printf(s->out, "%s Append {%d+}\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 (;;) {
unsigned int seqno = 0, uidno = 0;
c = prot_getc(backend_current->in);
if (c != '*') break;
c = prot_getc(backend_current->in);
if (c != ' ') { c = EOF; break; }
seqno = 0;
while (isdigit(c = prot_getc(backend_current->in))) {
seqno *= 10;
seqno += c - '0';
}
if (seqno == 0 || c != ' ') {
c = EOF; break;
}
c = chomp(backend_current->in, "fetch (");
if (c == EOF) {
c = chomp(backend_current->in, "exists\r");
if (c == '\n') {
prot_printf(imapd_out, "* %d EXISTS\r\n", seqno);
continue;
}
}
if (c == EOF) {
c = chomp(backend_current->in, "punge\r");
if (c == '\n') {
prot_printf(imapd_out, "* %d EXPUNGE\r\n", seqno);
continue;
}
}
if (c == EOF) {
c = chomp(backend_current->in, "recent\r");
if (c == '\n') {
prot_printf(imapd_out, "* %d RECENT\r\n", seqno);
continue;
}
}
if (c == EOF) {
break;
}
p = head;
while (p->next && seqno != p->next->seqno) p = p->next;
if (!p->next) break;
q = p->next;
p->next = q->next;
for (;;) {
int sz = 0;
switch (c) {
case 'u': case 'U':
c = chomp(backend_current->in, "id");
if (c != ' ') { c = EOF; }
else {
uidno = 0;
while (isdigit(c = prot_getc(backend_current->in))) {
uidno *= 10;
uidno += c - '0';
}
}
break;
case 'r': case 'R':
c = chomp(backend_current->in, "fc822");
if (c == ' ') c = prot_getc(backend_current->in);
if (c != '{') c = EOF;
else {
sz = 0;
while (isdigit(c = prot_getc(backend_current->in))) {
sz *= 10;
sz += c - '0';
}
}
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) {
prot_printf(s->out, " (%s) \"%s\" {%d+}\r\n",
q->flags, q->idate, sz);
while (sz) {
char buf[2048];
int j = (sz > sizeof(buf) ? 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;
default:
c = EOF;
break;
}
if (c == ' ') { c = prot_getc(backend_current->in); }
else if (c == ')') break;
else { c = EOF; break; }
}
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->idate);
free(q->flags);
free(q);
}
if (c != EOF) {
char *appenduid, *b;
int res;
prot_ungetc(c, backend_current->in);
assert(head->next == NULL);
prot_printf(s->out, "\r\n");
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, '[');
appenduid += strlen("[appenduid ");
b = strchr(appenduid, ']');
*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,
error_message(IMAP_OK_COMPLETED));
}
} else {
prot_printf(imapd_out, "%s %s", tag, s->last_result.s);
}
} else {
prot_printf(s->out, " {0}\r\n");
pipe_until_tag(backend_current, mytag, 0);
pipe_until_tag(s, tag, 0);
prot_printf(imapd_out, "%s NO inter-server COPY failed\r\n", tag);
}
while (head) {
p = head;
head = head->next;
if (p->idate) free(p->idate);
if (p->flags) free(p->flags);
free(p);
}
}
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 long uidvalidity = 0;
*size = 0;
*parseerr = NULL;
proxy_gentag(mytag, sizeof(mytag));
prot_printf(s->out, "%s Examine {%d+}\r\n%s\r\n",
mytag, strlen(url->mailbox), url->mailbox);
for (;;) {
c = prot_getc(s->in);
if (c != '*') break;
c = prot_getc(s->in);
if (c != ' ') { c = EOF; break; }
c = chomp(s->in, "ok [uidvalidity");
if (c == EOF) {
eatline(s->in, c);
continue;
}
while (isdigit(c = prot_getc(s->in))) {
uidvalidity *= 10;
uidvalidity += c - '0';
}
if (c != ']') { c = EOF; break; }
eatline(s->in, c);
}
if (c != EOF) {
prot_ungetc(c, s->in);
eatline(s->in, c);
}
if (c == EOF) {
fatal("Lost connection to backend", EC_UNAVAILABLE);
}
if (url->uidvalidity && (uidvalidity != url->uidvalidity)) {
*parseerr = "Uidvalidity of mailbox has changed";
r = IMAP_BADURL;
goto unselect;
}
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 (;;) {
unsigned long seqno;
next_resp:
c = prot_getc(s->in);
if (c != '*') break;
c = prot_getc(s->in);
if (c != ' ') { c = EOF; break; }
seqno = 0;
while (isdigit(c = prot_getc(s->in))) {
seqno *= 10;
seqno += c - '0';
}
if (seqno == 0 || c != ' ') {
c = EOF; break;
}
c = chomp(s->in, "fetch (");
if (c == EOF) {
eatline(s->in, c);
continue;
}
for (;;) {
unsigned long uid, sz;
switch (c) {
case 'u': case 'U':
c = chomp(s->in, "id");
if (c != ' ') { c = EOF; }
else {
uid = 0;
while (isdigit(c = prot_getc(s->in))) {
uid *= 10;
uid += c - '0';
}
if (uid != url->uid) {
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 == '{') {
sz = 0;
while (isdigit(c = prot_getc(s->in))) {
sz *= 10;
sz += c - '0';
}
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) {
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;
default:
eatline(s->in, c);
goto next_resp;
}
if (c == ' ') { c = prot_getc(s->in); }
else if (c == ')') break;
else { c = EOF; break; }
}
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);
eatline(s->in, c);
}
if (c == EOF) {
fatal("Lost connection to backend", EC_UNAVAILABLE);
}
unselect:
proxy_gentag(mytag, sizeof(mytag));
prot_printf(s->out, "%s Unselect\r\n", mytag);
for (;;) {
c = prot_getc(s->in);
if (c != '*') break;
c = prot_getc(s->in);
if (c != ' ') { c = EOF; break; }
eatline(s->in, c);
}
if (c != EOF) {
prot_ungetc(c, s->in);
eatline(s->in, c);
}
if (c == EOF) {
fatal("Lost connection to backend", EC_UNAVAILABLE);
}
if (!r && !found) {
r = IMAP_BADURL;
*parseerr = "No such message in mailbox";
}
return r;
}
int annotate_fetch_proxy(const char *server, const char *mbox_pat,
struct strlist *entry_pat,
struct strlist *attribute_pat)
{
struct backend *be;
struct strlist *l;
char mytag[128];
assert(server && mbox_pat && entry_pat && attribute_pat);
be = proxy_findserver(server, &protocol[PROTOCOL_IMAP],
proxy_userid, &backend_cached,
&backend_current, &backend_inbox, imapd_in);
if (!be) return IMAP_SERVER_UNAVAILABLE;
proxy_gentag(mytag, sizeof(mytag));
prot_printf(be->out, "%s GETANNOTATION \"%s\" (", mytag, mbox_pat);
for (l = entry_pat; l; l = l->next) {
prot_printf(be->out, "\"%s\"%s", l->s, l->next ? " " : "");
}
prot_printf(be->out, ") (");
for (l = attribute_pat; l; l = l->next) {
prot_printf(be->out, "\"%s\"%s", l->s, l->next ? " " : "");
}
prot_printf(be->out, ")\r\n");
prot_flush(be->out);
pipe_until_tag(be, mytag, 0);
return 0;
}
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, &protocol[PROTOCOL_IMAP],
proxy_userid, &backend_cached,
&backend_current, &backend_inbox, imapd_in);
if (!be) return IMAP_SERVER_UNAVAILABLE;
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\" \"%s\"%s", av->attrib, av->value,
av->next ? " " : "");
}
prot_printf(be->out, ")");
if (e->next) prot_printf(be->out, " ");
}
prot_printf(be->out, ")\r\n");
prot_flush(be->out);
pipe_until_tag(be, mytag, 0);
return 0;
}