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