1799
214
/* Create telemetry log */
1800
215
imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
1802
diff -urNad cyrus-imapd-2.2.13/imap/imapd.c.orig /tmp/dpep.bMlzdR/cyrus-imapd-2.2.13/imap/imapd.c.orig
1803
--- cyrus-imapd-2.2.13/imap/imapd.c.orig 1970-01-01 01:00:00.000000000 +0100
1804
+++ /tmp/dpep.bMlzdR/cyrus-imapd-2.2.13/imap/imapd.c.orig 2006-04-18 20:39:35.700095168 +0200
1807
+ * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
1809
+ * Redistribution and use in source and binary forms, with or without
1810
+ * modification, are permitted provided that the following conditions
1813
+ * 1. Redistributions of source code must retain the above copyright
1814
+ * notice, this list of conditions and the following disclaimer.
1816
+ * 2. Redistributions in binary form must reproduce the above copyright
1817
+ * notice, this list of conditions and the following disclaimer in
1818
+ * the documentation and/or other materials provided with the
1821
+ * 3. The name "Carnegie Mellon University" must not be used to
1822
+ * endorse or promote products derived from this software without
1823
+ * prior written permission. For permission or any other legal
1824
+ * details, please contact
1825
+ * Office of Technology Transfer
1826
+ * Carnegie Mellon University
1827
+ * 5000 Forbes Avenue
1828
+ * Pittsburgh, PA 15213-3890
1829
+ * (412) 268-4387, fax: (412) 268-7395
1830
+ * tech-transfer@andrew.cmu.edu
1832
+ * 4. Redistributions of any form whatsoever must retain the following
1833
+ * "This product includes software developed by Computing Services
1835
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
1837
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
1838
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
1839
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
1840
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1841
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
1842
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
1843
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1846
+/* $Id: imapd.c,v 1.498 2006/01/13 22:06:28 murch Exp $ */
1848
+#include <config.h>
1850
+#ifdef HAVE_UNISTD_H
1851
+#include <unistd.h>
1854
+#include <string.h>
1857
+#include <signal.h>
1859
+#include <sys/types.h>
1860
+#include <sys/param.h>
1861
+#include <sys/stat.h>
1862
+#include <syslog.h>
1864
+#include <sys/socket.h>
1865
+#include <sys/wait.h>
1866
+#include <netinet/in.h>
1867
+#include <arpa/inet.h>
1869
+#include <sasl/sasl.h>
1872
+#include "annotate.h"
1873
+#include "append.h"
1875
+#include "backend.h"
1876
+#include "charset.h"
1877
+#include "exitcodes.h"
1879
+#include "global.h"
1880
+#include "imap_err.h"
1882
+#include "imapurl.h"
1883
+#include "imparse.h"
1884
+#include "iptostring.h"
1885
+#include "mailbox.h"
1886
+#include "message.h"
1887
+#include "mboxname.h"
1888
+#include "mboxlist.h"
1889
+#include "mbdump.h"
1890
+#include "mkgmtime.h"
1891
+#include "mupdate-client.h"
1893
+#include "telemetry.h"
1897
+#include "version.h"
1898
+#include "xmalloc.h"
1900
+#include "pushstats.h" /* SNMP interface */
1902
+extern void seen_done(void);
1905
+extern char *optarg;
1908
+const int config_need_data = CONFIG_NEED_PARTITION_DATA;
1910
+static char shutdownfilename[1024];
1911
+static int imaps = 0;
1912
+static sasl_ssf_t extprops_ssf = 0;
1914
+/* per-user/session state */
1915
+struct protstream *imapd_out = NULL;
1916
+struct protstream *imapd_in = NULL;
1917
+static char imapd_clienthost[NI_MAXHOST*2+1] = "[local]";
1918
+static int imapd_logfd = -1;
1919
+char *imapd_userid;
1920
+static char *imapd_magicplus = NULL;
1921
+struct auth_state *imapd_authstate = 0;
1922
+static int imapd_userisadmin = 0;
1923
+static int imapd_userisproxyadmin = 0;
1924
+static sasl_conn_t *imapd_saslconn; /* the sasl connection context */
1925
+static int imapd_starttls_done = 0; /* have we done a successful starttls? */
1926
+const char *plaintextloginalert = NULL;
1928
+/* our tls connection, if any */
1929
+static SSL *tls_conn = NULL;
1930
+#endif /* HAVE_SSL */
1932
+/* stage(s) for APPEND */
1933
+struct appendstage {
1934
+ struct stagemsg *stage;
1936
+ int nflags, flagalloc;
1937
+ time_t internaldate;
1939
+unsigned long numstage = 0;
1941
+/* the sasl proxy policy context */
1942
+static struct proxy_context imapd_proxyctx = {
1943
+ 1, 1, &imapd_authstate, &imapd_userisadmin, &imapd_userisproxyadmin
1946
+/* current sub-user state */
1947
+static struct mailbox mboxstruct;
1948
+static struct mailbox *imapd_mailbox;
1949
+int imapd_exists = -1;
1951
+/* current namespace */
1952
+static struct namespace imapd_namespace;
1954
+static const char *monthname[] = {
1955
+ "jan", "feb", "mar", "apr", "may", "jun",
1956
+ "jul", "aug", "sep", "oct", "nov", "dec"
1959
+static const int max_monthdays[] = {
1960
+ 31, 29, 31, 30, 31, 30,
1961
+ 31, 31, 30, 31, 30, 31
1964
+void motd_file(int fd);
1965
+void shut_down(int code);
1966
+void fatal(const char *s, int code);
1968
+void cmdloop(void);
1969
+void cmd_login(char *tag, char *user);
1970
+void cmd_authenticate(char *tag, char *authtype, char *resp);
1971
+void cmd_noop(char *tag, char *cmd);
1972
+void cmd_capability(char *tag);
1973
+void cmd_append(char *tag, char *name);
1974
+void cmd_select(char *tag, char *cmd, char *name);
1975
+void cmd_close(char *tag);
1976
+void cmd_fetch(char *tag, char *sequence, int usinguid);
1977
+void cmd_partial(const char *tag, const char *msgno, char *data,
1978
+ const char *start, const char *count);
1979
+void cmd_store(char *tag, char *sequence, char *operation, int usinguid);
1980
+void cmd_search(char *tag, int usinguid);
1981
+void cmd_sort(char *tag, int usinguid);
1982
+void cmd_thread(char *tag, int usinguid);
1983
+void cmd_copy(char *tag, char *sequence, char *name, int usinguid);
1984
+void cmd_expunge(char *tag, char *sequence);
1985
+void cmd_create(char *tag, char *name, char *partition, int localonly);
1986
+void cmd_delete(char *tag, char *name, int localonly);
1987
+void cmd_dump(char *tag, char *name, int uid_start);
1988
+void cmd_undump(char *tag, char *name);
1989
+void cmd_xfer(char *tag, char *name, char *toserver, char *topart);
1990
+void cmd_rename(const char *tag, char *oldname,
1991
+ char *newname, char *partition);
1992
+void cmd_reconstruct(const char *tag, const char *name, int recursive);
1993
+void cmd_find(char *tag, char *namespace, char *pattern);
1994
+void cmd_list(char *tag, int subscribed, char *reference, char *pattern);
1995
+void cmd_changesub(char *tag, char *namespace, char *name, int add);
1996
+void cmd_getacl(const char *tag, const char *name);
1997
+void cmd_listrights(char *tag, char *name, char *identifier);
1998
+void cmd_myrights(const char *tag, const char *name);
1999
+void cmd_setacl(const char *tag, const char *name,
2000
+ const char *identifier, const char *rights);
2001
+void cmd_getquota(const char *tag, const char *name);
2002
+void cmd_getquotaroot(const char *tag, const char *name);
2003
+void cmd_setquota(const char *tag, const char *quotaroot);
2004
+void cmd_status(char *tag, char *name);
2005
+void cmd_getuids(char *tag, char *startuid);
2006
+void cmd_unselect(char* tag);
2007
+void cmd_namespace(char* tag);
2008
+void cmd_mupdatepush(char *tag, char *name);
2009
+void cmd_id(char* tag);
2010
+extern void id_getcmdline(int argc, char **argv);
2011
+extern void id_response(struct protstream *pout);
2013
+void cmd_idle(char* tag);
2014
+void idle_update(idle_flags_t flags);
2016
+void cmd_starttls(char *tag, int imaps);
2018
+#ifdef ENABLE_X_NETSCAPE_HACK
2019
+void cmd_netscrape(char* tag);
2022
+void cmd_getannotation(char* tag, char *mboxpat);
2023
+void cmd_setannotation(char* tag, char *mboxpat);
2025
+int getannotatefetchdata(char *tag,
2026
+ struct strlist **entries, struct strlist **attribs);
2027
+int getannotatestoredata(char *tag, struct entryattlist **entryatts);
2029
+void annotate_response(struct entryattlist *l);
2031
+#ifdef ENABLE_LISTEXT
2032
+int getlistopts(char *tag, int *listopts);
2035
+int getsearchprogram(char *tag, struct searchargs *searchargs,
2036
+ int *charset, int parsecharset);
2037
+int getsearchcriteria(char *tag, struct searchargs *searchargs,
2038
+ int *charset, int parsecharset);
2039
+int getsearchdate(time_t *start, time_t *end);
2040
+int getsortcriteria(char *tag, struct sortcrit **sortcrit);
2041
+int getdatetime(time_t *date);
2043
+void printstring(const char *s);
2044
+void printastring(const char *s);
2046
+void appendfieldlist(struct fieldlist **l, char *section,
2047
+ struct strlist *fields, char *trail,
2048
+ void *d, size_t size);
2049
+void freefieldlist(struct fieldlist *l);
2050
+void freestrlist(struct strlist *l);
2051
+void appendsearchargs(struct searchargs *s, struct searchargs *s1,
2052
+ struct searchargs *s2);
2053
+void freesearchargs(struct searchargs *s);
2054
+static void freesortcrit(struct sortcrit *s);
2056
+static int mailboxdata(char *name, int matchlen, int maycreate, void *rock);
2057
+static int listdata(char *name, int matchlen, int maycreate, void *rock);
2058
+static void mstringdata(char *cmd, char *name, int matchlen, int maycreate,
2061
+extern void setproctitle_init(int argc, char **argv, char **envp);
2062
+extern int proc_register(const char *progname, const char *clienthost,
2063
+ const char *userid, const char *mailbox);
2064
+extern void proc_cleanup(void);
2066
+extern int saslserver(sasl_conn_t *conn, const char *mech,
2067
+ const char *init_resp, const char *resp_prefix,
2068
+ const char *continuation, const char *empty_resp,
2069
+ struct protstream *pin, struct protstream *pout,
2070
+ int *sasl_result, char **success_data);
2072
+/* Enable the resetting of a sasl_conn_t */
2073
+static int reset_saslconn(sasl_conn_t **conn);
2077
+ char *ipremoteport;
2078
+ char *iplocalport;
2081
+} saslprops = {NULL,NULL,0,NULL};
2083
+static int imapd_canon_user(sasl_conn_t *conn, void *context,
2084
+ const char *user, unsigned ulen,
2085
+ unsigned flags, const char *user_realm,
2086
+ char *out, unsigned out_max, unsigned *out_ulen)
2088
+ char userbuf[MAX_MAILBOX_NAME+1], *p;
2092
+ if (!ulen) ulen = strlen(user);
2094
+ if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) {
2095
+ /* make a working copy of the auth[z]id */
2096
+ if (ulen > MAX_MAILBOX_NAME) {
2097
+ sasl_seterror(conn, 0, "buffer overflow while canonicalizing");
2098
+ return SASL_BUFOVER;
2100
+ memcpy(userbuf, user, ulen);
2101
+ userbuf[ulen] = '\0';
2104
+ /* See if we're using the magic plus
2105
+ (currently we don't support anything after '+') */
2106
+ if ((p = strchr(userbuf, '+')) &&
2107
+ (n = config_virtdomains ? strcspn(p, "@") : strlen(p)) == 1) {
2109
+ if (flags & SASL_CU_AUTHZID) {
2110
+ /* make a copy of the magic plus */
2111
+ if (imapd_magicplus) free(imapd_magicplus);
2112
+ imapd_magicplus = xstrndup(p, n);
2115
+ /* strip the magic plus from the auth[z]id */
2116
+ memmove(p, p+n, strlen(p+n)+1);
2121
+ r = mysasl_canon_user(conn, context, user, ulen, flags, user_realm,
2122
+ out, out_max, out_ulen);
2124
+ if (!r && imapd_magicplus && flags == SASL_CU_AUTHZID) {
2125
+ /* If we're only doing the authzid, put back the magic plus
2126
+ in case its used in the challenge/response calculation */
2127
+ n = strlen(imapd_magicplus);
2128
+ if (*out_ulen + n > out_max) {
2129
+ sasl_seterror(conn, 0, "buffer overflow while canonicalizing");
2133
+ p = (config_virtdomains && (p = strchr(out, '@'))) ?
2134
+ p : out + *out_ulen;
2135
+ memmove(p+n, p, strlen(p)+1);
2136
+ memcpy(p, imapd_magicplus, n);
2144
+static int imapd_proxy_policy(sasl_conn_t *conn,
2146
+ const char *requested_user, unsigned rlen,
2147
+ const char *auth_identity, unsigned alen,
2148
+ const char *def_realm,
2150
+ struct propctx *propctx)
2152
+ if (config_getswitch(IMAPOPT_IMAPMAGICPLUS)) {
2153
+ char userbuf[MAX_MAILBOX_NAME+1], *p;
2156
+ /* make a working copy of the authzid */
2157
+ if (!rlen) rlen = strlen(requested_user);
2158
+ if (rlen > MAX_MAILBOX_NAME) {
2159
+ sasl_seterror(conn, 0, "buffer overflow while proxying");
2160
+ return SASL_BUFOVER;
2162
+ memcpy(userbuf, requested_user, rlen);
2163
+ userbuf[rlen] = '\0';
2164
+ requested_user = userbuf;
2166
+ /* See if we're using the magic plus */
2167
+ if ((p = strchr(userbuf, '+'))) {
2168
+ n = config_virtdomains ? strcspn(p, "@") : strlen(p);
2170
+ /* strip the magic plus from the authzid */
2171
+ memmove(p, p+n, strlen(p+n)+1);
2176
+ return mysasl_proxy_policy(conn, context, requested_user, rlen,
2177
+ auth_identity, alen, def_realm, urlen, propctx);
2180
+static const struct sasl_callback mysasl_cb[] = {
2181
+ { SASL_CB_GETOPT, &mysasl_config, NULL },
2182
+ { SASL_CB_PROXY_POLICY, &imapd_proxy_policy, (void*) &imapd_proxyctx },
2183
+ { SASL_CB_CANON_USER, &imapd_canon_user, NULL },
2184
+ { SASL_CB_LIST_END, NULL, NULL }
2187
+/* imapd_refer() issues a referral to the client. */
2188
+static void imapd_refer(const char *tag,
2189
+ const char *server,
2190
+ const char *mailbox)
2192
+ char url[MAX_MAILBOX_PATH+1];
2194
+ if(!strcmp(imapd_userid, "anonymous")) {
2195
+ imapurl_toURL(url, server, mailbox, "ANONYMOUS");
2197
+ imapurl_toURL(url, server, mailbox, "*");
2200
+ prot_printf(imapd_out, "%s NO [REFERRAL %s] Remote mailbox.\r\n",
2204
+/* wrapper for mboxlist_lookup that will force a referral if we are remote
2205
+ * returns IMAP_SERVER_UNAVAILABLE if we don't have a place to send the client
2206
+ * (that'd be a bug).
2207
+ * returns IMAP_MAILBOX_MOVED if we referred the client */
2208
+/* ext_name is the external name of the mailbox */
2209
+/* you can avoid referring the client by setting tag or ext_name to NULL. */
2210
+static int mlookup(const char *tag, const char *ext_name,
2211
+ const char *name, int *flags, char **pathp, char **partp,
2212
+ char **aclp, struct txn **tid)
2215
+ char *remote, *acl;
2217
+ r = mboxlist_detail(name, &mbtype, pathp, &remote, &acl, tid);
2219
+ if(partp) *partp = remote;
2220
+ if(aclp) *aclp = acl;
2221
+ if(flags) *flags = mbtype;
2224
+ if(mbtype & MBTYPE_RESERVE) return IMAP_MAILBOX_RESERVED;
2226
+ if(mbtype & MBTYPE_MOVING) {
2227
+ /* do we have rights on the mailbox? */
2228
+ if(!imapd_userisadmin &&
2229
+ (!acl || !(cyrus_acl_myrights(imapd_authstate,acl) & ACL_LOOKUP))) {
2230
+ r = IMAP_MAILBOX_NONEXISTENT;
2231
+ } else if(tag && ext_name && remote && *remote) {
2234
+ c = strchr(remote, '!');
2236
+ imapd_refer(tag, remote, ext_name);
2237
+ r = IMAP_MAILBOX_MOVED;
2238
+ } else if(config_mupdate_server) {
2239
+ r = IMAP_SERVER_UNAVAILABLE;
2241
+ r = IMAP_MAILBOX_NOTSUPPORTED;
2248
+static void imapd_reset(void)
2252
+ if (imapd_mailbox) {
2253
+ index_closemailbox(imapd_mailbox);
2254
+ mailbox_close(imapd_mailbox);
2255
+ imapd_mailbox = 0;
2259
+ /* Flush the incoming buffer */
2260
+ prot_NONBLOCK(imapd_in);
2261
+ prot_fill(imapd_in);
2263
+ prot_free(imapd_in);
2267
+ /* Flush the outgoing buffer */
2268
+ prot_flush(imapd_out);
2270
+ prot_free(imapd_out);
2273
+ imapd_in = imapd_out = NULL;
2277
+ if (tls_reset_servertls(&tls_conn) == -1) {
2278
+ fatal("tls_reset() failed", EC_TEMPFAIL);
2284
+ cyrus_reset_stdio();
2286
+ strcpy(imapd_clienthost, "[local]");
2287
+ if (imapd_logfd != -1) {
2288
+ close(imapd_logfd);
2291
+ if (imapd_userid != NULL) {
2292
+ free(imapd_userid);
2293
+ imapd_userid = NULL;
2295
+ if (imapd_magicplus != NULL) {
2296
+ free(imapd_magicplus);
2297
+ imapd_magicplus = NULL;
2299
+ if (imapd_authstate) {
2300
+ auth_freestate(imapd_authstate);
2301
+ imapd_authstate = NULL;
2303
+ imapd_userisadmin = 0;
2304
+ imapd_userisproxyadmin = 0;
2305
+ if (imapd_saslconn) {
2306
+ sasl_dispose(&imapd_saslconn);
2307
+ imapd_saslconn = NULL;
2309
+ imapd_starttls_done = 0;
2310
+ plaintextloginalert = NULL;
2312
+ if(saslprops.iplocalport) {
2313
+ free(saslprops.iplocalport);
2314
+ saslprops.iplocalport = NULL;
2316
+ if(saslprops.ipremoteport) {
2317
+ free(saslprops.ipremoteport);
2318
+ saslprops.ipremoteport = NULL;
2320
+ if(saslprops.authid) {
2321
+ free(saslprops.authid);
2322
+ saslprops.authid = NULL;
2324
+ saslprops.ssf = 0;
2326
+ imapd_exists = -1;
2330
+ * run once when process is forked;
2331
+ * MUST NOT exit directly; must return with non-zero error code
2333
+int service_init(int argc, char **argv, char **envp)
2338
+ if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE);
2339
+ setproctitle_init(argc, argv, envp);
2341
+ /* set signal handlers */
2342
+ signals_set_shutdown(&shut_down);
2343
+ signal(SIGPIPE, SIG_IGN);
2345
+ /* load the SASL plugins */
2346
+ global_sasl_init(1, 1, mysasl_cb);
2348
+ ret = snprintf(shutdownfilename, sizeof(shutdownfilename),
2349
+ "%s/msg/shutdown", config_dir);
2351
+ if(ret < 0 || ret >= sizeof(shutdownfilename)) {
2352
+ fatal("shutdownfilename buffer too small (configdirectory too long)",
2356
+ /* open the mboxlist, we'll need it for real work */
2358
+ mboxlist_open(NULL);
2359
+ mailbox_initialize();
2361
+ /* open the quota db, we'll need it for real work */
2363
+ quotadb_open(NULL);
2365
+ /* setup for sending IMAP IDLE notifications */
2368
+ /* create connection to the SNMP listener, if available. */
2369
+ snmp_connect(); /* ignore return code */
2370
+ snmp_set_str(SERVER_NAME_VERSION,CYRUS_VERSION);
2372
+ while ((opt = getopt(argc, argv, "sp:")) != EOF) {
2374
+ case 's': /* imaps (do starttls right away) */
2376
+ if (!tls_enabled()) {
2377
+ syslog(LOG_ERR, "imaps: required OpenSSL options not present");
2378
+ fatal("imaps: required OpenSSL options not present",
2382
+ case 'p': /* external protection */
2383
+ extprops_ssf = atoi(optarg);
2390
+ /* Initialize the annotatemore extention */
2391
+ annotatemore_init(0, NULL, NULL);
2392
+ annotatemore_open(NULL);
2398
+ * run for each accepted connection
2400
+#ifdef ID_SAVE_CMDLINE
2401
+int service_main(int argc, char **argv, char **envp __attribute__((unused)))
2403
+int service_main(int argc __attribute__((unused)),
2404
+ char **argv __attribute__((unused)),
2405
+ char **envp __attribute__((unused)))
2410
+ sasl_security_properties_t *secprops = NULL;
2411
+ struct sockaddr_storage imapd_localaddr, imapd_remoteaddr;
2412
+ char localip[60], remoteip[60];
2413
+ char hbuf[NI_MAXHOST];
2415
+ int imapd_haveaddr = 0;
2419
+#ifdef ID_SAVE_CMDLINE
2420
+ /* get command line args for use in ID before getopt mangles them */
2421
+ id_getcmdline(argc, argv);
2424
+ imapd_in = prot_new(0, 0);
2425
+ imapd_out = prot_new(1, 1);
2427
+ /* Find out name of client host */
2428
+ salen = sizeof(imapd_remoteaddr);
2429
+ if (getpeername(0, (struct sockaddr *)&imapd_remoteaddr, &salen) == 0 &&
2430
+ (imapd_remoteaddr.ss_family == AF_INET ||
2431
+ imapd_remoteaddr.ss_family == AF_INET6)) {
2432
+ if (getnameinfo((struct sockaddr *)&imapd_remoteaddr, salen,
2433
+ hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD) == 0) {
2434
+ strncpy(imapd_clienthost, hbuf, sizeof(hbuf));
2435
+ strlcat(imapd_clienthost, " ", sizeof(imapd_clienthost));
2436
+ imapd_clienthost[sizeof(imapd_clienthost)-30] = '\0';
2438
+ imapd_clienthost[0] = '\0';
2440
+ niflags = NI_NUMERICHOST;
2441
+#ifdef NI_WITHSCOPEID
2442
+ if (((struct sockaddr *)&imapd_remoteaddr)->sa_family == AF_INET6)
2443
+ niflags |= NI_WITHSCOPEID;
2445
+ if (getnameinfo((struct sockaddr *)&imapd_remoteaddr, salen, hbuf,
2446
+ sizeof(hbuf), NULL, 0, niflags) != 0)
2447
+ strlcpy(hbuf, "unknown", sizeof(hbuf));
2448
+ strlcat(imapd_clienthost, "[", sizeof(imapd_clienthost));
2449
+ strlcat(imapd_clienthost, hbuf, sizeof(imapd_clienthost));
2450
+ strlcat(imapd_clienthost, "]", sizeof(imapd_clienthost));
2451
+ salen = sizeof(imapd_localaddr);
2452
+ if (getsockname(0, (struct sockaddr *)&imapd_localaddr, &salen) == 0) {
2453
+ if(iptostring((struct sockaddr *)&imapd_remoteaddr, salen,
2454
+ remoteip, sizeof(remoteip)) == 0
2455
+ && iptostring((struct sockaddr *)&imapd_localaddr, salen,
2456
+ localip, sizeof(localip)) == 0) {
2457
+ imapd_haveaddr = 1;
2462
+ /* create the SASL connection */
2463
+ if (sasl_server_new("imap", config_servername,
2464
+ NULL, NULL, NULL, NULL, 0,
2465
+ &imapd_saslconn) != SASL_OK) {
2466
+ fatal("SASL failed initializing: sasl_server_new()", EC_TEMPFAIL);
2469
+ /* never allow plaintext, since IMAP has the LOGIN command */
2470
+ secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
2471
+ sasl_setprop(imapd_saslconn, SASL_SEC_PROPS, secprops);
2472
+ sasl_setprop(imapd_saslconn, SASL_SSF_EXTERNAL, &extprops_ssf);
2474
+ if (imapd_haveaddr) {
2475
+ sasl_setprop(imapd_saslconn, SASL_IPREMOTEPORT, remoteip);
2476
+ saslprops.ipremoteport = xstrdup(remoteip);
2477
+ sasl_setprop(imapd_saslconn, SASL_IPLOCALPORT, localip);
2478
+ saslprops.iplocalport = xstrdup(localip);
2481
+ proc_register("imapd", imapd_clienthost, NULL, NULL);
2483
+ /* Set inactivity timer */
2484
+ timeout = config_getint(IMAPOPT_TIMEOUT);
2485
+ if (timeout < 30) timeout = 30;
2486
+ prot_settimeout(imapd_in, timeout*60);
2487
+ prot_setflushonread(imapd_in, imapd_out);
2489
+ /* we were connected on imaps port so we should do
2490
+ TLS negotiation immediately */
2491
+ if (imaps == 1) cmd_starttls(NULL, 1);
2493
+ snmp_increment(TOTAL_CONNECTIONS, 1);
2494
+ snmp_increment(ACTIVE_CONNECTIONS, 1);
2498
+ /* LOGOUT executed */
2499
+ prot_flush(imapd_out);
2500
+ snmp_increment(ACTIVE_CONNECTIONS, -1);
2508
+/* Called by service API to shut down the service */
2509
+void service_abort(int error)
2515
+ * found a motd file; spit out message and return
2520
+ struct protstream *motd_in;
2524
+ motd_in = prot_new(fd, 0);
2526
+ prot_fgets(buf, sizeof(buf), motd_in);
2527
+ if ((p = strchr(buf, '\r'))!=NULL) *p = 0;
2528
+ if ((p = strchr(buf, '\n'))!=NULL) *p = 0;
2530
+ for(p = buf; *p == '['; p++); /* can't have [ be first char, sigh */
2531
+ prot_printf(imapd_out, "* OK [ALERT] %s\r\n", p);
2535
+ * Cleanly shut down and exit
2537
+void shut_down(int code) __attribute__((noreturn));
2538
+void shut_down(int code)
2541
+ if (imapd_mailbox) {
2542
+ index_closemailbox(imapd_mailbox);
2543
+ mailbox_close(imapd_mailbox);
2552
+ annotatemore_close();
2553
+ annotatemore_done();
2556
+ /* Flush the incoming buffer */
2557
+ prot_NONBLOCK(imapd_in);
2558
+ prot_fill(imapd_in);
2560
+ prot_free(imapd_in);
2564
+ /* Flush the outgoing buffer */
2565
+ prot_flush(imapd_out);
2566
+ prot_free(imapd_out);
2568
+ /* one less active connection */
2569
+ snmp_increment(ACTIVE_CONNECTIONS, -1);
2573
+ tls_shutdown_serverengine();
2575
+ /* shutdown socket nicely */
2576
+ cyrus_close_sock(0);
2577
+ cyrus_close_sock(1);
2578
+ cyrus_close_sock(2);
2585
+void fatal(const char *s, int code)
2587
+ static int recurse_code = 0;
2589
+ if (recurse_code) {
2590
+ /* We were called recursively. Just give up */
2592
+ snmp_increment(ACTIVE_CONNECTIONS, -1);
2593
+ exit(recurse_code);
2595
+ recurse_code = code;
2597
+ prot_printf(imapd_out, "* BYE Fatal error: %s\r\n", s);
2598
+ prot_flush(imapd_out);
2601
+ /* Cleanup the stage(s) */
2602
+ while (numstage) {
2603
+ struct appendstage *curstage = stage[--numstage];
2605
+ append_removestage(curstage->stage);
2606
+ while (curstage->nflags--) {
2607
+ free(curstage->flag[curstage->nflags]);
2609
+ if (curstage->flag) free((char *) curstage->flag);
2615
+ syslog(LOG_ERR, "Fatal error: %s", s);
2620
+ * Top-level command loop parsing
2625
+ char motdfilename[1024];
2628
+ int usinguid, havepartition, havenamespace, recursive;
2629
+ static struct buf tag, cmd, arg1, arg2, arg3, arg4;
2630
+ char *p, shut[1024];
2633
+ prot_printf(imapd_out,
2634
+ "* OK %s Cyrus IMAP4 %s server ready\r\n", config_servername,
2637
+ ret = snprintf(motdfilename, sizeof(motdfilename), "%s/msg/motd",
2640
+ if(ret < 0 || ret >= sizeof(motdfilename)) {
2641
+ fatal("motdfilename buffer too small (configdirectory too long)",
2645
+ if ((fd = open(motdfilename, O_RDONLY, 0)) != -1) {
2651
+ if ( !imapd_userisadmin && imapd_userid
2652
+ && shutdown_file(shut, sizeof(shut))) {
2653
+ for (p = shut; *p == '['; p++); /* can't have [ be first char */
2654
+ prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
2661
+ c = getword(imapd_in, &tag);
2663
+ if ((err = prot_error(imapd_in))!=NULL
2664
+ && strcmp(err, PROT_EOF_STRING)) {
2665
+ syslog(LOG_WARNING, "%s, closing connection", err);
2666
+ prot_printf(imapd_out, "* BYE %s\r\n", err);
2670
+ if (c != ' ' || !imparse_isatom(tag.s) || (tag.s[0] == '*' && !tag.s[1])) {
2671
+ prot_printf(imapd_out, "* BAD Invalid tag\r\n");
2672
+ eatline(imapd_in, c);
2676
+ /* Parse command name */
2677
+ c = getword(imapd_in, &cmd);
2679
+ prot_printf(imapd_out, "%s BAD Null command\r\n", tag.s);
2680
+ eatline(imapd_in, c);
2683
+ if (islower((unsigned char) cmd.s[0]))
2684
+ cmd.s[0] = toupper((unsigned char) cmd.s[0]);
2685
+ for (p = &cmd.s[1]; *p; p++) {
2686
+ if (isupper((unsigned char) *p)) *p = tolower((unsigned char) *p);
2689
+ if (plaintextloginalert) {
2690
+ prot_printf(imapd_out, "* OK [ALERT] %s\r\n",
2691
+ plaintextloginalert);
2692
+ plaintextloginalert = NULL;
2695
+ /* Only Authenticate/Login/Logout/Noop/Capability/Id/Starttls
2696
+ allowed when not logged in */
2697
+ if (!imapd_userid && !strchr("ALNCIS", cmd.s[0])) goto nologin;
2699
+ /* note that about half the commands (the common ones that don't
2700
+ hit the mailboxes file) now close the mailboxes file just in
2701
+ case it was open. */
2702
+ switch (cmd.s[0]) {
2704
+ if (!strcmp(cmd.s, "Authenticate")) {
2705
+ int haveinitresp = 0;
2707
+ if (c != ' ') goto missingargs;
2708
+ c = getword(imapd_in, &arg1);
2709
+ if (!imparse_isatom(arg1.s)) {
2710
+ prot_printf(imapd_out, "%s BAD Invalid authenticate mechanism\r\n", tag.s);
2711
+ eatline(imapd_in, c);
2716
+ c = getword(imapd_in, &arg2);
2717
+ if (c == EOF) goto missingargs;
2719
+ if (c == '\r') c = prot_getc(imapd_in);
2720
+ if (c != '\n') goto extraargs;
2722
+ if (imapd_userid) {
2723
+ prot_printf(imapd_out, "%s BAD Already authenticated\r\n", tag.s);
2726
+ cmd_authenticate(tag.s, arg1.s, haveinitresp ? arg2.s : NULL);
2728
+ snmp_increment(AUTHENTICATE_COUNT, 1);
2730
+ else if (!imapd_userid) goto nologin;
2731
+ else if (!strcmp(cmd.s, "Append")) {
2732
+ if (c != ' ') goto missingargs;
2733
+ c = getastring(imapd_in, imapd_out, &arg1);
2734
+ if (c != ' ') goto missingargs;
2736
+ cmd_append(tag.s, arg1.s);
2738
+ snmp_increment(APPEND_COUNT, 1);
2744
+ if (!strcmp(cmd.s, "Bboard")) {
2745
+ if (c != ' ') goto missingargs;
2746
+ c = getastring(imapd_in, imapd_out, &arg1);
2747
+ if (c == EOF) goto missingargs;
2748
+ if (c == '\r') c = prot_getc(imapd_in);
2749
+ if (c != '\n') goto extraargs;
2751
+ cmd_select(tag.s, cmd.s, arg1.s);
2753
+ snmp_increment(BBOARD_COUNT, 1);
2759
+ if (!strcmp(cmd.s, "Capability")) {
2760
+ if (c == '\r') c = prot_getc(imapd_in);
2761
+ if (c != '\n') goto extraargs;
2762
+ cmd_capability(tag.s);
2764
+ snmp_increment(CAPABILITY_COUNT, 1);
2766
+ else if (!imapd_userid) goto nologin;
2767
+ else if (!strcmp(cmd.s, "Check")) {
2768
+ if (!imapd_mailbox) goto nomailbox;
2769
+ if (c == '\r') c = prot_getc(imapd_in);
2770
+ if (c != '\n') goto extraargs;
2772
+ cmd_noop(tag.s, cmd.s);
2774
+ snmp_increment(CHECK_COUNT, 1);
2776
+ else if (!strcmp(cmd.s, "Copy")) {
2777
+ if (!imapd_mailbox) goto nomailbox;
2779
+ if (c != ' ') goto missingargs;
2781
+ c = getword(imapd_in, &arg1);
2782
+ if (c == '\r') goto missingargs;
2783
+ if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
2784
+ c = getastring(imapd_in, imapd_out, &arg2);
2785
+ if (c == EOF) goto missingargs;
2786
+ if (c == '\r') c = prot_getc(imapd_in);
2787
+ if (c != '\n') goto extraargs;
2789
+ cmd_copy(tag.s, arg1.s, arg2.s, usinguid);
2791
+ snmp_increment(COPY_COUNT, 1);
2793
+ else if (!strcmp(cmd.s, "Create")) {
2794
+ havepartition = 0;
2795
+ if (c != ' ') goto missingargs;
2796
+ c = getastring(imapd_in, imapd_out, &arg1);
2797
+ if (c == EOF) goto missingargs;
2799
+ havepartition = 1;
2800
+ c = getword(imapd_in, &arg2);
2801
+ if (!imparse_isatom(arg2.s)) goto badpartition;
2803
+ if (c == '\r') c = prot_getc(imapd_in);
2804
+ if (c != '\n') goto extraargs;
2805
+ cmd_create(tag.s, arg1.s, havepartition ? arg2.s : 0, 0);
2807
+ snmp_increment(CREATE_COUNT, 1);
2809
+ else if (!strcmp(cmd.s, "Close")) {
2810
+ if (!imapd_mailbox) goto nomailbox;
2811
+ if (c == '\r') c = prot_getc(imapd_in);
2812
+ if (c != '\n') goto extraargs;
2816
+ snmp_increment(CLOSE_COUNT, 1);
2822
+ if (!strcmp(cmd.s, "Delete")) {
2823
+ if (c != ' ') goto missingargs;
2824
+ c = getastring(imapd_in, imapd_out, &arg1);
2825
+ if (c == EOF) goto missingargs;
2826
+ if (c == '\r') c = prot_getc(imapd_in);
2827
+ if (c != '\n') goto extraargs;
2828
+ cmd_delete(tag.s, arg1.s, 0);
2830
+ snmp_increment(DELETE_COUNT, 1);
2832
+ else if (!strcmp(cmd.s, "Deleteacl")) {
2833
+ if (c != ' ') goto missingargs;
2834
+ c = getastring(imapd_in, imapd_out, &arg1);
2835
+ if (c != ' ') goto missingargs;
2836
+ c = getastring(imapd_in, imapd_out, &arg2);
2837
+ if (c == EOF) goto missingargs;
2838
+ if (c == '\r') c = prot_getc(imapd_in);
2839
+ if (c != '\n') goto extraargs;
2840
+ cmd_setacl(tag.s, arg1.s, arg2.s, NULL);
2842
+ snmp_increment(DELETEACL_COUNT, 1);
2844
+ else if (!strcmp(cmd.s, "Dump")) {
2845
+ int uid_start = 0;
2847
+ if(c != ' ') goto missingargs;
2848
+ c = getastring(imapd_in, imapd_out, &arg1);
2850
+ c = getastring(imapd_in, imapd_out, &arg2);
2851
+ if(!imparse_isnumber(arg2.s)) goto extraargs;
2852
+ uid_start = atoi(arg2.s);
2855
+ if(c == '\r') c = prot_getc(imapd_in);
2856
+ if(c != '\n') goto extraargs;
2858
+ cmd_dump(tag.s, arg1.s, uid_start);
2859
+ /* snmp_increment(DUMP_COUNT, 1);*/
2865
+ if (!strcmp(cmd.s, "Expunge")) {
2866
+ if (!imapd_mailbox) goto nomailbox;
2867
+ if (c == '\r') c = prot_getc(imapd_in);
2868
+ if (c != '\n') goto extraargs;
2870
+ cmd_expunge(tag.s, 0);
2872
+ snmp_increment(EXPUNGE_COUNT, 1);
2874
+ else if (!strcmp(cmd.s, "Examine")) {
2875
+ if (c != ' ') goto missingargs;
2876
+ c = getastring(imapd_in, imapd_out, &arg1);
2877
+ if (c == EOF) goto missingargs;
2878
+ if (c == '\r') c = prot_getc(imapd_in);
2879
+ if (c != '\n') goto extraargs;
2881
+ cmd_select(tag.s, cmd.s, arg1.s);
2883
+ snmp_increment(EXAMINE_COUNT, 1);
2889
+ if (!strcmp(cmd.s, "Fetch")) {
2890
+ if (!imapd_mailbox) goto nomailbox;
2892
+ if (c != ' ') goto missingargs;
2894
+ c = getword(imapd_in, &arg1);
2895
+ if (c == '\r') goto missingargs;
2896
+ if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
2898
+ cmd_fetch(tag.s, arg1.s, usinguid);
2900
+ snmp_increment(FETCH_COUNT, 1);
2902
+ else if (!strcmp(cmd.s, "Find")) {
2903
+ c = getword(imapd_in, &arg1);
2904
+ if (c != ' ') goto missingargs;
2905
+ c = getastring(imapd_in, imapd_out, &arg2);
2906
+ if (c == EOF) goto missingargs;
2907
+ if (c == '\r') c = prot_getc(imapd_in);
2908
+ if (c != '\n') goto extraargs;
2909
+ cmd_find(tag.s, arg1.s, arg2.s);
2911
+ snmp_increment(FIND_COUNT, 1);
2917
+ if (!strcmp(cmd.s, "Getacl")) {
2918
+ if (c != ' ') goto missingargs;
2919
+ c = getastring(imapd_in, imapd_out, &arg1);
2920
+ if (c == EOF) goto missingargs;
2921
+ if (c == '\r') c = prot_getc(imapd_in);
2922
+ if (c != '\n') goto extraargs;
2923
+ cmd_getacl(tag.s, arg1.s);
2925
+ snmp_increment(GETACL_COUNT, 1);
2927
+ else if (!strcmp(cmd.s, "Getannotation")) {
2928
+ if (c != ' ') goto missingargs;
2929
+ c = getastring(imapd_in, imapd_out, &arg1);
2930
+ if (c != ' ') goto missingargs;
2932
+ cmd_getannotation(tag.s, arg1.s);
2934
+ snmp_increment(GETANNOTATION_COUNT, 1);
2936
+ else if (!strcmp(cmd.s, "Getquota")) {
2937
+ if (c != ' ') goto missingargs;
2938
+ c = getastring(imapd_in, imapd_out, &arg1);
2939
+ if (c == EOF) goto missingargs;
2940
+ if (c == '\r') c = prot_getc(imapd_in);
2941
+ if (c != '\n') goto extraargs;
2942
+ cmd_getquota(tag.s, arg1.s);
2944
+ snmp_increment(GETQUOTA_COUNT, 1);
2946
+ else if (!strcmp(cmd.s, "Getquotaroot")) {
2947
+ if (c != ' ') goto missingargs;
2948
+ c = getastring(imapd_in, imapd_out, &arg1);
2949
+ if (c == EOF) goto missingargs;
2950
+ if (c == '\r') c = prot_getc(imapd_in);
2951
+ if (c != '\n') goto extraargs;
2952
+ cmd_getquotaroot(tag.s, arg1.s);
2954
+ snmp_increment(GETQUOTAROOT_COUNT, 1);
2960
+ if (!strcmp(cmd.s, "Id")) {
2961
+ if (c != ' ') goto missingargs;
2964
+ snmp_increment(ID_COUNT, 1);
2966
+ else if (!imapd_userid) goto nologin;
2967
+ else if (!strcmp(cmd.s, "Idle")) {
2968
+ if (!idle_enabled()) {
2969
+ /* we don't support idle */
2973
+ if (c == '\r') c = prot_getc(imapd_in);
2974
+ if (c != '\n') goto extraargs;
2977
+ snmp_increment(IDLE_COUNT, 1);
2983
+ if (!strcmp(cmd.s, "Login")) {
2984
+ if (c != ' ') goto missingargs;
2985
+ c = getastring(imapd_in, imapd_out, &arg1);
2986
+ if(c != ' ') goto missingargs;
2988
+ cmd_login(tag.s, arg1.s);
2990
+ snmp_increment(LOGIN_COUNT, 1);
2992
+ else if (!strcmp(cmd.s, "Logout")) {
2993
+ if (c == '\r') c = prot_getc(imapd_in);
2994
+ if (c != '\n') goto extraargs;
2996
+ snmp_increment(LOGOUT_COUNT, 1);
2998
+ prot_printf(imapd_out, "* BYE %s\r\n",
2999
+ error_message(IMAP_BYE_LOGOUT));
3000
+ prot_printf(imapd_out, "%s OK %s\r\n", tag.s,
3001
+ error_message(IMAP_OK_COMPLETED));
3004
+ else if (!imapd_userid) goto nologin;
3005
+ else if (!strcmp(cmd.s, "List")) {
3006
+ int listopts = LIST_CHILDREN;
3007
+#ifdef ENABLE_LISTEXT
3008
+ /* Check for and parse LISTEXT options */
3009
+ c = prot_getc(imapd_in);
3011
+ c = getlistopts(tag.s, &listopts);
3013
+ eatline(imapd_in, c);
3018
+ prot_ungetc(c, imapd_in);
3019
+#endif /* ENABLE_LISTEXT */
3020
+ if (imapd_magicplus) listopts |= LIST_SUBSCRIBED;
3021
+ c = getastring(imapd_in, imapd_out, &arg1);
3022
+ if (c != ' ') goto missingargs;
3023
+ c = getastring(imapd_in, imapd_out, &arg2);
3024
+ if (c == '\r') c = prot_getc(imapd_in);
3025
+ if (c != '\n') goto extraargs;
3026
+ cmd_list(tag.s, listopts, arg1.s, arg2.s);
3028
+ snmp_increment(LIST_COUNT, 1);
3030
+ else if (!strcmp(cmd.s, "Lsub")) {
3031
+ c = getastring(imapd_in, imapd_out, &arg1);
3032
+ if (c != ' ') goto missingargs;
3033
+ c = getastring(imapd_in, imapd_out, &arg2);
3034
+ if (c == '\r') c = prot_getc(imapd_in);
3035
+ if (c != '\n') goto extraargs;
3036
+ cmd_list(tag.s, LIST_LSUB | LIST_CHILDREN, arg1.s, arg2.s);
3038
+ snmp_increment(LSUB_COUNT, 1);
3040
+ else if (!strcmp(cmd.s, "Listrights")) {
3041
+ c = getastring(imapd_in, imapd_out, &arg1);
3042
+ if (c != ' ') goto missingargs;
3043
+ c = getastring(imapd_in, imapd_out, &arg2);
3044
+ if (c == '\r') c = prot_getc(imapd_in);
3045
+ if (c != '\n') goto extraargs;
3046
+ cmd_listrights(tag.s, arg1.s, arg2.s);
3048
+ snmp_increment(LISTRIGHTS_COUNT, 1);
3050
+ else if (!strcmp(cmd.s, "Localcreate")) {
3051
+ /* create a local-only mailbox */
3052
+ havepartition = 0;
3053
+ if (c != ' ') goto missingargs;
3054
+ c = getastring(imapd_in, imapd_out, &arg1);
3055
+ if (c == EOF) goto missingargs;
3057
+ havepartition = 1;
3058
+ c = getword(imapd_in, &arg2);
3059
+ if (!imparse_isatom(arg2.s)) goto badpartition;
3061
+ if (c == '\r') c = prot_getc(imapd_in);
3062
+ if (c != '\n') goto extraargs;
3063
+ cmd_create(tag.s, arg1.s, havepartition ? arg2.s : NULL, 1);
3065
+ /* xxxx snmp_increment(CREATE_COUNT, 1); */
3067
+ else if (!strcmp(cmd.s, "Localdelete")) {
3068
+ /* delete a mailbox locally only */
3069
+ if (c != ' ') goto missingargs;
3070
+ c = getastring(imapd_in, imapd_out, &arg1);
3071
+ if (c == EOF) goto missingargs;
3072
+ if (c == '\r') c = prot_getc(imapd_in);
3073
+ if (c != '\n') goto extraargs;
3074
+ cmd_delete(tag.s, arg1.s, 1);
3076
+ /* xxxx snmp_increment(DELETE_COUNT, 1); */
3082
+ if (!strcmp(cmd.s, "Myrights")) {
3083
+ if (c != ' ') goto missingargs;
3084
+ c = getastring(imapd_in, imapd_out, &arg1);
3085
+ if (c == EOF) goto missingargs;
3086
+ if (c == '\r') c = prot_getc(imapd_in);
3087
+ if (c != '\n') goto extraargs;
3088
+ cmd_myrights(tag.s, arg1.s);
3090
+ /* xxxx snmp_increment(MYRIGHTS_COUNT, 1); */
3092
+ else if (!strcmp(cmd.s, "Mupdatepush")) {
3093
+ if (c != ' ') goto missingargs;
3094
+ c = getastring(imapd_in, imapd_out, &arg1);
3095
+ if(c == EOF) goto missingargs;
3096
+ if(c == '\r') c = prot_getc(imapd_in);
3097
+ if(c != '\n') goto extraargs;
3098
+ cmd_mupdatepush(tag.s, arg1.s);
3100
+ /* xxxx snmp_increment(MUPDATEPUSH_COUNT, 1); */
3101
+ } else goto badcmd;
3105
+ if (!strcmp(cmd.s, "Noop")) {
3106
+ if (c == '\r') c = prot_getc(imapd_in);
3107
+ if (c != '\n') goto extraargs;
3109
+ cmd_noop(tag.s, cmd.s);
3111
+ /* xxxx snmp_increment(NOOP_COUNT, 1); */
3113
+#ifdef ENABLE_X_NETSCAPE_HACK
3114
+ else if (!strcmp(cmd.s, "Netscape")) {
3115
+ if (c == '\r') c = prot_getc(imapd_in);
3116
+ if (c != '\n') goto extraargs;
3117
+ cmd_netscrape(tag.s);
3120
+ else if (!imapd_userid) goto nologin;
3121
+ else if (!strcmp(cmd.s, "Namespace")) {
3122
+ if (c == '\r') c = prot_getc(imapd_in);
3123
+ if (c != '\n') goto extraargs;
3124
+ cmd_namespace(tag.s);
3126
+ /* xxxx snmp_increment(NAMESPACE_COUNT, 1); */
3132
+ if (!strcmp(cmd.s, "Partial")) {
3133
+ if (!imapd_mailbox) goto nomailbox;
3134
+ if (c != ' ') goto missingargs;
3135
+ c = getword(imapd_in, &arg1);
3136
+ if (c != ' ') goto missingargs;
3137
+ c = getword(imapd_in, &arg2);
3138
+ if (c != ' ') goto missingargs;
3139
+ c = getword(imapd_in, &arg3);
3140
+ if (c != ' ') goto missingargs;
3141
+ c = getword(imapd_in, &arg4);
3142
+ if (c == '\r') c = prot_getc(imapd_in);
3143
+ if (c != '\n') goto extraargs;
3145
+ cmd_partial(tag.s, arg1.s, arg2.s, arg3.s, arg4.s);
3147
+ /* xxxx snmp_increment(PARTIAL_COUNT, 1); */
3153
+ if (!strcmp(cmd.s, "Rename")) {
3154
+ havepartition = 0;
3155
+ if (c != ' ') goto missingargs;
3156
+ c = getastring(imapd_in, imapd_out, &arg1);
3157
+ if (c != ' ') goto missingargs;
3158
+ c = getastring(imapd_in, imapd_out, &arg2);
3159
+ if (c == EOF) goto missingargs;
3161
+ havepartition = 1;
3162
+ c = getword(imapd_in, &arg3);
3163
+ if (!imparse_isatom(arg3.s)) goto badpartition;
3165
+ if (c == '\r') c = prot_getc(imapd_in);
3166
+ if (c != '\n') goto extraargs;
3167
+ cmd_rename(tag.s, arg1.s, arg2.s, havepartition ? arg3.s : 0);
3169
+ /* xxxx snmp_increment(RENAME_COUNT, 1); */
3170
+ } else if(!strcmp(cmd.s, "Reconstruct")) {
3172
+ if (c != ' ') goto missingargs;
3173
+ c = getastring(imapd_in, imapd_out, &arg1);
3175
+ /* Optional RECURSEIVE argument */
3176
+ c = getword(imapd_in, &arg2);
3177
+ if(!imparse_isatom(arg2.s))
3179
+ else if(!strcasecmp(arg2.s, "RECURSIVE"))
3184
+ if(c == '\r') c = prot_getc(imapd_in);
3185
+ if(c != '\n') goto extraargs;
3186
+ cmd_reconstruct(tag.s, arg1.s, recursive);
3188
+ /* snmp_increment(RECONSTRUCT_COUNT, 1); */
3190
+ else if (!strcmp(cmd.s, "Rlist")) {
3191
+ c = getastring(imapd_in, imapd_out, &arg1);
3192
+ if (c != ' ') goto missingargs;
3193
+ c = getastring(imapd_in, imapd_out, &arg2);
3194
+ if (c == '\r') c = prot_getc(imapd_in);
3195
+ if (c != '\n') goto extraargs;
3196
+ cmd_list(tag.s, LIST_CHILDREN | LIST_REMOTE, arg1.s, arg2.s);
3198
+/* snmp_increment(LIST_COUNT, 1); */
3200
+ else if (!strcmp(cmd.s, "Rlsub")) {
3201
+ c = getastring(imapd_in, imapd_out, &arg1);
3202
+ if (c != ' ') goto missingargs;
3203
+ c = getastring(imapd_in, imapd_out, &arg2);
3204
+ if (c == '\r') c = prot_getc(imapd_in);
3205
+ if (c != '\n') goto extraargs;
3206
+ cmd_list(tag.s, LIST_LSUB | LIST_CHILDREN | LIST_REMOTE,
3208
+/* snmp_increment(LSUB_COUNT, 1); */
3209
+ } else goto badcmd;
3213
+ if (!strcmp(cmd.s, "Starttls")) {
3214
+ if (!tls_enabled()) {
3215
+ /* we don't support starttls */
3219
+ if (c == '\r') c = prot_getc(imapd_in);
3220
+ if (c != '\n') goto extraargs;
3222
+ /* if we've already done SASL fail */
3223
+ if (imapd_userid != NULL) {
3224
+ prot_printf(imapd_out,
3225
+ "%s BAD Can't Starttls after authentication\r\n", tag.s);
3229
+ /* check if already did a successful tls */
3230
+ if (imapd_starttls_done == 1) {
3231
+ prot_printf(imapd_out,
3232
+ "%s BAD Already did a successful Starttls\r\n",
3236
+ cmd_starttls(tag.s, 0);
3238
+ snmp_increment(STARTTLS_COUNT, 1);
3241
+ if (!imapd_userid) {
3243
+ } else if (!strcmp(cmd.s, "Store")) {
3244
+ if (!imapd_mailbox) goto nomailbox;
3246
+ if (c != ' ') goto missingargs;
3248
+ c = getword(imapd_in, &arg1);
3249
+ if (c != ' ' || !imparse_issequence(arg1.s)) goto badsequence;
3250
+ c = getword(imapd_in, &arg2);
3251
+ if (c != ' ') goto missingargs;
3253
+ cmd_store(tag.s, arg1.s, arg2.s, usinguid);
3255
+ snmp_increment(STORE_COUNT, 1);
3257
+ else if (!strcmp(cmd.s, "Select")) {
3258
+ if (c != ' ') goto missingargs;
3259
+ c = getastring(imapd_in, imapd_out, &arg1);
3260
+ if (c == EOF) goto missingargs;
3261
+ if (c == '\r') c = prot_getc(imapd_in);
3262
+ if (c != '\n') goto extraargs;
3264
+ cmd_select(tag.s, cmd.s, arg1.s);
3266
+ snmp_increment(SELECT_COUNT, 1);
3268
+ else if (!strcmp(cmd.s, "Search")) {
3269
+ if (!imapd_mailbox) goto nomailbox;
3271
+ if (c != ' ') goto missingargs;
3274
+ cmd_search(tag.s, usinguid);
3276
+ snmp_increment(SEARCH_COUNT, 1);
3278
+ else if (!strcmp(cmd.s, "Subscribe")) {
3279
+ if (c != ' ') goto missingargs;
3280
+ havenamespace = 0;
3281
+ c = getastring(imapd_in, imapd_out, &arg1);
3283
+ havenamespace = 1;
3284
+ c = getastring(imapd_in, imapd_out, &arg2);
3286
+ if (c == EOF) goto missingargs;
3287
+ if (c == '\r') c = prot_getc(imapd_in);
3288
+ if (c != '\n') goto extraargs;
3289
+ if (havenamespace) {
3290
+ cmd_changesub(tag.s, arg1.s, arg2.s, 1);
3293
+ cmd_changesub(tag.s, (char *)0, arg1.s, 1);
3295
+ snmp_increment(SUBSCRIBE_COUNT, 1);
3297
+ else if (!strcmp(cmd.s, "Setacl")) {
3298
+ if (c != ' ') goto missingargs;
3299
+ c = getastring(imapd_in, imapd_out, &arg1);
3300
+ if (c != ' ') goto missingargs;
3301
+ c = getastring(imapd_in, imapd_out, &arg2);
3302
+ if (c != ' ') goto missingargs;
3303
+ c = getastring(imapd_in, imapd_out, &arg3);
3304
+ if (c == EOF) goto missingargs;
3305
+ if (c == '\r') c = prot_getc(imapd_in);
3306
+ if (c != '\n') goto extraargs;
3307
+ cmd_setacl(tag.s, arg1.s, arg2.s, arg3.s);
3309
+ snmp_increment(SETACL_COUNT, 1);
3311
+ else if (!strcmp(cmd.s, "Setannotation")) {
3312
+ if (c != ' ') goto missingargs;
3313
+ c = getastring(imapd_in, imapd_out, &arg1);
3314
+ if (c != ' ') goto missingargs;
3316
+ cmd_setannotation(tag.s, arg1.s);
3318
+ snmp_increment(SETANNOTATION_COUNT, 1);
3320
+ else if (!strcmp(cmd.s, "Setquota")) {
3321
+ if (c != ' ') goto missingargs;
3322
+ c = getastring(imapd_in, imapd_out, &arg1);
3323
+ if (c != ' ') goto missingargs;
3324
+ cmd_setquota(tag.s, arg1.s);
3326
+ snmp_increment(SETQUOTA_COUNT, 1);
3328
+ else if (!strcmp(cmd.s, "Sort")) {
3329
+ if (!imapd_mailbox) goto nomailbox;
3331
+ if (c != ' ') goto missingargs;
3333
+ cmd_sort(tag.s, usinguid);
3335
+ snmp_increment(SORT_COUNT, 1);
3337
+ else if (!strcmp(cmd.s, "Status")) {
3338
+ if (c != ' ') goto missingargs;
3339
+ c = getastring(imapd_in, imapd_out, &arg1);
3340
+ if (c != ' ') goto missingargs;
3341
+ cmd_status(tag.s, arg1.s);
3343
+ snmp_increment(STATUS_COUNT, 1);
3349
+ if (!strcmp(cmd.s, "Thread")) {
3350
+ if (!imapd_mailbox) goto nomailbox;
3352
+ if (c != ' ') goto missingargs;
3354
+ cmd_thread(tag.s, usinguid);
3356
+ snmp_increment(THREAD_COUNT, 1);
3362
+ if (!strcmp(cmd.s, "Uid")) {
3363
+ if (!imapd_mailbox) goto nomailbox;
3365
+ if (c != ' ') goto missingargs;
3366
+ c = getword(imapd_in, &arg1);
3367
+ if (c != ' ') goto missingargs;
3369
+ if (!strcmp(arg1.s, "fetch")) {
3372
+ else if (!strcmp(arg1.s, "store")) {
3375
+ else if (!strcmp(arg1.s, "search")) {
3378
+ else if (!strcmp(arg1.s, "sort")) {
3381
+ else if (!strcmp(arg1.s, "thread")) {
3384
+ else if (!strcmp(arg1.s, "copy")) {
3387
+ else if (!strcmp(arg1.s, "expunge")) {
3388
+ c = getword(imapd_in, &arg1);
3389
+ if (!imparse_issequence(arg1.s)) goto badsequence;
3390
+ if (c == '\r') c = prot_getc(imapd_in);
3391
+ if (c != '\n') goto extraargs;
3392
+ cmd_expunge(tag.s, arg1.s);
3394
+ snmp_increment(EXPUNGE_COUNT, 1);
3397
+ prot_printf(imapd_out, "%s BAD Unrecognized UID subcommand\r\n", tag.s);
3398
+ eatline(imapd_in, c);
3401
+ else if (!strcmp(cmd.s, "Unsubscribe")) {
3402
+ if (c != ' ') goto missingargs;
3403
+ havenamespace = 0;
3404
+ c = getastring(imapd_in, imapd_out, &arg1);
3406
+ havenamespace = 1;
3407
+ c = getastring(imapd_in, imapd_out, &arg2);
3409
+ if (c == EOF) goto missingargs;
3410
+ if (c == '\r') c = prot_getc(imapd_in);
3411
+ if (c != '\n') goto extraargs;
3412
+ if (havenamespace) {
3413
+ cmd_changesub(tag.s, arg1.s, arg2.s, 0);
3416
+ cmd_changesub(tag.s, (char *)0, arg1.s, 0);
3419
+ snmp_increment(UNSUBSCRIBE_COUNT, 1);
3421
+ else if (!strcmp(cmd.s, "Unselect")) {
3422
+ if (!imapd_mailbox) goto nomailbox;
3423
+ if (c == '\r') c = prot_getc(imapd_in);
3424
+ if (c != '\n') goto extraargs;
3425
+ cmd_unselect(tag.s);
3427
+ snmp_increment(UNSELECT_COUNT, 1);
3429
+ else if (!strcmp(cmd.s, "Undump")) {
3430
+ if(c != ' ') goto missingargs;
3431
+ c = getastring(imapd_in, imapd_out, &arg1);
3433
+ /* we want to get a list at this point */
3434
+ if(c != ' ') goto missingargs;
3436
+ cmd_undump(tag.s, arg1.s);
3437
+ /* snmp_increment(UNDUMP_COUNT, 1);*/
3443
+ if (!strcmp(cmd.s, "Xfer")) {
3444
+ int havepartition = 0;
3447
+ if(c != ' ') goto missingargs;
3448
+ c = getastring(imapd_in, imapd_out, &arg1);
3451
+ if(c != ' ') goto missingargs;
3452
+ c = getastring(imapd_in, imapd_out, &arg2);
3455
+ /* Dest Partition */
3456
+ c = getastring(imapd_in, imapd_out, &arg3);
3457
+ if (!imparse_isatom(arg3.s)) goto badpartition;
3458
+ havepartition = 1;
3461
+ if (c == '\r') c = prot_getc(imapd_in);
3462
+ if (c != '\n') goto extraargs;
3464
+ cmd_xfer(tag.s, arg1.s, arg2.s,
3465
+ (havepartition ? arg3.s : NULL));
3466
+ /* snmp_increment(XFER_COUNT, 1);*/
3473
+ prot_printf(imapd_out, "%s BAD Unrecognized command\r\n", tag.s);
3474
+ eatline(imapd_in, c);
3480
+ prot_printf(imapd_out, "%s BAD Please login first\r\n", tag.s);
3481
+ eatline(imapd_in, c);
3485
+ prot_printf(imapd_out, "%s BAD Please select a mailbox first\r\n", tag.s);
3486
+ eatline(imapd_in, c);
3490
+ prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag.s, cmd.s);
3491
+ eatline(imapd_in, c);
3495
+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag.s, cmd.s);
3496
+ eatline(imapd_in, c);
3500
+ prot_printf(imapd_out, "%s BAD Invalid sequence in %s\r\n", tag.s, cmd.s);
3501
+ eatline(imapd_in, c);
3505
+ prot_printf(imapd_out, "%s BAD Invalid partition name in %s\r\n",
3507
+ eatline(imapd_in, c);
3513
+ * Perform a LOGIN command
3515
+void cmd_login(char *tag, char *user)
3517
+ char userbuf[MAX_MAILBOX_NAME+1];
3519
+ const char *canon_user = userbuf;
3521
+ struct buf passwdbuf;
3523
+ const char *reply = NULL;
3526
+ if (imapd_userid) {
3527
+ eatline(imapd_in, ' ');
3528
+ prot_printf(imapd_out, "%s BAD Already logged in\r\n", tag);
3532
+ r = imapd_canon_user(imapd_saslconn, NULL, user, 0,
3533
+ SASL_CU_AUTHID | SASL_CU_AUTHZID, NULL,
3534
+ userbuf, sizeof(userbuf), &userlen);
3537
+ syslog(LOG_NOTICE, "badlogin: %s plaintext %s invalid user",
3538
+ imapd_clienthost, beautify_string(user));
3539
+ prot_printf(imapd_out, "%s NO %s\r\n", tag,
3540
+ error_message(IMAP_INVALID_USER));
3544
+ /* possibly disallow login */
3545
+ if ((imapd_starttls_done == 0) &&
3546
+ (config_getswitch(IMAPOPT_ALLOWPLAINTEXT) == 0) &&
3547
+ !is_userid_anonymous(canon_user)) {
3548
+ eatline(imapd_in, ' ');
3549
+ prot_printf(imapd_out, "%s NO Login only available under a layer\r\n",
3554
+ memset(&passwdbuf,0,sizeof(struct buf));
3555
+ c = getastring(imapd_in, imapd_out, &passwdbuf);
3557
+ if(c == '\r') c = prot_getc(imapd_in);
3559
+ freebuf(&passwdbuf);
3560
+ prot_printf(imapd_out,
3561
+ "%s BAD Unexpected extra arguments to LOGIN\r\n",
3563
+ eatline(imapd_in, c);
3567
+ passwd = passwdbuf.s;
3569
+ if (is_userid_anonymous(canon_user)) {
3570
+ if (config_getswitch(IMAPOPT_ALLOWANONYMOUSLOGIN)) {
3571
+ passwd = beautify_string(passwd);
3572
+ if (strlen(passwd) > 500) passwd[500] = '\0';
3573
+ syslog(LOG_NOTICE, "login: %s anonymous %s",
3574
+ imapd_clienthost, passwd);
3575
+ reply = "Anonymous access granted";
3576
+ imapd_userid = xstrdup("anonymous");
3579
+ syslog(LOG_NOTICE, "badlogin: %s anonymous login refused",
3580
+ imapd_clienthost);
3581
+ prot_printf(imapd_out, "%s NO %s\r\n", tag,
3582
+ error_message(IMAP_ANONYMOUS_NOT_PERMITTED));
3583
+ freebuf(&passwdbuf);
3587
+ else if ((r = sasl_checkpass(imapd_saslconn,
3589
+ strlen(canon_user),
3591
+ strlen(passwd))) != SASL_OK) {
3592
+ syslog(LOG_NOTICE, "badlogin: %s plaintext %s %s",
3593
+ imapd_clienthost, canon_user, sasl_errdetail(imapd_saslconn));
3597
+ if ((reply = sasl_errstring(r, NULL, NULL)) != NULL) {
3598
+ prot_printf(imapd_out, "%s NO Login failed: %s\r\n", tag, reply);
3600
+ prot_printf(imapd_out, "%s NO Login failed: %d\r\n", tag, r);
3603
+ snmp_increment_args(AUTHENTICATION_NO, 1,
3604
+ VARIABLE_AUTH, 0 /* hash_simple("LOGIN") */,
3605
+ VARIABLE_LISTEND);
3606
+ freebuf(&passwdbuf);
3610
+ r = sasl_getprop(imapd_saslconn, SASL_USERNAME,
3611
+ (const void **) &canon_user);
3613
+ if(r != SASL_OK) {
3614
+ if ((reply = sasl_errstring(r, NULL, NULL)) != NULL) {
3615
+ prot_printf(imapd_out, "%s NO Login failed: %s\r\n",
3618
+ prot_printf(imapd_out, "%s NO Login failed: %d\r\n", tag, r);
3621
+ snmp_increment_args(AUTHENTICATION_NO, 1,
3622
+ VARIABLE_AUTH, 0 /* hash_simple("LOGIN") */,
3623
+ VARIABLE_LISTEND);
3624
+ freebuf(&passwdbuf);
3628
+ reply = "User logged in";
3629
+ imapd_userid = xstrdup(canon_user);
3630
+ snmp_increment_args(AUTHENTICATION_YES, 1,
3631
+ VARIABLE_AUTH, 0 /*hash_simple("LOGIN") */,
3632
+ VARIABLE_LISTEND);
3633
+ syslog(LOG_NOTICE, "login: %s %s%s plaintext%s %s", imapd_clienthost,
3634
+ imapd_userid, imapd_magicplus ? imapd_magicplus : "",
3635
+ imapd_starttls_done ? "+TLS" : "",
3636
+ reply ? reply : "");
3638
+ /* Apply penalty only if not under layer */
3639
+ if (!imapd_starttls_done) {
3640
+ int plaintextloginpause = config_getint(IMAPOPT_PLAINTEXTLOGINPAUSE);
3641
+ if (plaintextloginpause) {
3642
+ sleep(plaintextloginpause);
3645
+ /* Fetch plaintext login nag message */
3646
+ plaintextloginalert = config_getstring(IMAPOPT_PLAINTEXTLOGINALERT);
3650
+ imapd_authstate = auth_newstate(imapd_userid);
3652
+ imapd_userisadmin = global_authisa(imapd_authstate, IMAPOPT_ADMINS);
3654
+ prot_printf(imapd_out, "%s OK %s\r\n", tag, reply);
3656
+ /* Create telemetry log */
3657
+ imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
3659
+ /* Set namespace */
3660
+ if ((r = mboxname_init_namespace(&imapd_namespace,
3661
+ imapd_userisadmin || imapd_userisproxyadmin)) != 0) {
3662
+ syslog(LOG_ERR, error_message(r));
3663
+ fatal(error_message(r), EC_CONFIG);
3666
+ /* Translate any separators in userid */
3667
+ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid,
3668
+ config_virtdomains ?
3669
+ strcspn(imapd_userid, "@") : 0);
3671
+ freebuf(&passwdbuf);
3676
+ * Perform an AUTHENTICATE command
3679
+cmd_authenticate(char *tag, char *authtype, char *resp)
3684
+ char *ssfmsg=NULL;
3686
+ const char *canon_user;
3690
+ r = saslserver(imapd_saslconn, authtype, resp, "", "+ ", "",
3691
+ imapd_in, imapd_out, &sasl_result, NULL);
3694
+ const char *errorstring = NULL;
3697
+ case IMAP_SASL_CANCEL:
3698
+ prot_printf(imapd_out,
3699
+ "%s BAD Client canceled authentication\r\n", tag);
3701
+ case IMAP_SASL_PROTERR:
3702
+ errorstring = prot_error(imapd_in);
3704
+ prot_printf(imapd_out,
3705
+ "%s NO Error reading client response: %s\r\n",
3706
+ tag, errorstring ? errorstring : "");
3709
+ /* failed authentication */
3710
+ errorstring = sasl_errstring(sasl_result, NULL, NULL);
3712
+ syslog(LOG_NOTICE, "badlogin: %s %s [%s]",
3713
+ imapd_clienthost, authtype, sasl_errdetail(imapd_saslconn));
3715
+ snmp_increment_args(AUTHENTICATION_NO, 1,
3716
+ VARIABLE_AUTH, 0, /* hash_simple(authtype) */
3717
+ VARIABLE_LISTEND);
3720
+ if (errorstring) {
3721
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, errorstring);
3723
+ prot_printf(imapd_out, "%s NO Error authenticating\r\n", tag);
3727
+ reset_saslconn(&imapd_saslconn);
3731
+ /* successful authentication */
3733
+ /* get the userid from SASL --- already canonicalized from
3734
+ * mysasl_proxy_policy()
3736
+ sasl_result = sasl_getprop(imapd_saslconn, SASL_USERNAME,
3737
+ (const void **) &canon_user);
3738
+ if (sasl_result != SASL_OK) {
3739
+ prot_printf(imapd_out, "%s NO weird SASL error %d SASL_USERNAME\r\n",
3740
+ tag, sasl_result);
3741
+ syslog(LOG_ERR, "weird SASL error %d getting SASL_USERNAME",
3743
+ reset_saslconn(&imapd_saslconn);
3747
+ /* If we're proxying, the authzid may contain a magic plus,
3748
+ so re-canonify it */
3749
+ if (config_getswitch(IMAPOPT_IMAPMAGICPLUS) && strchr(canon_user, '+')) {
3750
+ char userbuf[MAX_MAILBOX_NAME+1];
3753
+ sasl_result = imapd_canon_user(imapd_saslconn, NULL, canon_user, 0,
3754
+ SASL_CU_AUTHID | SASL_CU_AUTHZID,
3755
+ NULL, userbuf, sizeof(userbuf), &userlen);
3756
+ if (sasl_result != SASL_OK) {
3757
+ prot_printf(imapd_out,
3758
+ "%s NO SASL canonification error %d\r\n",
3759
+ tag, sasl_result);
3760
+ reset_saslconn(&imapd_saslconn);
3764
+ imapd_userid = xstrdup(userbuf);
3766
+ imapd_userid = xstrdup(canon_user);
3769
+ proc_register("imapd", imapd_clienthost, imapd_userid, (char *)0);
3771
+ syslog(LOG_NOTICE, "login: %s %s%s %s%s %s", imapd_clienthost,
3772
+ imapd_userid, imapd_magicplus ? imapd_magicplus : "",
3773
+ authtype, imapd_starttls_done ? "+TLS" : "", "User logged in");
3775
+ sasl_getprop(imapd_saslconn, SASL_SSF, (const void **) &ssfp);
3777
+ /* really, we should be doing a sasl_getprop on SASL_SSF_EXTERNAL,
3778
+ but the current libsasl doesn't allow that. */
3779
+ if (imapd_starttls_done) {
3781
+ case 0: ssfmsg = "tls protection"; break;
3782
+ case 1: ssfmsg = "tls plus integrity protection"; break;
3783
+ default: ssfmsg = "tls plus privacy protection"; break;
3787
+ case 0: ssfmsg = "no protection"; break;
3788
+ case 1: ssfmsg = "integrity protection"; break;
3789
+ default: ssfmsg = "privacy protection"; break;
3793
+ snmp_increment_args(AUTHENTICATION_YES, 1,
3794
+ VARIABLE_AUTH, 0, /* hash_simple(authtype) */
3795
+ VARIABLE_LISTEND);
3797
+ prot_printf(imapd_out, "%s OK Success (%s)\r\n", tag, ssfmsg);
3799
+ prot_setsasl(imapd_in, imapd_saslconn);
3800
+ prot_setsasl(imapd_out, imapd_saslconn);
3802
+ /* Create telemetry log */
3803
+ imapd_logfd = telemetry_log(imapd_userid, imapd_in, imapd_out, 0);
3805
+ /* Set namespace */
3806
+ if ((r = mboxname_init_namespace(&imapd_namespace,
3807
+ imapd_userisadmin || imapd_userisproxyadmin)) != 0) {
3808
+ syslog(LOG_ERR, error_message(r));
3809
+ fatal(error_message(r), EC_CONFIG);
3812
+ /* Translate any separators in userid */
3813
+ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid,
3814
+ config_virtdomains ?
3815
+ strcspn(imapd_userid, "@") : 0);
3821
+ * Perform a NOOP command
3824
+cmd_noop(char *tag, char *cmd __attribute__((unused)))
3826
+ if (imapd_mailbox) {
3827
+ index_check(imapd_mailbox, 0, 1);
3829
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
3830
+ error_message(IMAP_OK_COMPLETED));
3834
+ * Parse and perform an ID command.
3836
+ * the command has been parsed up to the parameter list.
3838
+ * we only allow one ID in non-authenticated state from a given client.
3839
+ * we only allow MAXIDFAILED consecutive failed IDs from a given client.
3840
+ * we only record MAXIDLOG ID responses from a given client.
3842
+void cmd_id(char *tag)
3844
+ static int did_id = 0;
3845
+ static int failed_id = 0;
3846
+ static int logged_id = 0;
3848
+ int c = EOF, npair = 0;
3849
+ static struct buf arg, field;
3850
+ struct attvaluelist *params = 0;
3852
+ /* check if we've already had an ID in non-authenticated state */
3853
+ if (!imapd_userid && did_id) {
3854
+ prot_printf(imapd_out,
3855
+ "%s NO Only one Id allowed in non-authenticated state\r\n",
3857
+ eatline(imapd_in, c);
3861
+ /* check if we've had too many failed IDs in a row */
3862
+ if (failed_id >= MAXIDFAILED) {
3863
+ prot_printf(imapd_out, "%s NO Too many (%u) invalid Id commands\r\n",
3865
+ eatline(imapd_in, c);
3869
+ /* ok, accept parameter list */
3870
+ c = getword(imapd_in, &arg);
3871
+ /* check for "NIL" or start of parameter list */
3872
+ if (strcasecmp(arg.s, "NIL") && c != '(') {
3873
+ prot_printf(imapd_out, "%s BAD Invalid parameter list in Id\r\n", tag);
3874
+ eatline(imapd_in, c);
3879
+ /* parse parameter list */
3883
+ /* end of string/value pairs */
3887
+ /* get field name */
3888
+ c = getstring(imapd_in, imapd_out, &field);
3890
+ prot_printf(imapd_out,
3891
+ "%s BAD Invalid/missing field name in Id\r\n",
3897
+ /* get field value */
3898
+ c = getnstring(imapd_in, imapd_out, &arg);
3899
+ if (c != ' ' && c != ')') {
3900
+ prot_printf(imapd_out,
3901
+ "%s BAD Invalid/missing value in Id\r\n",
3907
+ /* ok, we're anal, but we'll still process the ID command */
3908
+ if (strlen(field.s) > MAXIDFIELDLEN) {
3909
+ prot_printf(imapd_out,
3910
+ "%s BAD field longer than %u octets in Id\r\n",
3911
+ tag, MAXIDFIELDLEN);
3915
+ if (strlen(arg.s) > MAXIDVALUELEN) {
3916
+ prot_printf(imapd_out,
3917
+ "%s BAD value longer than %u octets in Id\r\n",
3918
+ tag, MAXIDVALUELEN);
3922
+ if (++npair > MAXIDPAIRS) {
3923
+ prot_printf(imapd_out,
3924
+ "%s BAD too many (%u) field-value pairs in ID\r\n",
3930
+ /* ok, we're happy enough */
3931
+ appendattvalue(¶ms, field.s, arg.s);
3934
+ if (error || c != ')') {
3936
+ eatline(imapd_in, c);
3937
+ freeattvalues(params);
3941
+ c = prot_getc(imapd_in);
3944
+ /* check for CRLF */
3945
+ if (c == '\r') c = prot_getc(imapd_in);
3947
+ prot_printf(imapd_out,
3948
+ "%s BAD Unexpected extra arguments to Id\r\n", tag);
3949
+ eatline(imapd_in, c);
3950
+ freeattvalues(params);
3955
+ /* log the client's ID string.
3956
+ eventually this should be a callback or something. */
3957
+ if (npair && logged_id < MAXIDLOG) {
3958
+ char logbuf[MAXIDLOGLEN + 1] = "";
3959
+ struct attvaluelist *pptr;
3961
+ for (pptr = params; pptr; pptr = pptr->next) {
3962
+ /* should we check for and format literals here ??? */
3963
+ snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf),
3964
+ " \"%s\" ", pptr->attrib);
3965
+ if (!strcmp(pptr->value, "NIL"))
3966
+ snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf),
3969
+ snprintf(logbuf + strlen(logbuf), MAXIDLOGLEN - strlen(logbuf),
3970
+ "\"%s\"", pptr->value);
3973
+ syslog(LOG_INFO, "client id:%s", logbuf);
3978
+ freeattvalues(params);
3980
+ /* spit out our ID string.
3981
+ eventually this might be configurable. */
3982
+ if (config_getswitch(IMAPOPT_IMAPIDRESPONSE)) {
3983
+ id_response(imapd_out);
3984
+ prot_printf(imapd_out, ")\r\n");
3987
+ prot_printf(imapd_out, "* ID NIL\r\n");
3989
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
3990
+ error_message(IMAP_OK_COMPLETED));
3997
+ * Perform an IDLE command
3999
+void cmd_idle(char *tag)
4002
+ static struct buf arg;
4004
+ /* Setup for doing mailbox updates */
4005
+ if (!idle_init(idle_update)) {
4006
+ prot_printf(imapd_out,
4007
+ "%s NO cannot start idling\r\n", tag);
4011
+ /* Tell client we are idling and waiting for end of command */
4012
+ prot_printf(imapd_out, "+ idling\r\n");
4013
+ prot_flush(imapd_out);
4015
+ /* Start doing mailbox updates */
4016
+ if (imapd_mailbox) index_check(imapd_mailbox, 0, 1);
4017
+ idle_start(imapd_mailbox);
4019
+ /* Get continuation data */
4020
+ c = getword(imapd_in, &arg);
4022
+ /* Stop updates and do any necessary cleanup */
4023
+ idle_done(imapd_mailbox);
4025
+ if (imapd_mailbox) index_check(imapd_mailbox, 0, 1);
4028
+ if (!strcasecmp(arg.s, "Done") &&
4029
+ (c = (c == '\r') ? prot_getc(imapd_in) : c) == '\n') {
4030
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
4031
+ error_message(IMAP_OK_COMPLETED));
4035
+ prot_printf(imapd_out,
4036
+ "%s BAD Invalid Idle continuation\r\n", tag);
4037
+ eatline(imapd_in, c);
4044
+/* Send unsolicited untagged responses to the client */
4045
+void idle_update(idle_flags_t flags)
4047
+ if ((flags & IDLE_MAILBOX) && imapd_mailbox)
4048
+ index_check(imapd_mailbox, 0, 1);
4050
+ if (flags & IDLE_ALERT) {
4052
+ if (! imapd_userisadmin && shutdown_file(shut, sizeof(shut))) {
4054
+ for (p = shut; *p == '['; p++); /* can't have [ be first char */
4055
+ prot_printf(imapd_out, "* BYE [ALERT] %s\r\n", p);
4060
+ prot_flush(imapd_out);
4064
+ * Perform a CAPABILITY command
4066
+void cmd_capability(char *tag)
4068
+ const char *sasllist; /* the list of SASL mechanisms */
4069
+ unsigned mechcount;
4071
+ if (imapd_mailbox) {
4072
+ index_check(imapd_mailbox, 0, 0);
4074
+ prot_printf(imapd_out, "* CAPABILITY " CAPABILITY_STRING);
4076
+ if (idle_enabled()) {
4077
+ prot_printf(imapd_out, " IDLE");
4080
+ if (tls_enabled() && !imapd_starttls_done && !imapd_authstate) {
4081
+ prot_printf(imapd_out, " STARTTLS");
4083
+ if (imapd_authstate ||
4084
+ (!imapd_starttls_done && !config_getswitch(IMAPOPT_ALLOWPLAINTEXT))) {
4085
+ prot_printf(imapd_out, " LOGINDISABLED");
4088
+ if(config_mupdate_server) {
4089
+ prot_printf(imapd_out, " MUPDATE=mupdate://%s/", config_mupdate_server);
4092
+ /* add the SASL mechs */
4093
+ if (!imapd_authstate &&
4094
+ sasl_listmech(imapd_saslconn, NULL,
4095
+ "AUTH=", " AUTH=", " SASL-IR",
4097
+ NULL, &mechcount) == SASL_OK && mechcount > 0) {
4098
+ prot_printf(imapd_out, " %s", sasllist);
4100
+ /* else don't show anything */
4103
+#ifdef ENABLE_LISTEXT
4104
+ prot_printf(imapd_out, " LISTEXT LIST-SUBSCRIBED");
4105
+#endif /* ENABLE_LISTEXT */
4107
+#ifdef ENABLE_X_NETSCAPE_HACK
4108
+ prot_printf(imapd_out, " X-NETSCAPE");
4110
+ prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag,
4111
+ error_message(IMAP_OK_COMPLETED));
4115
+ * Parse and perform an APPEND command.
4116
+ * The command has been parsed up to and including
4117
+ * the mailbox name.
4119
+static int isokflag(char *s)
4121
+ if (s[0] == '\\') {
4123
+ if (!strcmp(s, "\\seen")) return 1;
4124
+ if (!strcmp(s, "\\answered")) return 1;
4125
+ if (!strcmp(s, "\\flagged")) return 1;
4126
+ if (!strcmp(s, "\\draft")) return 1;
4127
+ if (!strcmp(s, "\\deleted")) return 1;
4129
+ /* uh oh, system flag i don't recognize */
4132
+ /* valid user flag? */
4133
+ return imparse_isatom(s);
4137
+#define FLAGGROW 10
4138
+void cmd_append(char *tag, char *name)
4142
+ static struct buf arg;
4144
+ time_t now = time(NULL);
4145
+ unsigned size, totalsize = 0;
4149
+ char mailboxname[MAX_MAILBOX_NAME+1];
4150
+ struct appendstate mailbox;
4151
+ unsigned long uidvalidity;
4152
+ unsigned long firstuid, num;
4153
+ long doappenduid = 0;
4154
+ const char *parseerr = NULL;
4157
+ struct appendstage *curstage;
4159
+ /* See if we can append */
4160
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
4161
+ imapd_userid, mailboxname);
4163
+ r = append_check(mailboxname, MAILBOX_FORMAT_NORMAL,
4164
+ imapd_authstate, ACL_INSERT, totalsize);
4167
+ eatline(imapd_in, ' ');
4168
+ prot_printf(imapd_out, "%s NO %s%s\r\n",
4170
+ (r == IMAP_MAILBOX_NONEXISTENT &&
4171
+ mboxlist_createmailboxcheck(mailboxname, 0, 0,
4172
+ imapd_userisadmin,
4173
+ imapd_userid, imapd_authstate,
4174
+ (char **)0, (char **)0) == 0)
4175
+ ? "[TRYCREATE] " : "", error_message(r));
4179
+ stage = xmalloc(numalloc * sizeof(struct appendstage *));
4181
+ c = ' '; /* just parsed a space */
4182
+ /* we loop, to support MULTIAPPEND */
4183
+ while (!r && c == ' ') {
4184
+ /* Grow the stage array, if necessary */
4185
+ if (numstage == numalloc) {
4186
+ /* Avoid integer wrap as arg to xrealloc */
4187
+ if (numalloc > INT_MAX/(2*sizeof(struct appendstage *)))
4190
+ stage = xrealloc(stage, numalloc * sizeof(struct appendstage *));
4192
+ curstage = stage[numstage] = xzmalloc(sizeof(struct appendstage));
4195
+ c = getword(imapd_in, &arg);
4196
+ if (c == '(' && !arg.s[0]) {
4197
+ curstage->nflags = 0;
4199
+ c = getword(imapd_in, &arg);
4200
+ if (!curstage->nflags && !arg.s[0] && c == ')') break; /* empty list */
4201
+ if (!isokflag(arg.s)) {
4202
+ parseerr = "Invalid flag in Append command";
4203
+ r = IMAP_PROTOCOL_ERROR;
4206
+ if (curstage->nflags == curstage->flagalloc) {
4207
+ curstage->flagalloc += FLAGGROW;
4209
+ (char **) xrealloc((char *) curstage->flag,
4210
+ curstage->flagalloc * sizeof(char *));
4212
+ curstage->flag[curstage->nflags] = xstrdup(arg.s);
4213
+ curstage->nflags++;
4214
+ } while (c == ' ');
4217
+ "Missing space or ) after flag name in Append command";
4218
+ r = IMAP_PROTOCOL_ERROR;
4221
+ c = prot_getc(imapd_in);
4223
+ parseerr = "Missing space after flag list in Append command";
4224
+ r = IMAP_PROTOCOL_ERROR;
4227
+ c = getword(imapd_in, &arg);
4230
+ /* Parse internaldate */
4231
+ if (c == '\"' && !arg.s[0]) {
4232
+ prot_ungetc(c, imapd_in);
4233
+ c = getdatetime(&(curstage->internaldate));
4235
+ parseerr = "Invalid date-time in Append command";
4236
+ r = IMAP_PROTOCOL_ERROR;
4239
+ c = getword(imapd_in, &arg);
4241
+ curstage->internaldate = now;
4245
+ /* Check for literal8 */
4248
+ /* We don't support binary append yet */
4249
+ r = IMAP_NO_UNKNOWN_CTE;
4253
+ parseerr = "Missing required argument to Append command";
4254
+ r = IMAP_PROTOCOL_ERROR;
4258
+ /* Read size from literal */
4261
+ for (++p; *p && isdigit((int) *p); p++) {
4263
+ size = size*10 + *p - '0';
4276
+ c = prot_getc(imapd_in);
4279
+ prot_ungetc(c, imapd_in);
4280
+ c = ' '; /* Force a syntax error */
4283
+ if (*p != '}' || p[1] || c != '\n' || !sawdigit) {
4284
+ parseerr = "Invalid literal in Append command";
4285
+ r = IMAP_PROTOCOL_ERROR;
4290
+ /* Tell client to send the message */
4291
+ prot_printf(imapd_out, "+ go ahead\r\n");
4292
+ prot_flush(imapd_out);
4295
+ /* Stage the message */
4296
+ f = append_newstage(mailboxname, now, numstage, &(curstage->stage));
4298
+ totalsize += size;
4299
+ r = message_copy_strict(imapd_in, f, size);
4305
+ /* if we see a SP, we're trying to append more than one message */
4307
+ /* Parse newline terminating command */
4308
+ c = prot_getc(imapd_in);
4313
+ eatline(imapd_in, c);
4315
+ /* we should be looking at the end of the line */
4316
+ if (c == '\r') c = prot_getc(imapd_in);
4318
+ parseerr = "junk after literal";
4319
+ r = IMAP_PROTOCOL_ERROR;
4320
+ eatline(imapd_in, c);
4324
+ /* Append from the stage(s) */
4326
+ r = append_setup(&mailbox, mailboxname, MAILBOX_FORMAT_NORMAL,
4327
+ imapd_userid, imapd_authstate, ACL_INSERT, totalsize);
4330
+ doappenduid = (mailbox.m.myrights & ACL_READ);
4332
+ for (i = 0; !r && i < numstage; i++) {
4333
+ r = append_fromstage(&mailbox, stage[i]->stage, stage[i]->internaldate,
4334
+ (const char **) stage[i]->flag, stage[i]->nflags, 0);
4338
+ r = append_commit(&mailbox, totalsize, &uidvalidity, &firstuid, &num);
4340
+ append_abort(&mailbox);
4344
+ /* Cleanup the stage(s) */
4345
+ while (numstage) {
4346
+ curstage = stage[--numstage];
4348
+ append_removestage(curstage->stage);
4349
+ while (curstage->nflags--) {
4350
+ free(curstage->flag[curstage->nflags]);
4352
+ if (curstage->flag) free((char *) curstage->flag);
4355
+ if (stage) free(stage);
4358
+ if (imapd_mailbox) {
4359
+ index_check(imapd_mailbox, 0, 0);
4362
+ if (r == IMAP_PROTOCOL_ERROR && parseerr) {
4363
+ prot_printf(imapd_out, "%s BAD %s\r\n", tag, parseerr);
4365
+ prot_printf(imapd_out, "%s NO %s%s\r\n",
4367
+ (r == IMAP_MAILBOX_NONEXISTENT &&
4368
+ mboxlist_createmailboxcheck(mailboxname, 0, 0,
4369
+ imapd_userisadmin,
4370
+ imapd_userid, imapd_authstate,
4371
+ (char **)0, (char **)0) == 0)
4372
+ ? "[TRYCREATE] " : "", error_message(r));
4373
+ } else if (doappenduid) {
4374
+ prot_printf(imapd_out, "%s OK [APPENDUID %lu", tag, uidvalidity);
4376
+ prot_printf(imapd_out, " %lu", firstuid);
4378
+ prot_printf(imapd_out, " %lu:%lu", firstuid, firstuid + num - 1);
4380
+ prot_printf(imapd_out, "] %s\r\n", error_message(IMAP_OK_COMPLETED));
4382
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
4383
+ error_message(IMAP_OK_COMPLETED));
4388
+ * Perform a SELECT/EXAMINE/BBOARD command
4390
+void cmd_select(char *tag, char *cmd, char *name)
4392
+ struct mailbox mailbox;
4393
+ char mailboxname[MAX_MAILBOX_NAME+1];
4397
+ static char lastqr[MAX_MAILBOX_PATH+1] = "";
4398
+ static time_t nextalert = 0;
4400
+ if (imapd_mailbox) {
4401
+ index_closemailbox(imapd_mailbox);
4402
+ mailbox_close(imapd_mailbox);
4403
+ imapd_mailbox = 0;
4406
+ if (cmd[0] == 'B') {
4407
+ /* BBoard namespace is empty */
4408
+ r = IMAP_MAILBOX_NONEXISTENT;
4411
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
4412
+ imapd_userid, mailboxname);
4416
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
4418
+ if (r == IMAP_MAILBOX_MOVED) return;
4421
+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
4426
+ r = mailbox_open_index(&mailbox);
4428
+ if (!r && !(mailbox.myrights & ACL_READ)) {
4429
+ r = (imapd_userisadmin || (mailbox.myrights & ACL_LOOKUP)) ?
4430
+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
4432
+ if (!r && chdir(mailbox.path)) {
4433
+ syslog(LOG_ERR, "IOERROR: changing directory to %s: %m", mailbox.path);
4438
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
4439
+ if (doclose) mailbox_close(&mailbox);
4443
+ mboxstruct = mailbox;
4444
+ imapd_mailbox = &mboxstruct;
4446
+ index_newmailbox(imapd_mailbox, cmd[0] == 'E');
4448
+ /* Examine command puts mailbox in read-only mode */
4449
+ if (cmd[0] == 'E') {
4450
+ imapd_mailbox->myrights &= ~(ACL_SEEN|ACL_WRITE|ACL_DELETE);
4453
+ if (imapd_mailbox->myrights & ACL_DELETE) {
4454
+ time_t now = time(NULL);
4456
+ /* Warn if mailbox is close to or over quota */
4457
+ r = quota_read(&imapd_mailbox->quota, NULL, 0);
4458
+ if (!r && imapd_mailbox->quota.limit > 0 &&
4459
+ (strcmp(imapd_mailbox->quota.root, lastqr) || now > nextalert)) {
4460
+ /* Warn if the following possibilities occur:
4461
+ * - quotawarnkb not set + quotawarn hit
4462
+ * - quotawarnkb set larger than mailbox + quotawarn hit
4463
+ * - quotawarnkb set + hit + quotawarn hit
4465
+ int warnsize = config_getint(IMAPOPT_QUOTAWARNKB);
4466
+ if (warnsize <= 0 || warnsize >= imapd_mailbox->quota.limit ||
4467
+ (int)((imapd_mailbox->quota.limit * QUOTA_UNITS) -
4468
+ imapd_mailbox->quota.used) < (warnsize * QUOTA_UNITS)) {
4469
+ usage = ((double) imapd_mailbox->quota.used * 100.0) / (double)
4470
+ (imapd_mailbox->quota.limit * QUOTA_UNITS);
4471
+ if (usage >= 100.0) {
4472
+ prot_printf(imapd_out, "* NO [ALERT] %s\r\n",
4473
+ error_message(IMAP_NO_OVERQUOTA));
4475
+ else if (usage > config_getint(IMAPOPT_QUOTAWARN)) {
4476
+ int usageint = (int) usage;
4477
+ prot_printf(imapd_out, "* NO [ALERT] ");
4478
+ prot_printf(imapd_out, error_message(IMAP_NO_CLOSEQUOTA),
4480
+ prot_printf(imapd_out, "\r\n");
4483
+ strlcpy(lastqr, imapd_mailbox->quota.root, sizeof(lastqr));
4484
+ nextalert = now + 600; /* ALERT every 10 min regardless */
4488
+ prot_printf(imapd_out, "%s OK [READ-%s] %s\r\n", tag,
4489
+ (imapd_mailbox->myrights & (ACL_WRITE|ACL_DELETE)) ?
4490
+ "WRITE" : "ONLY", error_message(IMAP_OK_COMPLETED));
4492
+ proc_register("imapd", imapd_clienthost, imapd_userid, mailboxname);
4493
+ syslog(LOG_DEBUG, "open: user %s opened %s", imapd_userid, name);
4497
+ * Perform a CLOSE command
4505
+ if (!(imapd_mailbox->myrights & ACL_DELETE)) r = 0;
4507
+ r = mailbox_expunge(imapd_mailbox, 1, (int (*)())0, (char *)0);
4510
+ index_closemailbox(imapd_mailbox);
4511
+ mailbox_close(imapd_mailbox);
4512
+ imapd_mailbox = 0;
4515
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
4518
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
4519
+ error_message(IMAP_OK_COMPLETED));
4524
+ * Perform an UNSELECT command -- for some support of IMAP proxy.
4525
+ * Just like close except no expunge.
4531
+ index_closemailbox(imapd_mailbox);
4532
+ mailbox_close(imapd_mailbox);
4533
+ imapd_mailbox = 0;
4535
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
4536
+ error_message(IMAP_OK_COMPLETED));
4540
+ * Parse the syntax for a partial fetch:
4541
+ * "<" number "." nz-number ">"
4543
+#define PARSE_PARTIAL(start_octet, octet_count) \
4544
+ (start_octet) = (octet_count) = 0; \
4545
+ if (*p == '<' && isdigit((int) p[1])) { \
4546
+ (start_octet) = p[1] - '0'; \
4548
+ while (isdigit((int) *p)) { \
4550
+ (start_octet) * 10 + *p++ - '0'; \
4553
+ if (*p == '.' && p[1] >= '1' && p[1] <= '9') { \
4554
+ (octet_count) = p[1] - '0'; \
4555
+ p[0] = '>'; p[1] = '\0'; /* clip off the octet count \
4556
+ (its not used in the reply) */ \
4558
+ while (isdigit((int) *p)) { \
4560
+ (octet_count) * 10 + *p++ - '0'; \
4565
+ if (*p != '>') { \
4566
+ prot_printf(imapd_out, \
4567
+ "%s BAD Invalid body partial\r\n", tag); \
4568
+ eatline(imapd_in, c); \
4575
+ * Parse and perform a FETCH/UID FETCH command
4576
+ * The command has been parsed up to and including
4579
+void cmd_fetch(char *tag, char *sequence, int usinguid)
4581
+ char *cmd = usinguid ? "UID Fetch" : "Fetch";
4582
+ static struct buf fetchatt, fieldname;
4585
+ int fetchitems = 0;
4586
+ struct fetchargs fetchargs;
4587
+ struct octetinfo oi;
4588
+ struct strlist *newfields = 0;
4589
+ char *p, *section;
4590
+ int fetchedsomething, r;
4591
+ clock_t start = clock();
4594
+ memset(&fetchargs, 0, sizeof(struct fetchargs));
4596
+ c = getword(imapd_in, &fetchatt);
4597
+ if (c == '(' && !fetchatt.s[0]) {
4599
+ c = getword(imapd_in, &fetchatt);
4602
+ ucase(fetchatt.s);
4603
+ switch (fetchatt.s[0]) {
4605
+ if (!inlist && !strcmp(fetchatt.s, "ALL")) {
4606
+ fetchitems |= FETCH_ALL;
4612
+ if (!strncmp(fetchatt.s, "BINARY[", 7) ||
4613
+ !strncmp(fetchatt.s, "BINARY.PEEK[", 12) ||
4614
+ !strncmp(fetchatt.s, "BINARY.SIZE[", 12)) {
4617
+ p = section = fetchatt.s + 7;
4618
+ if (!strncmp(p, "PEEK[", 5)) {
4621
+ else if (!strncmp(p, "SIZE[", 5)) {
4626
+ fetchitems |= FETCH_SETSEEN;
4628
+ while ((*p >= '1' && *p <= '9') || *p == '.') {
4629
+ if (*p == '.' && !isdigit((int) p[-1])) break;
4634
+ prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag);
4635
+ eatline(imapd_in, c);
4640
+ if (!binsize) PARSE_PARTIAL(oi.start_octet, oi.octet_count);
4643
+ prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
4644
+ eatline(imapd_in, c);
4648
+ appendstrlist_withdata(&fetchargs.sizesections, section, &oi, sizeof(oi));
4650
+ appendstrlist_withdata(&fetchargs.binsections, section, &oi, sizeof(oi));
4652
+ else if (!strcmp(fetchatt.s, "BODY")) {
4653
+ fetchitems |= FETCH_BODY;
4655
+ else if (!strcmp(fetchatt.s, "BODYSTRUCTURE")) {
4656
+ fetchitems |= FETCH_BODYSTRUCTURE;
4658
+ else if (!strncmp(fetchatt.s, "BODY[", 5) ||
4659
+ !strncmp(fetchatt.s, "BODY.PEEK[", 10)) {
4660
+ p = section = fetchatt.s + 5;
4661
+ if (!strncmp(p, "PEEK[", 5)) {
4665
+ fetchitems |= FETCH_SETSEEN;
4667
+ while (isdigit((int) *p) || *p == '.') {
4668
+ if (*p == '.' && !isdigit((int) p[-1])) break;
4669
+ /* Obsolete section 0 can only occur before close brace */
4670
+ if (*p == '0' && !isdigit((int) p[-1]) && p[1] != ']') break;
4674
+ if (*p == 'H' && !strncmp(p, "HEADER.FIELDS", 13) &&
4675
+ (p == section || p[-1] == '.') &&
4676
+ (p[13] == '\0' || !strcmp(p+13, ".NOT"))) {
4679
+ * If not top-level or a HEADER.FIELDS.NOT, can't pull
4680
+ * the headers out of the cache.
4682
+ if (p != section || p[13] != '\0') {
4683
+ fetchargs.cache_atleast = BIT32_MAX;
4687
+ prot_printf(imapd_out,
4688
+ "%s BAD Missing required argument to %s %s\r\n",
4689
+ tag, cmd, fetchatt.s);
4690
+ eatline(imapd_in, c);
4693
+ c = prot_getc(imapd_in);
4695
+ prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n",
4696
+ tag, cmd, fetchatt.s);
4697
+ eatline(imapd_in, c);
4701
+ c = getastring(imapd_in, imapd_out, &fieldname);
4702
+ for (p = fieldname.s; *p; p++) {
4703
+ if (*p <= ' ' || *p & 0x80 || *p == ':') break;
4705
+ if (*p || !*fieldname.s) {
4706
+ prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n",
4707
+ tag, cmd, fetchatt.s);
4708
+ eatline(imapd_in, c);
4711
+ appendstrlist(&newfields, fieldname.s);
4712
+ if (fetchargs.cache_atleast < BIT32_MAX) {
4714
+ mailbox_cached_header(fieldname.s);
4715
+ if(this_ver > fetchargs.cache_atleast)
4716
+ fetchargs.cache_atleast = this_ver;
4718
+ } while (c == ' ');
4720
+ prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
4721
+ tag, cmd, fetchatt.s);
4722
+ eatline(imapd_in, c);
4726
+ /* Grab/parse the ]<x.y> part */
4727
+ c = getword(imapd_in, &fieldname);
4729
+ if (*p++ != ']') {
4730
+ prot_printf(imapd_out, "%s BAD Missing required close bracket after %s %s\r\n",
4731
+ tag, cmd, fetchatt.s);
4732
+ eatline(imapd_in, c);
4736
+ PARSE_PARTIAL(oi.start_octet, oi.octet_count);
4739
+ prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
4740
+ eatline(imapd_in, c);
4743
+ appendfieldlist(&fetchargs.fsections,
4744
+ section, newfields, fieldname.s,
4752
+ if (p != section && p[-1] != '.') break;
4753
+ if (!strncmp(p, "HEADER]", 7)) p += 6;
4757
+ if (!strncmp(p-1, ".MIME]", 6)) p += 4;
4761
+ if (p != section && p[-1] != '.') break;
4762
+ if (!strncmp(p, "TEXT]", 5)) p += 4;
4767
+ prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag);
4768
+ eatline(imapd_in, c);
4773
+ PARSE_PARTIAL(oi.start_octet, oi.octet_count);
4776
+ prot_printf(imapd_out, "%s BAD Junk after body section\r\n", tag);
4777
+ eatline(imapd_in, c);
4780
+ appendstrlist_withdata(&fetchargs.bodysections, section,
4787
+ if (!strcmp(fetchatt.s, "ENVELOPE")) {
4788
+ fetchitems |= FETCH_ENVELOPE;
4794
+ if (!inlist && !strcmp(fetchatt.s, "FAST")) {
4795
+ fetchitems |= FETCH_FAST;
4797
+ else if (!inlist && !strcmp(fetchatt.s, "FULL")) {
4798
+ fetchitems |= FETCH_FULL;
4800
+ else if (!strcmp(fetchatt.s, "FLAGS")) {
4801
+ fetchitems |= FETCH_FLAGS;
4807
+ if (!strcmp(fetchatt.s, "INTERNALDATE")) {
4808
+ fetchitems |= FETCH_INTERNALDATE;
4814
+ if (!strcmp(fetchatt.s, "RFC822")) {
4815
+ fetchitems |= FETCH_RFC822|FETCH_SETSEEN;
4817
+ else if (!strcmp(fetchatt.s, "RFC822.HEADER")) {
4818
+ fetchitems |= FETCH_HEADER;
4820
+ else if (!strcmp(fetchatt.s, "RFC822.PEEK")) {
4821
+ fetchitems |= FETCH_RFC822;
4823
+ else if (!strcmp(fetchatt.s, "RFC822.SIZE")) {
4824
+ fetchitems |= FETCH_SIZE;
4826
+ else if (!strcmp(fetchatt.s, "RFC822.TEXT")) {
4827
+ fetchitems |= FETCH_TEXT|FETCH_SETSEEN;
4829
+ else if (!strcmp(fetchatt.s, "RFC822.TEXT.PEEK")) {
4830
+ fetchitems |= FETCH_TEXT;
4832
+ else if (!strcmp(fetchatt.s, "RFC822.HEADER.LINES") ||
4833
+ !strcmp(fetchatt.s, "RFC822.HEADER.LINES.NOT")) {
4835
+ prot_printf(imapd_out, "%s BAD Missing required argument to %s %s\r\n",
4836
+ tag, cmd, fetchatt.s);
4837
+ eatline(imapd_in, c);
4840
+ c = prot_getc(imapd_in);
4842
+ prot_printf(imapd_out, "%s BAD Missing required open parenthesis in %s %s\r\n",
4843
+ tag, cmd, fetchatt.s);
4844
+ eatline(imapd_in, c);
4848
+ c = getastring(imapd_in, imapd_out, &fieldname);
4849
+ for (p = fieldname.s; *p; p++) {
4850
+ if (*p <= ' ' || *p & 0x80 || *p == ':') break;
4852
+ if (*p || !*fieldname.s) {
4853
+ prot_printf(imapd_out, "%s BAD Invalid field-name in %s %s\r\n",
4854
+ tag, cmd, fetchatt.s);
4855
+ eatline(imapd_in, c);
4858
+ lcase(fieldname.s);;
4859
+ /* 19 is magic number -- length of
4860
+ * "RFC822.HEADERS.NOT" */
4861
+ appendstrlist(strlen(fetchatt.s) == 19 ?
4862
+ &fetchargs.headers : &fetchargs.headers_not,
4864
+ if (strlen(fetchatt.s) != 19) {
4865
+ fetchargs.cache_atleast = BIT32_MAX;
4867
+ if (fetchargs.cache_atleast < BIT32_MAX) {
4869
+ mailbox_cached_header(fieldname.s);
4870
+ if(this_ver > fetchargs.cache_atleast)
4871
+ fetchargs.cache_atleast = this_ver;
4873
+ } while (c == ' ');
4875
+ prot_printf(imapd_out, "%s BAD Missing required close parenthesis in %s %s\r\n",
4876
+ tag, cmd, fetchatt.s);
4877
+ eatline(imapd_in, c);
4880
+ c = prot_getc(imapd_in);
4886
+ if (!strcmp(fetchatt.s, "UID")) {
4887
+ fetchitems |= FETCH_UID;
4894
+ prot_printf(imapd_out, "%s BAD Invalid %s attribute %s\r\n", tag, cmd, fetchatt.s);
4895
+ eatline(imapd_in, c);
4899
+ if (inlist && c == ' ') c = getword(imapd_in, &fetchatt);
4903
+ if (inlist && c == ')') {
4905
+ c = prot_getc(imapd_in);
4908
+ prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n", tag, cmd);
4909
+ eatline(imapd_in, c);
4912
+ if (c == '\r') c = prot_getc(imapd_in);
4914
+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag, cmd);
4915
+ eatline(imapd_in, c);
4919
+ if (!fetchitems && !fetchargs.bodysections && !fetchargs.fsections &&
4920
+ !fetchargs.binsections && !fetchargs.sizesections &&
4921
+ !fetchargs.headers && !fetchargs.headers_not) {
4922
+ prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd);
4927
+ fetchitems |= FETCH_UID;
4928
+ index_check(imapd_mailbox, 1, 0);
4931
+ fetchargs.fetchitems = fetchitems;
4932
+ r = index_fetch(imapd_mailbox, sequence, usinguid, &fetchargs,
4933
+ &fetchedsomething);
4935
+ snprintf(mytime, sizeof(mytime), "%2.3f",
4936
+ (clock() - start) / (double) CLOCKS_PER_SEC);
4939
+ prot_printf(imapd_out, "%s NO %s (%s sec)\r\n", tag,
4940
+ error_message(r), mytime);
4941
+ } else if (fetchedsomething || usinguid) {
4942
+ prot_printf(imapd_out, "%s OK %s (%s sec)\r\n", tag,
4943
+ error_message(IMAP_OK_COMPLETED), mytime);
4945
+ /* normal FETCH, nothing came back */
4946
+ prot_printf(imapd_out, "%s NO %s (%s sec)\r\n", tag,
4947
+ error_message(IMAP_NO_NOSUCHMSG), mytime);
4951
+ freestrlist(newfields);
4952
+ freestrlist(fetchargs.bodysections);
4953
+ freefieldlist(fetchargs.fsections);
4954
+ freestrlist(fetchargs.headers);
4955
+ freestrlist(fetchargs.headers_not);
4958
+#undef PARSE_PARTIAL /* cleanup */
4961
+ * Perform a PARTIAL command
4963
+void cmd_partial(const char *tag, const char *msgno, char *data,
4964
+ const char *start, const char *count)
4968
+ struct fetchargs fetchargs;
4971
+ int fetchedsomething;
4973
+ memset(&fetchargs, 0, sizeof(struct fetchargs));
4975
+ for (pc = msgno; *pc; pc++) {
4976
+ if (!isdigit((int) *pc)) break;
4978
+ if (*pc || !*msgno) {
4979
+ prot_printf(imapd_out, "%s BAD Invalid message number\r\n", tag);
4984
+ if (!strcmp(data, "rfc822")) {
4985
+ fetchargs.fetchitems = FETCH_RFC822|FETCH_SETSEEN;
4987
+ else if (!strcmp(data, "rfc822.header")) {
4988
+ fetchargs.fetchitems = FETCH_HEADER;
4990
+ else if (!strcmp(data, "rfc822.peek")) {
4991
+ fetchargs.fetchitems = FETCH_RFC822;
4993
+ else if (!strcmp(data, "rfc822.text")) {
4994
+ fetchargs.fetchitems = FETCH_TEXT|FETCH_SETSEEN;
4996
+ else if (!strcmp(data, "rfc822.text.peek")) {
4997
+ fetchargs.fetchitems = FETCH_TEXT;
4999
+ else if (!strncmp(data, "body[", 5) ||
5000
+ !strncmp(data, "body.peek[", 10)) {
5001
+ p = section = data + 5;
5002
+ if (!strncmp(p, "peek[", 5)) {
5006
+ fetchargs.fetchitems = FETCH_SETSEEN;
5008
+ while (isdigit((int) *p) || *p == '.') {
5009
+ if (*p == '.' && (p == section || !isdigit((int) p[1]))) break;
5012
+ if (p == section || *p != ']' || p[1]) {
5013
+ prot_printf(imapd_out, "%s BAD Invalid body section\r\n", tag);
5014
+ freestrlist(fetchargs.bodysections);
5017
+ *(p+1) = '\0'; /* Keep the closing bracket in place */
5018
+ appendstrlist(&fetchargs.bodysections, section);
5021
+ prot_printf(imapd_out, "%s BAD Invalid Partial item\r\n", tag);
5022
+ freestrlist(fetchargs.bodysections);
5026
+ for (pc = start; *pc; pc++) {
5027
+ if (!isdigit((int) *pc)) break;
5028
+ prev = fetchargs.start_octet;
5029
+ fetchargs.start_octet = fetchargs.start_octet*10 + *pc - '0';
5030
+ if(fetchargs.start_octet < prev) {
5031
+ fetchargs.start_octet = 0;
5035
+ if (*pc || !fetchargs.start_octet) {
5036
+ prot_printf(imapd_out, "%s BAD Invalid starting octet\r\n", tag);
5037
+ freestrlist(fetchargs.bodysections);
5040
+ fetchargs.start_octet--; /* Normalize to be 0-based */
5042
+ prev = fetchargs.octet_count;
5043
+ for (pc = count; *pc; pc++) {
5044
+ if (!isdigit((int) *pc)) break;
5045
+ prev = fetchargs.octet_count;
5046
+ fetchargs.octet_count = fetchargs.octet_count*10 + *pc - '0';
5047
+ if(fetchargs.octet_count < prev) {
5052
+ if (*pc || !*count || prev == -1) {
5053
+ prot_printf(imapd_out, "%s BAD Invalid octet count\r\n", tag);
5054
+ freestrlist(fetchargs.bodysections);
5058
+ fetchargs.fetchitems |= FETCH_IS_PARTIAL;
5060
+ index_fetch(imapd_mailbox, msgno, 0, &fetchargs, &fetchedsomething);
5062
+ index_check(imapd_mailbox, 0, 0);
5064
+ if (fetchedsomething) {
5065
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
5066
+ error_message(IMAP_OK_COMPLETED));
5068
+ prot_printf(imapd_out,
5069
+ "%s BAD Invalid sequence in PARTIAL command\r\n",
5073
+ freestrlist(fetchargs.bodysections);
5077
+ * Parse and perform a STORE/UID STORE command
5078
+ * The command has been parsed up to and including
5079
+ * the FLAGS/+FLAGS/-FLAGS
5081
+void cmd_store(char *tag, char *sequence, char *operation, int usinguid)
5083
+ char *cmd = usinguid ? "UID Store" : "Store";
5084
+ struct storeargs storeargs;
5085
+ static struct buf flagname;
5088
+ int nflags = 0, flagalloc = 0;
5089
+ int flagsparsed = 0, inlist = 0;
5092
+ memset(&storeargs, 0, sizeof storeargs);
5096
+ len = strlen(operation);
5097
+ if (len > 7 && !strcmp(operation+len-7, ".silent")) {
5098
+ storeargs.silent = 1;
5099
+ operation[len-7] = '\0';
5102
+ if (!strcmp(operation, "+flags")) {
5103
+ storeargs.operation = STORE_ADD;
5105
+ else if (!strcmp(operation, "-flags")) {
5106
+ storeargs.operation = STORE_REMOVE;
5108
+ else if (!strcmp(operation, "flags")) {
5109
+ storeargs.operation = STORE_REPLACE;
5112
+ prot_printf(imapd_out, "%s BAD Invalid %s attribute\r\n", tag, cmd);
5113
+ eatline(imapd_in, ' ');
5118
+ c = getword(imapd_in, &flagname);
5119
+ if (c == '(' && !flagname.s[0] && !flagsparsed && !inlist) {
5124
+ if (!flagname.s[0]) break;
5126
+ if (flagname.s[0] == '\\') {
5127
+ lcase(flagname.s);
5128
+ if (!strcmp(flagname.s, "\\seen")) {
5129
+ storeargs.seen = 1;
5131
+ else if (!strcmp(flagname.s, "\\answered")) {
5132
+ storeargs.system_flags |= FLAG_ANSWERED;
5134
+ else if (!strcmp(flagname.s, "\\flagged")) {
5135
+ storeargs.system_flags |= FLAG_FLAGGED;
5137
+ else if (!strcmp(flagname.s, "\\deleted")) {
5138
+ storeargs.system_flags |= FLAG_DELETED;
5140
+ else if (!strcmp(flagname.s, "\\draft")) {
5141
+ storeargs.system_flags |= FLAG_DRAFT;
5144
+ prot_printf(imapd_out, "%s BAD Invalid system flag in %s command\r\n",
5146
+ eatline(imapd_in, c);
5150
+ else if (!imparse_isatom(flagname.s)) {
5151
+ prot_printf(imapd_out, "%s BAD Invalid flag name %s in %s command\r\n",
5152
+ tag, flagname.s, cmd);
5153
+ eatline(imapd_in, c);
5157
+ if (nflags == flagalloc) {
5158
+ flagalloc += FLAGGROW;
5159
+ flag = (char **)xrealloc((char *)flag,
5160
+ flagalloc*sizeof(char *));
5162
+ flag[nflags] = xstrdup(flagname.s);
5167
+ if (c != ' ') break;
5170
+ if (!inlist && !flagsparsed) {
5171
+ prot_printf(imapd_out, "%s BAD Missing required argument to %s\r\n", tag, cmd);
5172
+ eatline(imapd_in, c);
5175
+ if (inlist && c == ')') {
5177
+ c = prot_getc(imapd_in);
5180
+ prot_printf(imapd_out, "%s BAD Missing close parenthesis in %s\r\n", tag, cmd);
5181
+ eatline(imapd_in, c);
5184
+ if (c == '\r') c = prot_getc(imapd_in);
5186
+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to %s\r\n", tag, cmd);
5187
+ eatline(imapd_in, c);
5191
+ r = index_store(imapd_mailbox, sequence, usinguid, &storeargs,
5195
+ index_check(imapd_mailbox, 1, 0);
5199
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
5202
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
5203
+ error_message(IMAP_OK_COMPLETED));
5207
+ while (nflags--) {
5208
+ free(flag[nflags]);
5210
+ if (flag) free((char *)flag);
5214
+cmd_search(tag, usinguid)
5220
+ struct searchargs *searchargs;
5221
+ clock_t start = clock();
5225
+ searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
5227
+ c = getsearchprogram(tag, searchargs, &charset, 1);
5229
+ eatline(imapd_in, ' ');
5230
+ freesearchargs(searchargs);
5234
+ if (c == '\r') c = prot_getc(imapd_in);
5236
+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to Search\r\n", tag);
5237
+ eatline(imapd_in, c);
5238
+ freesearchargs(searchargs);
5242
+ if (charset == -1) {
5243
+ prot_printf(imapd_out, "%s NO %s\r\n", tag,
5244
+ error_message(IMAP_UNRECOGNIZED_CHARSET));
5247
+ n = index_search(imapd_mailbox, searchargs, usinguid);
5248
+ snprintf(mytime, sizeof(mytime), "%2.3f",
5249
+ (clock() - start) / (double) CLOCKS_PER_SEC);
5250
+ prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag,
5251
+ error_message(IMAP_OK_COMPLETED), n, mytime);
5254
+ freesearchargs(searchargs);
5258
+ * Perform a SORT/UID SORT command
5261
+cmd_sort(tag, usinguid)
5266
+ struct sortcrit *sortcrit = NULL;
5267
+ static struct buf arg;
5269
+ struct searchargs *searchargs;
5270
+ clock_t start = clock();
5274
+ c = getsortcriteria(tag, &sortcrit);
5276
+ eatline(imapd_in, ' ');
5277
+ freesortcrit(sortcrit);
5283
+ prot_printf(imapd_out, "%s BAD Missing charset in Sort\r\n",
5285
+ eatline(imapd_in, c);
5286
+ freesortcrit(sortcrit);
5290
+ c = getword(imapd_in, &arg);
5292
+ prot_printf(imapd_out, "%s BAD Missing search criteria in Sort\r\n",
5294
+ eatline(imapd_in, c);
5295
+ freesortcrit(sortcrit);
5299
+ charset = charset_lookupname(arg.s);
5301
+ if (charset == -1) {
5302
+ prot_printf(imapd_out, "%s NO %s\r\n", tag,
5303
+ error_message(IMAP_UNRECOGNIZED_CHARSET));
5304
+ eatline(imapd_in, c);
5305
+ freesortcrit(sortcrit);
5309
+ searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
5311
+ c = getsearchprogram(tag, searchargs, &charset, 0);
5313
+ eatline(imapd_in, ' ');
5314
+ freesearchargs(searchargs);
5315
+ freesortcrit(sortcrit);
5319
+ if (c == '\r') c = prot_getc(imapd_in);
5321
+ prot_printf(imapd_out,
5322
+ "%s BAD Unexpected extra arguments to Sort\r\n", tag);
5323
+ eatline(imapd_in, c);
5324
+ freesearchargs(searchargs);
5325
+ freesortcrit(sortcrit);
5329
+ n = index_sort(imapd_mailbox, sortcrit, searchargs, usinguid);
5330
+ snprintf(mytime, sizeof(mytime), "%2.3f",
5331
+ (clock() - start) / (double) CLOCKS_PER_SEC);
5332
+ prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag,
5333
+ error_message(IMAP_OK_COMPLETED), n, mytime);
5335
+ freesortcrit(sortcrit);
5336
+ freesearchargs(searchargs);
5341
+ * Perform a THREAD/UID THREAD command
5344
+cmd_thread(tag, usinguid)
5348
+ static struct buf arg;
5352
+ struct searchargs *searchargs;
5353
+ clock_t start = clock();
5357
+ /* get algorithm */
5358
+ c = getword(imapd_in, &arg);
5360
+ prot_printf(imapd_out, "%s BAD Missing algorithm in Thread\r\n", tag);
5361
+ eatline(imapd_in, c);
5365
+ if ((alg = find_thread_algorithm(arg.s)) == -1) {
5366
+ prot_printf(imapd_out, "%s BAD Invalid Thread algorithm %s\r\n",
5368
+ eatline(imapd_in, c);
5373
+ c = getword(imapd_in, &arg);
5375
+ prot_printf(imapd_out, "%s BAD Missing charset in Thread\r\n",
5377
+ eatline(imapd_in, c);
5381
+ charset = charset_lookupname(arg.s);
5383
+ if (charset == -1) {
5384
+ prot_printf(imapd_out, "%s NO %s\r\n", tag,
5385
+ error_message(IMAP_UNRECOGNIZED_CHARSET));
5386
+ eatline(imapd_in, c);
5390
+ searchargs = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
5392
+ c = getsearchprogram(tag, searchargs, &charset, 0);
5394
+ eatline(imapd_in, ' ');
5395
+ freesearchargs(searchargs);
5399
+ if (c == '\r') c = prot_getc(imapd_in);
5401
+ prot_printf(imapd_out,
5402
+ "%s BAD Unexpected extra arguments to Thread\r\n", tag);
5403
+ eatline(imapd_in, c);
5404
+ freesearchargs(searchargs);
5408
+ n = index_thread(imapd_mailbox, alg, searchargs, usinguid);
5409
+ snprintf(mytime, sizeof(mytime), "%2.3f",
5410
+ (clock() - start) / (double) CLOCKS_PER_SEC);
5411
+ prot_printf(imapd_out, "%s OK %s (%d msgs in %s secs)\r\n", tag,
5412
+ error_message(IMAP_OK_COMPLETED), n, mytime);
5414
+ freesearchargs(searchargs);
5419
+ * Perform a COPY/UID COPY command
5422
+cmd_copy(tag, sequence, name, usinguid)
5429
+ char mailboxname[MAX_MAILBOX_NAME+1];
5432
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
5433
+ imapd_userid, mailboxname);
5435
+ r = index_copy(imapd_mailbox, sequence, usinguid, mailboxname,
5436
+ ©uid, !config_getswitch(IMAPOPT_SINGLEINSTANCESTORE));
5439
+ index_check(imapd_mailbox, usinguid, 0);
5441
+ if (r && !(usinguid && r == IMAP_NO_NOSUCHMSG)) {
5442
+ prot_printf(imapd_out, "%s NO %s%s\r\n", tag,
5443
+ (r == IMAP_MAILBOX_NONEXISTENT &&
5444
+ mboxlist_createmailboxcheck(mailboxname, 0, 0,
5445
+ imapd_userisadmin,
5446
+ imapd_userid, imapd_authstate,
5447
+ (char **)0, (char **)0) == 0)
5448
+ ? "[TRYCREATE] " : "", error_message(r));
5450
+ else if (copyuid) {
5451
+ prot_printf(imapd_out, "%s OK [COPYUID %s] %s\r\n", tag,
5452
+ copyuid, error_message(IMAP_OK_COMPLETED));
5456
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
5457
+ error_message(IMAP_OK_COMPLETED));
5462
+ * Perform an EXPUNGE command
5465
+cmd_expunge(tag, sequence)
5471
+ if (!(imapd_mailbox->myrights & ACL_DELETE)) r = IMAP_PERMISSION_DENIED;
5472
+ else if (sequence) {
5473
+ r = mailbox_expunge(imapd_mailbox, 1, index_expungeuidlist, sequence);
5476
+ r = mailbox_expunge(imapd_mailbox, 1, (mailbox_decideproc_t *)0,
5480
+ index_check(imapd_mailbox, 0, 0);
5483
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
5486
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
5487
+ error_message(IMAP_OK_COMPLETED));
5492
+ * Perform a CREATE command
5495
+cmd_create(char *tag, char *name, char *partition, int localonly)
5498
+ char mailboxname[MAX_MAILBOX_NAME+1];
5499
+ int autocreatequota;
5501
+ if (partition && !imapd_userisadmin) {
5502
+ r = IMAP_PERMISSION_DENIED;
5505
+ if (name[0] && name[strlen(name)-1] == imapd_namespace.hier_sep) {
5506
+ /* We don't care about trailing hierarchy delimiters. */
5507
+ name[strlen(name)-1] = '\0';
5511
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
5512
+ imapd_userid, mailboxname);
5516
+ /* xxx we do forced user creates on LOCALCREATE to facilitate
5517
+ * mailbox moves */
5518
+ r = mboxlist_createmailbox(mailboxname, 0, partition,
5519
+ imapd_userisadmin,
5520
+ imapd_userid, imapd_authstate,
5521
+ localonly, localonly, 0);
5523
+ if (r == IMAP_PERMISSION_DENIED && !strcasecmp(name, "INBOX") &&
5524
+ (autocreatequota = config_getint(IMAPOPT_AUTOCREATEQUOTA))) {
5527
+ r = mboxlist_createmailbox(mailboxname, 0,
5528
+ partition, 1, imapd_userid,
5529
+ imapd_authstate, 0, 0, 0);
5531
+ if (!r && autocreatequota > 0) {
5532
+ (void) mboxlist_setquota(mailboxname, autocreatequota, 0);
5537
+ if (imapd_mailbox) {
5538
+ index_check(imapd_mailbox, 0, 0);
5542
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
5545
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
5546
+ error_message(IMAP_OK_COMPLETED));
5550
+/* Callback for use by cmd_delete */
5551
+static int delmbox(char *name,
5552
+ int matchlen __attribute__((unused)),
5553
+ int maycreate __attribute__((unused)),
5554
+ void *rock __attribute__((unused)))
5558
+ r = mboxlist_deletemailbox(name, imapd_userisadmin,
5559
+ imapd_userid, imapd_authstate,
5563
+ prot_printf(imapd_out, "* NO delete %s: %s\r\n",
5564
+ name, error_message(r));
5571
+ * Perform a DELETE command
5573
+void cmd_delete(char *tag, char *name, int localonly)
5576
+ char mailboxname[MAX_MAILBOX_NAME+1];
5578
+ int domainlen = 0;
5580
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
5581
+ imapd_userid, mailboxname);
5584
+ if (config_virtdomains && (p = strchr(mailboxname, '!')))
5585
+ domainlen = p - mailboxname + 1;
5587
+ r = mboxlist_deletemailbox(mailboxname, imapd_userisadmin,
5588
+ imapd_userid, imapd_authstate, 1,
5592
+ /* was it a top-level user mailbox? */
5593
+ /* localonly deletes are only per-mailbox */
5594
+ if (!r && !localonly &&
5595
+ !strncmp(mailboxname+domainlen, "user.", 5) &&
5596
+ !strchr(mailboxname+domainlen+5, '.')) {
5597
+ int mailboxname_len = strlen(mailboxname);
5599
+ /* If we aren't too close to MAX_MAILBOX_NAME, append .* */
5600
+ p = mailboxname + mailboxname_len; /* end of mailboxname */
5601
+ if (mailboxname_len < sizeof(mailboxname) - 3) {
5605
+ /* build a list of mailboxes - we're using internal names here */
5606
+ mboxlist_findall(NULL, mailboxname, imapd_userisadmin, imapd_userid,
5607
+ imapd_authstate, delmbox, NULL);
5609
+ /* take care of deleting ACLs, subscriptions, seen state and quotas */
5610
+ *p = '\0'; /* clip off pattern */
5611
+ if ((!domainlen) ||
5612
+ (domainlen+1 < (sizeof(mailboxname) - mailboxname_len))) {
5614
+ /* fully qualify the userid */
5615
+ snprintf(p, (sizeof(mailboxname) - mailboxname_len), "@%.*s",
5616
+ domainlen-1, mailboxname);
5618
+ user_deletedata(mailboxname+domainlen+5, imapd_userid,
5619
+ imapd_authstate, 1);
5623
+ if (imapd_mailbox) {
5624
+ index_check(imapd_mailbox, 0, 0);
5628
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
5631
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
5632
+ error_message(IMAP_OK_COMPLETED));
5641
+ char *olduser, *newuser;
5642
+ char *acl_olduser, *acl_newuser;
5643
+ char *newmailboxname;
5647
+/* Callback for use by cmd_rename */
5648
+static int renmbox(char *name,
5649
+ int matchlen __attribute__((unused)),
5650
+ int maycreate __attribute__((unused)),
5653
+ char oldextname[MAX_MAILBOX_NAME+1];
5654
+ char newextname[MAX_MAILBOX_NAME+1];
5655
+ struct renrock *text = (struct renrock *)rock;
5658
+ if((text->nl + strlen(name + text->ol)) > MAX_MAILBOX_NAME)
5661
+ strcpy(text->newmailboxname + text->nl, name + text->ol);
5663
+ r = mboxlist_renamemailbox(name, text->newmailboxname,
5665
+ 1, imapd_userid, imapd_authstate);
5667
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
5669
+ imapd_userid, oldextname);
5670
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
5671
+ text->newmailboxname,
5672
+ imapd_userid, newextname);
5675
+ prot_printf(imapd_out, "* NO rename %s %s: %s\r\n",
5676
+ oldextname, newextname, error_message(r));
5677
+ if (RENAME_STOP_ON_ERROR) return r;
5679
+ /* If we're renaming a user, change quotaroot and ACL */
5680
+ if (text->rename_user) {
5681
+ user_copyquotaroot(name, text->newmailboxname);
5682
+ user_renameacl(text->newmailboxname,
5683
+ text->acl_olduser, text->acl_newuser);
5686
+ /* Rename mailbox annotations */
5687
+ annotatemore_rename(name, text->newmailboxname,
5688
+ text->rename_user ? text->olduser : NULL,
5691
+ prot_printf(imapd_out, "* OK rename %s %s\r\n",
5692
+ oldextname, newextname);
5695
+ prot_flush(imapd_out);
5701
+ * Perform a RENAME command
5703
+void cmd_rename(const char *tag,
5704
+ char *oldname, char *newname, char *partition)
5707
+ char oldmailboxname[MAX_MAILBOX_NAME+3];
5708
+ char newmailboxname[MAX_MAILBOX_NAME+2];
5709
+ char oldextname[MAX_MAILBOX_NAME+1];
5710
+ char newextname[MAX_MAILBOX_NAME+1];
5713
+ int recursive_rename = 1;
5714
+ int rename_user = 0;
5715
+ char olduser[128], newuser[128];
5716
+ char acl_olduser[128], acl_newuser[128];
5718
+ /* canonicalize names */
5719
+ if (partition && !imapd_userisadmin) {
5720
+ r = IMAP_PERMISSION_DENIED;
5724
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, oldname,
5725
+ imapd_userid, oldmailboxname);
5727
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, newname,
5728
+ imapd_userid, newmailboxname);
5730
+ /* if this is my inbox, don't do recursive renames */
5731
+ if (!strcasecmp(oldname, "inbox")) {
5732
+ recursive_rename = 0;
5734
+ /* check if we're an admin renaming a user */
5735
+ else if (config_getswitch(IMAPOPT_ALLOWUSERMOVES) &&
5736
+ mboxname_isusermailbox(oldmailboxname, 1) &&
5737
+ mboxname_isusermailbox(newmailboxname, 1) &&
5738
+ strcmp(oldmailboxname, newmailboxname) && /* different user */
5739
+ imapd_userisadmin) {
5743
+ /* if we're renaming something inside of something else,
5744
+ don't recursively rename stuff */
5745
+ omlen = strlen(oldmailboxname);
5746
+ nmlen = strlen(newmailboxname);
5747
+ if (omlen < nmlen) {
5748
+ if (!strncmp(oldmailboxname, newmailboxname, omlen) &&
5749
+ newmailboxname[omlen] == '.') {
5750
+ recursive_rename = 0;
5753
+ if (!strncmp(oldmailboxname, newmailboxname, nmlen) &&
5754
+ oldmailboxname[nmlen] == '.') {
5755
+ recursive_rename = 0;
5759
+ /* verify that the mailbox doesn't have a wildcard in it */
5760
+ for (p = oldmailboxname; !r && *p; p++) {
5761
+ if (*p == '*' || *p == '%') r = IMAP_MAILBOX_BADNAME;
5764
+ /* attempt to rename the base mailbox */
5766
+ r = mboxlist_renamemailbox(oldmailboxname, newmailboxname, partition,
5767
+ imapd_userisadmin,
5768
+ imapd_userid, imapd_authstate);
5771
+ /* If we're renaming a user, take care of changing quotaroot, ACL,
5772
+ seen state, subscriptions and sieve scripts */
5773
+ if (!r && rename_user) {
5776
+ /* create canonified userids */
5778
+ domain = strchr(oldmailboxname, '!');
5779
+ strcpy(olduser, domain ? domain+6 : oldmailboxname+5);
5781
+ sprintf(olduser+strlen(olduser), "@%.*s",
5782
+ domain - oldmailboxname, oldmailboxname);
5783
+ strcpy(acl_olduser, olduser);
5785
+ /* Translate any separators in source old userid (for ACLs) */
5786
+ mboxname_hiersep_toexternal(&imapd_namespace, acl_olduser,
5787
+ config_virtdomains ?
5788
+ strcspn(acl_olduser, "@") : 0);
5790
+ domain = strchr(newmailboxname, '!');
5791
+ strcpy(newuser, domain ? domain+6 : newmailboxname+5);
5793
+ sprintf(newuser+strlen(newuser), "@%.*s",
5794
+ domain - newmailboxname, newmailboxname);
5795
+ strcpy(acl_newuser, newuser);
5797
+ /* Translate any separators in destination new userid (for ACLs) */
5798
+ mboxname_hiersep_toexternal(&imapd_namespace, acl_newuser,
5799
+ config_virtdomains ?
5800
+ strcspn(acl_newuser, "@") : 0);
5802
+ user_copyquotaroot(oldmailboxname, newmailboxname);
5803
+ user_renameacl(newmailboxname, acl_olduser, acl_newuser);
5804
+ user_renamedata(olduser, newuser, imapd_userid, imapd_authstate);
5806
+ /* XXX report status/progress of meta-data */
5810
+ /* Rename mailbox annotations */
5811
+ annotatemore_rename(oldmailboxname, newmailboxname,
5812
+ rename_user ? olduser : NULL,
5816
+ /* rename all mailboxes matching this */
5817
+ if (!r && recursive_rename) {
5818
+ struct renrock rock;
5819
+ int ol = omlen + 1;
5820
+ int nl = nmlen + 1;
5822
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
5824
+ imapd_userid, oldextname);
5825
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
5827
+ imapd_userid, newextname);
5829
+ prot_printf(imapd_out, "* OK rename %s %s\r\n",
5830
+ oldextname, newextname);
5831
+ prot_flush(imapd_out);
5833
+ strcat(oldmailboxname, ".*");
5834
+ strcat(newmailboxname, ".");
5836
+ /* setup the rock */
5837
+ rock.newmailboxname = newmailboxname;
5840
+ rock.olduser = olduser;
5841
+ rock.newuser = newuser;
5842
+ rock.acl_olduser = acl_olduser;
5843
+ rock.acl_newuser = acl_newuser;
5844
+ rock.partition = partition;
5845
+ rock.rename_user = rename_user;
5847
+ /* add submailboxes; we pretend we're an admin since we successfully
5848
+ renamed the parent - we're using internal names here */
5849
+ r = mboxlist_findall(NULL, oldmailboxname, 1, imapd_userid,
5850
+ imapd_authstate, renmbox, &rock);
5853
+ /* take care of deleting old ACLs, subscriptions, seen state and quotas */
5854
+ if (!r && rename_user)
5855
+ user_deletedata(olduser, imapd_userid, imapd_authstate, 1);
5857
+ if (imapd_mailbox) {
5858
+ index_check(imapd_mailbox, 0, 0);
5862
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
5864
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
5865
+ error_message(IMAP_OK_COMPLETED));
5870
+ * Perform a RECONSTRUCT command
5873
+cmd_reconstruct(const char *tag, const char *name, int recursive)
5876
+ char mailboxname[MAX_MAILBOX_NAME+1];
5877
+ char quotaroot[MAX_MAILBOX_NAME+1];
5878
+ struct mailbox mailbox;
5880
+ /* administrators only please */
5881
+ if (!imapd_userisadmin) {
5882
+ r = IMAP_PERMISSION_DENIED;
5886
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
5887
+ imapd_userid, mailboxname);
5891
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
5893
+ if (r == IMAP_MAILBOX_MOVED) return;
5898
+ /* Reconstruct it */
5902
+ r = IMAP_SYS_ERROR;
5903
+ } else if(pid == 0) {
5907
+ /* Child - exec reconstruct*/
5908
+ syslog(LOG_NOTICE, "Reconstructing '%s' (%s) for user '%s'",
5909
+ mailboxname, recursive ? "recursive" : "not recursive",
5916
+ ret = snprintf(buf, sizeof(buf), "%s/reconstruct", SERVICE_PATH);
5917
+ if(ret < 0 || ret >= sizeof(buf)) {
5918
+ /* in child, so fatailing won't disconnect our user */
5919
+ fatal("reconstruct buffer not sufficiently big", EC_CONFIG);
5923
+ execl(buf, buf, "-C", config_filename, "-r", "-f",
5924
+ mailboxname, NULL);
5926
+ execl(buf, buf, "-C", config_filename, mailboxname, NULL);
5929
+ /* if we are here, we have a problem */
5934
+ /* Parent, wait on child */
5935
+ if(waitpid(pid, &status, 0) < 0) r = IMAP_SYS_ERROR;
5937
+ /* Did we fail? */
5938
+ if(WEXITSTATUS(status) != 0) r = IMAP_SYS_ERROR;
5942
+ /* Still in parent, need to re-quota the mailbox*/
5944
+ /* Find its quota root */
5946
+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
5950
+ if(mailbox.quota.root) {
5951
+ strcpy(quotaroot, mailbox.quota.root);
5953
+ strcpy(quotaroot, mailboxname);
5955
+ mailbox_close(&mailbox);
5958
+ /* Run quota -f */
5964
+ r = IMAP_SYS_ERROR;
5965
+ } else if(pid == 0) {
5969
+ /* Child - exec reconstruct*/
5970
+ syslog(LOG_NOTICE,
5971
+ "Regenerating quota roots starting with '%s' for user '%s'",
5972
+ mailboxname, imapd_userid);
5978
+ ret = snprintf(buf, sizeof(buf), "%s/quota", SERVICE_PATH);
5979
+ if(ret < 0 || ret >= sizeof(buf)) {
5980
+ /* in child, so fatailing won't disconnect our user */
5981
+ fatal("quota buffer not sufficiently big", EC_CONFIG);
5984
+ execl(buf, buf, "-C", config_filename, "-f", quotaroot, NULL);
5986
+ /* if we are here, we have a problem */
5991
+ /* Parent, wait on child */
5992
+ if(waitpid(pid, &status, 0) < 0) r = IMAP_SYS_ERROR;
5994
+ /* Did we fail? */
5995
+ if(WEXITSTATUS(status) != 0) r = IMAP_SYS_ERROR;
6000
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6002
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6003
+ error_message(IMAP_OK_COMPLETED));
6008
+ * Perform a FIND command
6011
+cmd_find(tag, namespace, pattern)
6019
+ for (p = pattern; *p; p++) {
6020
+ if (*p == '%') *p = '?';
6023
+ /* Translate any separators in pattern */
6024
+ mboxname_hiersep_tointernal(&imapd_namespace, pattern,
6025
+ config_virtdomains ?
6026
+ strcspn(pattern, "@") : 0);
6028
+ if (!strcasecmp(namespace, "mailboxes")) {
6029
+ int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE);
6031
+ (*imapd_namespace.mboxlist_findsub)(&imapd_namespace, pattern,
6032
+ imapd_userisadmin, imapd_userid,
6033
+ imapd_authstate, mailboxdata,
6036
+ else if (!strcasecmp(namespace, "all.mailboxes")) {
6037
+ (*imapd_namespace.mboxlist_findall)(&imapd_namespace, pattern,
6038
+ imapd_userisadmin, imapd_userid,
6039
+ imapd_authstate, mailboxdata, NULL);
6041
+ else if (!strcasecmp(namespace, "bboards")
6042
+ || !strcasecmp(namespace, "all.bboards")) {
6046
+ prot_printf(imapd_out, "%s BAD Invalid FIND subcommand\r\n", tag);
6049
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6050
+ error_message(IMAP_OK_COMPLETED));
6053
+static int mstringdatacalls;
6056
+ * Perform a LIST or LSUB command
6058
+void cmd_list(char *tag, int listopts, char *reference, char *pattern)
6063
+ static int ignorereference = 0;
6064
+ clock_t start = clock();
6066
+ int (*findall)(struct namespace *namespace,
6067
+ const char *pattern, int isadmin, char *userid,
6068
+ struct auth_state *auth_state, int (*proc)(),
6070
+ int (*findsub)(struct namespace *namespace,
6071
+ const char *pattern, int isadmin, char *userid,
6072
+ struct auth_state *auth_state, int (*proc)(),
6073
+ void *rock, int force);
6075
+ /* Ignore the reference argument?
6076
+ (the behavior in 1.5.10 & older) */
6077
+ if (ignorereference == 0) {
6078
+ ignorereference = config_getswitch(IMAPOPT_IGNOREREFERENCE);
6081
+ /* Reset state in mstringdata */
6082
+ mstringdata(NULL, NULL, 0, 0, 0);
6084
+ if (!pattern[0] && !(listopts & LIST_LSUB)) {
6085
+ /* Special case: query top-level hierarchy separator */
6086
+ prot_printf(imapd_out, "* LIST (\\Noselect) \"%c\" \"\"\r\n",
6087
+ imapd_namespace.hier_sep);
6089
+ /* Do we need to concatenate fields? */
6090
+ if (!ignorereference || pattern[0] == imapd_namespace.hier_sep) {
6092
+ * - name begins with dot
6093
+ * - we're configured to honor the reference argument */
6095
+ /* Allocate a buffer, figure out how to stick the arguments
6096
+ together, do it, then do that instead of using pattern. */
6097
+ patlen = strlen(pattern);
6098
+ reflen = strlen(reference);
6100
+ buf = xmalloc(patlen + reflen + 1);
6104
+ /* check for LIST A. .B, change to LIST "" A.B */
6105
+ if (reference[reflen-1] == imapd_namespace.hier_sep &&
6106
+ pattern[0] == imapd_namespace.hier_sep) {
6107
+ reference[--reflen] = '\0';
6109
+ strcpy(buf, reference);
6111
+ strcat(buf, pattern);
6115
+ /* Translate any separators in pattern */
6116
+ mboxname_hiersep_tointernal(&imapd_namespace, pattern,
6117
+ config_virtdomains ?
6118
+ strcspn(pattern, "@") : 0);
6120
+ /* Check to see if we should only list the personal namespace */
6121
+ if (!strcmp(pattern, "*")
6122
+ && config_getswitch(IMAPOPT_FOOLSTUPIDCLIENTS)) {
6123
+ if (buf) free(buf);
6124
+ buf = xstrdup("INBOX*");
6126
+ findsub = mboxlist_findsub;
6127
+ findall = mboxlist_findall;
6130
+ findsub = imapd_namespace.mboxlist_findsub;
6131
+ findall = imapd_namespace.mboxlist_findall;
6134
+ if (listopts & (LIST_LSUB | LIST_SUBSCRIBED)) {
6135
+ int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE);
6137
+ (*findsub)(&imapd_namespace, pattern,
6138
+ imapd_userisadmin, imapd_userid, imapd_authstate,
6139
+ listdata, &listopts, force);
6142
+ (*findall)(&imapd_namespace, pattern,
6143
+ imapd_userisadmin, imapd_userid, imapd_authstate,
6144
+ listdata, &listopts);
6147
+ listdata((char *)0, 0, 0, &listopts);
6149
+ if (buf) free(buf);
6151
+ snprintf(mytime, sizeof(mytime), "%2.3f",
6152
+ (clock() - start) / (double) CLOCKS_PER_SEC);
6153
+ prot_printf(imapd_out, "%s OK %s (%s secs %d calls)\r\n", tag,
6154
+ error_message(IMAP_OK_COMPLETED), mytime, mstringdatacalls);
6158
+ * Perform a SUBSCRIBE (add is nonzero) or
6159
+ * UNSUBSCRIBE (add is zero) command
6161
+void cmd_changesub(char *tag, char *namespace,
6162
+ char *name, int add)
6165
+ char mailboxname[MAX_MAILBOX_NAME+1];
6166
+ int force = config_getswitch(IMAPOPT_ALLOWALLSUBSCRIBE);
6168
+ if (namespace) lcase(namespace);
6169
+ if (!namespace || !strcmp(namespace, "mailbox")) {
6170
+ int len = strlen(name);
6171
+ if (force && imapd_namespace.isalt &&
6172
+ (((len == strlen(imapd_namespace.prefix[NAMESPACE_USER]) - 1) &&
6173
+ !strncmp(name, imapd_namespace.prefix[NAMESPACE_USER], len)) ||
6174
+ ((len == strlen(imapd_namespace.prefix[NAMESPACE_SHARED]) - 1) &&
6175
+ !strncmp(name, imapd_namespace.prefix[NAMESPACE_SHARED], len)))) {
6179
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6180
+ imapd_userid, mailboxname);
6182
+ r = mboxlist_changesub(mailboxname, imapd_userid,
6183
+ imapd_authstate, add, force);
6187
+ else if (!strcmp(namespace, "bboard")) {
6188
+ r = add ? IMAP_MAILBOX_NONEXISTENT : 0;
6191
+ prot_printf(imapd_out, "%s BAD Invalid %s subcommand\r\n", tag,
6192
+ add ? "Subscribe" : "Unsubscribe");
6197
+ prot_printf(imapd_out, "%s NO %s: %s\r\n", tag,
6198
+ add ? "Subscribe" : "Unsubscribe", error_message(r));
6201
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6202
+ error_message(IMAP_OK_COMPLETED));
6207
+ * Perform a GETACL command
6209
+void cmd_getacl(const char *tag, const char *name)
6211
+ char mailboxname[MAX_MAILBOX_NAME+1];
6214
+ char *rights, *nextid;
6216
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6217
+ imapd_userid, mailboxname);
6220
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, &acl, NULL);
6222
+ if (r == IMAP_MAILBOX_MOVED) return;
6225
+ access = cyrus_acl_myrights(imapd_authstate, acl);
6227
+ if (!(access & (ACL_READ|ACL_ADMIN)) &&
6228
+ !imapd_userisadmin &&
6229
+ !mboxname_userownsmailbox(imapd_userid, mailboxname)) {
6230
+ r = (access&ACL_LOOKUP) ?
6231
+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
6235
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6239
+ prot_printf(imapd_out, "* ACL ");
6240
+ printastring(name);
6243
+ rights = strchr(acl, '\t');
6244
+ if (!rights) break;
6247
+ nextid = strchr(rights, '\t');
6248
+ if (!nextid) break;
6251
+ prot_printf(imapd_out, " ");
6252
+ printastring(acl);
6253
+ prot_printf(imapd_out, " ");
6254
+ printastring(rights);
6257
+ prot_printf(imapd_out, "\r\n");
6258
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6259
+ error_message(IMAP_OK_COMPLETED));
6263
+ * Perform a LISTRIGHTS command
6266
+cmd_listrights(tag, name, identifier)
6271
+ char mailboxname[MAX_MAILBOX_NAME+1];
6275
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6276
+ imapd_userid, mailboxname);
6279
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, &acl, NULL);
6281
+ if (r == IMAP_MAILBOX_MOVED) return;
6284
+ rights = cyrus_acl_myrights(imapd_authstate, acl);
6286
+ if (!rights && !imapd_userisadmin &&
6287
+ !mboxname_userownsmailbox(imapd_userid, mailboxname)) {
6288
+ r = IMAP_MAILBOX_NONEXISTENT;
6293
+ struct auth_state *authstate = auth_newstate(identifier);
6294
+ char *canon_identifier;
6295
+ int canonidlen = 0;
6297
+ char rightsdesc[100], optional[33];
6299
+ if (global_authisa(authstate, IMAPOPT_ADMINS))
6300
+ canon_identifier = identifier; /* don't canonify global admins */
6302
+ canon_identifier = canonify_userid(identifier, imapd_userid, NULL);
6303
+ auth_freestate(authstate);
6305
+ if (canon_identifier) canonidlen = strlen(canon_identifier);
6307
+ if (!canon_identifier) {
6310
+ else if (mboxname_userownsmailbox(canon_identifier, mailboxname)) {
6311
+ /* identifier's personal mailbox */
6312
+ implicit = config_implicitrights;
6314
+ else if (mboxname_isusermailbox(mailboxname, 1)) {
6315
+ /* anyone can post to an INBOX */
6316
+ implicit = ACL_POST;
6322
+ /* calculate optional rights */
6323
+ cyrus_acl_masktostr(implicit ^ (canon_identifier ? ACL_FULL : 0),
6326
+ /* build the rights string */
6328
+ cyrus_acl_masktostr(implicit, rightsdesc);
6331
+ strcpy(rightsdesc, "\"\"");
6335
+ int i, n = strlen(optional);
6336
+ char *p = rightsdesc + strlen(rightsdesc);
6338
+ for (i = 0; i < n; i++) {
6340
+ *p++ = optional[i];
6345
+ prot_printf(imapd_out, "* LISTRIGHTS ");
6346
+ printastring(name);
6347
+ prot_putc(' ', imapd_out);
6348
+ printastring(identifier);
6349
+ prot_printf(imapd_out, " %s", rightsdesc);
6351
+ prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag,
6352
+ error_message(IMAP_OK_COMPLETED));
6356
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6360
+ * Perform a MYRIGHTS command
6362
+void cmd_myrights(const char *tag, const char *name)
6364
+ char mailboxname[MAX_MAILBOX_NAME+1];
6365
+ int r, rights = 0;
6367
+ char str[ACL_MAXSTR];
6369
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6370
+ imapd_userid, mailboxname);
6373
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, &acl, NULL);
6375
+ if (r == IMAP_MAILBOX_MOVED) return;
6378
+ rights = cyrus_acl_myrights(imapd_authstate, acl);
6380
+ /* Add in implicit rights */
6381
+ if (imapd_userisadmin) {
6382
+ rights |= ACL_LOOKUP|ACL_ADMIN;
6384
+ else if (mboxname_userownsmailbox(imapd_userid, mailboxname)) {
6385
+ rights |= config_implicitrights;
6389
+ r = IMAP_MAILBOX_NONEXISTENT;
6393
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6397
+ prot_printf(imapd_out, "* MYRIGHTS ");
6398
+ printastring(name);
6399
+ prot_printf(imapd_out, " ");
6400
+ printastring(cyrus_acl_masktostr(rights, str));
6401
+ prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag,
6402
+ error_message(IMAP_OK_COMPLETED));
6406
+ * Perform a SETACL command
6408
+void cmd_setacl(const char *tag, const char *name,
6409
+ const char *identifier, const char *rights)
6412
+ char mailboxname[MAX_MAILBOX_NAME+1];
6414
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6415
+ imapd_userid, mailboxname);
6417
+ /* is it remote? */
6419
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
6421
+ if (r == IMAP_MAILBOX_MOVED) return;
6424
+ r = mboxlist_setacl(mailboxname, identifier, rights,
6425
+ imapd_userisadmin, imapd_userid, imapd_authstate);
6429
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6433
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6434
+ error_message(IMAP_OK_COMPLETED));
6438
+ * Perform a GETQUOTA command
6441
+cmd_getquota(const char *tag, const char *name)
6444
+ struct quota quota;
6445
+ char mailboxname[MAX_MAILBOX_NAME+1];
6447
+ if (!imapd_userisadmin) r = IMAP_PERMISSION_DENIED;
6449
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6450
+ imapd_userid, mailboxname);
6452
+ quota.root = mailboxname;
6453
+ r = quota_read("a, NULL, 0);
6458
+ prot_printf(imapd_out, "* QUOTA ");
6459
+ printastring(name);
6460
+ prot_printf(imapd_out, " (");
6461
+ if (quota.limit >= 0) {
6462
+ prot_printf(imapd_out, "STORAGE %lu %d",
6463
+ quota.used/QUOTA_UNITS, quota.limit);
6465
+ prot_printf(imapd_out, ")\r\n");
6469
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6473
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6474
+ error_message(IMAP_OK_COMPLETED));
6479
+ * Perform a GETQUOTAROOT command
6482
+cmd_getquotaroot(const char *tag, const char *name)
6484
+ char mailboxname[MAX_MAILBOX_NAME+1];
6485
+ struct mailbox mailbox;
6489
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6490
+ imapd_userid, mailboxname);
6493
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
6495
+ if (r == IMAP_MAILBOX_MOVED) return;
6498
+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
6503
+ if (!imapd_userisadmin && !(mailbox.myrights & ACL_READ)) {
6504
+ r = (mailbox.myrights & ACL_LOOKUP) ?
6505
+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
6510
+ prot_printf(imapd_out, "* QUOTAROOT ");
6511
+ printastring(name);
6512
+ if (mailbox.quota.root) {
6513
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
6514
+ mailbox.quota.root,
6515
+ imapd_userid, mailboxname);
6516
+ prot_printf(imapd_out, " ");
6517
+ printastring(mailboxname);
6518
+ r = quota_read(&mailbox.quota, NULL, 0);
6520
+ prot_printf(imapd_out, "\r\n* QUOTA ");
6521
+ printastring(mailboxname);
6522
+ prot_printf(imapd_out, " (");
6523
+ if (mailbox.quota.limit >= 0) {
6524
+ prot_printf(imapd_out, "STORAGE %lu %d",
6525
+ mailbox.quota.used/QUOTA_UNITS,
6526
+ mailbox.quota.limit);
6528
+ prot_putc(')', imapd_out);
6531
+ prot_printf(imapd_out, "\r\n");
6534
+ if (doclose) mailbox_close(&mailbox);
6537
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6541
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6542
+ error_message(IMAP_OK_COMPLETED));
6546
+ * Parse and perform a SETQUOTA command
6547
+ * The command has been parsed up to the resource list
6550
+cmd_setquota(const char *tag, const char *quotaroot)
6552
+ int newquota = -1;
6553
+ int badresource = 0;
6556
+ static struct buf arg;
6559
+ char mailboxname[MAX_MAILBOX_NAME+1];
6561
+ c = prot_getc(imapd_in);
6562
+ if (c != '(') goto badlist;
6564
+ c = getword(imapd_in, &arg);
6565
+ if (c != ')' || arg.s[0] != '\0') {
6567
+ if (c != ' ') goto badlist;
6568
+ if (strcasecmp(arg.s, "storage") != 0) badresource = 1;
6569
+ c = getword(imapd_in, &arg);
6570
+ if (c != ' ' && c != ')') goto badlist;
6571
+ if (arg.s[0] == '\0') goto badlist;
6573
+ for (p = arg.s; *p; p++) {
6574
+ if (!isdigit((int) *p)) goto badlist;
6575
+ newquota = newquota * 10 + *p - '0';
6576
+ if (newquota < 0) goto badlist; /* overflow */
6578
+ if (c == ')') break;
6581
+ c = prot_getc(imapd_in);
6582
+ if (c == '\r') c = prot_getc(imapd_in);
6584
+ prot_printf(imapd_out, "%s BAD Unexpected extra arguments to SETQUOTA\r\n", tag);
6585
+ eatline(imapd_in, c);
6589
+ if (badresource) r = IMAP_UNSUPPORTED_QUOTA;
6590
+ else if (!imapd_userisadmin && !imapd_userisproxyadmin) {
6591
+ /* need to allow proxies so that mailbox moves can set initial quota
6593
+ r = IMAP_PERMISSION_DENIED;
6595
+ /* are we forcing the creation of a quotaroot by having a leading +? */
6596
+ if(quotaroot[0] == '+') {
6601
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, quotaroot,
6602
+ imapd_userid, mailboxname);
6605
+ r = mboxlist_setquota(mailboxname, newquota, force);
6610
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6614
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6615
+ error_message(IMAP_OK_COMPLETED));
6619
+ prot_printf(imapd_out, "%s BAD Invalid quota list in Setquota\r\n", tag);
6620
+ eatline(imapd_in, c);
6625
+ * this implements the STARTTLS command, as described in RFC 2595.
6626
+ * one caveat: it assumes that no external layer is currently present.
6627
+ * if a client executes this command, information about the external
6628
+ * layer that was passed on the command line is disgarded. this should
6631
+/* imaps - whether this is an imaps transaction or not */
6632
+void cmd_starttls(char *tag, int imaps)
6640
+ /* SASL and openssl have different ideas about whether ssf is signed */
6641
+ layerp = (int *) &ssf;
6643
+ if (imapd_starttls_done == 1)
6645
+ prot_printf(imapd_out, "%s NO TLS already active\r\n", tag);
6649
+ result=tls_init_serverengine("imap",
6650
+ 5, /* depth to verify */
6651
+ !imaps, /* can client auth? */
6652
+ !imaps); /* TLS only? */
6654
+ if (result == -1) {
6656
+ syslog(LOG_ERR, "error initializing TLS");
6659
+ prot_printf(imapd_out, "%s NO Error initializing TLS\r\n", tag);
6661
+ fatal("tls_init() failed", EC_CONFIG);
6669
+ prot_printf(imapd_out, "%s OK Begin TLS negotiation now\r\n", tag);
6670
+ /* must flush our buffers before starting tls */
6671
+ prot_flush(imapd_out);
6674
+ result=tls_start_servertls(0, /* read */
6683
+ prot_printf(imapd_out, "%s NO Starttls negotiation failed\r\n",
6685
+ syslog(LOG_NOTICE, "STARTTLS negotiation failed: %s",
6686
+ imapd_clienthost);
6689
+ syslog(LOG_NOTICE, "imaps TLS negotiation failed: %s",
6690
+ imapd_clienthost);
6691
+ fatal("tls_start_servertls() failed", EC_TEMPFAIL);
6696
+ /* tell SASL about the negotiated layer */
6697
+ result = sasl_setprop(imapd_saslconn, SASL_SSF_EXTERNAL, &ssf);
6698
+ if (result != SASL_OK) {
6699
+ fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
6701
+ saslprops.ssf = ssf;
6703
+ result = sasl_setprop(imapd_saslconn, SASL_AUTH_EXTERNAL, auth_id);
6704
+ if (result != SASL_OK) {
6705
+ fatal("sasl_setprop() failed: cmd_starttls()", EC_TEMPFAIL);
6707
+ if(saslprops.authid) {
6708
+ free(saslprops.authid);
6709
+ saslprops.authid = NULL;
6712
+ saslprops.authid = xstrdup(auth_id);
6714
+ /* tell the prot layer about our new layers */
6715
+ prot_settls(imapd_in, tls_conn);
6716
+ prot_settls(imapd_out, tls_conn);
6718
+ imapd_starttls_done = 1;
6721
+void cmd_starttls(char *tag, int imaps)
6723
+ fatal("cmd_starttls() executed, but starttls isn't implemented!",
6726
+#endif /* HAVE_SSL */
6729
+ * Parse and perform a STATUS command
6730
+ * The command has been parsed up to the attribute list
6733
+cmd_status(tag, name)
6738
+ int statusitems = 0;
6739
+ static struct buf arg;
6740
+ struct mailbox mailbox;
6741
+ char mailboxname[MAX_MAILBOX_NAME+1];
6745
+ c = prot_getc(imapd_in);
6746
+ if (c != '(') goto badlist;
6748
+ c = getword(imapd_in, &arg);
6749
+ if (arg.s[0] == '\0') goto badlist;
6752
+ if (!strcmp(arg.s, "messages")) {
6753
+ statusitems |= STATUS_MESSAGES;
6755
+ else if (!strcmp(arg.s, "recent")) {
6756
+ statusitems |= STATUS_RECENT;
6758
+ else if (!strcmp(arg.s, "uidnext")) {
6759
+ statusitems |= STATUS_UIDNEXT;
6761
+ else if (!strcmp(arg.s, "uidvalidity")) {
6762
+ statusitems |= STATUS_UIDVALIDITY;
6764
+ else if (!strcmp(arg.s, "unseen")) {
6765
+ statusitems |= STATUS_UNSEEN;
6768
+ prot_printf(imapd_out, "%s BAD Invalid Status attribute %s\r\n",
6770
+ eatline(imapd_in, c);
6774
+ if (c == ' ') c = getword(imapd_in, &arg);
6779
+ prot_printf(imapd_out,
6780
+ "%s BAD Missing close parenthesis in Status\r\n", tag);
6781
+ eatline(imapd_in, c);
6785
+ c = prot_getc(imapd_in);
6786
+ if (c == '\r') c = prot_getc(imapd_in);
6788
+ prot_printf(imapd_out,
6789
+ "%s BAD Unexpected extra arguments to Status\r\n", tag);
6790
+ eatline(imapd_in, c);
6795
+ * Perform a full checkpoint of any open mailbox, in case we're
6796
+ * doing a STATUS check of the current mailbox.
6798
+ if (imapd_mailbox) {
6799
+ index_check(imapd_mailbox, 0, 1);
6802
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
6803
+ imapd_userid, mailboxname);
6806
+ r = mlookup(tag, name, mailboxname, NULL, NULL, NULL, NULL, NULL);
6808
+ if (r == IMAP_MAILBOX_MOVED) return;
6811
+ r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
6816
+ r = mailbox_open_index(&mailbox);
6818
+ if (!r && !(mailbox.myrights & ACL_READ)) {
6819
+ r = (imapd_userisadmin || (mailbox.myrights & ACL_LOOKUP)) ?
6820
+ IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
6824
+ r = index_status(&mailbox, name, statusitems);
6827
+ if (doclose) mailbox_close(&mailbox);
6830
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
6834
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6835
+ error_message(IMAP_OK_COMPLETED));
6839
+ prot_printf(imapd_out, "%s BAD Invalid status list in Status\r\n", tag);
6840
+ eatline(imapd_in, c);
6843
+#ifdef ENABLE_X_NETSCAPE_HACK
6845
+ * Reply to Netscape's crock with a crock of my own
6847
+void cmd_netscrape(char *tag)
6851
+ url = config_getstring(IMAPOPT_NETSCAPEURL);
6853
+ /* I only know of three things to reply with: */
6854
+ prot_printf(imapd_out,
6855
+ "* OK [NETSCAPE] Carnegie Mellon Cyrus IMAP\r\n"
6856
+ "* VERSION %s\r\n",
6858
+ if (url) prot_printf(imapd_out, "* ACCOUNT-URL %s\r\n", url);
6859
+ prot_printf(imapd_out, "%s OK %s\r\n",
6860
+ tag, error_message(IMAP_OK_COMPLETED));
6862
+#endif /* ENABLE_X_NETSCAPE_HACK */
6864
+/* Callback for cmd_namespace to be passed to mboxlist_findall.
6865
+ * For each top-level mailbox found, print a bit of the response
6866
+ * if it is a shared namespace. The rock is used as an integer in
6867
+ * order to ensure the namespace response is correct on a server with
6868
+ * no shared namespace.
6870
+static int namespacedata(char *name,
6871
+ int matchlen __attribute__((unused)),
6872
+ int maycreate __attribute__((unused)),
6875
+ int* sawone = (int*) rock;
6881
+ if (!(strncmp(name, "INBOX.", 6))) {
6882
+ /* The user has a "personal" namespace. */
6883
+ sawone[NAMESPACE_INBOX] = 1;
6884
+ } else if (mboxname_isusermailbox(name, 0)) {
6885
+ /* The user can see the "other users" namespace. */
6886
+ sawone[NAMESPACE_USER] = 1;
6888
+ /* The user can see the "shared" namespace. */
6889
+ sawone[NAMESPACE_SHARED] = 1;
6896
+ * Print out a response to the NAMESPACE command defined by
6899
+void cmd_namespace(tag)
6902
+ int sawone[3] = {0, 0, 0};
6905
+ if (SLEEZY_NAMESPACE) {
6906
+ char inboxname[MAX_MAILBOX_NAME+1];
6908
+ if (strlen(imapd_userid) + 5 > MAX_MAILBOX_NAME)
6909
+ sawone[NAMESPACE_INBOX] = 0;
6911
+ (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX",
6912
+ imapd_userid, inboxname);
6913
+ sawone[NAMESPACE_INBOX] =
6914
+ !mboxlist_lookup(inboxname, NULL, NULL, NULL);
6916
+ sawone[NAMESPACE_USER] = 1;
6917
+ sawone[NAMESPACE_SHARED] = 1;
6919
+ pattern = xstrdup("%");
6920
+ /* now find all the exciting toplevel namespaces -
6921
+ * we're using internal names here
6923
+ mboxlist_findall(NULL, pattern, imapd_userisadmin, imapd_userid,
6924
+ imapd_authstate, namespacedata, (void*) sawone);
6928
+ prot_printf(imapd_out, "* NAMESPACE");
6929
+ if (sawone[NAMESPACE_INBOX]) {
6930
+ prot_printf(imapd_out, " ((\"%s\" \"%c\"))",
6931
+ imapd_namespace.prefix[NAMESPACE_INBOX],
6932
+ imapd_namespace.hier_sep);
6934
+ prot_printf(imapd_out, " NIL");
6936
+ if (sawone[NAMESPACE_USER]) {
6937
+ prot_printf(imapd_out, " ((\"%s\" \"%c\"))",
6938
+ imapd_namespace.prefix[NAMESPACE_USER],
6939
+ imapd_namespace.hier_sep);
6941
+ prot_printf(imapd_out, " NIL");
6943
+ if (sawone[NAMESPACE_SHARED]) {
6944
+ prot_printf(imapd_out, " ((\"%s\" \"%c\"))",
6945
+ imapd_namespace.prefix[NAMESPACE_SHARED],
6946
+ imapd_namespace.hier_sep);
6948
+ prot_printf(imapd_out, " NIL");
6950
+ prot_printf(imapd_out, "\r\n");
6952
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
6953
+ error_message(IMAP_OK_COMPLETED));
6957
+ * Parse annotate fetch data.
6959
+ * This is a generic routine which parses just the annotation data.
6960
+ * Any surrounding command text must be parsed elsewhere, ie,
6961
+ * GETANNOTATION, FETCH.
6964
+int getannotatefetchdata(char *tag,
6965
+ struct strlist **entries, struct strlist **attribs)
6968
+ static struct buf arg;
6970
+ *entries = *attribs = NULL;
6972
+ c = prot_getc(imapd_in);
6974
+ prot_printf(imapd_out,
6975
+ "%s BAD Missing annotation entry\r\n", tag);
6978
+ else if (c == '(') {
6981
+ c = getqstring(imapd_in, imapd_out, &arg);
6983
+ prot_printf(imapd_out,
6984
+ "%s BAD Missing annotation entry\r\n", tag);
6988
+ /* add the entry to the list */
6989
+ appendstrlist(entries, arg.s);
6991
+ } while (c == ' ');
6994
+ prot_printf(imapd_out,
6995
+ "%s BAD Missing close paren in annotation entry list \r\n",
7000
+ c = prot_getc(imapd_in);
7003
+ /* single entry -- add it to the list */
7004
+ prot_ungetc(c, imapd_in);
7005
+ c = getqstring(imapd_in, imapd_out, &arg);
7007
+ prot_printf(imapd_out,
7008
+ "%s BAD Missing annotation entry\r\n", tag);
7012
+ appendstrlist(entries, arg.s);
7015
+ if (c != ' ' || (c = prot_getc(imapd_in)) == EOF) {
7016
+ prot_printf(imapd_out,
7017
+ "%s BAD Missing annotation attribute(s)\r\n", tag);
7024
+ c = getnstring(imapd_in, imapd_out, &arg);
7026
+ prot_printf(imapd_out,
7027
+ "%s BAD Missing annotation attribute(s)\r\n", tag);
7031
+ /* add the attrib to the list */
7032
+ appendstrlist(attribs, arg.s);
7034
+ } while (c == ' ');
7037
+ prot_printf(imapd_out,
7038
+ "%s BAD Missing close paren in "
7039
+ "annotation attribute list\r\n", tag);
7043
+ c = prot_getc(imapd_in);
7046
+ /* single attrib */
7047
+ prot_ungetc(c, imapd_in);
7048
+ c = getqstring(imapd_in, imapd_out, &arg);
7050
+ prot_printf(imapd_out,
7051
+ "%s BAD Missing annotation attribute\r\n", tag);
7055
+ appendstrlist(attribs, arg.s);
7061
+ if (c != EOF) prot_ungetc(c, imapd_in);
7066
+ * Parse annotate store data.
7068
+ * This is a generic routine which parses just the annotation data.
7069
+ * Any surrounding command text must be parsed elsewhere, ie,
7070
+ * SETANNOTATION, STORE, APPEND.
7073
+int getannotatestoredata(char *tag, struct entryattlist **entryatts)
7075
+ int c, islist = 0;
7076
+ static struct buf entry, attrib, value;
7077
+ struct attvaluelist *attvalues = NULL;
7079
+ *entryatts = NULL;
7081
+ c = prot_getc(imapd_in);
7083
+ prot_printf(imapd_out,
7084
+ "%s BAD Missing annotation entry\r\n", tag);
7087
+ else if (c == '(') {
7092
+ /* single entry -- put the char back */
7093
+ prot_ungetc(c, imapd_in);
7098
+ c = getqstring(imapd_in, imapd_out, &entry);
7100
+ prot_printf(imapd_out,
7101
+ "%s BAD Missing annotation entry\r\n", tag);
7105
+ /* parse att-value list */
7106
+ if (c != ' ' || (c = prot_getc(imapd_in)) != '(') {
7107
+ prot_printf(imapd_out,
7108
+ "%s BAD Missing annotation attribute-values list\r\n",
7115
+ c = getqstring(imapd_in, imapd_out, &attrib);
7117
+ prot_printf(imapd_out,
7118
+ "%s BAD Missing annotation attribute\r\n", tag);
7124
+ (c = getnstring(imapd_in, imapd_out, &value)) == EOF) {
7125
+ prot_printf(imapd_out,
7126
+ "%s BAD Missing annotation value\r\n", tag);
7130
+ /* add the attrib-value pair to the list */
7131
+ appendattvalue(&attvalues, attrib.s, value.s);
7133
+ } while (c == ' ');
7136
+ prot_printf(imapd_out,
7137
+ "%s BAD Missing close paren in annotation "
7138
+ "attribute-values list\r\n", tag);
7142
+ /* add the entry to the list */
7143
+ appendentryatt(entryatts, entry.s, attvalues);
7146
+ c = prot_getc(imapd_in);
7148
+ } while (c == ' ');
7152
+ prot_printf(imapd_out,
7153
+ "%s BAD Missing close paren in annotation entry list \r\n",
7158
+ c = prot_getc(imapd_in);
7164
+ if (attvalues) freeattvalues(attvalues);
7165
+ if (c != EOF) prot_ungetc(c, imapd_in);
7170
+ * Output an entry/attribute-value list response.
7172
+ * This is a generic routine which outputs just the annotation data.
7173
+ * Any surrounding response text must be output elsewhere, ie,
7174
+ * GETANNOTATION, FETCH.
7176
+void annotate_response(struct entryattlist *l)
7178
+ int islist; /* do we have more than one entry? */
7182
+ islist = (l->next != NULL);
7184
+ if (islist) prot_printf(imapd_out, "(");
7187
+ prot_printf(imapd_out, "\"%s\"", l->entry);
7189
+ /* do we have attributes? solicited vs. unsolicited */
7190
+ if (l->attvalues) {
7191
+ struct attvaluelist *av = l->attvalues;
7193
+ prot_printf(imapd_out, " (");
7195
+ prot_printf(imapd_out, "\"%s\" ", av->attrib);
7196
+ if (!strcasecmp(av->value, "NIL"))
7197
+ prot_printf(imapd_out, "NIL");
7199
+ prot_printf(imapd_out, "\"%s\"", av->value);
7201
+ if ((av = av->next) == NULL)
7202
+ prot_printf(imapd_out, ")");
7204
+ prot_printf(imapd_out, " ");
7208
+ if ((l = l->next) != NULL)
7209
+ prot_printf(imapd_out, " ");
7212
+ if (islist) prot_printf(imapd_out, ")");
7216
+ * Perform a GETANNOTATION command
7218
+ * The command has been parsed up to the entries
7220
+void cmd_getannotation(char *tag, char *mboxpat)
7223
+ struct strlist *entries = NULL, *attribs = NULL;
7225
+ c = getannotatefetchdata(tag, &entries, &attribs);
7227
+ eatline(imapd_in, c);
7231
+ /* check for CRLF */
7232
+ if (c == '\r') c = prot_getc(imapd_in);
7234
+ prot_printf(imapd_out,
7235
+ "%s BAD Unexpected extra arguments to Getannotation\r\n",
7237
+ eatline(imapd_in, c);
7241
+ r = annotatemore_fetch(mboxpat, entries, attribs, &imapd_namespace,
7242
+ imapd_userisadmin || imapd_userisproxyadmin,
7243
+ imapd_userid, imapd_authstate, imapd_out);
7246
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
7248
+ prot_printf(imapd_out, "%s OK %s\r\n",
7249
+ tag, error_message(IMAP_OK_COMPLETED));
7253
+ if (entries) freestrlist(entries);
7254
+ if (attribs) freestrlist(attribs);
7260
+ * Perform a SETANNOTATION command
7262
+ * The command has been parsed up to the entry-att list
7264
+void cmd_setannotation(char *tag, char *mboxpat)
7267
+ struct entryattlist *entryatts = NULL;
7269
+ c = getannotatestoredata(tag, &entryatts);
7271
+ eatline(imapd_in, c);
7275
+ /* check for CRLF */
7276
+ if (c == '\r') c = prot_getc(imapd_in);
7278
+ prot_printf(imapd_out,
7279
+ "%s BAD Unexpected extra arguments to Setannotation\r\n",
7281
+ eatline(imapd_in, c);
7285
+ r = annotatemore_store(mboxpat,
7286
+ entryatts, &imapd_namespace, imapd_userisadmin,
7287
+ imapd_userid, imapd_authstate);
7290
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
7292
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
7293
+ error_message(IMAP_OK_COMPLETED));
7297
+ if (entryatts) freeentryatts(entryatts);
7302
+ * Parse a search program
7304
+int getsearchprogram(tag, searchargs, charset, parsecharset)
7306
+struct searchargs *searchargs;
7313
+ c = getsearchcriteria(tag, searchargs, charset, parsecharset);
7315
+ } while (c == ' ');
7320
+ * Parse a search criteria
7322
+int getsearchcriteria(tag, searchargs, charset, parsecharset)
7324
+struct searchargs *searchargs;
7328
+ static struct buf criteria, arg;
7329
+ struct searchargs *sub1, *sub2;
7333
+ time_t start, end;
7335
+ c = getword(imapd_in, &criteria);
7336
+ lcase(criteria.s);
7337
+ switch (criteria.s[0]) {
7339
+ if (c != '(') goto badcri;
7340
+ c = getsearchprogram(tag, searchargs, charset, 0);
7341
+ if (c == EOF) return EOF;
7343
+ prot_printf(imapd_out, "%s BAD Missing required close paren in Search command\r\n",
7345
+ if (c != EOF) prot_ungetc(c, imapd_in);
7348
+ c = prot_getc(imapd_in);
7351
+ case '0': case '1': case '2': case '3': case '4':
7352
+ case '5': case '6': case '7': case '8': case '9':
7354
+ if (imparse_issequence(criteria.s)) {
7355
+ appendstrlist(&searchargs->sequence, criteria.s);
7361
+ if (!strcmp(criteria.s, "answered")) {
7362
+ searchargs->system_flags_set |= FLAG_ANSWERED;
7364
+ else if (!strcmp(criteria.s, "all")) {
7371
+ if (!strcmp(criteria.s, "before")) {
7372
+ if (c != ' ') goto missingarg;
7373
+ c = getsearchdate(&start, &end);
7374
+ if (c == EOF) goto baddate;
7375
+ if (!searchargs->before || searchargs->before > start) {
7376
+ searchargs->before = start;
7379
+ else if (!strcmp(criteria.s, "bcc")) {
7380
+ if (c != ' ') goto missingarg;
7381
+ c = getastring(imapd_in, imapd_out, &arg);
7382
+ if (c == EOF) goto missingarg;
7383
+ str = charset_convert(arg.s, *charset, NULL, 0);
7384
+ if (strchr(str, EMPTY)) {
7385
+ /* Force failure */
7386
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7389
+ appendstrlistpat(&searchargs->bcc, str);
7392
+ else if (!strcmp(criteria.s, "body")) {
7393
+ if (c != ' ') goto missingarg;
7394
+ c = getastring(imapd_in, imapd_out, &arg);
7395
+ if (c == EOF) goto missingarg;
7396
+ str = charset_convert(arg.s, *charset, NULL, 0);
7397
+ if (strchr(str, EMPTY)) {
7398
+ /* Force failure */
7399
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7402
+ appendstrlistpat(&searchargs->body, str);
7409
+ if (!strcmp(criteria.s, "cc")) {
7410
+ if (c != ' ') goto missingarg;
7411
+ c = getastring(imapd_in, imapd_out, &arg);
7412
+ if (c == EOF) goto missingarg;
7413
+ str = charset_convert(arg.s, *charset, NULL, 0);
7414
+ if (strchr(str, EMPTY)) {
7415
+ /* Force failure */
7416
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7419
+ appendstrlistpat(&searchargs->cc, str);
7422
+ else if (parsecharset && !strcmp(criteria.s, "charset")) {
7423
+ if (c != ' ') goto missingarg;
7424
+ c = getastring(imapd_in, imapd_out, &arg);
7425
+ if (c != ' ') goto missingarg;
7427
+ *charset = charset_lookupname(arg.s);
7433
+ if (!strcmp(criteria.s, "deleted")) {
7434
+ searchargs->system_flags_set |= FLAG_DELETED;
7436
+ else if (!strcmp(criteria.s, "draft")) {
7437
+ searchargs->system_flags_set |= FLAG_DRAFT;
7443
+ if (!strcmp(criteria.s, "flagged")) {
7444
+ searchargs->system_flags_set |= FLAG_FLAGGED;
7446
+ else if (!strcmp(criteria.s, "from")) {
7447
+ if (c != ' ') goto missingarg;
7448
+ c = getastring(imapd_in, imapd_out, &arg);
7449
+ if (c == EOF) goto missingarg;
7450
+ str = charset_convert(arg.s, *charset, NULL, 0);
7451
+ if (strchr(str, EMPTY)) {
7452
+ /* Force failure */
7453
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7456
+ appendstrlistpat(&searchargs->from, str);
7463
+ if (!strcmp(criteria.s, "header")) {
7464
+ struct strlist **patlist;
7466
+ if (c != ' ') goto missingarg;
7467
+ c = getastring(imapd_in, imapd_out, &arg);
7468
+ if (c != ' ') goto missingarg;
7471
+ /* some headers can be reduced to search terms */
7472
+ if (!strcmp(arg.s, "bcc")) {
7473
+ patlist = &searchargs->bcc;
7475
+ else if (!strcmp(arg.s, "cc")) {
7476
+ patlist = &searchargs->cc;
7478
+ else if (!strcmp(arg.s, "to")) {
7479
+ patlist = &searchargs->to;
7481
+ else if (!strcmp(arg.s, "from")) {
7482
+ patlist = &searchargs->from;
7484
+ else if (!strcmp(arg.s, "subject")) {
7485
+ patlist = &searchargs->subject;
7488
+ /* we look message-id up in the envelope */
7489
+ else if (!strcmp(arg.s, "message-id")) {
7490
+ patlist = &searchargs->messageid;
7493
+ /* all other headers we handle normally */
7495
+ if (searchargs->cache_atleast < BIT32_MAX) {
7497
+ mailbox_cached_header(arg.s);
7498
+ if(this_ver > searchargs->cache_atleast)
7499
+ searchargs->cache_atleast = this_ver;
7501
+ appendstrlist(&searchargs->header_name, arg.s);
7502
+ patlist = &searchargs->header;
7505
+ c = getastring(imapd_in, imapd_out, &arg);
7506
+ if (c == EOF) goto missingarg;
7507
+ str = charset_convert(arg.s, *charset, NULL, 0);
7508
+ if (strchr(str, EMPTY)) {
7509
+ /* Force failure */
7510
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7513
+ appendstrlistpat(patlist, str);
7520
+ if (!strcmp(criteria.s, "keyword")) {
7521
+ if (c != ' ') goto missingarg;
7522
+ c = getword(imapd_in, &arg);
7523
+ if (!imparse_isatom(arg.s)) goto badflag;
7525
+ for (flag=0; flag < MAX_USER_FLAGS; flag++) {
7526
+ if (imapd_mailbox->flagname[flag] &&
7527
+ !strcasecmp(imapd_mailbox->flagname[flag], arg.s)) break;
7529
+ if (flag == MAX_USER_FLAGS) {
7530
+ /* Force failure */
7531
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7534
+ searchargs->user_flags_set[flag/32] |= 1<<(flag&31);
7540
+ if (!strcmp(criteria.s, "larger")) {
7541
+ if (c != ' ') goto missingarg;
7542
+ c = getword(imapd_in, &arg);
7544
+ for (p = arg.s; *p && isdigit((int) *p); p++) {
7545
+ size = size * 10 + *p - '0';
7546
+ /* if (size < 0) goto badnumber; */
7548
+ if (!arg.s || *p) goto badnumber;
7549
+ if (size > searchargs->larger) searchargs->larger = size;
7555
+ if (!strcmp(criteria.s, "not")) {
7556
+ if (c != ' ') goto missingarg;
7557
+ sub1 = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
7558
+ c = getsearchcriteria(tag, sub1, charset, 0);
7560
+ freesearchargs(sub1);
7564
+ appendsearchargs(searchargs, sub1, (struct searchargs *)0);
7566
+ else if (!strcmp(criteria.s, "new")) {
7567
+ searchargs->flags |= (SEARCH_SEEN_UNSET|SEARCH_RECENT_SET);
7573
+ if (!strcmp(criteria.s, "or")) {
7574
+ if (c != ' ') goto missingarg;
7575
+ sub1 = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
7576
+ c = getsearchcriteria(tag, sub1, charset, 0);
7578
+ freesearchargs(sub1);
7581
+ if (c != ' ') goto missingarg;
7582
+ sub2 = (struct searchargs *)xzmalloc(sizeof(struct searchargs));
7583
+ c = getsearchcriteria(tag, sub2, charset, 0);
7585
+ freesearchargs(sub1);
7586
+ freesearchargs(sub2);
7589
+ appendsearchargs(searchargs, sub1, sub2);
7591
+ else if (!strcmp(criteria.s, "old")) {
7592
+ searchargs->flags |= SEARCH_RECENT_UNSET;
7594
+ else if (!strcmp(criteria.s, "on")) {
7595
+ if (c != ' ') goto missingarg;
7596
+ c = getsearchdate(&start, &end);
7597
+ if (c == EOF) goto baddate;
7598
+ if (!searchargs->before || searchargs->before > end) {
7599
+ searchargs->before = end;
7601
+ if (!searchargs->after || searchargs->after < start) {
7602
+ searchargs->after = start;
7609
+ if (!strcmp(criteria.s, "recent")) {
7610
+ searchargs->flags |= SEARCH_RECENT_SET;
7616
+ if (!strcmp(criteria.s, "seen")) {
7617
+ searchargs->flags |= SEARCH_SEEN_SET;
7619
+ else if (!strcmp(criteria.s, "sentbefore")) {
7620
+ if (c != ' ') goto missingarg;
7621
+ c = getsearchdate(&start, &end);
7622
+ if (c == EOF) goto baddate;
7623
+ if (!searchargs->sentbefore || searchargs->sentbefore > start) {
7624
+ searchargs->sentbefore = start;
7627
+ else if (!strcmp(criteria.s, "senton")) {
7628
+ if (c != ' ') goto missingarg;
7629
+ c = getsearchdate(&start, &end);
7630
+ if (c == EOF) goto baddate;
7631
+ if (!searchargs->sentbefore || searchargs->sentbefore > end) {
7632
+ searchargs->sentbefore = end;
7634
+ if (!searchargs->sentafter || searchargs->sentafter < start) {
7635
+ searchargs->sentafter = start;
7638
+ else if (!strcmp(criteria.s, "sentsince")) {
7639
+ if (c != ' ') goto missingarg;
7640
+ c = getsearchdate(&start, &end);
7641
+ if (c == EOF) goto baddate;
7642
+ if (!searchargs->sentafter || searchargs->sentafter < start) {
7643
+ searchargs->sentafter = start;
7646
+ else if (!strcmp(criteria.s, "since")) {
7647
+ if (c != ' ') goto missingarg;
7648
+ c = getsearchdate(&start, &end);
7649
+ if (c == EOF) goto baddate;
7650
+ if (!searchargs->after || searchargs->after < start) {
7651
+ searchargs->after = start;
7654
+ else if (!strcmp(criteria.s, "smaller")) {
7655
+ if (c != ' ') goto missingarg;
7656
+ c = getword(imapd_in, &arg);
7658
+ for (p = arg.s; *p && isdigit((int) *p); p++) {
7659
+ size = size * 10 + *p - '0';
7660
+ /* if (size < 0) goto badnumber; */
7662
+ if (!arg.s || *p) goto badnumber;
7663
+ if (size == 0) size = 1;
7664
+ if (!searchargs->smaller || size < searchargs->smaller)
7665
+ searchargs->smaller = size;
7667
+ else if (!strcmp(criteria.s, "subject")) {
7668
+ if (c != ' ') goto missingarg;
7669
+ c = getastring(imapd_in, imapd_out, &arg);
7670
+ if (c == EOF) goto missingarg;
7671
+ str = charset_convert(arg.s, *charset, NULL, 0);
7672
+ if (strchr(str, EMPTY)) {
7673
+ /* Force failure */
7674
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7677
+ appendstrlistpat(&searchargs->subject, str);
7684
+ if (!strcmp(criteria.s, "to")) {
7685
+ if (c != ' ') goto missingarg;
7686
+ c = getastring(imapd_in, imapd_out, &arg);
7687
+ if (c == EOF) goto missingarg;
7688
+ str = charset_convert(arg.s, *charset, NULL, 0);
7689
+ if (strchr(str, EMPTY)) {
7690
+ /* Force failure */
7691
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7694
+ appendstrlistpat(&searchargs->to, str);
7697
+ else if (!strcmp(criteria.s, "text")) {
7698
+ if (c != ' ') goto missingarg;
7699
+ c = getastring(imapd_in, imapd_out, &arg);
7700
+ if (c == EOF) goto missingarg;
7701
+ str = charset_convert(arg.s, *charset, NULL, 0);
7702
+ if (strchr(str, EMPTY)) {
7703
+ /* Force failure */
7704
+ searchargs->flags = (SEARCH_RECENT_SET|SEARCH_RECENT_UNSET);
7707
+ appendstrlistpat(&searchargs->text, str);
7714
+ if (!strcmp(criteria.s, "uid")) {
7715
+ if (c != ' ') goto missingarg;
7716
+ c = getword(imapd_in, &arg);
7717
+ if (!imparse_issequence(arg.s)) goto badcri;
7718
+ appendstrlist(&searchargs->uidsequence, arg.s);
7720
+ else if (!strcmp(criteria.s, "unseen")) {
7721
+ searchargs->flags |= SEARCH_SEEN_UNSET;
7723
+ else if (!strcmp(criteria.s, "unanswered")) {
7724
+ searchargs->system_flags_unset |= FLAG_ANSWERED;
7726
+ else if (!strcmp(criteria.s, "undeleted")) {
7727
+ searchargs->system_flags_unset |= FLAG_DELETED;
7729
+ else if (!strcmp(criteria.s, "undraft")) {
7730
+ searchargs->system_flags_unset |= FLAG_DRAFT;
7732
+ else if (!strcmp(criteria.s, "unflagged")) {
7733
+ searchargs->system_flags_unset |= FLAG_FLAGGED;
7735
+ else if (!strcmp(criteria.s, "unkeyword")) {
7736
+ if (c != ' ') goto missingarg;
7737
+ c = getword(imapd_in, &arg);
7738
+ if (!imparse_isatom(arg.s)) goto badflag;
7740
+ for (flag=0; flag < MAX_USER_FLAGS; flag++) {
7741
+ if (imapd_mailbox->flagname[flag] &&
7742
+ !strcasecmp(imapd_mailbox->flagname[flag], arg.s)) break;
7744
+ if (flag != MAX_USER_FLAGS) {
7745
+ searchargs->user_flags_unset[flag/32] |= 1<<(flag&31);
7753
+ prot_printf(imapd_out, "%s BAD Invalid Search criteria\r\n", tag);
7754
+ if (c != EOF) prot_ungetc(c, imapd_in);
7761
+ prot_printf(imapd_out, "%s BAD Missing required argument to Search %s\r\n",
7763
+ if (c != EOF) prot_ungetc(c, imapd_in);
7767
+ prot_printf(imapd_out, "%s BAD Invalid flag name %s in Search command\r\n",
7769
+ if (c != EOF) prot_ungetc(c, imapd_in);
7773
+ prot_printf(imapd_out, "%s BAD Invalid date in Search command\r\n", tag);
7774
+ if (c != EOF) prot_ungetc(c, imapd_in);
7778
+ prot_printf(imapd_out, "%s BAD Invalid number in Search command\r\n", tag);
7779
+ if (c != EOF) prot_ungetc(c, imapd_in);
7783
+void cmd_dump(char *tag, char *name, int uid_start)
7786
+ char mailboxname[MAX_MAILBOX_NAME+1];
7789
+ /* administrators only please */
7790
+ if (!imapd_userisadmin) {
7791
+ r = IMAP_PERMISSION_DENIED;
7795
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
7796
+ imapd_userid, mailboxname);
7800
+ r = mlookup(tag, name, mailboxname, NULL, &path, NULL, &acl, NULL);
7802
+ if (r == IMAP_MAILBOX_MOVED) return;
7805
+ r = dump_mailbox(tag, mailboxname, path, acl, uid_start, imapd_in,
7806
+ imapd_out, imapd_authstate);
7810
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
7812
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
7813
+ error_message(IMAP_OK_COMPLETED));
7817
+void cmd_undump(char *tag, char *name)
7820
+ char mailboxname[MAX_MAILBOX_NAME+1];
7823
+ /* administrators only please */
7824
+ if (!imapd_userisadmin) {
7825
+ r = IMAP_PERMISSION_DENIED;
7829
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
7830
+ imapd_userid, mailboxname);
7834
+ r = mlookup(tag, name, mailboxname, NULL, &path, NULL, &acl, NULL);
7836
+ if (r == IMAP_MAILBOX_MOVED) return;
7839
+ /* save this stuff from additional mlookups */
7840
+ char *safe_path = xstrdup(path);
7841
+ char *safe_acl = xstrdup(acl);
7842
+ r = undump_mailbox(mailboxname, safe_path, safe_acl,
7843
+ imapd_in, imapd_out,
7850
+ prot_printf(imapd_out, "%s NO %s%s\r\n",
7852
+ (r == IMAP_MAILBOX_NONEXISTENT &&
7853
+ mboxlist_createmailboxcheck(mailboxname, 0, 0,
7854
+ imapd_userisadmin,
7855
+ imapd_userid, imapd_authstate,
7857
+ ? "[TRYCREATE] " : "", error_message(r));
7859
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
7860
+ error_message(IMAP_OK_COMPLETED));
7864
+static int getresult(struct protstream *p, char *tag)
7867
+ char *str = (char *) buf;
7870
+ if (!prot_fgets(str, sizeof(buf), p)) {
7871
+ return IMAP_SERVER_UNAVAILABLE;
7873
+ if (!strncmp(str, tag, strlen(tag))) {
7874
+ str += strlen(tag);
7876
+ /* We got a tag, but no response */
7877
+ return IMAP_SERVER_UNAVAILABLE;
7880
+ if (!strncasecmp(str, "OK ", 3)) { return 0; }
7881
+ if (!strncasecmp(str, "NO ", 3)) { return IMAP_REMOTE_DENIED; }
7882
+ return IMAP_SERVER_UNAVAILABLE; /* huh? */
7884
+ /* skip this line, we don't really care */
7888
+/* given 2 protstreams and a mailbox, gets the acl and then wipes it */
7889
+static int trashacl(struct protstream *pin, struct protstream *pout,
7894
+ int c; /* getword() returns an int */
7895
+ struct buf tag, cmd, tmp, user;
7898
+ memset(&tag, 0, sizeof(struct buf));
7899
+ memset(&cmd, 0, sizeof(struct buf));
7900
+ memset(&tmp, 0, sizeof(struct buf));
7901
+ memset(&user, 0, sizeof(struct buf));
7903
+ prot_printf(pout, "ACL0 GETACL {%lu+}\r\n%s\r\n",
7904
+ (unsigned long) strlen(mailbox), mailbox);
7907
+ c = getword(pin, &tag);
7909
+ r = IMAP_SERVER_UNAVAILABLE;
7913
+ c = getword(pin, &cmd);
7915
+ r = IMAP_SERVER_UNAVAILABLE;
7920
+ c = prot_getc(pin);
7922
+ r = IMAP_SERVER_UNAVAILABLE;
7926
+ if(c == '\n') goto cleanup;
7928
+ if (tag.s[0] == '*' && !strncmp(cmd.s, "ACL", 3)) {
7929
+ while(c != '\n') {
7930
+ /* An ACL response, we should send a DELETEACL command */
7931
+ c = getastring(pin, pout, &tmp);
7933
+ r = IMAP_SERVER_UNAVAILABLE;
7938
+ c = prot_getc(pin);
7940
+ r = IMAP_SERVER_UNAVAILABLE;
7944
+ if(c == '\n') goto cleanup;
7946
+ c = getastring(pin, pout, &user);
7948
+ r = IMAP_SERVER_UNAVAILABLE;
7952
+ snprintf(tagbuf, sizeof(tagbuf), "ACL%d", ++i);
7954
+ prot_printf(pout, "%s DELETEACL {%lu+}\r\n%s {%lu+}\r\n%s\r\n",
7955
+ tagbuf, (unsigned long) strlen(mailbox), mailbox,
7956
+ (unsigned long) strlen(user.s), user.s);
7958
+ c = prot_getc(pin);
7960
+ r = IMAP_SERVER_UNAVAILABLE;
7964
+ /* if the next character is \n, we'll exit the loop */
7967
+ } else if (!strncmp(tag.s, "ACL0", 4)) {
7968
+ /* end of this command */
7969
+ if (!strcasecmp(cmd.s, "OK")) { break; }
7970
+ if (!strcasecmp(cmd.s, "NO")) { r = IMAP_REMOTE_DENIED; break; }
7971
+ r = IMAP_SERVER_UNAVAILABLE;
7978
+ /* Now cleanup after all the DELETEACL commands */
7981
+ c = getword(pin, &tag);
7983
+ r = IMAP_SERVER_UNAVAILABLE;
7989
+ if(!strncmp("ACL", tag.s, 3)) {
7995
+ if(r) eatline(pin, c);
8005
+static int dumpacl(struct protstream *pin, struct protstream *pout,
8006
+ char *mailbox, char *acl_in)
8009
+ int c; /* getword() returns an int */
8012
+ char *rights, *nextid;
8013
+ int mailboxlen = strlen(mailbox);
8014
+ char *acl_safe = acl_in ? xstrdup(acl_in) : NULL;
8015
+ char *acl = acl_safe;
8018
+ memset(&inbuf, 0, sizeof(struct buf));
8021
+ rights = strchr(acl, '\t');
8022
+ if (!rights) break;
8025
+ nextid = strchr(rights, '\t');
8026
+ if (!nextid) break;
8029
+ snprintf(tag, sizeof(tag), "SACL%d", tagnum++);
8031
+ prot_printf(pout, "%s SETACL {%d+}\r\n%s {%lu+}\r\n%s {%lu+}\r\n%s\r\n",
8033
+ mailboxlen, mailbox,
8034
+ (unsigned long) strlen(acl), acl,
8035
+ (unsigned long) strlen(rights), rights);
8038
+ c = getword(pin, &inbuf);
8040
+ r = IMAP_SERVER_UNAVAILABLE;
8043
+ if(strncmp(tag, inbuf.s, strlen(tag))) {
8047
+ /* this is our line */
8054
+ c = getword(pin, &inbuf);
8056
+ r = IMAP_SERVER_UNAVAILABLE;
8060
+ if(strncmp("OK", inbuf.s, 2)) {
8061
+ r = IMAP_REMOTE_DENIED;
8065
+ /* Eat the line and get the next one */
8071
+ if(acl_safe) free(acl_safe);
8076
+static int do_xfer_single(char *toserver, char *topart,
8077
+ char *name, char *mailboxname,
8079
+ char *path, char *part, char *acl,
8081
+ mupdate_handle *h_in,
8082
+ struct backend *be_in)
8084
+ int r = 0, rerr = 0;
8085
+ char buf[MAX_PARTITION_LEN+HOSTNAME_SIZE+2];
8086
+ struct backend *be = NULL;
8087
+ mupdate_handle *mupdate_h = NULL;
8088
+ int backout_mupdate = 0;
8089
+ int backout_remotebox = 0;
8090
+ int backout_remoteflag = 0;
8092
+ /* Make sure we're given a sane value */
8093
+ if(topart && !imparse_isatom(topart)) {
8094
+ return IMAP_PARTITION_UNKNOWN;
8097
+ if(!strcmp(toserver, config_servername)) {
8098
+ return IMAP_BAD_SERVER;
8101
+ /* Okay, we have the mailbox, now the order of steps is:
8103
+ * 1) Connect to remote server.
8104
+ * 2) LOCALCREATE on remote server
8105
+ * 2.5) Set mailbox as REMOTE on local server
8106
+ * 3) mupdate.DEACTIVATE(mailbox, remoteserver) xxx what partition?
8107
+ * 4) undump mailbox from local to remote
8108
+ * 5) Sync remote acl
8109
+ * 6) mupdate.ACTIVATE(mailbox, remoteserver)
8110
+ * ** MAILBOX NOW LIVING ON REMOTE SERVER
8111
+ * 6.5) force remote server to push the final mupdate entry to ensure
8112
+ * that the state of the world is correct (required if we do not
8113
+ * know the remote partition, but worst case it will be caught
8114
+ * when they next sync)
8115
+ * 7) local delete of mailbox
8116
+ * 8) remove local remote mailbox entry??????
8119
+ /* Step 1: Connect to remote server */
8120
+ if(!r && !be_in) {
8121
+ /* Just authorize as the IMAP server, so pass "" as our authzid */
8122
+ be = backend_connect(NULL, toserver, &protocol[PROTOCOL_IMAP], "", NULL);
8123
+ if(!be) r = IMAP_SERVER_UNAVAILABLE;
8124
+ if(r) syslog(LOG_ERR,
8125
+ "Could not move mailbox: %s, Backend connect failed",
8131
+ /* Step 1a: Connect to mupdate (as needed) */
8134
+ } else if (config_mupdate_server) {
8135
+ r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
8138
+ "Could not move mailbox: %s, MUPDATE connect failed",
8145
+ /* Step 2: LOCALCREATE on remote server */
8148
+ /* need to send partition as an atom */
8149
+ prot_printf(be->out, "LC1 LOCALCREATE {%lu+}\r\n%s %s\r\n",
8150
+ (unsigned long) strlen(name), name, topart);
8152
+ prot_printf(be->out, "LC1 LOCALCREATE {%lu+}\r\n%s\r\n",
8153
+ (unsigned long) strlen(name), name);
8155
+ r = getresult(be->in, "LC1");
8156
+ if(r) syslog(LOG_ERR, "Could not move mailbox: %s, LOCALCREATE failed",
8158
+ else backout_remotebox = 1;
8161
+ /* Step 2.5: Set mailbox as REMOTE on local server */
8163
+ snprintf(buf, sizeof(buf), "%s!%s", toserver, part);
8164
+ r = mboxlist_update(mailboxname, mbflags|MBTYPE_MOVING, buf, acl, 1);
8165
+ if(r) syslog(LOG_ERR, "Could not move mailbox: %s, " \
8166
+ "mboxlist_update failed", mailboxname);
8169
+ /* Step 3: mupdate.DEACTIVATE(mailbox, newserver) */
8170
+ /* (only if mailbox has not been already deactivated by our caller) */
8171
+ if(!r && mupdate_h && !prereserved) {
8172
+ backout_remoteflag = 1;
8174
+ /* Note we are making the reservation on OUR host so that recovery
8176
+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
8177
+ r = mupdate_deactivate(mupdate_h, mailboxname, buf);
8178
+ if(r) syslog(LOG_ERR,
8179
+ "Could not move mailbox: %s, MUPDATE DEACTIVATE failed",
8183
+ /* Step 4: Dump local -> remote */
8185
+ backout_mupdate = 1;
8187
+ prot_printf(be->out, "D01 UNDUMP {%lu+}\r\n%s ",
8188
+ (unsigned long) strlen(name), name);
8190
+ r = dump_mailbox(NULL, mailboxname, path, acl, 0, be->in, be->out,
8195
+ "Could not move mailbox: %s, dump_mailbox() failed",
8200
+ r = getresult(be->in, "D01");
8201
+ if(r) syslog(LOG_ERR, "Could not move mailbox: %s, UNDUMP failed",
8205
+ /* Step 5: Set ACL on remote */
8207
+ r = trashacl(be->in, be->out, name);
8208
+ if(r) syslog(LOG_ERR, "Could not clear remote acl on %s",
8212
+ r = dumpacl(be->in, be->out, name, acl);
8213
+ if(r) syslog(LOG_ERR, "Could not set remote acl on %s",
8217
+ /* Step 6: mupdate.activate(mailbox, remote) */
8218
+ /* We do this from the local server first so that recovery is easier */
8219
+ if(!r && mupdate_h) {
8220
+ /* Note the flag that we don't have a valid partiton at the moment */
8221
+ snprintf(buf, sizeof(buf), "%s!MOVED", toserver);
8222
+ r = mupdate_activate(mupdate_h, mailboxname, buf, acl);
8225
+ /* MAILBOX NOW LIVES ON REMOTE */
8227
+ backout_remotebox = 0;
8228
+ backout_mupdate = 0;
8229
+ backout_remoteflag = 0;
8231
+ /* 6.5) Kick remote server to correct mupdate entry */
8232
+ /* Note that we don't really care if this succeeds or not */
8234
+ prot_printf(be->out, "MP1 MUPDATEPUSH {%lu+}\r\n%s\r\n",
8235
+ (unsigned long) strlen(name), name);
8236
+ rerr = getresult(be->in, "MP1");
8239
+ "Could not trigger remote push to mupdate server" \
8240
+ "during move of %s",
8246
+ /* 7) local delete of mailbox
8247
+ * & remove local "remote" mailboxlist entry */
8249
+ /* Note that we do not check the ACL, and we don't update MUPDATE */
8250
+ /* note also that we need to remember to let proxyadmins do this */
8251
+ r = mboxlist_deletemailbox(mailboxname,
8252
+ imapd_userisadmin || imapd_userisproxyadmin,
8253
+ imapd_userid, imapd_authstate, 0, 1, 0);
8254
+ if(r) syslog(LOG_ERR,
8255
+ "Could not delete local mailbox during move of %s",
8260
+ if(r && mupdate_h && backout_mupdate) {
8262
+ /* xxx if the mupdate server is what failed, then this won't
8264
+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
8265
+ rerr = mupdate_activate(mupdate_h, mailboxname, buf, acl);
8268
+ "Could not back out mupdate during move of %s (%s)",
8269
+ mailboxname, error_message(rerr));
8272
+ if(r && backout_remotebox) {
8274
+ prot_printf(be->out, "LD1 LOCALDELETE {%lu+}\r\n%s\r\n",
8275
+ (unsigned long) strlen(name), name);
8276
+ rerr = getresult(be->in, "LD1");
8279
+ "Could not back out remote mailbox during move of %s (%s)",
8280
+ name, error_message(rerr));
8283
+ if(r && backout_remoteflag) {
8286
+ rerr = mboxlist_update(mailboxname, mbflags, part, acl, 1);
8287
+ if(rerr) syslog(LOG_ERR, "Could not unset remote flag on mailbox: %s",
8291
+ /* release the handles we got locally if necessary */
8292
+ if(mupdate_h && !h_in)
8293
+ mupdate_disconnect(&mupdate_h);
8295
+ backend_disconnect(be, &protocol[PROTOCOL_IMAP]);
8300
+struct xfer_user_rock
8304
+ mupdate_handle *h;
8305
+ struct backend *be;
8308
+static int xfer_user_cb(char *name,
8309
+ int matchlen __attribute__((unused)),
8310
+ int maycreate __attribute__((unused)),
8313
+ mupdate_handle *mupdate_h = ((struct xfer_user_rock *)rock)->h;
8314
+ char *toserver = ((struct xfer_user_rock *)rock)->toserver;
8315
+ char *topart = ((struct xfer_user_rock *)rock)->topart;
8316
+ struct backend *be = ((struct xfer_user_rock *)rock)->be;
8317
+ char externalname[MAX_MAILBOX_NAME+1];
8320
+ char *inpath, *inpart, *inacl;
8321
+ char *path = NULL, *part = NULL, *acl = NULL;
8324
+ /* NOTE: NOT mlookup() because we don't want to issue a referral */
8325
+ /* xxx but what happens if they are remote
8327
+ r = mboxlist_detail(name, &mbflags,
8328
+ &inpath, &inpart, &inacl, NULL);
8332
+ path = xstrdup(inpath);
8333
+ part = xstrdup(inpart);
8334
+ acl = xstrdup(inacl);
8338
+ r = (*imapd_namespace.mboxname_toexternal)(&imapd_namespace,
8345
+ r = do_xfer_single(toserver, topart, externalname, name, mbflags,
8346
+ path, part, acl, 0, mupdate_h, be);
8349
+ if(path) free(path);
8350
+ if(part) free(part);
8351
+ if(acl) free(acl);
8357
+void cmd_xfer(char *tag, char *name, char *toserver, char *topart)
8360
+ char buf[MAX_PARTITION_LEN+HOSTNAME_SIZE+2];
8361
+ char mailboxname[MAX_MAILBOX_NAME+1];
8363
+ int moving_user = 0;
8364
+ int backout_mupdate = 0;
8365
+ mupdate_handle *mupdate_h = NULL;
8366
+ char *inpath, *inpart, *inacl;
8367
+ char *path = NULL, *part = NULL, *acl = NULL;
8368
+ char *p, *mbox = mailboxname;
8370
+ /* administrators only please */
8371
+ /* however, proxys can do this, if their authzid is an admin */
8372
+ if (!imapd_userisadmin && !imapd_userisproxyadmin) {
8373
+ r = IMAP_PERMISSION_DENIED;
8377
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace,
8383
+ /* NOTE: Since XFER can only be used by an admin, and we always connect
8384
+ * to the destination backend as an admin, we take advantage of the fact
8385
+ * that admins *always* use a consistent mailbox naming scheme.
8386
+ * So, 'name' should be used in any command we send to a backend, and
8387
+ * 'mailboxname' is the internal name to be used for mupdate and findall.
8390
+ if (config_virtdomains && (p = strchr(mailboxname, '!'))) {
8391
+ /* pointer to mailbox w/o domain prefix */
8395
+ if(!strncmp(mbox, "user.", 5) && !strchr(mbox+5, '.')) {
8396
+ if ((strlen(mbox+5) == (strlen(imapd_userid) - (mbox - mailboxname))) &&
8397
+ !strncmp(mbox+5, imapd_userid, strlen(mbox+5))) {
8398
+ /* don't move your own inbox, that could be troublesome */
8399
+ r = IMAP_MAILBOX_NOTSUPPORTED;
8400
+ } else if (!config_getswitch(IMAPOPT_ALLOWUSERMOVES)) {
8401
+ /* not configured to allow user moves */
8402
+ r = IMAP_MAILBOX_NOTSUPPORTED;
8409
+ r = mlookup(tag, name, mailboxname, &mbflags,
8410
+ &inpath, &inpart, &inacl, NULL);
8412
+ if (r == IMAP_MAILBOX_MOVED) return;
8415
+ path = xstrdup(inpath);
8416
+ part = xstrdup(inpart);
8417
+ acl = xstrdup(inacl);
8420
+ /* if we are not moving a user, just move the one mailbox */
8421
+ if(!r && !moving_user) {
8422
+ r = do_xfer_single(toserver, topart, name, mailboxname, mbflags,
8423
+ path, part, acl, 0, NULL, NULL);
8425
+ struct backend *be = NULL;
8427
+ /* we need to reserve the users inbox - connect to mupdate */
8428
+ if(!r && config_mupdate_server) {
8429
+ r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
8432
+ "Could not move mailbox: %s, MUPDATE connect failed",
8438
+ /* Get a single connection to the remote backend */
8439
+ be = backend_connect(NULL, toserver, &protocol[PROTOCOL_IMAP], "", NULL);
8441
+ r = IMAP_SERVER_UNAVAILABLE;
8443
+ "Could not move mailbox: %s, " \
8444
+ "Initial backend connect failed",
8448
+ /* deactivate their inbox */
8449
+ if(!r && mupdate_h) {
8450
+ /* Note we are making the reservation on OUR host so that recovery
8452
+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
8453
+ r = mupdate_deactivate(mupdate_h, mailboxname, buf);
8454
+ if(r) syslog(LOG_ERR,
8455
+ "Could deactivate mailbox: %s, during move",
8457
+ else backout_mupdate = 1;
8460
+ /* If needed, set an uppermost quota root */
8462
+ struct quota quota;
8464
+ quota.root = mailboxname;
8465
+ r = quota_read("a, NULL, 0);
8468
+ /* note use of + to force the setting of a nonexistant
8470
+ prot_printf(be->out, "Q01 SETQUOTA {%lu+}\r\n" \
8471
+ "+%s (STORAGE %d)\r\n",
8472
+ (unsigned long) strlen(name)+1,
8473
+ name, quota.limit);
8474
+ r = getresult(be->in, "Q01");
8475
+ if(r) syslog(LOG_ERR,
8476
+ "Could not move mailbox: %s, " \
8477
+ "failed setting initial quota root\r\n",
8480
+ else if (r == IMAP_QUOTAROOT_NONEXISTENT) r = 0;
8484
+ /* recursively move all sub-mailboxes, using internal names */
8486
+ struct xfer_user_rock rock;
8488
+ rock.toserver = toserver;
8489
+ rock.topart = topart;
8490
+ rock.h = mupdate_h;
8493
+ snprintf(buf, sizeof(buf), "%s.*", mailboxname);
8494
+ r = mboxlist_findall(NULL, buf, 1, imapd_userid,
8495
+ imapd_authstate, xfer_user_cb,
8499
+ /* xxx how do you back out if one of the above moves fails? */
8501
+ /* move this mailbox */
8502
+ /* ...and seen file, and subs file, and sieve scripts... */
8504
+ r = do_xfer_single(toserver, topart, name, mailboxname, mbflags,
8505
+ path, part, acl, 1, mupdate_h, be);
8509
+ backend_disconnect(be, &protocol[PROTOCOL_IMAP]);
8513
+ if(r && mupdate_h && backout_mupdate) {
8515
+ /* xxx if the mupdate server is what failed, then this won't
8517
+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
8518
+ rerr = mupdate_activate(mupdate_h, mailboxname, buf, acl);
8521
+ "Could not back out mupdate during move of %s (%s)",
8522
+ mailboxname, error_message(rerr));
8525
+ /* this was a successful user delete, and we need to delete
8526
+ certain user meta-data (but not seen state!) */
8527
+ user_deletedata(mailboxname+5, imapd_userid, imapd_authstate, 0);
8530
+ if(!r && mupdate_h) {
8531
+ mupdate_disconnect(&mupdate_h);
8536
+ if(part) free(part);
8537
+ if(path) free(path);
8538
+ if(acl) free(acl);
8541
+ prot_printf(imapd_out, "%s NO %s\r\n",
8543
+ error_message(r));
8545
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
8546
+ error_message(IMAP_OK_COMPLETED));
8553
+ * Parse a "date", for SEARCH criteria
8554
+ * The time_t's pointed to by 'start' and 'end' are set to the
8555
+ * times of the start and end of the parsed date.
8557
+int getsearchdate(start, end)
8558
+time_t *start, *end;
8565
+ memset(&tm, 0, sizeof tm);
8567
+ c = prot_getc(imapd_in);
8570
+ c = prot_getc(imapd_in);
8573
+ /* Day of month */
8574
+ if (!isdigit(c)) goto baddate;
8575
+ tm.tm_mday = c - '0';
8576
+ c = prot_getc(imapd_in);
8578
+ tm.tm_mday = tm.tm_mday * 10 + c - '0';
8579
+ c = prot_getc(imapd_in);
8582
+ if (c != '-') goto baddate;
8583
+ c = prot_getc(imapd_in);
8586
+ if (!isalpha(c)) goto baddate;
8588
+ c = prot_getc(imapd_in);
8589
+ if (!isalpha(c)) goto baddate;
8591
+ c = prot_getc(imapd_in);
8592
+ if (!isalpha(c)) goto baddate;
8594
+ c = prot_getc(imapd_in);
8598
+ for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
8599
+ if (!strcmp(month, monthname[tm.tm_mon])) break;
8601
+ if (tm.tm_mon == 12) goto baddate;
8603
+ if (c != '-') goto baddate;
8604
+ c = prot_getc(imapd_in);
8607
+ if (!isdigit(c)) goto baddate;
8608
+ tm.tm_year = c - '0';
8609
+ c = prot_getc(imapd_in);
8610
+ if (!isdigit(c)) goto baddate;
8611
+ tm.tm_year = tm.tm_year * 10 + c - '0';
8612
+ c = prot_getc(imapd_in);
8614
+ if (tm.tm_year < 19) goto baddate;
8616
+ tm.tm_year = tm.tm_year * 10 + c - '0';
8617
+ c = prot_getc(imapd_in);
8618
+ if (!isdigit(c)) goto baddate;
8619
+ tm.tm_year = tm.tm_year * 10 + c - '0';
8620
+ c = prot_getc(imapd_in);
8624
+ if (c != '\"') goto baddate;
8625
+ c = prot_getc(imapd_in);
8629
+ *start = mktime(&tm);
8631
+ tm.tm_sec = tm.tm_min = 59;
8634
+ *end = mktime(&tm);
8639
+ prot_ungetc(c, imapd_in);
8643
+#define SORTGROWSIZE 10
8646
+ * Parse sort criteria
8648
+int getsortcriteria(char *tag, struct sortcrit **sortcrit)
8651
+ static struct buf criteria;
8656
+ c = prot_getc(imapd_in);
8657
+ if (c != '(') goto missingcrit;
8659
+ c = getword(imapd_in, &criteria);
8660
+ if (criteria.s[0] == '\0') goto missingcrit;
8665
+ if (n >= nsort - 1) { /* leave room for implicit criterion */
8666
+ /* (Re)allocate an array for sort criteria */
8667
+ nsort += SORTGROWSIZE;
8669
+ (struct sortcrit *) xrealloc(*sortcrit,
8670
+ nsort * sizeof(struct sortcrit));
8671
+ /* Zero out the newly added sortcrit */
8672
+ memset((*sortcrit)+n, 0, SORTGROWSIZE * sizeof(struct sortcrit));
8675
+ lcase(criteria.s);
8676
+ if (!strcmp(criteria.s, "reverse")) {
8677
+ (*sortcrit)[n].flags |= SORT_REVERSE;
8680
+ else if (!strcmp(criteria.s, "arrival"))
8681
+ (*sortcrit)[n].key = SORT_ARRIVAL;
8682
+ else if (!strcmp(criteria.s, "cc"))
8683
+ (*sortcrit)[n].key = SORT_CC;
8684
+ else if (!strcmp(criteria.s, "date"))
8685
+ (*sortcrit)[n].key = SORT_DATE;
8686
+ else if (!strcmp(criteria.s, "from"))
8687
+ (*sortcrit)[n].key = SORT_FROM;
8688
+ else if (!strcmp(criteria.s, "size"))
8689
+ (*sortcrit)[n].key = SORT_SIZE;
8690
+ else if (!strcmp(criteria.s, "subject"))
8691
+ (*sortcrit)[n].key = SORT_SUBJECT;
8692
+ else if (!strcmp(criteria.s, "to"))
8693
+ (*sortcrit)[n].key = SORT_TO;
8695
+ else if (!strcmp(criteria.s, "annotation")) {
8696
+ (*sortcrit)[n].key = SORT_ANNOTATION;
8697
+ if (c != ' ') goto missingarg;
8698
+ c = getstring(imapd_in, &arg);
8699
+ if (c != ' ') goto missingarg;
8700
+ (*sortcrit)[n].args.annot.entry = xstrdup(arg.s);
8701
+ c = getstring(imapd_in, &arg);
8702
+ if (c == EOF) goto missingarg;
8703
+ (*sortcrit)[n].args.annot.attrib = xstrdup(arg.s);
8707
+ prot_printf(imapd_out, "%s BAD Invalid Sort criterion %s\r\n",
8709
+ if (c != EOF) prot_ungetc(c, imapd_in);
8716
+ if (c == ' ') c = getword(imapd_in, &criteria);
8720
+ if ((*sortcrit)[n].flags & SORT_REVERSE && !(*sortcrit)[n].key) {
8721
+ prot_printf(imapd_out,
8722
+ "%s BAD Missing Sort criterion to reverse\r\n", tag);
8723
+ if (c != EOF) prot_ungetc(c, imapd_in);
8728
+ prot_printf(imapd_out,
8729
+ "%s BAD Missing close parenthesis in Sort\r\n", tag);
8730
+ if (c != EOF) prot_ungetc(c, imapd_in);
8734
+ /* Terminate the list with the implicit sort criterion */
8735
+ (*sortcrit)[n++].key = SORT_SEQUENCE;
8737
+ c = prot_getc(imapd_in);
8742
+ prot_printf(imapd_out, "%s BAD Missing Sort criteria\r\n", tag);
8743
+ if (c != EOF) prot_ungetc(c, imapd_in);
8745
+#if 0 /* For annotations stuff above */
8747
+ prot_printf(imapd_out, "%s BAD Missing argument to Sort criterion %s\r\n",
8749
+ if (c != EOF) prot_ungetc(c, imapd_in);
8754
+#ifdef ENABLE_LISTEXT
8756
+ * Parse LIST options.
8757
+ * The command has been parsed up to and including the opening '('.
8759
+int getlistopts(char *tag, int *listopts)
8762
+ static struct buf arg;
8764
+ *listopts = LIST_EXT;
8767
+ c = getword(imapd_in, &arg);
8768
+ if (!arg.s[0]) break;
8771
+ if (!strcmp(arg.s, "subscribed")) {
8772
+ *listopts |= LIST_SUBSCRIBED;
8774
+ else if (!strcmp(arg.s, "children")) {
8775
+ *listopts |= LIST_CHILDREN;
8777
+ else if (!strcmp(arg.s, "remote")) {
8778
+ *listopts |= LIST_REMOTE;
8781
+ prot_printf(imapd_out, "%s BAD Invalid List option %s\r\n",
8786
+ if (c != ' ') break;
8790
+ prot_printf(imapd_out,
8791
+ "%s BAD Missing close parenthesis in List\r\n", tag);
8795
+ c = prot_getc(imapd_in);
8799
+#endif /* ENABLE_LISTEXT */
8802
+ * Parse a date_time, for the APPEND command
8804
+int getdatetime(date)
8809
+ int old_format = 0;
8810
+ char month[4], zone[4], *p;
8811
+ time_t tmp_gmtime;
8814
+ memset(&tm, 0, sizeof tm);
8816
+ c = prot_getc(imapd_in);
8817
+ if (c != '\"') goto baddate;
8819
+ /* Day of month */
8820
+ c = prot_getc(imapd_in);
8821
+ if (c == ' ') c = '0';
8822
+ if (!isdigit(c)) goto baddate;
8823
+ tm.tm_mday = c - '0';
8824
+ c = prot_getc(imapd_in);
8826
+ tm.tm_mday = tm.tm_mday * 10 + c - '0';
8827
+ c = prot_getc(imapd_in);
8828
+ if(tm.tm_mday <= 0 || tm.tm_mday > 31)
8832
+ if (c != '-') goto baddate;
8833
+ c = prot_getc(imapd_in);
8836
+ if (!isalpha(c)) goto baddate;
8838
+ c = prot_getc(imapd_in);
8839
+ if (!isalpha(c)) goto baddate;
8841
+ c = prot_getc(imapd_in);
8842
+ if (!isalpha(c)) goto baddate;
8844
+ c = prot_getc(imapd_in);
8848
+ for (tm.tm_mon = 0; tm.tm_mon < 12; tm.tm_mon++) {
8849
+ if (!strcmp(month, monthname[tm.tm_mon])) break;
8851
+ if (tm.tm_mon == 12) goto baddate;
8852
+ /* xxx this doesn't quite work in leap years */
8853
+ if (tm.tm_mday > max_monthdays[tm.tm_mon]) goto baddate;
8855
+ if (c != '-') goto baddate;
8856
+ c = prot_getc(imapd_in);
8859
+ if (!isdigit(c)) goto baddate;
8860
+ tm.tm_year = c - '0';
8861
+ c = prot_getc(imapd_in);
8862
+ if (!isdigit(c)) goto baddate;
8863
+ tm.tm_year = tm.tm_year * 10 + c - '0';
8864
+ c = prot_getc(imapd_in);
8866
+ if (tm.tm_year < 19) goto baddate;
8868
+ tm.tm_year = tm.tm_year * 10 + c - '0';
8869
+ c = prot_getc(imapd_in);
8870
+ if (!isdigit(c)) goto baddate;
8871
+ tm.tm_year = tm.tm_year * 10 + c - '0';
8872
+ c = prot_getc(imapd_in);
8874
+ else old_format++;
8877
+ if (c != ' ') goto baddate;
8878
+ c = prot_getc(imapd_in);
8879
+ if (!isdigit(c)) goto baddate;
8880
+ tm.tm_hour = c - '0';
8881
+ c = prot_getc(imapd_in);
8882
+ if (!isdigit(c)) goto baddate;
8883
+ tm.tm_hour = tm.tm_hour * 10 + c - '0';
8884
+ c = prot_getc(imapd_in);
8885
+ if (tm.tm_hour > 23) goto baddate;
8888
+ if (c != ':') goto baddate;
8889
+ c = prot_getc(imapd_in);
8890
+ if (!isdigit(c)) goto baddate;
8891
+ tm.tm_min = c - '0';
8892
+ c = prot_getc(imapd_in);
8893
+ if (!isdigit(c)) goto baddate;
8894
+ tm.tm_min = tm.tm_min * 10 + c - '0';
8895
+ c = prot_getc(imapd_in);
8896
+ if (tm.tm_min > 59) goto baddate;
8899
+ if (c != ':') goto baddate;
8900
+ c = prot_getc(imapd_in);
8901
+ if (!isdigit(c)) goto baddate;
8902
+ tm.tm_sec = c - '0';
8903
+ c = prot_getc(imapd_in);
8904
+ if (!isdigit(c)) goto baddate;
8905
+ tm.tm_sec = tm.tm_sec * 10 + c - '0';
8906
+ c = prot_getc(imapd_in);
8907
+ if (tm.tm_min > 60) goto baddate;
8911
+ if (c != '-') goto baddate;
8912
+ c = prot_getc(imapd_in);
8914
+ if (!isalpha(c)) goto baddate;
8916
+ c = prot_getc(imapd_in);
8919
+ /* Military (single-char) zones */
8922
+ if (zone[0] <= 'm') {
8923
+ zone_off = (zone[0] - 'a' + 1)*60;
8925
+ else if (zone[0] < 'z') {
8926
+ zone_off = ('m' - zone[0])*60;
8928
+ else zone_off = 0;
8931
+ /* UT (universal time) */
8933
+ c = prot_getc(imapd_in);
8937
+ if (!strcmp(zone, "ut")) goto baddate;
8941
+ /* 3-char time zone */
8943
+ c = prot_getc(imapd_in);
8944
+ if (c != '\"') goto baddate;
8947
+ p = strchr("aecmpyhb", zone[0]);
8948
+ if (c != '\"' || zone[2] != 't' || !p) goto baddate;
8949
+ zone_off = (strlen(p) - 12)*60;
8950
+ if (zone[1] == 'd') zone_off -= 60;
8951
+ else if (zone[1] != 's') goto baddate;
8956
+ if (c != ' ') goto baddate;
8957
+ c = prot_getc(imapd_in);
8959
+ if (c != '+' && c != '-') goto baddate;
8962
+ c = prot_getc(imapd_in);
8963
+ if (!isdigit(c)) goto baddate;
8964
+ zone_off = c - '0';
8965
+ c = prot_getc(imapd_in);
8966
+ if (!isdigit(c)) goto baddate;
8967
+ zone_off = zone_off * 10 + c - '0';
8968
+ c = prot_getc(imapd_in);
8969
+ if (!isdigit(c)) goto baddate;
8970
+ zone_off = zone_off * 6 + c - '0';
8971
+ c = prot_getc(imapd_in);
8972
+ if (!isdigit(c)) goto baddate;
8973
+ zone_off = zone_off * 10 + c - '0';
8975
+ if (zone[0] == '-') zone_off = -zone_off;
8977
+ c = prot_getc(imapd_in);
8978
+ if (c != '\"') goto baddate;
8982
+ c = prot_getc(imapd_in);
8986
+ tmp_gmtime = mkgmtime(&tm);
8987
+ if(tmp_gmtime == -1) goto baddate;
8989
+ *date = tmp_gmtime - zone_off*60;
8994
+ prot_ungetc(c, imapd_in);
8999
+ * Print 's' as a quoted-string or literal (but not an atom)
9008
+ /* Look for any non-QCHAR characters */
9009
+ for (p = s; *p && len < 1024; p++) {
9011
+ if (*p & 0x80 || *p == '\r' || *p == '\n'
9012
+ || *p == '\"' || *p == '%' || *p == '\\') break;
9015
+ /* if it's too long, literal it */
9016
+ if (*p || len >= 1024) {
9017
+ prot_printf(imapd_out, "{%lu}\r\n%s", (unsigned long) strlen(s), s);
9019
+ prot_printf(imapd_out, "\"%s\"", s);
9024
+ * Print 's' as an atom, quoted-string, or literal
9033
+ if (imparse_isatom(s)) {
9034
+ prot_printf(imapd_out, "%s", s);
9038
+ /* Look for any non-QCHAR characters */
9039
+ for (p = s; *p && len < 1024; p++) {
9041
+ if (*p & 0x80 || *p == '\r' || *p == '\n'
9042
+ || *p == '\"' || *p == '%' || *p == '\\') break;
9045
+ /* if it's too long, literal it */
9046
+ if (*p || len >= 1024) {
9047
+ prot_printf(imapd_out, "{%lu}\r\n%s", (unsigned long) strlen(s), s);
9049
+ prot_printf(imapd_out, "\"%s\"", s);
9054
+ * Append 'section', 'fields', 'trail' to the fieldlist 'l'.
9057
+appendfieldlist(struct fieldlist **l, char *section,
9058
+ struct strlist *fields, char *trail,
9059
+ void *d, size_t size)
9061
+ struct fieldlist **tail = l;
9063
+ while (*tail) tail = &(*tail)->next;
9065
+ *tail = (struct fieldlist *)xmalloc(sizeof(struct fieldlist));
9066
+ (*tail)->section = xstrdup(section);
9067
+ (*tail)->fields = fields;
9068
+ (*tail)->trail = xstrdup(trail);
9070
+ (*tail)->rock = xmalloc(size);
9071
+ memcpy((*tail)->rock, d, size);
9073
+ (*tail)->rock = NULL;
9075
+ (*tail)->next = 0;
9080
+ * Free the fieldlist 'l'
9082
+void freefieldlist(struct fieldlist *l)
9084
+ struct fieldlist *n;
9089
+ freestrlist(l->fields);
9091
+ if (l->rock) free(l->rock);
9098
+ * Append the searchargs 's1' and 's2' to the sublist of 's'
9101
+appendsearchargs(s, s1, s2)
9102
+struct searchargs *s, *s1, *s2;
9104
+ struct searchsub **tail = &s->sublist;
9106
+ while (*tail) tail = &(*tail)->next;
9108
+ *tail = (struct searchsub *)xmalloc(sizeof(struct searchsub));
9109
+ (*tail)->sub1 = s1;
9110
+ (*tail)->sub2 = s2;
9111
+ (*tail)->next = 0;
9116
+ * Free the searchargs 's'
9120
+struct searchargs *s;
9122
+ struct searchsub *sub, *n;
9126
+ freestrlist(s->sequence);
9127
+ freestrlist(s->uidsequence);
9128
+ freestrlist(s->from);
9129
+ freestrlist(s->to);
9130
+ freestrlist(s->cc);
9131
+ freestrlist(s->bcc);
9132
+ freestrlist(s->subject);
9133
+ freestrlist(s->body);
9134
+ freestrlist(s->text);
9135
+ freestrlist(s->header_name);
9136
+ freestrlist(s->header);
9138
+ for (sub = s->sublist; sub; sub = n) {
9140
+ freesearchargs(sub->sub1);
9141
+ freesearchargs(sub->sub2);
9148
+ * Free an array of sortcrit
9150
+static void freesortcrit(struct sortcrit *s)
9156
+ switch (s[i].key) {
9157
+ case SORT_ANNOTATION:
9158
+ free(s[i].args.annot.entry);
9159
+ free(s[i].args.annot.attrib);
9163
+ } while (s[i].key != SORT_SEQUENCE);
9168
+ * Issue a MAILBOX untagged response
9170
+static int mailboxdata(char *name,
9171
+ int matchlen __attribute__((unused)),
9172
+ int maycreate __attribute__((unused)),
9173
+ void *rock __attribute__((unused)))
9175
+ char mboxname[MAX_MAILBOX_PATH+1];
9177
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, name,
9178
+ imapd_userid, mboxname);
9179
+ prot_printf(imapd_out, "* MAILBOX %s\r\n", mboxname);
9184
+ * Issue a LIST or LSUB untagged response
9186
+static void mstringdata(char *cmd, char *name, int matchlen, int maycreate,
9189
+ static char lastname[MAX_MAILBOX_PATH+1];
9190
+ static int lastnamedelayed = 0;
9191
+ static int lastnamenoinferiors = 0;
9192
+ static int nonexistent = 0;
9193
+ static int sawuser = 0;
9194
+ int lastnamehassub = 0;
9196
+ char mboxname[MAX_MAILBOX_PATH+1];
9198
+ /* We have to reset the sawuser flag before each list command.
9199
+ * Handle it as a dirty hack.
9201
+ if (cmd == NULL) {
9203
+ mstringdatacalls = 0;
9206
+ mstringdatacalls++;
9208
+ if (lastnamedelayed) {
9209
+ /* Check if lastname has children */
9210
+ if (name && strncmp(lastname, name, strlen(lastname)) == 0 &&
9211
+ name[strlen(lastname)] == '.') {
9212
+ lastnamehassub = 1;
9214
+ prot_printf(imapd_out, "* %s (", cmd);
9215
+ if (nonexistent == IMAP_MAILBOX_RESERVED) {
9216
+ /* LISTEXT wants \\PlaceHolder instead of \\Noselect */
9217
+ if (listopts & LIST_EXT)
9218
+ prot_printf(imapd_out, "\\PlaceHolder");
9220
+ prot_printf(imapd_out, "\\Noselect");
9221
+ } else if (nonexistent) {
9222
+ prot_printf(imapd_out, "\\NonExistent");
9224
+ if (lastnamenoinferiors) {
9225
+ prot_printf(imapd_out, "%s\\Noinferiors", nonexistent ? " " : "");
9227
+ else if ((listopts & LIST_CHILDREN) &&
9228
+ /* we can't determine \HasNoChildren for subscriptions */
9229
+ (lastnamehassub ||
9230
+ !(listopts & (LIST_LSUB | LIST_SUBSCRIBED)))) {
9231
+ prot_printf(imapd_out, "%s%s", nonexistent ? " " : "",
9232
+ lastnamehassub ? "\\HasChildren" : "\\HasNoChildren");
9234
+ prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep);
9236
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, lastname,
9237
+ imapd_userid, mboxname);
9238
+ printstring(mboxname);
9239
+ prot_printf(imapd_out, "\r\n");
9240
+ lastnamedelayed = lastnamenoinferiors = nonexistent = 0;
9243
+ /* Special-case to flush any final state */
9245
+ lastname[0] = '\0';
9249
+ /* Suppress any output of a partial match */
9250
+ if ((name[matchlen]
9251
+ && strncmp(lastname, name, matchlen) == 0
9252
+ && (lastname[matchlen] == '\0' || lastname[matchlen] == '.'))) {
9257
+ * We can get a partial match for "user" multiple times with
9258
+ * other matches inbetween. Handle it as a special case
9260
+ if (matchlen == 4 && strncasecmp(name, "user", 4) == 0) {
9261
+ if (sawuser) return;
9265
+ strlcpy(lastname, name, sizeof(lastname));
9266
+ lastname[matchlen] = '\0';
9269
+ /* Now we need to see if this mailbox exists */
9270
+ /* first convert "INBOX" to "user.<userid>" */
9271
+ if (!strncasecmp(lastname, "inbox", 5)) {
9272
+ (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, "INBOX",
9273
+ imapd_userid, mboxname);
9274
+ strlcat(mboxname, lastname+5, sizeof(mboxname));
9277
+ strlcpy(mboxname, lastname, sizeof(mboxname));
9280
+ nonexistent = mboxlist_detail(mboxname, &mbtype,
9281
+ NULL, NULL, NULL, NULL);
9282
+ if(!nonexistent && (mbtype & MBTYPE_RESERVE))
9283
+ nonexistent = IMAP_MAILBOX_RESERVED;
9285
+ if (!name[matchlen]) {
9286
+ lastnamedelayed = 1;
9287
+ if (!maycreate) lastnamenoinferiors = 1;
9291
+ c = name[matchlen];
9292
+ if (c) name[matchlen] = '\0';
9293
+ prot_printf(imapd_out, "* %s (", cmd);
9295
+ /* Handle namespace prefix as a special case */
9296
+ if (!strcmp(name, "user") ||
9297
+ !strcmp(name, imapd_namespace.prefix[NAMESPACE_SHARED])) {
9298
+ prot_printf(imapd_out, "\\Noselect");
9299
+ if (listopts & LIST_EXT)
9300
+ prot_printf(imapd_out, " \\PlaceHolder");
9304
+ prot_printf(imapd_out, "\\NonExistent");
9305
+ /* LISTEXT uses \PlaceHolder instead of \Noselect */
9306
+ if (listopts & LIST_EXT)
9307
+ prot_printf(imapd_out, "%s\\PlaceHolder", nonexistent ? " " : "");
9309
+ prot_printf(imapd_out, "%s\\Noselect", nonexistent ? " " : "");
9311
+ if (listopts & LIST_CHILDREN)
9312
+ prot_printf(imapd_out, " \\HasChildren");
9314
+ prot_printf(imapd_out, ") \"%c\" ", imapd_namespace.hier_sep);
9316
+ (*imapd_namespace.mboxname_toexternal)(&imapd_namespace, name,
9317
+ imapd_userid, mboxname);
9318
+ printstring(mboxname);
9319
+ prot_printf(imapd_out, "\r\n");
9320
+ if (c) name[matchlen] = c;
9325
+ * Issue a LIST untagged response
9327
+static int listdata(char *name, int matchlen, int maycreate, void *rock)
9329
+ int listopts = *((int *)rock);
9331
+ mstringdata(((listopts & LIST_LSUB) ? "LSUB" : "LIST"),
9332
+ name, matchlen, maycreate, listopts);
9337
+/* Reset the given sasl_conn_t to a sane state */
9338
+static int reset_saslconn(sasl_conn_t **conn)
9341
+ sasl_security_properties_t *secprops = NULL;
9343
+ sasl_dispose(conn);
9344
+ /* do initialization typical of service_main */
9345
+ ret = sasl_server_new("imap", config_servername,
9348
+ if(ret != SASL_OK) return ret;
9350
+ if(saslprops.ipremoteport)
9351
+ ret = sasl_setprop(*conn, SASL_IPREMOTEPORT,
9352
+ saslprops.ipremoteport);
9353
+ if(ret != SASL_OK) return ret;
9355
+ if(saslprops.iplocalport)
9356
+ ret = sasl_setprop(*conn, SASL_IPLOCALPORT,
9357
+ saslprops.iplocalport);
9358
+ if(ret != SASL_OK) return ret;
9360
+ secprops = mysasl_secprops(SASL_SEC_NOPLAINTEXT);
9361
+ ret = sasl_setprop(*conn, SASL_SEC_PROPS, secprops);
9362
+ if(ret != SASL_OK) return ret;
9363
+ /* end of service_main initialization excepting SSF */
9365
+ /* If we have TLS/SSL info, set it */
9366
+ if(saslprops.ssf) {
9367
+ ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &saslprops.ssf);
9369
+ ret = sasl_setprop(*conn, SASL_SSF_EXTERNAL, &extprops_ssf);
9371
+ if(ret != SASL_OK) return ret;
9373
+ if(saslprops.authid) {
9374
+ ret = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, saslprops.authid);
9375
+ if(ret != SASL_OK) return ret;
9377
+ /* End TLS/SSL Info */
9382
+void cmd_mupdatepush(char *tag, char *name)
9385
+ char mailboxname[MAX_MAILBOX_NAME+1];
9387
+ mupdate_handle *mupdate_h = NULL;
9388
+ char buf[MAX_PARTITION_LEN + HOSTNAME_SIZE + 2];
9390
+ if (!imapd_userisadmin) {
9391
+ r = IMAP_PERMISSION_DENIED;
9393
+ if (!config_mupdate_server) {
9394
+ r = IMAP_SERVER_UNAVAILABLE;
9398
+ r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
9399
+ imapd_userid, mailboxname);
9403
+ r = mlookup(tag, name, mailboxname, NULL, NULL, &part, &acl, NULL);
9405
+ if (r == IMAP_MAILBOX_MOVED) return;
9407
+ /* Push mailbox to mupdate server */
9409
+ r = mupdate_connect(config_mupdate_server, NULL, &mupdate_h, NULL);
9413
+ snprintf(buf, sizeof(buf), "%s!%s", config_servername, part);
9415
+ r = mupdate_activate(mupdate_h, mailboxname, buf, acl);
9419
+ mupdate_disconnect(&mupdate_h);
9423
+ prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
9426
+ prot_printf(imapd_out, "%s OK %s\r\n", tag,
9427
+ error_message(IMAP_OK_COMPLETED));
9430
diff -urNad cyrus-imapd-2.2.13/imap/pop3d.c /tmp/dpep.bMlzdR/cyrus-imapd-2.2.13/imap/pop3d.c
9431
--- cyrus-imapd-2.2.13/imap/pop3d.c 2006-04-18 20:39:34.000000000 +0200
9432
+++ /tmp/dpep.bMlzdR/cyrus-imapd-2.2.13/imap/pop3d.c 2006-04-18 20:39:35.702094974 +0200
217
diff -urNad cyrus-imapd-2.2.13/imap/pop3d.c /tmp/dpep.ikKBvZ/cyrus-imapd-2.2.13/imap/pop3d.c
218
--- cyrus-imapd-2.2.13/imap/pop3d.c 2006-09-14 22:52:27.000000000 +0200
219
+++ /tmp/dpep.ikKBvZ/cyrus-imapd-2.2.13/imap/pop3d.c 2006-09-14 22:52:28.360084093 +0200
9433
220
@@ -100,6 +100,10 @@
9434
221
extern int opterr;