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