1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "imap4d.h"
18 #include <mailutils/property.h>
19
20 mu_stream_t iostream;
21
22 static void
log_cipher(mu_stream_t stream)23 log_cipher (mu_stream_t stream)
24 {
25 mu_property_t prop;
26 int rc = mu_stream_ioctl (stream, MU_IOCTL_TLSSTREAM,
27 MU_IOCTL_TLS_GET_CIPHER_INFO, &prop);
28 if (rc)
29 {
30 mu_diag_output (MU_DIAG_INFO, _("TLS established"));
31 mu_diag_output (MU_DIAG_ERROR, _("can't get TLS details: %s"),
32 mu_strerror (rc));
33 }
34 else
35 {
36 char const *cipher, *mac, *proto;
37 if (mu_property_sget_value (prop, "cipher", &cipher))
38 cipher = "UNKNOWN";
39 if (mu_property_sget_value (prop, "mac", &mac))
40 mac = "UNKNOWN";
41 if (mu_property_sget_value (prop, "protocol", &proto))
42 proto = "UNKNOWN";
43
44 mu_diag_output (MU_DIAG_INFO, _("TLS established using %s-%s (%s)"),
45 cipher, mac, proto);
46
47 mu_property_destroy (&prop);
48 }
49 }
50
51 void
io_setio(int ifd,int ofd,struct mu_tls_config * tls_conf)52 io_setio (int ifd, int ofd, struct mu_tls_config *tls_conf)
53 {
54 mu_stream_t str, istream, ostream;
55
56 if (ifd == -1)
57 imap4d_bye (ERR_NO_IFILE);
58 if (ofd == -1)
59 imap4d_bye (ERR_NO_OFILE);
60
61 if (mu_stdio_stream_create (&istream, ifd, MU_STREAM_READ))
62 imap4d_bye (ERR_STREAM_CREATE);
63 mu_stream_set_buffer (istream, mu_buffer_line, 0);
64
65 if (mu_stdio_stream_create (&ostream, ofd, MU_STREAM_WRITE))
66 imap4d_bye (ERR_STREAM_CREATE);
67 mu_stream_set_buffer (ostream, mu_buffer_line, 0);
68
69 /* Combine the two streams into an I/O one. */
70 if (tls_conf)
71 {
72 int rc = mu_tls_stream_create (&str, istream, ostream,
73 tls_conf,
74 MU_TLS_SERVER,
75 0);
76 if (rc)
77 {
78 mu_error (_("failed to create TLS stream: %s"), mu_strerror (rc));
79 imap4d_bye (ERR_STREAM_CREATE);
80 }
81 log_cipher (str);
82 }
83 else if (mu_iostream_create (&str, istream, ostream))
84 imap4d_bye (ERR_STREAM_CREATE);
85
86 mu_stream_unref (istream);
87 mu_stream_unref (ostream);
88
89 /* Convert all writes to CRLF form.
90 There is no need to convert reads, as the code ignores extra \r anyway.
91 */
92 if (mu_filter_create (&iostream, str, "CRLF", MU_FILTER_ENCODE,
93 MU_STREAM_WRITE | MU_STREAM_RDTHRU))
94 imap4d_bye (ERR_STREAM_CREATE);
95 /* Change buffering scheme: filter streams are fully buffered by default. */
96 mu_stream_set_buffer (iostream, mu_buffer_line, 0);
97
98 if (imap4d_transcript)
99 {
100 int rc;
101 mu_stream_t dstr, xstr;
102
103 rc = mu_dbgstream_create (&dstr, MU_DIAG_DEBUG);
104 if (rc)
105 mu_error (_("cannot create debug stream; transcript disabled: %s"),
106 mu_strerror (rc));
107 else
108 {
109 rc = mu_xscript_stream_create (&xstr, iostream, dstr, NULL);
110 mu_stream_unref (dstr);
111 if (rc)
112 mu_error (_("cannot create transcript stream: %s"),
113 mu_strerror (rc));
114 else
115 {
116 mu_stream_unref (iostream);
117 iostream = xstr;
118 }
119 }
120 }
121 }
122
123 int
imap4d_init_tls_server(struct mu_tls_config * tls_conf)124 imap4d_init_tls_server (struct mu_tls_config *tls_conf)
125 {
126 mu_stream_t tlsstream, stream[2];
127 int rc;
128
129 rc = mu_stream_ioctl (iostream, MU_IOCTL_SUBSTREAM, MU_IOCTL_OP_GET, stream);
130 if (rc)
131 {
132 mu_error (_("%s failed: %s"), "MU_IOCTL_GET_STREAM",
133 mu_stream_strerror (iostream, rc));
134 return 1;
135 }
136
137 rc = mu_tls_stream_create (&tlsstream, stream[0], stream[1],
138 tls_conf,
139 MU_TLS_SERVER,
140 0);
141 mu_stream_unref (stream[0]);
142 mu_stream_unref (stream[1]);
143 if (rc)
144 {
145 mu_diag_output (MU_DIAG_ERROR, _("cannot open TLS stream: %s"),
146 mu_strerror (rc));
147 return 1;
148 }
149
150 log_cipher (tlsstream);
151
152 stream[0] = stream[1] = tlsstream;
153
154 rc = mu_stream_ioctl (iostream, MU_IOCTL_SUBSTREAM, MU_IOCTL_OP_SET, stream);
155 if (rc)
156 {
157 mu_error (_("%s failed: %s"), "MU_IOCTL_SET_STREAM",
158 mu_stream_strerror (iostream, rc));
159 imap4d_bye (ERR_STREAM_CREATE);
160 }
161 mu_stream_unref (stream[0]);
162 mu_stream_unref (stream[1]);
163
164 return 0;
165 }
166
167 /* Status Code to String. */
168 static const char *
sc2string(int rc)169 sc2string (int rc)
170 {
171 switch (rc)
172 {
173 case RESP_OK:
174 return "OK ";
175
176 case RESP_BAD:
177 return "BAD ";
178
179 case RESP_NO:
180 return "NO ";
181
182 case RESP_BYE:
183 return "BYE ";
184
185 case RESP_PREAUTH:
186 return "PREAUTH ";
187 }
188 return "";
189 }
190
191 /* FIXME: Check return values from the output functions */
192
193 int
io_copy_out(mu_stream_t str,size_t size)194 io_copy_out (mu_stream_t str, size_t size)
195 {
196 int rc;
197 struct mu_buffer_query oldbuf, newbuf;
198
199 oldbuf.type = MU_TRANSPORT_OUTPUT;
200 if (mu_stream_ioctl (iostream, MU_IOCTL_TRANSPORT_BUFFER,
201 MU_IOCTL_OP_GET, &oldbuf) == 0)
202 {
203 newbuf.type = MU_TRANSPORT_OUTPUT;
204 newbuf.buftype = mu_buffer_full;
205 newbuf.bufsize = 64*1024;
206 mu_stream_ioctl (iostream, MU_IOCTL_TRANSPORT_BUFFER,
207 MU_IOCTL_OP_SET, &newbuf);
208 }
209
210 rc = mu_stream_copy (iostream, str, size, NULL);
211
212 mu_stream_ioctl (iostream, MU_IOCTL_TRANSPORT_BUFFER,
213 MU_IOCTL_OP_SET, &oldbuf);
214
215 return rc;
216 }
217
218 int
io_send_bytes(const char * buf,size_t size)219 io_send_bytes (const char *buf, size_t size)
220 {
221 return mu_stream_write (iostream, buf, size, NULL);
222 }
223
224 int
io_sendf(const char * format,...)225 io_sendf (const char *format, ...)
226 {
227 int status;
228 va_list ap;
229
230 va_start (ap, format);
231 status = mu_stream_vprintf (iostream, format, ap);
232 va_end (ap);
233 return status;
234 }
235
236 /* Send NIL if empty string, change the quoted string to a literal if the
237 string contains: double quotes, CR, LF, and '\\'. CR, LF will be changed
238 to spaces. */
239 int
io_send_qstring(const char * buffer)240 io_send_qstring (const char *buffer)
241 {
242 if (buffer == NULL || *buffer == '\0')
243 return io_sendf ("NIL");
244 if (strpbrk (buffer, "\"\r\n\\"))
245 {
246 char *s;
247 int ret;
248 char *b = mu_strdup (buffer);
249 while ((s = strchr (b, '\n')) || (s = strchr (b, '\r')))
250 *s = ' ';
251 ret = io_send_literal (b);
252 free (b);
253 return ret;
254 }
255 return io_sendf ("\"%s\"", buffer);
256 }
257
258 int
io_send_astring(const char * buffer)259 io_send_astring (const char *buffer)
260 {
261 if (buffer == NULL)
262 return io_sendf ("NIL");
263 else if (*buffer == 0)
264 return io_sendf ("\"\"");
265 if (strpbrk (buffer, "\"\r\n\\"))
266 {
267 char *s;
268 int ret;
269 char *b = mu_strdup (buffer);
270 while ((s = strchr (b, '\n')) || (s = strchr (b, '\r')))
271 *s = ' ';
272 ret = io_send_literal (b);
273 free (b);
274 return ret;
275 }
276 return io_sendf ("\"%s\"", buffer);
277 }
278
279 int
io_send_literal(const char * buffer)280 io_send_literal (const char *buffer)
281 {
282 return io_sendf ("{%lu}\n%s", (unsigned long) strlen (buffer), buffer);
283 }
284
285 /* Send an untagged response. */
286 int
io_untagged_response(int rc,const char * format,...)287 io_untagged_response (int rc, const char *format, ...)
288 {
289 int status;
290 va_list ap;
291
292 if (iostream == NULL)
293 {
294 if (rc == RESP_BYE)
295 /* RESP_BYE can be emitted when iostream has not been initialized,
296 e.g. when a recently started child receives termination signal
297 during initialization. */
298 return 0;
299 mu_diag_output (MU_DIAG_ERROR,
300 /* TANSLATORS: %s is replaced with the untagged response
301 name, followed by a space character. */
302 _("iostream is NULL while trying to send the %suntagged response"),
303 sc2string (rc));
304 exit (EX_SOFTWARE);
305 }
306
307 mu_stream_printf (iostream, "* %s", sc2string (rc));
308 va_start (ap, format);
309 status = mu_stream_vprintf (iostream, format, ap);
310 va_end (ap);
311 mu_stream_write (iostream, "\n", 1, NULL);
312 return status;
313 }
314
315 /* Send the completion response and reset the state. */
316 int
io_format_completion_response(mu_stream_t str,struct imap4d_command * command,int rc,const char * format,va_list ap)317 io_format_completion_response (mu_stream_t str,
318 struct imap4d_command *command, int rc,
319 const char *format, va_list ap)
320 {
321 int new_state;
322 int status = 0;
323 const char *sc = sc2string (rc);
324
325 imap4d_sync ();
326
327 mu_stream_printf (str, "%s %s%s ",
328 command->tag, sc, command->name);
329 mu_stream_vprintf (str, format, ap);
330 mu_stream_write (str, "\n", 1, NULL);
331
332 /* Reset the state. */
333 if (rc == RESP_OK)
334 new_state = command->success;
335 else if (command->failure <= state)
336 new_state = command->failure;
337 else
338 new_state = STATE_NONE;
339
340 if (new_state != STATE_NONE)
341 {
342 if (new_state == STATE_AUTH)
343 set_xscript_level (MU_XSCRIPT_NORMAL);
344 state = new_state;
345 }
346
347 return status;
348 }
349
350 int
io_completion_response(struct imap4d_command * command,int rc,const char * format,...)351 io_completion_response (struct imap4d_command *command, int rc,
352 const char *format, ...)
353 {
354 va_list ap;
355 int status;
356
357 va_start (ap, format);
358 status = io_format_completion_response (iostream, command, rc, format, ap);
359 va_end (ap);
360 return status;
361 }
362
363 int
io_stream_completion_response(mu_stream_t str,struct imap4d_command * command,int rc,const char * format,...)364 io_stream_completion_response (mu_stream_t str,
365 struct imap4d_command *command, int rc,
366 const char *format, ...)
367 {
368 va_list ap;
369 int status;
370
371 va_start (ap, format);
372 status = io_format_completion_response (str, command, rc, format, ap);
373 va_end (ap);
374 return status;
375 }
376
377 /* Wait TIMEOUT seconds for data on the input stream.
378 Returns 0 if no data available
379 1 if some data is available
380 -1 an error occurred */
381 int
io_wait_input(int timeout)382 io_wait_input (int timeout)
383 {
384 int wflags = MU_STREAM_READY_RD;
385 struct timeval tv;
386 int status;
387
388 tv.tv_sec = timeout;
389 tv.tv_usec = 0;
390 status = mu_stream_wait (iostream, &wflags, &tv);
391 if (status)
392 {
393 mu_diag_output (MU_DIAG_ERROR, _("cannot poll input stream: %s"),
394 mu_strerror(status));
395 return -1;
396 }
397 return wflags & MU_STREAM_READY_RD;
398 }
399
400 void
io_flush()401 io_flush ()
402 {
403 mu_stream_flush (iostream);
404 }
405
406 void
io_getline(char ** pbuf,size_t * psize,size_t * pnbytes)407 io_getline (char **pbuf, size_t *psize, size_t *pnbytes)
408 {
409 size_t len;
410 int rc = mu_stream_getline (iostream, pbuf, psize, &len);
411 if (rc == 0)
412 {
413 char *s = *pbuf;
414
415 if (len == 0)
416 {
417 imap4d_bye (ERR_NO_IFILE);
418 /*FIXME rc = ECONNABORTED;*/
419 }
420 len = mu_rtrim_class (s, MU_CTYPE_ENDLN);
421 if (pnbytes)
422 *pnbytes = len;
423 }
424 else
425 {
426 mu_error (_("read error: %s"), mu_strerror (rc));
427 imap4d_bye (ERR_NO_IFILE);
428 }
429 }
430
431
432 static size_t
unquote(char * line,size_t len)433 unquote (char *line, size_t len)
434 {
435 char *prev = NULL;
436 size_t rlen = len;
437 char *p;
438 int off = 0;
439 while ((p = memchr (line + off, '\\', len - off)))
440 {
441 if (p[1] == '\\' || p[1] == '"')
442 {
443 if (prev)
444 {
445 memmove (prev, line, p - line);
446 prev += p - line;
447 }
448 else
449 prev = p;
450 off = p[1] == '\\';
451 rlen--;
452 len -= p - line + 1;
453 line = p + 1;
454 }
455 }
456 if (prev)
457 memmove (prev, line, len);
458 return rlen;
459 }
460
461 struct imap4d_tokbuf
462 {
463 char *buffer;
464 size_t size;
465 size_t level;
466 int argc;
467 int argmax;
468 size_t *argp;
469 };
470
471 struct imap4d_tokbuf *
imap4d_tokbuf_init()472 imap4d_tokbuf_init ()
473 {
474 struct imap4d_tokbuf *tok = mu_alloc (sizeof (tok[0]));
475 memset (tok, 0, sizeof (*tok));
476 return tok;
477 }
478
479 void
imap4d_tokbuf_destroy(struct imap4d_tokbuf ** ptok)480 imap4d_tokbuf_destroy (struct imap4d_tokbuf **ptok)
481 {
482 struct imap4d_tokbuf *tok = *ptok;
483 free (tok->buffer);
484 free (tok->argp);
485 free (tok);
486 *ptok = NULL;
487 }
488
489 int
imap4d_tokbuf_argc(struct imap4d_tokbuf * tok)490 imap4d_tokbuf_argc (struct imap4d_tokbuf *tok)
491 {
492 return tok->argc;
493 }
494
495 char *
imap4d_tokbuf_getarg(struct imap4d_tokbuf * tok,int n)496 imap4d_tokbuf_getarg (struct imap4d_tokbuf *tok, int n)
497 {
498 if (n < tok->argc)
499 return tok->buffer + tok->argp[n];
500 return NULL;
501 }
502
503 static void
imap4d_tokbuf_unquote(struct imap4d_tokbuf * tok,size_t * poff,size_t * plen)504 imap4d_tokbuf_unquote (struct imap4d_tokbuf *tok, size_t *poff, size_t *plen)
505 {
506 char *buf = tok->buffer + *poff;
507 if (buf[0] == '"' && buf[*plen - 1] == '"')
508 {
509 ++*poff;
510 *plen = unquote (buf + 1, *plen - 1);
511 }
512 }
513
514 static void
imap4d_tokbuf_decrlf(struct imap4d_tokbuf * tok,size_t off,size_t * plen)515 imap4d_tokbuf_decrlf (struct imap4d_tokbuf *tok, size_t off, size_t *plen)
516 {
517 char *buf = tok->buffer + off;
518 size_t len = *plen;
519 char *p, *end = buf + len;
520
521 for (p = buf; p < end; )
522 {
523 if (*p == '\r' && p + 1 < end && p[1] == '\n')
524 {
525 p++;
526 len--;
527 }
528 else
529 *buf++ = *p++;
530 }
531 *plen = len;
532 }
533
534 static void
imap4d_tokbuf_expand(struct imap4d_tokbuf * tok,size_t size)535 imap4d_tokbuf_expand (struct imap4d_tokbuf *tok, size_t size)
536 {
537 if (tok->size - tok->level < size)
538 {
539 tok->size = tok->level + size;
540 tok->buffer = realloc (tok->buffer, tok->size);
541 if (!tok->buffer)
542 imap4d_bye (ERR_NO_MEM);
543 }
544 }
545
546 #define ISDELIM(c) (strchr ("()", (c)) != NULL)
547
548 static size_t
insert_nul(struct imap4d_tokbuf * tok,size_t off)549 insert_nul (struct imap4d_tokbuf *tok, size_t off)
550 {
551 imap4d_tokbuf_expand (tok, 1);
552 if (off < tok->level)
553 {
554 memmove (tok->buffer + off + 1, tok->buffer + off, tok->level - off);
555 tok->level++;
556 }
557 tok->buffer[off] = 0;
558 return off + 1;
559 }
560
561 static size_t
gettok(struct imap4d_tokbuf * tok,size_t off)562 gettok (struct imap4d_tokbuf *tok, size_t off)
563 {
564 char *buf = tok->buffer;
565
566 while (off < tok->level && mu_isblank (buf[off]))
567 off++;
568
569 if (tok->argc == tok->argmax)
570 {
571 if (tok->argmax == 0)
572 tok->argmax = 16;
573 else
574 tok->argmax *= 2;
575 tok->argp = realloc (tok->argp, tok->argmax * sizeof (tok->argp[0]));
576 if (!tok->argp)
577 imap4d_bye (ERR_NO_MEM);
578 }
579
580 if (buf[off] == '"')
581 {
582 char *start = buf + off + 1;
583 char *p = NULL;
584
585 while (*start && (p = strchr (start, '"')))
586 {
587 if (p == start || p[-1] != '\\')
588 break;
589 start = p + 1;
590 }
591
592 if (p)
593 {
594 size_t len;
595 off++;
596 len = unquote (buf + off, p - (buf + off));
597 buf[off + len] = 0;
598 tok->argp[tok->argc++] = off;
599 return p - buf + 1;
600 }
601 }
602
603 tok->argp[tok->argc++] = off;
604 if (ISDELIM (buf[off]))
605 return insert_nul (tok, off + 1);
606
607 while (off < tok->level && !mu_isblank (buf[off]))
608 {
609 if (ISDELIM (buf[off]))
610 return insert_nul (tok, off);
611 off++;
612 }
613 insert_nul (tok, off);
614
615 return off + 1;
616 }
617
618 static void
imap4d_tokbuf_tokenize(struct imap4d_tokbuf * tok,size_t off)619 imap4d_tokbuf_tokenize (struct imap4d_tokbuf *tok, size_t off)
620 {
621 while (off < tok->level)
622 off = gettok (tok, off);
623 }
624
625 static void
check_input_err(int rc,size_t sz)626 check_input_err (int rc, size_t sz)
627 {
628 if (rc)
629 {
630 const char *p = mu_stream_strerror (iostream, rc);
631 if (!p)
632 p = mu_strerror (rc);
633
634 mu_diag_output (MU_DIAG_INFO,
635 _("error reading from input file: %s"), p);
636 imap4d_bye (ERR_NO_IFILE);
637 }
638 else if (sz == 0)
639 {
640 mu_diag_output (MU_DIAG_INFO, _("unexpected eof on input"));
641 imap4d_bye (ERR_NO_IFILE);
642 }
643 }
644
645 static size_t
imap4d_tokbuf_getline(struct imap4d_tokbuf * tok)646 imap4d_tokbuf_getline (struct imap4d_tokbuf *tok)
647 {
648 char buffer[512];
649 size_t level = tok->level;
650
651 do
652 {
653 size_t len;
654 int rc;
655
656 rc = mu_stream_readline (iostream, buffer, sizeof (buffer), &len);
657 check_input_err (rc, len);
658 imap4d_tokbuf_expand (tok, len);
659
660 memcpy (tok->buffer + tok->level, buffer, len);
661 tok->level += len;
662 }
663 while (tok->level && tok->buffer[tok->level - 1] != '\n');
664 tok->buffer[--tok->level] = 0;
665 if (tok->level > 0 && tok->buffer[tok->level - 1] == '\r')
666 tok->buffer[--tok->level] = 0;
667 while (tok->level > 0 && mu_isblank (tok->buffer[tok->level-1]))
668 tok->buffer[--tok->level] = 0;
669 return level;
670 }
671
672 void
imap4d_readline(struct imap4d_tokbuf * tok)673 imap4d_readline (struct imap4d_tokbuf *tok)
674 {
675 tok->argc = 0;
676 tok->level = 0;
677 for (;;)
678 {
679 char *last_arg;
680 size_t off = imap4d_tokbuf_getline (tok);
681 imap4d_tokbuf_tokenize (tok, off);
682 if (tok->argc == 0)
683 break;
684 last_arg = tok->buffer + tok->argp[tok->argc - 1];
685 if (last_arg[0] == '{' && last_arg[strlen(last_arg)-1] == '}')
686 {
687 int rc;
688 unsigned long number;
689 char *sp = NULL;
690 char *buf;
691 size_t len;
692
693 number = strtoul (last_arg + 1, &sp, 10);
694 /* Client can ask for non-synchronised literal,
695 if a '+' is appended to the octet count. */
696 if (*sp == '}')
697 io_sendf ("+ GO AHEAD\n");
698 else if (*sp != '+')
699 break;
700 xscript_declare_client_payload (number);
701 imap4d_tokbuf_expand (tok, number + 1);
702 off = tok->level;
703 buf = tok->buffer + off;
704 len = 0;
705 while (len < number)
706 {
707 size_t sz;
708 rc = mu_stream_read (iostream, buf + len, number - len, &sz);
709 if (rc || sz == 0)
710 break;
711 len += sz;
712 }
713 check_input_err (rc, len);
714 imap4d_tokbuf_unquote (tok, &off, &len);
715 imap4d_tokbuf_decrlf (tok, off, &len);
716 tok->level += len;
717 tok->buffer[tok->level++] = 0;
718 tok->argp[tok->argc - 1] = off;
719 }
720 else
721 break;
722 }
723 }
724
725 struct imap4d_tokbuf *
imap4d_tokbuf_from_string(char * str)726 imap4d_tokbuf_from_string (char *str)
727 {
728 struct imap4d_tokbuf *tok = imap4d_tokbuf_init ();
729 tok->buffer = mu_strdup (str);
730 if (!tok->buffer)
731 imap4d_bye (ERR_NO_MEM);
732 tok->level = strlen (str);
733 tok->size = tok->level + 1;
734 imap4d_tokbuf_tokenize (tok, 0);
735 return tok;
736 }
737
738 void
io_enable_crlf(int enable)739 io_enable_crlf (int enable)
740 {
741 enable = !enable;
742 mu_stream_ioctl (iostream, MU_IOCTL_FILTER,
743 MU_IOCTL_FILTER_SET_DISABLED,
744 &enable);
745 }
746