int mu_vgetyn (const char *fmt, va_list ap) { char repl[64]; while (1) { int rc; size_t n; mu_stream_vprintf (mu_strout, fmt, ap); mu_stream_write (mu_strout, "? ", 2, NULL); mu_stream_flush (mu_strout); if (mu_stream_read (mu_strin, repl, sizeof repl, &n) || n == 0) return 0; mu_rtrim_class (repl, MU_CTYPE_ENDLN); rc = mu_true_answer_p (repl); if (rc >= 0) return rc; /* TRANSLATORS: See msgids "nN" and "yY". */ mu_stream_printf (mu_strout, "%s", _("Please answer yes or no: ")); } return 0; /* to pacify gcc */ }
/* Send the completion response and reset the state. */ int io_format_completion_response (mu_stream_t str, struct imap4d_command *command, int rc, const char *format, va_list ap) { int new_state; int status = 0; const char *sc = sc2string (rc); imap4d_sync (); mu_stream_printf (str, "%s %s%s ", command->tag, sc, command->name); mu_stream_vprintf (str, format, ap); mu_stream_write (str, "\n", 1, NULL); /* Reset the state. */ if (rc == RESP_OK) new_state = command->success; else if (command->failure <= state) new_state = command->failure; else new_state = STATE_NONE; if (new_state != STATE_NONE) { if (new_state == STATE_AUTH) set_xscript_level (MU_XSCRIPT_NORMAL); state = new_state; } return status; }
int mu_vgetans (const char *variants, const char *fmt, va_list ap) { char repl[64]; while (1) { size_t n; char *p; mu_stream_vprintf (mu_strout, fmt, ap); mu_stream_write (mu_strout, "? ", 2, NULL); mu_stream_flush (mu_strout); if (mu_stream_read (mu_strin, repl, sizeof repl, &n) || n == 0) return 0; mu_rtrim_class (repl, MU_CTYPE_ENDLN); p = strchr (variants, *repl); if (p) return *p; mu_stream_printf (mu_strout, _("Please answer one of [%s]: "), variants); } return 0; /* to pacify gcc */ }
static int _iostream_write (struct _mu_stream *str, const char *buf, size_t bufsize, size_t *pnwrite) { struct _mu_iostream *sp = (struct _mu_iostream *)str; int rc = mu_stream_write (sp->transport[_MU_STREAM_OUTPUT], buf, bufsize, pnwrite); if (rc) sp->last_err_str = _MU_STREAM_OUTPUT; return rc; }
/* Send an untagged response. */ int io_untagged_response (int rc, const char *format, ...) { int status; va_list ap; mu_stream_printf (iostream, "* %s", sc2string (rc)); va_start (ap, format); status = mu_stream_vprintf (iostream, format, ap); va_end (ap); mu_stream_write (iostream, "\n", 1, NULL); return status; }
static PyObject * api_stream_write (PyObject *self, PyObject *args) { int status; char *wbuf; size_t size, write_count; PyStream *py_stm; if (!PyArg_ParseTuple (args, "O!si", &PyStreamType, &py_stm, &wbuf, &size)) return NULL; status = mu_stream_write (py_stm->stm, wbuf, size, &write_count); return status_object (status, PyInt_FromLong (write_count)); }
static void c_copy (mu_stream_t out, mu_stream_t in) { if (verbose) { mu_stream_set_stat (in, MU_STREAM_STAT_MASK_ALL, instat); mu_stream_set_stat (out, MU_STREAM_STAT_MASK_ALL, outstat); } if (printable) { char c; size_t size; while (mu_stream_read (in, &c, 1, &size) == 0 && size > 0) { int rc; if (printable && !ISPRINT (c)) { char outbuf[24]; sprintf (outbuf, "\\%03o", (unsigned char) c); rc = mu_stream_write (out, outbuf, strlen (outbuf), NULL); } else rc = mu_stream_write (out, &c, 1, NULL); } } else MU_ASSERT (mu_stream_copy (out, in, 0, NULL)); }
int mu_stream_vprintf (mu_stream_t str, const char *fmt, va_list ap) { char *buf = NULL; size_t buflen = 0; size_t n; int rc; rc = mu_vasnprintf (&buf, &buflen, fmt, ap); if (rc) return rc; n = strlen (buf); rc = mu_stream_write (str, buf, n, NULL); free (buf); return rc; }
/* A locale-independent version of strftime */ size_t mu_strftime (char *buf, size_t size, const char *format, struct tm *tm) { int rc; mu_stream_t str; mu_stream_stat_buffer stat; if (mu_fixed_memory_stream_create (&str, buf, size, MU_STREAM_WRITE)) return 0; mu_stream_set_stat (str, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT), stat); rc = mu_c_streamftime (str, format, tm, NULL); if (rc == 0) rc = mu_stream_write (str, "", 1, NULL); mu_stream_unref (str); return rc ? 0 : stat[MU_STREAM_STAT_OUT] - 1; }
static void mu_port_flush (SCM port) { struct mu_port *mp = MU_PORT (port); scm_port *pt = SCM_PTAB_ENTRY (port); int wrsize = pt->write_pos - pt->write_buf; size_t n; if (wrsize) { if (mu_stream_write (mp->stream, (const char*)pt->write_buf, wrsize, mp->offset, &n)) return; mp->offset += n; } pt->write_pos = pt->write_buf; pt->rw_active = SCM_PORT_NEITHER; }
static PyObject * api_memory_stream_create (PyObject *self, PyObject *args) { int status; char *s; PyStream *py_stm; if (!PyArg_ParseTuple (args, "O!s", &PyStreamType, &py_stm, &s)) return NULL; status = mu_memory_stream_create (&py_stm->stm, MU_STREAM_RDWR); if (status == 0) { status = mu_stream_write (py_stm->stm, s, strlen (s), NULL); mu_stream_seek (py_stm->stm, MU_SEEK_SET, 0, NULL); } return _ro (PyInt_FromLong (status)); }
static int _streamref_write (struct _mu_stream *str, const char *buf, size_t bufsize, size_t *pnwrite) { struct _mu_streamref *sp = (struct _mu_streamref *)str; int rc; size_t nwrite; rc = mu_stream_seek (sp->transport, sp->offset, MU_SEEK_SET, NULL); if (rc == 0) { rc = mu_stream_write (sp->transport, buf, bufsize, &nwrite); if (rc == 0) { sp->offset += nwrite; *pnwrite = nwrite; } } return streamref_return (sp, rc); }
void ioloop (char *id, mu_stream_t in, mu_stream_t out) { char *buf = NULL; size_t size = 0, n; int rc; while ((rc = mu_stream_getline (in, &buf, &size, &n)) == 0 && n > 0) { if (rc) { mu_error("%s: read error: %s", id, mu_stream_strerror (in, rc)); exit (1); } MU_ASSERT (mu_stream_write (out, buf, n, NULL)); } mu_stream_flush (out); if (verbose) fprintf (stderr, "%s exited\n", id); }
static int write_array (mu_stream_t stream, mu_off_t *poff, int count, const char **array) { int status; for (; count; count--, array++) { size_t len = strlen (*array); size_t i = 0, n; do { status = mu_stream_write (stream, *array + i, len - i, *poff, &n); if (status) return status; *poff += n; i += n; } while (i < len); } return 0; }
void mh_comp_draft (const char *formfile, const char *draftfile) { char *s; if (mh_find_file (formfile, &s) == 0) { if (mh_file_copy (s, draftfile)) exit (1); free (s); } else { /* I doubt if we need that: */ int rc; mu_stream_t stream; if ((rc = mu_file_stream_create (&stream, draftfile, MU_STREAM_WRITE|MU_STREAM_CREAT))) { mu_error (_("cannot open output file \"%s\": %s"), draftfile, mu_strerror (rc)); exit (1); } rc = mu_stream_write (stream, default_format_str, strlen (default_format_str), NULL); mu_stream_close (stream); mu_stream_destroy (&stream); if (rc) { mu_error (_("error writing to \"%s\": %s"), draftfile, mu_strerror (rc)); exit (1); } } }
static int show_part (struct mime_descend_closure *closure, void *data) { size_t width; size_t size = 0; format_msgset (mu_strout, closure->msgset, &width); for (; width < 5; width++) mu_stream_write (mu_strout, " ", 1, NULL); mu_printf (" %-25s", closure->type); mu_message_size (closure->message, &size); if (size < 1024) mu_printf (" %4lu", (unsigned long) size); else if (size < 1024*1024) mu_printf ("%4luK", (unsigned long) size / 1024); else mu_printf ("%4luM", (unsigned long) size / 1024 / 1024); mu_printf ("\n"); return 0; }
int mutool_filter (int argc, char **argv) { int rc, index; mu_stream_t flt, prev_stream; const char *fltname; int mode; if (argp_parse (&filter_argp, argc, argv, ARGP_IN_ORDER, &index, NULL)) return 1; argc -= index; argv += index; if (list_option) { if (argc) { mu_error (_("excess arguments")); return 1; } return list_filters (); } if (argc == 0) { mu_error (_("what filter do you want?")); return 1; } prev_stream = mu_strin; mu_stream_ref (mu_strin); do { int i; fltname = argv[0]; if (fltname[0] == '~') { mode = negate_filter_mode (filter_mode); fltname++; } else mode = filter_mode; for (i = 1; i < argc; i++) if (strcmp (argv[i], "+") == 0) break; rc = mu_filter_create_args (&flt, prev_stream, fltname, i, (const char **)argv, mode, MU_STREAM_READ); mu_stream_unref (prev_stream); if (rc) { mu_error (_("cannot open filter stream: %s"), mu_strerror (rc)); return 1; } prev_stream = flt; argc -= i; argv += i; if (argc) { argc--; argv++; } } while (argc); rc = mu_stream_copy (mu_strout, flt, 0, NULL); if (rc) { mu_error ("%s", mu_strerror (rc)); return 1; } if (newline_option) mu_stream_write (mu_strout, "\n", 1, NULL); mu_stream_destroy (&flt); mu_stream_flush (mu_strout); return 0; }
int main (int argc, char * argv []) { mu_stream_t in, out, flt; int i; int mode = MU_FILTER_ENCODE; int flags = MU_STREAM_READ; char *fltname; mu_off_t shift = 0; int newline_option = 0; size_t bufsize = 0; if (argc == 1) usage (NULL); if (argc < 4) usage ("not enough arguments"); fltname = argv[1]; if (strcmp (argv[2], "encode") == 0) mode = MU_FILTER_ENCODE; else if (strcmp (argv[2], "decode") == 0) mode = MU_FILTER_DECODE; else usage ("2nd arg is wrong"); if (strcmp (argv[3], "read") == 0) flags = MU_STREAM_READ; else if (strcmp (argv[3], "write") == 0) flags = MU_STREAM_WRITE; else usage ("3rd arg is wrong"); for (i = 4; i < argc; i++) { if (strncmp (argv[i], "shift=", 6) == 0) shift = strtoul (argv[i] + 6, NULL, 0); else if (strncmp (argv[i], "bufsize=", 8) == 0) bufsize = strtoul (argv[i] + 8, NULL, 0); else if (strcmp (argv[i], "verbose") == 0) verbose++; else if (strcmp (argv[i], "printable") == 0) printable++; else if (strcmp (argv[i], "nl") == 0) newline_option++; else if (strcmp (argv[i], "--") == 0) { argv[i] = fltname; break; } else usage ("wrong option"); } argc -= i; argv += i; MU_ASSERT (mu_stdio_stream_create (&in, MU_STDIN_FD, 0)); if (bufsize) mu_stream_set_buffer (in, mu_buffer_full, bufsize); MU_ASSERT (mu_stdio_stream_create (&out, MU_STDOUT_FD, 0)); if (flags == MU_STREAM_READ) { MU_ASSERT (mu_filter_create_args (&flt, in, fltname, argc, (const char **)argv, mode, MU_STREAM_READ|MU_STREAM_SEEK)); mu_stream_unref (in); if (bufsize) mu_stream_set_buffer (flt, mu_buffer_full, bufsize); if (shift) MU_ASSERT (mu_stream_seek (flt, shift, MU_SEEK_SET, NULL)); c_copy (out, flt); } else { MU_ASSERT (mu_filter_create_args (&flt, out, fltname, argc, (const char **)argv, mode, MU_STREAM_WRITE)); if (bufsize) mu_stream_set_buffer (flt, mu_buffer_full, bufsize); if (shift) MU_ASSERT (mu_stream_seek (in, shift, MU_SEEK_SET, NULL)); c_copy (flt, in); mu_stream_close (in); mu_stream_destroy (&in); } mu_stream_close (flt); mu_stream_destroy (&flt); if (newline_option) mu_stream_write (out, "\n", 1, NULL); mu_stream_close (out); mu_stream_destroy (&out); if (verbose) { fprintf (stderr, "\nInput stream stats:\n"); fprintf (stderr, "Bytes in: %lu\n", (unsigned long) instat[MU_STREAM_STAT_IN]); fprintf (stderr, "Bytes out: %lu\n", (unsigned long) instat[MU_STREAM_STAT_OUT]); fprintf (stderr, "Reads: %lu\n", (unsigned long) instat[MU_STREAM_STAT_READS]); fprintf (stderr, "Seeks: %lu\n", (unsigned long) instat[MU_STREAM_STAT_SEEKS]); fprintf (stderr, "\nOutput stream stats:\n"); fprintf (stderr, "Bytes in: %lu\n", (unsigned long) outstat[MU_STREAM_STAT_IN]); fprintf (stderr, "Bytes out: %lu\n", (unsigned long) outstat[MU_STREAM_STAT_OUT]); fprintf (stderr, "Writes: %lu\n", (unsigned long) outstat[MU_STREAM_STAT_WRITES]); fprintf (stderr, "Seeks: %lu\n", (unsigned long) outstat[MU_STREAM_STAT_SEEKS]); } return 0; }
/* Copy SIZE bytes from SRC to DST. If SIZE is 0, copy everything up to EOF. */ int mu_stream_copy (mu_stream_t dst, mu_stream_t src, mu_off_t size, mu_off_t *pcsz) { int status; size_t bufsize, n; char *buf; mu_off_t total = 0; if (pcsz) *pcsz = 0; if (size == 0) { status = mu_stream_size (src, &size); switch (status) { case 0: break; case ENOSYS: size = 0; break; default: return status; } if (size) { mu_off_t pos; status = mu_stream_seek (src, 0, MU_SEEK_CUR, &pos); switch (status) { case 0: if (pos > size) return ESPIPE; size -= pos; break; case EACCES: mu_stream_clearerr (src); case ENOSYS: size = 0; break; default: return status; } } } bufsize = size; if (!bufsize) bufsize = STREAMCPY_MAX_BUF_SIZE; for (; (buf = malloc (bufsize)) == NULL; bufsize >>= 1) if (bufsize < STREAMCPY_MIN_BUF_SIZE) return ENOMEM; if (size) while (size) { size_t rdsize = bufsize < size ? bufsize : size; status = mu_stream_read (src, buf, rdsize, &n); if (status) break; if (n == 0) break; status = mu_stream_write (dst, buf, n, NULL); if (status) break; size -= n; total += n; } else while ((status = mu_stream_read (src, buf, bufsize, &n)) == 0 && n > 0) { status = mu_stream_write (dst, buf, n, NULL); if (status) break; total += n; } if (pcsz) *pcsz = total; /* FIXME: When EOF error code is implemented: else if (total == 0) status = EOF; */ free (buf); return status; }
int main (int argc, char **argv) { int i; char *host = NULL; char *infile = NULL; char *port = NULL; int tls = 0; int raw = 1; int flags = 0; mu_stream_t stream; mu_smtp_t smtp; mu_stream_t instr; char *from = NULL; mu_list_t rcpt_list = NULL; mu_list_t meth_list = NULL; mu_list_t skiphdr_list = NULL; struct mu_sockaddr *sa; struct mu_sockaddr_hints hints; mu_set_program_name (argv[0]); mu_stdstream_setup (MU_STDSTREAM_RESET_NONE); if (argc < 2) usage (); memset (&hints, 0, sizeof (hints)); hints.flags = MU_AH_DETECT_FAMILY; hints.port = 25; hints.protocol = IPPROTO_TCP; hints.socktype = SOCK_STREAM; MU_ASSERT (mu_smtp_create (&smtp)); for (i = 1; i < argc; i++) { if (strncmp (argv[i], "port=", 5) == 0) port = argv[i] + 5; else if (strncmp (argv[i], "family=", 7) == 0) { hints.flags &= ~MU_AH_DETECT_FAMILY; switch (argv[i][7]) { case '4': hints.family = AF_INET; break; case '6': hints.family = AF_INET6; break; default: mu_error ("invalid family name: %s", argv[i]+7); exit (1); } } else if (strncmp (argv[i], "trace=", 6) == 0) { char *arg = argv[i] + 6; if (mu_isdigit (arg[0])) mu_smtp_trace (smtp, atoi (argv[i] + 6) ? MU_SMTP_TRACE_SET : MU_SMTP_TRACE_CLR); else { mu_smtp_trace (smtp, MU_SMTP_TRACE_SET); if (strcmp (arg, "secure") == 0) mu_smtp_trace_mask (smtp, MU_SMTP_TRACE_SET, MU_XSCRIPT_SECURE); else if (strcmp (arg, "payload") == 0) mu_smtp_trace_mask (smtp, MU_SMTP_TRACE_SET, MU_XSCRIPT_PAYLOAD); } } else if (strncmp (argv[i], "tls=", 4) == 0) tls = atoi (argv[i] + 4); else if (strncmp (argv[i], "domain=", 7) == 0) MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_DOMAIN, argv[i] + 7)); else if (strncmp (argv[i], "user="******"pass="******"service=", 8) == 0) MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_SERVICE, argv[i] + 8)); else if (strncmp (argv[i], "realm=", 6) == 0) MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_REALM, argv[i] + 6)); else if (strncmp (argv[i], "host=", 5) == 0) MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_HOST, argv[i] + 5)); else if (strncmp (argv[i], "url=", 4) == 0) MU_ASSERT (mu_smtp_set_param (smtp, MU_SMTP_PARAM_URL, argv[i] + 4)); else if (strncmp (argv[i], "input=", 6) == 0) infile = argv[i] + 6; else if (strncmp (argv[i], "raw=", 4) == 0) raw = atoi (argv[i] + 4); else if (strncmp (argv[i], "rcpt=", 5) == 0) { if (!rcpt_list) MU_ASSERT (mu_list_create (&rcpt_list)); MU_ASSERT (mu_list_append (rcpt_list, argv[i] + 5)); } else if (strncmp (argv[i], "from=", 5) == 0) from = argv[i] + 5; else if (strncmp (argv[i], "auth=", 5) == 0) update_list (&meth_list, argv[i] + 5); else if (strncmp (argv[i], "skiphdr=", 8) == 0) { update_list (&skiphdr_list, argv[i] + 8); raw = 0; } else if (host) { mu_error ("server name already given: %s, new name %s?", host, argv[i]); exit (1); } else host = argv[i]; } if (!host) usage (); if (!raw) flags = MU_STREAM_SEEK; if (infile) MU_ASSERT (mu_file_stream_create (&instr, infile, MU_STREAM_READ|flags)); else MU_ASSERT (mu_stdio_stream_create (&instr, MU_STDIN_FD, flags)); host = argv[1]; MU_ASSERT (mu_sockaddr_from_node (&sa, host, port, &hints)); MU_ASSERT (mu_tcp_stream_create_from_sa (&stream, sa, NULL, MU_STREAM_RDWR)); mu_smtp_set_carrier (smtp, stream); mu_stream_unref (stream); if (!from) { from = getenv ("USER"); if (!from) { mu_error ("cannot determine sender name"); exit (1); } } if (raw && !rcpt_list) { mu_error ("no recipients"); exit (1); } MU_ASSERT (mu_smtp_open (smtp)); MU_ASSERT (mu_smtp_ehlo (smtp)); if (tls && mu_smtp_capa_test (smtp, "STARTTLS", NULL) == 0) { MU_ASSERT (mu_smtp_starttls (smtp)); MU_ASSERT (mu_smtp_ehlo (smtp)); } if (meth_list) { int status; MU_ASSERT (mu_smtp_add_auth_mech_list (smtp, meth_list)); status = mu_smtp_auth (smtp); switch (status) { case 0: MU_ASSERT (mu_smtp_ehlo (smtp)); break; case ENOSYS: case MU_ERR_NOENT: /* Ok, skip it */ break; default: mu_error ("authentication failed: %s", mu_strerror (status)); exit (1); } } MU_ASSERT (mu_smtp_mail_basic (smtp, from, NULL)); mu_list_foreach (rcpt_list, send_rcpt_command, smtp); if (raw) { /* Raw sending mode: send from the stream directly */ MU_ASSERT (mu_smtp_send_stream (smtp, instr)); } else { /* Message (standard) sending mode: send a MU message. */ mu_message_t msg; mu_stream_t ostr, bstr; mu_header_t hdr; mu_iterator_t itr; mu_body_t body; if (skiphdr_list) mu_list_set_comparator (skiphdr_list, headercmp); MU_ASSERT (mu_stream_to_message (instr, &msg)); mu_stream_unref (instr); MU_ASSERT (mu_smtp_data (smtp, &ostr)); MU_ASSERT (mu_message_get_header (msg, &hdr)); MU_ASSERT (mu_header_get_iterator (hdr, &itr)); for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { const char *name; void *value; mu_iterator_current_kv (itr, (void*) &name, &value); if (mu_list_locate (skiphdr_list, (void*) name, NULL) == 0) continue; mu_stream_printf (ostr, "%s: %s\n", name, (char*)value); } mu_iterator_destroy (&itr); MU_ASSERT (mu_stream_write (ostr, "\n", 1, NULL)); MU_ASSERT (mu_message_get_body (msg, &body)); MU_ASSERT (mu_body_get_streamref (body, &bstr)); MU_ASSERT (mu_stream_copy (ostr, bstr, 0, NULL)); mu_stream_destroy (&bstr); mu_stream_close (ostr); mu_stream_destroy (&ostr); } MU_ASSERT (mu_smtp_dot (smtp)); MU_ASSERT (mu_smtp_quit (smtp)); mu_smtp_destroy (&smtp); mu_stream_close (instr); mu_stream_destroy (&instr); return 0; }
oo\n\ "; int main (int argc, char **argv) { int i; char *p; mu_message_t msg; mu_stream_t stream = NULL; mu_header_t hdr; mu_body_t body; mu_set_program_name (argv[0]); mu_static_memory_stream_create (&stream, text, strlen (text)); assert (mu_stream_to_message (stream, &msg) == 0); mu_stream_unref (stream); assert (mu_message_get_header (msg, &hdr) == 0); assert (mu_message_get_body (msg, &body) == 0); assert (mu_body_get_streamref (body, &stream) == 0); assert (mu_stream_seek (stream, 0, MU_SEEK_END, NULL) == 0); for (i = 1; i < argc; i++) { if (strcmp (argv[i], "-h") == 0) { mu_printf ("usage: %s [-a HDR:VAL] [-t TEXT]\n", mu_program_name); return 0; } if (strcmp (argv[i], "-a") == 0) { i++; assert (argv[i] != NULL); p = strchr (argv[i], ':'); assert (p != NULL); *p++ = 0; while (*p && mu_isspace (*p)) p++; assert (mu_header_set_value (hdr, argv[i], p, 1) == 0); } else if (strcmp (argv[i], "-l") == 0) { mu_off_t off; int whence = MU_SEEK_SET; i++; assert (argv[i] != NULL); off = strtol (argv[i], &p, 10); assert (*p == 0); if (off < 0) whence = MU_SEEK_END; assert (mu_stream_seek (stream, off, whence, NULL) == 0); } else if (strcmp (argv[i], "-t") == 0) { mu_wordsplit_t ws; i++; assert (argv[i] != NULL); if (mu_wordsplit (argv[i], &ws, MU_WRDSF_NOSPLIT | MU_WRDSF_DEFFLAGS)) { mu_error ("mu_wordsplit: %s", mu_wordsplit_strerror (&ws)); exit (1); } else assert (mu_stream_write (stream, ws.ws_wordv[0], strlen (ws.ws_wordv[0]), NULL) == 0); mu_wordsplit_free (&ws); } else mu_error ("ignoring unknown argument %s", argv[i]); } mu_stream_unref (stream); assert (mu_message_get_streamref (msg, &stream) == 0); assert (mu_stream_copy (mu_strout, stream, 0, NULL) == 0); mu_stream_unref (stream); return 0; }
int main (int argc, char **argv) { int index; int rc; mu_stream_t in, tmp; mu_message_t msg; mu_header_t hdr; mu_iterator_t itr; const char *file; char *newval; mu_off_t size; mu_body_t body; mu_stream_t bstr; MU_APP_INIT_NLS (); mh_argp_init (); mh_argp_parse (&argc, &argv, 0, options, mh_option, args_doc, doc, opt_handler, NULL, &index); if (index == argc) { mu_error (_("file name not given")); exit (1); } file = argv[index]; prompter_init (); if (erase_seq) prompter_set_erase (erase_seq); if (kill_seq) prompter_set_erase (kill_seq); if ((rc = mu_stdio_stream_create (&strout, MU_STDOUT_FD, MU_STREAM_WRITE))) { mu_error (_("cannot open stdout: %s"), mu_strerror (rc)); return 1; } if ((rc = mu_file_stream_create (&in, file, MU_STREAM_RDWR))) { mu_error (_("cannot open input file `%s': %s"), file, mu_strerror (rc)); return 1; } rc = mu_stream_to_message (in, &msg); mu_stream_unref (in); if (rc) { mu_error (_("input stream %s is not a message (%s)"), file, mu_strerror (rc)); return 1; } if ((rc = mu_temp_file_stream_create (&tmp, NULL, 0))) { mu_error (_("Cannot open temporary file: %s"), mu_strerror (rc)); return 1; } /* Copy headers */ mu_message_get_header (msg, &hdr); mu_header_get_iterator (hdr, &itr); for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { const char *name, *val; mu_iterator_current_kv (itr, (const void **)&name, (void**)&val); if (!is_empty_string (val)) { mu_stream_printf (tmp, "%s: %s\n", name, val); mu_stream_printf (strout, "%s: %s\n", name, val); } else { int cont = 0; mu_opool_t opool; const char *prompt = name; mu_opool_create (&opool, 1); do { size_t len; char *p; p = prompter_get_value (prompt); if (!p) return 1; prompt = NULL; if (cont) { mu_opool_append_char (opool, '\n'); if (!mu_isspace (p[0])) mu_opool_append_char (opool, '\t'); } len = strlen (p); if (len > 0 && p[len-1] == '\\') { len--; cont = 1; } else cont = 0; mu_opool_append (opool, p, len); free (p); } while (cont); mu_opool_append_char (opool, 0); newval = mu_opool_finish (opool, NULL); if (!is_empty_string (newval)) mu_stream_printf (tmp, "%s: %s\n", name, newval); mu_opool_destroy (&opool); } } mu_iterator_destroy (&itr); mu_stream_printf (strout, "--------\n"); mu_stream_write (tmp, "\n", 1, NULL); /* Copy body */ if (prepend_option) { mu_stream_printf (strout, "\n--------%s\n\n", _("Enter initial text")); while ((newval = prompter_get_line ())) { mu_stream_write (tmp, newval, strlen (newval), NULL); free (newval); mu_stream_write (tmp, "\n", 1, NULL); } } mu_message_get_body (msg, &body); mu_body_get_streamref (body, &bstr); if (!prepend_option && !rapid_option) { mu_stream_copy (strout, bstr, 0, NULL); mu_stream_seek (bstr, 0, MU_SEEK_SET, NULL); } mu_stream_copy (tmp, bstr, 0, NULL); mu_stream_unref (bstr); if (!prepend_option && !rapid_option) { printf ("\n--------%s\n\n", _("Enter additional text")); while ((newval = prompter_get_line ())) { mu_stream_write (tmp, newval, strlen (newval), NULL); free (newval); mu_stream_write (tmp, "\n", 1, NULL); } } /* Destroy the message */ mu_message_destroy (&msg, mu_message_get_owner (msg)); /* Rewind the streams and copy data back to in. */ mu_stream_seek (in, 0, MU_SEEK_SET, NULL); mu_stream_seek (tmp, 0, MU_SEEK_SET, NULL); mu_stream_copy (in, tmp, 0, &size); mu_stream_truncate (in, size); mu_stream_destroy (&in); mu_stream_destroy (&tmp); mu_stream_destroy (&strout); prompter_done (); return 0; }
/* FIXME: Do we need to escape body line that begins with "From "? This will require reading the body line by line instead of by chunks, considerably hurting perfomance when expunging. But should not this be the responsibility of the client ? */ static int mbox_append_message0 (mu_mailbox_t mailbox, mu_message_t msg, mu_off_t *psize, int is_expunging, int first) { mbox_data_t mud = mailbox->data; int status = 0; size_t n = 0; char nl = '\n'; size_t orig_size = *psize; char *s; switch (mud->state) { case MBOX_NO_STATE: mud->off = 0; mud->state = MBOX_STATE_APPEND_SENDER; case MBOX_STATE_APPEND_SENDER: /* Generate the sender for the "From " separator. */ { char *s; mu_envelope_t envelope = NULL; mu_message_get_envelope (msg, &envelope); status = mu_envelope_aget_sender (envelope, &mud->sender); switch (status) { case 0: break; case EAGAIN: return status; case MU_ERR_NOENT: /* Envelope headers not found: try to guess */ free (mud->sender); mud->sender = NULL; status = restore_sender (msg, mud); if (status == 0) break; default: free (mud->sender); free (mud->date); mud->date = mud->sender = NULL; mud->state = MBOX_NO_STATE; return status; } /* Nuke trailing newline. */ s = strchr (mud->sender, nl); if (s) *s = '\0'; mud->state = MBOX_STATE_APPEND_DATE; } case MBOX_STATE_APPEND_DATE: /* Generate a date for the "From " separator. */ { mu_envelope_t envelope = NULL; const char *envarr[5]; mu_message_get_envelope (msg, &envelope); status = mu_envelope_aget_date (envelope, &mud->date); switch (status) { case 0: break; case EAGAIN: return status; case MU_ERR_NOENT: /* Envelope headers not found: try to guess */ free (mud->date); mud->date = NULL; status = restore_date (msg, mud); if (status == 0) break; default: free (mud->sender); free (mud->date); mud->date = mud->sender = NULL; mud->state = MBOX_NO_STATE; return status; } /* Nuke trailing newline. */ s = strchr (mud->date, nl); if (s) *s = '\0'; /* Write the separator to the mailbox. */ envarr[0] = "From "; envarr[1] = mud->sender; envarr[2] = " "; envarr[3] = mud->date; envarr[4] = "\n"; status = write_array (mailbox->stream, psize, 5, envarr); if (status) break; free (mud->sender); free (mud->date); mud->sender = mud->date = NULL; /* If we are not expunging get the message in one block via the stream message instead of the header/body. This is good for POP where there is no separation between header and body(RETR). */ if (! is_expunging) { mud->state = MBOX_STATE_APPEND_MESSAGE; break; } mud->state = MBOX_STATE_APPEND_HEADER; } case MBOX_STATE_APPEND_HEADER: /* Append the Header. */ { char buffer[1024]; size_t nread = 0; mu_stream_t is = NULL; mu_header_t header = NULL; mu_message_get_header (msg, &header); mu_header_get_stream (header, &is); do { status = mu_stream_readline (is, buffer, sizeof (buffer), mud->off, &nread); if (status != 0) { if (status != EAGAIN) { mud->state = MBOX_NO_STATE; mud->off = 0; } mu_stream_truncate (mailbox->stream, orig_size); return status; } mud->off += nread; if (*buffer == '\n') break; /* We do not copy the Status since it is rewritten by the attribute code below. Ditto for X-UID and X-IMAPBase. FIXME: - We have a problem here the header may not fit the buffer. - Should we skip the IMAP "X-Status"? */ if ((mu_c_strncasecmp (buffer, "Status", 6) == 0) || (mu_c_strncasecmp (buffer, "X-IMAPbase", 10) == 0) /* FIXME: isn't the length of "X-UID" 5, not 4? And this will match X-UID and X-UIDL, is this intended? */ || (mu_c_strncasecmp (buffer, "X-UID", 4) == 0 && (buffer[5] == ':' || buffer[5] == ' ' || buffer[5] == '\t'))) continue; status = mu_stream_write (mailbox->stream, buffer, nread, *psize, &n); if (status) break; *psize += n; } while (nread > 0); mud->off = 0; /* Rewrite the X-IMAPbase marker. */ if (first && is_expunging) { n = sprintf (buffer, "X-IMAPbase: %lu %u\n", (unsigned long) mud->uidvalidity, (unsigned) mud->uidnext); status = mu_stream_write (mailbox->stream, buffer, n, *psize, &n); if (status) break; *psize += n; } mud->state = MBOX_STATE_APPEND_ATTRIBUTE; } case MBOX_STATE_APPEND_ATTRIBUTE: /* Put the new attributes. */ { #define STATUS_PREFIX_LEN (sizeof(MU_HEADER_STATUS) - 1 + 2) char abuf[STATUS_PREFIX_LEN + MU_STATUS_BUF_SIZE + 1]; size_t na = 0; mu_attribute_t attr = NULL; strcpy(abuf, MU_HEADER_STATUS); strcat(abuf, ": "); mu_message_get_attribute (msg, &attr); mu_attribute_to_string (attr, abuf + STATUS_PREFIX_LEN, sizeof(abuf) - STATUS_PREFIX_LEN - 1, &na); strcat (abuf, "\n"); na = strlen (abuf); mu_stream_write (mailbox->stream, abuf, na, *psize, &n); if (status != 0) break; *psize += n; mud->state = MBOX_STATE_APPEND_UID; } case MBOX_STATE_APPEND_UID: /* The new X-UID. */ { char suid[64]; size_t uid = 0; if (is_expunging) { status = mu_message_get_uid (msg, &uid); if (status == EAGAIN) return status; } else uid = mud->uidnext++; if (status == 0 || uid != 0) { n = sprintf (suid, "X-UID: %u\n", (unsigned) uid); /* Put the UID. */ status = mu_stream_write (mailbox->stream, suid, n, *psize, &n); if (status) break; *psize += n; } /* New line separator of the Header. */ status = mu_stream_write (mailbox->stream, &nl , 1, *psize, &n); if (status) break; *psize += n; mud->state = MBOX_STATE_APPEND_BODY; } case MBOX_STATE_APPEND_BODY: /* Append the Body. */ { char buffer[1024]; size_t nread = 0; mu_stream_t is = NULL; mu_body_t body = NULL; mu_message_get_body (msg, &body); mu_body_get_stream (body, &is); do { status = mu_stream_read (is, buffer, sizeof (buffer), mud->off, &nread); if (status != 0) { if (status != EAGAIN) { mud->state = MBOX_NO_STATE; mud->off = 0; } return status; } mud->off += nread; status = mu_stream_write (mailbox->stream, buffer, nread, *psize, &n); if (status) break; *psize += n; } while (nread > 0); mud->off = 0; n = 0; status = mu_stream_write (mailbox->stream, &nl, 1, *psize, &n); if (status) break; *psize += n; } default: break; } /* If not expunging we are taking the stream message. */ if (!is_expunging) { switch (mud->state) { case MBOX_STATE_APPEND_MESSAGE: { /* Append the Message. */ char buffer[1024]; size_t nread = 0; mu_stream_t is = NULL; mu_message_get_stream (msg, &is); do { status = mu_stream_read (is, buffer, sizeof (buffer), mud->off, &nread); if (status != 0) { if (status != EAGAIN) { mud->state = MBOX_NO_STATE; mud->off = 0; } mu_stream_truncate (mailbox->stream, orig_size); return status; } status = mu_stream_write (mailbox->stream, buffer, nread, *psize, &n); if (status) break; mud->off += nread; *psize += n; } while (nread > 0); if (status) break; status = mu_stream_write (mailbox->stream, &nl, 1, *psize, &n); if (status == 0) *psize += n; } default: break; } } /* is_expunging */ mud->state = MBOX_NO_STATE; if (status) mu_stream_truncate (mailbox->stream, orig_size); else mu_stream_flush (mailbox->stream); return status; }
/* For the expunge bits we took a very cautionnary approach, meaning we create a temporary mailbox in the tmpdir copy all the message not mark deleted(Actually we copy all the message that may have been modified i.e new header values set; UIDL or UID or etc .... and skip the deleted ones, truncate the real mailbox to the desired size and overwrite with the tmp mailbox. The approach to do everyting in core is tempting but require - to much memory, it is not rare nowadays to have 30 Megs mailbox, - also there is danger for filesystems with quotas, - if we run out of disk space everything is lost. - or some program may not respect the advisory lock and decide to append a new message while your expunging etc ... The real downside to the approach is that when things go wrong the temporary file may be left in /tmp, which is not all that bad because at least, we have something to recuperate when failure. */ static int mbox_expunge0 (mu_mailbox_t mailbox, int remove_deleted) { mbox_data_t mud = mailbox->data; mbox_message_t mum; int status = 0; sigset_t signalset; int tempfile; size_t i, j, dirty; /* dirty will indicate the first modified message. */ mu_off_t marker = 0; /* marker will be the position to truncate. */ mu_off_t total = 0; char *tmpmboxname = NULL; mu_mailbox_t tmpmailbox = NULL; size_t save_imapbase = 0; /* uidvalidity is save in the first message. */ #ifdef WITH_PTHREAD int state; #endif if (mud == NULL) return EINVAL; MU_DEBUG1 (mailbox->debug, MU_DEBUG_TRACE1, "mbox_expunge (%s)\n", mud->name); /* Noop. */ if (mud->messages_count == 0) return 0; /* Find the first dirty(modified) message. */ for (dirty = 0; dirty < mud->messages_count; dirty++) { mum = mud->umessages[dirty]; /* Message may have been tampered, break here. */ if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) || (mum->attr_flags & MU_ATTRIBUTE_DELETED) || (mum->message && mu_message_is_modified (mum->message))) break; } /* Did something change ? */ if (dirty == mud->messages_count) return 0; /* Nothing change, bail out. */ /* Create a temporary file. */ tempfile = mbox_tmpfile (mailbox, &tmpmboxname); if (tempfile == -1) { if (tmpmboxname) free (tmpmboxname); mu_error (_("failed to create temporary file when expunging")); return errno; } /* This is redundant, we go to the loop again. But it's more secure here since we don't want to be disturb when expunging. Destroy all the messages mark for deletion. */ if (remove_deleted) { for (j = 0; j < mud->messages_count; j++) { mum = mud->umessages[j]; if (mum && mum->message && ATTRIBUTE_IS_DELETED (mum->attr_flags)) mu_message_destroy (&(mum->message), mum); } } /* Create temporary mu_mailbox_t. */ { mbox_data_t tmp_mud; char *m = malloc (5 + strlen (tmpmboxname) + 1); if (!m) return ENOMEM; /* Try via the mbox: protocol. */ sprintf (m, "mbox:%s", tmpmboxname); status = mu_mailbox_create (&tmpmailbox, m); if (status != 0) { /* Do not give up just yet, maybe they register the mu_path_record. */ sprintf (m, "%s", tmpmboxname); status = mu_mailbox_create (&tmpmailbox, m); if (status != 0) { /* Ok give up. */ close (tempfile); remove (tmpmboxname); free (m); free (tmpmboxname); return status; } } free (m); /* Must be flag CREATE if not the mu_mailbox_open will try to mmap() the file. */ status = mu_mailbox_open (tmpmailbox, MU_STREAM_CREAT | MU_STREAM_RDWR); if (status != 0) { close (tempfile); remove (tmpmboxname); free (tmpmboxname); return status; } close (tempfile); /* This one is useless the mailbox have its own. */ tmp_mud = tmpmailbox->data; /* May need when appending. */ tmp_mud->uidvalidity = mud->uidvalidity; tmp_mud->uidnext = mud->uidnext; } /* Get the File lock. */ if ((status = mu_locker_lock (mailbox->locker)) != 0) { mu_mailbox_close (tmpmailbox); mu_mailbox_destroy (&tmpmailbox); remove (tmpmboxname); free (tmpmboxname); mu_error (_("failed to grab the lock: %s"), mu_strerror (status)); return status; } /* Critical section, we can not allowed signal here. */ #ifdef WITH_PTHREAD pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state); #endif sigemptyset (&signalset); sigaddset (&signalset, SIGTERM); sigaddset (&signalset, SIGHUP); sigaddset (&signalset, SIGTSTP); sigaddset (&signalset, SIGINT); sigaddset (&signalset, SIGWINCH); sigprocmask (SIG_BLOCK, &signalset, 0); /* Set the marker position. */ marker = mud->umessages[dirty]->header_from; total = 0; /* Copy to the temporary mailbox emails not mark deleted. */ for (i = dirty; i < mud->messages_count; i++) { mum = mud->umessages[i]; /* Skip it, if mark for deletion. */ if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags)) { /* We save the uidvalidity in the first message, if it is being deleted we need to move the uidvalidity to the first available (non-deleted) message. */ if (i == save_imapbase) { save_imapbase = i + 1; if (save_imapbase < mud->messages_count) (mud->umessages[save_imapbase])->attr_flags |= MU_ATTRIBUTE_MODIFIED; } continue; } /* Do the expensive mbox_append_message0() only if mark dirty. */ if ((mum->attr_flags & MU_ATTRIBUTE_MODIFIED) || (mum->message && mu_message_is_modified (mum->message))) { /* The message was not instantiated, probably the dirty flag was set by mbox_scan(), create one here. */ if (mum->message == 0) { mu_message_t msg; status = mbox_get_message (mailbox, i + 1, &msg); if (status != 0) { mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout0; } } status = mbox_append_message0 (tmpmailbox, mum->message, &total, 1, (i == save_imapbase)); if (status != 0) { mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout0; } /* Clear the dirty bits. */ mum->attr_flags &= ~MU_ATTRIBUTE_MODIFIED; mu_message_clear_modified (mum->message); } else { /* Nothing changed copy the message straight. */ char buffer [1024]; size_t n; mu_off_t offset = mum->header_from; size_t len = mum->body_end - mum->header_from; while (len > 0) { n = (len < sizeof (buffer)) ? len : sizeof (buffer); if ((status = mu_stream_read (mailbox->stream, buffer, n, offset, &n) != 0) || (status = mu_stream_write (tmpmailbox->stream, buffer, n, total, &n) != 0)) { mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout0; } len -= n; total += n; offset += n; } /* Add the newline separator. */ status = mu_stream_write (tmpmailbox->stream, "\n", 1, total, &n); if (status != 0) { mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout0; } total++; } } /* for (;;) */ /* Caution: before ftruncate()ing the file see - if we've receive new mails. Some programs may not respect the lock, - or the lock was held for too long. - The mailbox may not have been properly updated before expunging. */ { mu_off_t size = 0; if (mu_stream_size (mailbox->stream, &size) == 0) { mu_off_t len = size - mud->size; mu_off_t offset = mud->size; char buffer [1024]; size_t n = 0; if (len > 0 ) { while ((status = mu_stream_read (mailbox->stream, buffer, sizeof (buffer), offset, &n)) == 0 && n > 0) { status = mu_stream_write (tmpmailbox->stream, buffer, n, total, &n); if (status != 0) { mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout0; } total += n; offset += n; } } else if (len < 0) { /* Corrupted mailbox. */ mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout0; } } } /* End of precaution. */ /* Seek and rewrite it. */ if (total > 0) { char buffer [1024]; size_t n = 0; mu_off_t off = 0; mu_off_t offset = marker; while ((status = mu_stream_read (tmpmailbox->stream, buffer, sizeof (buffer), off, &n)) == 0 && n > 0) { status = mu_stream_write (mailbox->stream, buffer, n, offset, &n); if (status != 0) { mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout; } off += n; offset += n; } } /* Flush/truncation. Need to flush before truncate. */ mu_stream_flush (mailbox->stream); status = mu_stream_truncate (mailbox->stream, total + marker); if (status != 0) { mu_error (_("error expunging:%d: %s"), __LINE__, mu_strerror (status)); goto bailout; } /* Don't remove the tmp mbox in case of errors, when writing back. */ bailout0: remove (tmpmboxname); bailout: free (tmpmboxname); /* Release the File lock. */ mu_locker_unlock (mailbox->locker); mu_mailbox_close (tmpmailbox); mu_mailbox_destroy (&tmpmailbox); /* Reenable signal. */ #ifdef WITH_PTHREAD pthread_setcancelstate (state, &state); #endif sigprocmask (SIG_UNBLOCK, &signalset, 0); /* We need to readjust the pointers. It is a little hairy because we need to keep the message pointers alive So we are going through the array and "defragmentize". For example in (1 2 3 4) if 2 was deleted we need to move 3 4 up by one etc .. */ if (status == 0) { size_t dlast; mu_monitor_wrlock (mailbox->monitor); for (j = dirty, dlast = mud->messages_count - 1; j <= dlast; j++) { /* Clear all the references, any attach messages been already destroy above. */ mum = mud->umessages[j]; if (remove_deleted && ATTRIBUTE_IS_DELETED (mum->attr_flags)) { if ((j + 1) <= dlast) { /* Move all the pointers up. So the message pointer part of mum will be at the right position. */ memmove (mud->umessages + j, mud->umessages + j + 1, (dlast - j) * sizeof (mum)); #if 0 mum->header_from = mum->header_from_end = 0; mum->body = mum->body_end = 0; mum->header_lines = mum->body_lines = 0; #endif memset (mum, 0, sizeof (*mum)); /* We are not free()ing the useless mum, but instead we put it back in the pool, to be reuse. */ mud->umessages[dlast] = mum; dlast--; /* Set mum to the new value after the memmove so it gets cleared to. */ mum = mud->umessages[j]; } else { memset (mum, 0, sizeof (*mum)); } } mum->header_from = mum->header_from_end = 0; mum->body = mum->body_end = 0; mum->header_lines = mum->body_lines = 0; } mu_monitor_unlock (mailbox->monitor); /* This is should reset the messages_count, the last argument 0 means not to send event notification. */ mbox_scan0 (mailbox, dirty, NULL, 0); } return status; }
int io_send_bytes (const char *buf, size_t size) { return mu_stream_write (iostream, buf, size, NULL); }