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