1 /* mupdate-client.c -- cyrus murder database clients
2  *
3  * Copyright (c) 1994-2008 Carnegie Mellon University.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. The name "Carnegie Mellon University" must not be used to
18  *    endorse or promote products derived from this software without
19  *    prior written permission. For permission or any legal
20  *    details, please contact
21  *      Carnegie Mellon University
22  *      Center for Technology Transfer and Enterprise Creation
23  *      4615 Forbes Avenue
24  *      Suite 302
25  *      Pittsburgh, PA  15213
26  *      (412) 268-7393, fax: (412) 268-7395
27  *      innovation@andrew.cmu.edu
28  *
29  * 4. Redistributions of any form whatsoever must retain the following
30  *    acknowledgment:
31  *    "This product includes software developed by Computing Services
32  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
33  *
34  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
35  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
36  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
37  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
38  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
39  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
40  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
41  */
42 
43 #include <config.h>
44 #include <stdio.h>
45 #include <errno.h>
46 #include <string.h>
47 #include <signal.h>
48 #include <ctype.h>
49 #include <sasl/sasl.h>
50 #include <sasl/saslutil.h>
51 #include <syslog.h>
52 #include <stdarg.h>
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56 #include <sys/types.h>
57 #include <sys/socket.h>
58 #include <sys/un.h>
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
61 #ifdef HAVE_SYS_SELECT_H
62 #include <sys/select.h>
63 #endif
64 
65 #include "exitcodes.h"
66 #include "global.h"
67 #include "mupdate.h"
68 #include "prot.h"
69 #include "protocol.h"
70 #include "util.h"
71 #include "xmalloc.h"
72 #include "xstrlcpy.h"
73 #include "xstrlcat.h"
74 
75 const char service_name[] = "mupdate";
76 
77 static struct protocol_t mupdate_protocol =
78 { "mupdate", "mupdate", TYPE_STD,
79   { { { 1, "* OK" },
80       { NULL, NULL, "* OK", NULL,
81         CAPAF_ONE_PER_LINE|CAPAF_SKIP_FIRST_WORD,
82         { { "AUTH", CAPA_AUTH },
83           { "STARTTLS", CAPA_STARTTLS },
84           { "COMPRESS=DEFLATE", CAPA_COMPRESS },
85           { NULL, 0 } } },
86       { "S01 STARTTLS", "S01 OK", "S01 NO", 1 },
87       { "A01 AUTHENTICATE", USHRT_MAX, 1, "A01 OK", "A01 NO", "", "*", NULL, 0 },
88       { "Z01 COMPRESS \"DEFLATE\"", NULL, "Z01 OK" },
89       { "N01 NOOP", NULL, "N01 OK" },
90       { "Q01 LOGOUT", NULL, "Q01 " } } }
91 };
92 
mupdate_connect(const char * server,const char * port,mupdate_handle ** handle,sasl_callback_t * cbs)93 EXPORTED int mupdate_connect(const char *server,
94                     const char *port __attribute__((unused)),
95                     mupdate_handle **handle,
96                     sasl_callback_t *cbs)
97 {
98     mupdate_handle *h = NULL;
99     const char *status = NULL;
100 
101     if (!handle) {
102         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
103         return MUPDATE_BADPARAM;
104     }
105 
106     /* open connection to 'server' */
107     if (!server) {
108         server = config_mupdate_server;
109         if (server == NULL) {
110             fatal("couldn't get mupdate server name", EC_UNAVAILABLE);
111         }
112     }
113 
114     h = xzmalloc(sizeof(mupdate_handle));
115     *handle = h;
116 
117     h->sasl_cb = NULL;
118     if (!cbs) {
119         cbs = mysasl_callbacks(config_getstring(IMAPOPT_MUPDATE_USERNAME),
120                                config_getstring(IMAPOPT_MUPDATE_AUTHNAME),
121                                config_getstring(IMAPOPT_MUPDATE_REALM),
122                                config_getstring(IMAPOPT_MUPDATE_PASSWORD));
123         h->sasl_cb = cbs;
124     }
125 
126     h->conn = backend_connect(NULL, server, &mupdate_protocol,
127                               "", cbs, &status, -1);
128 
129     if (!h->conn) {
130         free_callbacks(h->sasl_cb);
131         h->sasl_cb = NULL;
132         syslog(LOG_ERR, "mupdate_connect failed: %s", status ? status : "no auth status");
133         return MUPDATE_NOCONN;
134     }
135 
136     h->saslcompleted = 1;
137 
138     /* SUCCESS */
139     return 0;
140 }
141 
mupdate_disconnect(mupdate_handle ** hp)142 EXPORTED void mupdate_disconnect(mupdate_handle **hp)
143 {
144     mupdate_handle *h;
145 
146     if (!hp || !(*hp)) return;
147     h = *hp;
148 
149     backend_disconnect(h->conn);
150     free(h->conn);
151 
152     free_callbacks(h->sasl_cb);
153     h->sasl_cb = NULL;
154 
155     buf_free(&(h->tag));
156     buf_free(&(h->cmd));
157     buf_free(&(h->arg1));
158     buf_free(&(h->arg2));
159     buf_free(&(h->arg3));
160 
161     free(h->acl);
162 
163     free(h);
164     *hp = NULL;
165 }
166 
167 /* We're really only looking for an OK or NO or BAD here -- and the callback
168  * is never called in those cases.  So if the callback is called, we have
169  * an error! */
mupdate_scarf_one(struct mupdate_mailboxdata * mdata,const char * cmd,void * context)170 static int mupdate_scarf_one(struct mupdate_mailboxdata *mdata __attribute__((unused)),
171                              const char *cmd,
172                              void *context __attribute__((unused)))
173 {
174     syslog(LOG_ERR, "mupdate_scarf_one was called, but shouldn't be.  Command received was %s", cmd);
175     return -1;
176 }
177 
mupdate_activate(mupdate_handle * handle,const char * mailbox,const char * location,const char * acl)178 EXPORTED int mupdate_activate(mupdate_handle *handle,
179                      const char *mailbox, const char *location,
180                      const char *acl)
181 {
182     int ret;
183     enum mupdate_cmd_response response;
184     const char *p;
185 
186     if (!handle) {
187         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
188         return MUPDATE_BADPARAM;
189     }
190 
191     if (!mailbox) {
192         syslog(LOG_ERR, "%s: no mailbox", __func__);
193         return MUPDATE_BADPARAM;
194     }
195 
196     if (!location) {
197         syslog(LOG_ERR, "%s: no location", __func__);
198         return MUPDATE_BADPARAM;
199     }
200 
201     if (!handle->saslcompleted) return MUPDATE_NOAUTH;
202 
203     /* make sure we don't have a double server!partition */
204     if ((p = strchr(location, '!')) && strchr(p+1, '!')) {
205         syslog(
206                 LOG_ERR,
207                 "%s: double ! detected in location '%s'",
208                 __func__,
209                 location
210             );
211 
212         return MUPDATE_BADPARAM;
213     }
214 
215     if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_REPLICATED) {
216         /* we don't care about the server part, everything is local */
217         if (p) location = p + 1;
218     }
219 
220     prot_printf(handle->conn->out,
221                 "X%u ACTIVATE "
222                 "{" SIZE_T_FMT "+}\r\n%s "
223                 "{" SIZE_T_FMT "+}\r\n%s "
224                 "{" SIZE_T_FMT "+}\r\n%s\r\n",
225                 handle->tagn++,
226                 strlen(mailbox), mailbox,
227                 strlen(location), location,
228                 (acl ? strlen(acl): 0), (acl ? acl : "")
229         );
230 
231     ret = mupdate_scarf(handle, mupdate_scarf_one, NULL, 1, &response);
232     if (ret) {
233         return ret;
234     } else if (response != MUPDATE_OK) {
235         return MUPDATE_FAIL;
236     } else {
237         return 0;
238     }
239 }
240 
mupdate_reserve(mupdate_handle * handle,const char * mailbox,const char * location)241 HIDDEN int mupdate_reserve(mupdate_handle *handle,
242                     const char *mailbox, const char *location)
243 {
244     int ret;
245     enum mupdate_cmd_response response;
246     const char *p;
247 
248     if (!handle) {
249         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
250         return MUPDATE_BADPARAM;
251     }
252 
253     if (!mailbox) {
254         syslog(LOG_ERR, "%s: no mailbox", __func__);
255         return MUPDATE_BADPARAM;
256     }
257 
258     if (!location) {
259         syslog(LOG_ERR, "%s: no location", __func__);
260         return MUPDATE_BADPARAM;
261     }
262 
263     if (!handle->saslcompleted) return MUPDATE_NOAUTH;
264 
265     /* make sure we don't have a double server!partition */
266     if ((p = strchr(location, '!')) && strchr(p+1, '!')) {
267         syslog(
268                 LOG_ERR,
269                 "%s: double ! detected in location '%s'",
270                 __func__,
271                 location
272             );
273 
274         return MUPDATE_BADPARAM;
275     }
276 
277     if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_REPLICATED) {
278         /* we don't care about the location part, everything is local */
279         if (p) location = p + 1;
280     }
281 
282     prot_printf(handle->conn->out,
283                 "X%u RESERVE "
284                 "{" SIZE_T_FMT "+}\r\n%s "
285                 "{" SIZE_T_FMT "+}\r\n%s\r\n",
286                 handle->tagn++,
287                 strlen(mailbox), mailbox,
288                 strlen(location), location
289         );
290 
291     ret = mupdate_scarf(handle, mupdate_scarf_one, NULL, 1, &response);
292     if (ret) {
293         return ret;
294     } else if (response != MUPDATE_OK) {
295         return MUPDATE_FAIL_RESERVE;
296     } else {
297         return 0;
298     }
299 }
300 
mupdate_deactivate(mupdate_handle * handle,const char * mailbox,const char * location)301 EXPORTED int mupdate_deactivate(mupdate_handle *handle,
302                        const char *mailbox, const char *location)
303 {
304     int ret;
305     enum mupdate_cmd_response response;
306     const char *p;
307 
308     if (!handle) {
309         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
310         return MUPDATE_BADPARAM;
311     }
312 
313     if (!mailbox) {
314         syslog(LOG_ERR, "%s: no mailbox", __func__);
315         return MUPDATE_BADPARAM;
316     }
317 
318     if (!location) {
319         syslog(LOG_ERR, "%s: no location", __func__);
320         return MUPDATE_BADPARAM;
321     }
322 
323     if (!handle->saslcompleted) return MUPDATE_NOAUTH;
324 
325     /* make sure we don't have a double server!partition */
326     if ((p = strchr(location, '!')) && strchr(p+1, '!')) {
327         syslog(
328                 LOG_ERR,
329                 "%s: double ! detected in location '%s'",
330                 __func__,
331                 location
332             );
333 
334         return MUPDATE_BADPARAM;
335     }
336 
337     if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_REPLICATED) {
338         /* we don't care about the server part, everything is local */
339         if (p) location = p + 1;
340     }
341 
342     prot_printf(handle->conn->out,
343             "X%u DEACTIVATE "
344             "{" SIZE_T_FMT "+}\r\n%s "
345             "{" SIZE_T_FMT "+}\r\n%s\r\n",
346             handle->tagn++,
347             strlen(mailbox), mailbox,
348             strlen(location), location
349         );
350 
351     ret = mupdate_scarf(handle, mupdate_scarf_one, NULL, 1, &response);
352     if (ret) {
353         return ret;
354     } else if (response != MUPDATE_OK) {
355         return MUPDATE_FAIL_RESERVE;
356     } else {
357         return 0;
358     }
359 }
360 
mupdate_delete(mupdate_handle * handle,const char * mailbox)361 EXPORTED int mupdate_delete(mupdate_handle *handle,
362                    const char *mailbox)
363 {
364     int ret;
365     enum mupdate_cmd_response response;
366 
367     if (!handle) {
368         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
369         return MUPDATE_BADPARAM;
370     }
371 
372     if (!mailbox) {
373         syslog(LOG_ERR, "%s: no mailbox", __func__);
374         return MUPDATE_BADPARAM;
375     }
376 
377     if (!handle->saslcompleted) return MUPDATE_NOAUTH;
378 
379     prot_printf(handle->conn->out,
380                 "X%u DELETE {" SIZE_T_FMT "+}\r\n%s\r\n", handle->tagn++,
381                 strlen(mailbox), mailbox);
382 
383     ret = mupdate_scarf(handle, mupdate_scarf_one, NULL, 1, &response);
384     if (ret) {
385         return ret;
386     } else if (response != MUPDATE_OK) {
387         return MUPDATE_FAIL;
388     } else {
389         return 0;
390     }
391 }
392 
393 
mupdate_find_cb(struct mupdate_mailboxdata * mdata,const char * cmd,void * context)394 static int mupdate_find_cb(struct mupdate_mailboxdata *mdata,
395                            const char *cmd, void *context)
396 {
397     struct mupdate_handle_s *h = (struct mupdate_handle_s *)context;
398 
399     if (!h || !cmd || !mdata) return 1;
400 
401     /* coyp the data to the handle storage */
402     /* xxx why can't we just point to the 'mdata' buffers? */
403     strlcpy(h->mailbox_buf, mdata->mailbox, sizeof(h->mailbox_buf));
404     strlcpy(h->location_buf, mdata->location, sizeof(h->location_buf));
405 
406     if (!strncmp(cmd, "MAILBOX", 7)) {
407         h->mailboxdata_buf.t = ACTIVE;
408 
409         free(h->acl);
410         h->acl = xstrdup(mdata->acl);
411     } else if (!strncmp(cmd, "RESERVE", 7)) {
412         h->mailboxdata_buf.t = RESERVE;
413         free(h->acl);
414         h->acl = xstrdup("");
415     } else {
416         /* Bad command */
417         return 1;
418     }
419 
420     h->mailboxdata_buf.mailbox = h->mailbox_buf;
421     h->mailboxdata_buf.location = h->location_buf;
422     h->mailboxdata_buf.acl = h->acl;
423 
424     return 0;
425 }
426 
mupdate_find(mupdate_handle * handle,const char * mailbox,struct mupdate_mailboxdata ** target)427 EXPORTED int mupdate_find(mupdate_handle *handle, const char *mailbox,
428                  struct mupdate_mailboxdata **target)
429 {
430     int ret;
431     enum mupdate_cmd_response response;
432 
433     if (!handle) {
434         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
435         return MUPDATE_BADPARAM;
436     }
437 
438     if (!mailbox) {
439         syslog(LOG_ERR, "%s: no mailbox", __func__);
440         return MUPDATE_BADPARAM;
441     }
442 
443     if (!target) {
444         syslog(LOG_ERR, "%s: no target", __func__);
445         return MUPDATE_BADPARAM;
446     }
447 
448     prot_printf(handle->conn->out,
449                 "X%u FIND {" SIZE_T_FMT "+}\r\n%s\r\n", handle->tagn++,
450                 strlen(mailbox), mailbox);
451 
452     memset(&(handle->mailboxdata_buf), 0, sizeof(handle->mailboxdata_buf));
453 
454     ret = mupdate_scarf(handle, mupdate_find_cb, handle, 1, &response);
455 
456     /* note that the response is still OK even if there was no data returned,
457      * so we have to make sure we actually filled in the data too */
458     if (!ret && response == MUPDATE_OK && handle->mailboxdata_buf.mailbox) {
459         *target = &(handle->mailboxdata_buf);
460         return 0;
461     } else if (!ret && response == MUPDATE_OK) {
462         /* it looked okay, but we didn't get a mailbox */
463         *target = NULL;
464         return MUPDATE_MAILBOX_UNKNOWN;
465     } else {
466         /* Something Bad happened */
467         *target = NULL;
468         return ret ? ret : MUPDATE_FAIL;
469     }
470 }
471 
mupdate_list(mupdate_handle * handle,mupdate_callback callback,const char * prefix,void * context)472 EXPORTED int mupdate_list(mupdate_handle *handle, mupdate_callback callback,
473                  const char *prefix, void *context)
474 {
475     int ret;
476     enum mupdate_cmd_response response;
477 
478     if (!handle) {
479         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
480         return MUPDATE_BADPARAM;
481     }
482 
483     if (!callback) {
484         syslog(LOG_ERR, "%s: no callback", __func__);
485         return MUPDATE_BADPARAM;
486     }
487 
488     if (prefix) {
489         prot_printf(handle->conn->out,
490                     "X%u LIST {" SIZE_T_FMT "+}\r\n%s\r\n", handle->tagn++,
491                     strlen(prefix), prefix);
492     } else {
493         prot_printf(handle->conn->out,
494                     "X%u LIST\r\n", handle->tagn++);
495     }
496 
497     ret = mupdate_scarf(handle, callback, context, 1, &response);
498 
499     if (ret) {
500         return ret;
501     } else if (response != MUPDATE_OK) {
502         return MUPDATE_FAIL;
503     } else {
504         return 0;
505     }
506 }
507 
508 
mupdate_noop(mupdate_handle * handle,mupdate_callback callback,void * context)509 EXPORTED int mupdate_noop(mupdate_handle *handle, mupdate_callback callback,
510                  void *context)
511 {
512     int ret;
513     enum mupdate_cmd_response response;
514 
515     if (!handle) {
516         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
517         return MUPDATE_BADPARAM;
518     }
519 
520     if (!callback) {
521         syslog(LOG_ERR, "%s: no callback", __func__);
522         return MUPDATE_BADPARAM;
523     }
524 
525     prot_printf(handle->conn->out,
526                 "X%u NOOP\r\n", handle->tagn++);
527 
528     ret = mupdate_scarf(handle, callback, context, 1, &response);
529 
530     if (!ret && response == MUPDATE_OK) {
531         return 0;
532     } else {
533         return ret ? ret : MUPDATE_FAIL;
534     }
535 }
536 
537 #define CHECKNEWLINE(c, ch) do { if ((ch) == '\r') (ch)=prot_getc((c)->conn->in); \
538                                  if ((ch) != '\n') { syslog(LOG_ERR, \
539                              "extra arguments received, aborting connection");\
540                                  r = MUPDATE_PROTOCOL_ERROR;\
541                                  goto done; }} while(0)
542 
543 /* Scarf up the incoming data and perform the requested operations */
mupdate_scarf(mupdate_handle * handle,mupdate_callback callback,void * context,int wait_for_ok,enum mupdate_cmd_response * response)544 EXPORTED int mupdate_scarf(mupdate_handle *handle,
545                   mupdate_callback callback,
546                   void *context,
547                   int wait_for_ok,
548                   enum mupdate_cmd_response *response)
549 {
550     struct mupdate_mailboxdata box;
551     int r = 0;
552 
553     if (!handle) {
554         syslog(LOG_ERR, "%s: no mupdate_handle", __func__);
555         return MUPDATE_BADPARAM;
556     }
557 
558     if (!callback) {
559         syslog(LOG_ERR, "%s: no callback", __func__);
560         return MUPDATE_BADPARAM;
561     }
562 
563     /* keep going while we have input or if we're waiting for an OK */
564     while (!r) {
565         int ch;
566         char *p;
567 
568         if (wait_for_ok) {
569             prot_BLOCK(handle->conn->in);
570         } else {
571             /* check for input */
572             prot_NONBLOCK(handle->conn->in);
573             ch = prot_getc(handle->conn->in);
574 
575             if (ch == EOF && errno == EAGAIN) {
576                 /* this was just "no input" we return 0 */
577                 goto done;
578             } else if (ch == EOF) {
579                 /* this was a fatal error */
580                 r = MUPDATE_NOCONN;
581                 goto done;
582             } else {
583                 /* there's input waiting, put back our character */
584                 prot_ungetc(ch, handle->conn->in);
585             }
586 
587             /* Set it back to blocking so we don't get half a word */
588             prot_BLOCK(handle->conn->in);
589         }
590 
591         ch = getword(handle->conn->in, &(handle->tag));
592         if (ch == EOF) {
593             /* this was a fatal error */
594             r = MUPDATE_NOCONN;
595             goto done;
596         }
597 
598         if (ch != ' ') {
599             /* We always have a command */
600             syslog(LOG_ERR, "Protocol error from master: no tag");
601             r = MUPDATE_PROTOCOL_ERROR;
602             goto done;
603         }
604         ch = getword(handle->conn->in, &(handle->cmd));
605         if (ch != ' ') {
606             /* We always have an argument */
607             syslog(LOG_ERR, "Protocol error from master: no keyword");
608             r = MUPDATE_PROTOCOL_ERROR;
609             break;
610         }
611 
612         if (Uislower(handle->cmd.s[0])) {
613             handle->cmd.s[0] = toupper((unsigned char) handle->cmd.s[0]);
614         }
615         for (p = &(handle->cmd.s[1]); *p; p++) {
616             if (Uislower(*p))
617                 *p = toupper((unsigned char) *p);
618         }
619 
620         switch(handle->cmd.s[0]) {
621         case 'B':
622             if (!strncmp(handle->cmd.s, "BAD", 3)) {
623                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg1));
624                 CHECKNEWLINE(handle, ch);
625 
626                 syslog(LOG_ERR, "mupdate BAD response: %s", handle->arg1.s);
627                 if (wait_for_ok && response) {
628                     *response = MUPDATE_BAD;
629                 }
630                 goto done;
631             } else if (!strncmp(handle->cmd.s, "BYE", 3)) {
632                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg1));
633                 CHECKNEWLINE(handle, ch);
634 
635                 syslog(LOG_ERR, "mupdate BYE response: %s", handle->arg1.s);
636                 if (wait_for_ok && response) {
637                     *response = MUPDATE_BYE;
638                 }
639                 goto done;
640             }
641             goto badcmd;
642 
643         case 'D':
644             if (!strncmp(handle->cmd.s, "DELETE", 6)) {
645                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg1));
646                 CHECKNEWLINE(handle, ch);
647 
648                 memset(&box, 0, sizeof(box));
649                 box.mailbox = handle->arg1.s;
650 
651                 /* Handle delete command */
652                 r = callback(&box, handle->cmd.s, context);
653                 if (r) {
654                     syslog(LOG_ERR,
655                            "error deleting mailbox: callback returned %d", r);
656                     goto done;
657                 }
658                 break;
659             }
660             goto badcmd;
661 
662         case 'M':
663             if (!strncmp(handle->cmd.s, "MAILBOX", 7)) {
664                 /* Mailbox Name */
665                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg1));
666                 if (ch != ' ') {
667                     r = MUPDATE_PROTOCOL_ERROR;
668                     goto done;
669                 }
670 
671                 /* Server */
672                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg2));
673                 if (ch != ' ') {
674                     r = MUPDATE_PROTOCOL_ERROR;
675                     goto done;
676                 }
677 
678                 /* ACL */
679                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg3));
680                 CHECKNEWLINE(handle, ch);
681 
682                 /* Handle mailbox command */
683                 memset(&box, 0, sizeof(box));
684                 box.mailbox = handle->arg1.s;
685                 box.location = handle->arg2.s;
686                 box.acl = handle->arg3.s;
687                 r = callback(&box, handle->cmd.s, context);
688                 if (r) { /* callback error ? */
689                     syslog(LOG_ERR,
690                            "error activating mailbox: callback returned %d", r);
691                     goto done;
692                 }
693                 break;
694             }
695             goto badcmd;
696         case 'N':
697             if (!strncmp(handle->cmd.s, "NO", 2)) {
698                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg1));
699                 CHECKNEWLINE(handle, ch);
700 
701                 syslog(LOG_DEBUG, "mupdate NO response: %s", handle->arg1.s);
702                 if (wait_for_ok) {
703                     if (response) *response = MUPDATE_NO;
704                     goto done;
705                 }
706                 break;
707             }
708             goto badcmd;
709         case 'O':
710             if (!strncmp(handle->cmd.s, "OK", 2)) {
711                 /* It's all good, grab the attached string and move on */
712                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg1));
713 
714                 CHECKNEWLINE(handle, ch);
715                 if (wait_for_ok) {
716                     if (response) *response = MUPDATE_OK;
717                     goto done;
718                 }
719                 break;
720             }
721             goto badcmd;
722         case 'R':
723             if (!strncmp(handle->cmd.s, "RESERVE", 7)) {
724                 /* Mailbox Name */
725                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg1));
726                 if (ch != ' ') {
727                     r = MUPDATE_PROTOCOL_ERROR;
728                     goto done;
729                 }
730 
731                 /* Server */
732                 ch = getstring(handle->conn->in, handle->conn->out, &(handle->arg2));
733                 CHECKNEWLINE(handle, ch);
734 
735                 /* Handle reserve command */
736                 memset(&box, 0, sizeof(box));
737                 box.mailbox = handle->arg1.s;
738                 box.location = handle->arg2.s;
739                 r = callback(&box, handle->cmd.s, context);
740                 if (r) { /* callback error ? */
741                     syslog(LOG_ERR,
742                            "error reserving mailbox: callback returned %d", r);
743                     goto done;
744                 }
745 
746                 break;
747             }
748             goto badcmd;
749 
750         default:
751         badcmd:
752             /* Bad Command */
753             syslog(LOG_ERR, "bad/unexpected command from master: %s",
754                    handle->cmd.s);
755             r = MUPDATE_PROTOCOL_ERROR;
756             goto done;
757         }
758     }
759 
760  done:
761     /* reset blocking */
762     prot_NONBLOCK(handle->conn->in);
763 
764     return r;
765 }
766 
kick_mupdate(void)767 EXPORTED void kick_mupdate(void)
768 {
769     char buf[2048];
770     struct buf addrbuf = BUF_INITIALIZER;
771     struct sockaddr_un srvaddr;
772     int s, r;
773     int len;
774 
775     /* no server? drop out */
776     if (!config_mupdate_server)
777         return;
778 
779     /* don't kick on standard backends */
780     if (config_mupdate_config == IMAP_ENUM_MUPDATE_CONFIG_STANDARD
781         && config_getstring(IMAPOPT_PROXYSERVERS))
782         return;
783 
784     s = socket(AF_UNIX, SOCK_STREAM, 0);
785     if (s == -1) {
786         syslog(LOG_ERR, "socket: %m");
787         return;
788     }
789 
790     buf_appendcstr(&addrbuf, config_dir);
791     buf_appendcstr(&addrbuf, FNAME_MUPDATE_TARGET_SOCK);
792     if (buf_len(&addrbuf) >= sizeof(srvaddr.sun_path)) {
793         syslog(LOG_ERR, "kick_mupdate: configured socket path '%s' is too long"
794                         " (maximum length is " SIZE_T_FMT ")",
795                         buf_cstring(&addrbuf), sizeof(srvaddr.sun_path) - 1);
796         fatal("socket path too long", EC_CONFIG);
797     }
798 
799     memset((char *)&srvaddr, 0, sizeof(srvaddr));
800     srvaddr.sun_family = AF_UNIX;
801     strlcpy(srvaddr.sun_path, buf_cstring(&addrbuf), sizeof(srvaddr.sun_path));
802     len = sizeof(srvaddr.sun_family) + strlen(srvaddr.sun_path) + 1;
803 
804     r = connect(s, (struct sockaddr *)&srvaddr, len);
805     if (r == -1) {
806         syslog(LOG_ERR, "kick_mupdate: can't connect to target: %m");
807         goto done;
808     }
809 
810     r = read(s, buf, sizeof(buf));
811     if (r <= 0) {
812         syslog(LOG_ERR, "kick_mupdate: can't read from target: %m");
813     }
814 
815  done:
816     buf_free(&addrbuf);
817     close(s);
818     return;
819 }
820