/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2021 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see . */
#include "mail.h"
#include
#include
#include
#include
#include
#ifdef HAVE_TERMIOS_H
# include
#endif
#include
#include
#ifdef HAVE_FCNTL_H
# include
#else
# include
#endif
#include
int
util_do_command (const char *fmt, ...)
{
struct mu_wordsplit ws;
int argc;
char **argv;
int status = 0;
const struct mail_command_entry *entry = NULL;
char *cmd = NULL;
size_t size = 0;
va_list ap;
va_start (ap, fmt);
status = mu_vasnprintf (&cmd, &size, fmt, ap);
va_end (ap);
if (status)
return status;
if (cmd)
{
/* Ignore comments */
if (cmd[0] == '#')
{
free (cmd);
return 0;
}
if (cmd[0] == '\0')
{
free (cmd);
/* Hitting return i.e. no command, is equivalent to next
according to the POSIX spec. Note, that this applies
to interactive state only. */
if (interactive)
cmd = mu_strdup ("next");
else
return 0;
}
ws.ws_offs = 1; /* Keep one extra slot in case we need it
for expansion (see below) */
if (mu_wordsplit (cmd, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DOOFFS))
{
mu_error ("\"%s\": %s", cmd, mu_wordsplit_strerror (&ws));
free (cmd);
return MU_ERR_PARSE;
}
else
{
char *p;
argc = ws.ws_wordc;
argv = ws.ws_wordv + 1;
/* Special case: a number alone implies "print" */
if (argc == 1
&& ((strtoul (argv[0], &p, 10) > 0 && *p == 0)
|| (argv[0][1] == 0 && strchr ("^$", argv[0][0]))))
{
/* Use the extra slot for "print" command */
argc++;
argv--;
argv[0] = "print";
}
entry = mail_find_command (argv[0]);
free (cmd);
}
}
else
entry = mail_find_command (mailvar_name_quit);
if (!entry)
{
/* argv[0] might be a traditional /bin/mail contracted form, e.g.
`d*' or `p4'. */
char *p;
for (p = argv[0] + strlen (argv[0]) - 1;
p > argv[0] && !mu_isalpha (*p);
p--)
;
p++;
if (strlen (p))
{
/* Expand contracted form. That's what we have kept an extra
ws slot for. */
argc++;
argv--;
argv[0] = argv[1];
argv[1] = mu_strdup (p);
*p = 0;
/* Register the new entry in WS */
ws.ws_wordc++;
ws.ws_offs = 0;
}
entry = mail_find_command (argv[0]);
}
if (entry)
{
/* Make sure we are not in any if/else */
if (!(if_cond () == 0 && (entry->flags & EF_FLOW) == 0))
status = entry->func (argc, argv);
}
else
{
if (argc)
mu_error (_("Unknown command: %s"), argv[0]);
else
mu_error (_("Invalid command"));
status = 1;
}
mu_wordsplit_free (&ws);
return status;
}
int
util_foreach_msg (int argc, char **argv, int flags,
msg_handler_t func, void *data)
{
msgset_t *list = NULL, *mp;
int status = 0;
if (msgset_parse (argc, argv, flags, &list))
return 1;
for (mp = list; mp; mp = mp->next)
{
mu_message_t mesg;
if (util_get_message (mbox, msgset_msgno (mp), &mesg) == 0)
{
if (func (mp, mesg, data) != 0)
status = 1;
/* Bail out if we receive an interrupt. */
if (ml_got_interrupt () != 0)
break;
}
}
msgset_free (list);
return status;
}
size_t
util_range_msg (size_t low, size_t high, int flags,
msg_handler_t func, void *data)
{
size_t count, expect_count;
if (!func)
flags |= MSG_SILENT;
if (low > total)
return 0;
if (!(flags & MSG_COUNT))
{
if (high < low)
return 0;
expect_count = high - low + 1;
}
else
expect_count = high;
for (count = 0; count < expect_count && low <= total; low++)
{
mu_message_t mesg;
if ((flags & MSG_NODELETED) && util_isdeleted (low))
{
if (!(flags & MSG_SILENT))
mu_error (_("%lu: Inappropriate message (has been deleted)"),
(unsigned long) low);
continue;
}
if (util_get_message (mbox, low, &mesg) == 0)
{
count ++;
if (func)
{
msgset_t *set = msgset_make_1 (low);
func (set, mesg, data);
msgset_free (set);
}
/* Bail out if we receive an interrupt. */
if (ml_got_interrupt () != 0)
break;
}
}
return count;
}
/*
* returns the function to run for command
*/
function_t *
util_command_get (const char *cmd)
{
const struct mail_command_entry *entry = mail_find_command (cmd);
return entry ? entry->func : NULL;
}
/*
* returns the mail_command_entry structure for the command matching cmd
*/
void *
util_find_entry (void *table, size_t nmemb, size_t size, const char *cmd)
{
int i;
int len = strlen (cmd);
char *p;
for (p = table, i = 0; i < nmemb; i++, p += size)
{
struct mail_command *cp = (struct mail_command *)p;
int ll = strlen (cp->longname);
int sl = strlen (cp->shortname);
if (sl > ll && !strncmp (cp->shortname, cmd, sl))
return p;
else if (sl == len && !strcmp (cp->shortname, cmd))
return p;
else if (sl < len && !strncmp (cp->longname, cmd, len))
return p;
}
return NULL;
}
int
util_help (void *table, size_t nmemb, size_t size, const char *word)
{
if (!word)
{
int i = 0;
mu_stream_t out;
char *p;
out = open_pager (util_screen_lines () + 1);
for (p = table, i = 0; i < nmemb; i++, p += size)
{
struct mail_command *cp = (struct mail_command *)p;
if (cp->synopsis == NULL)
continue;
mu_stream_printf (out, "%s\n", cp->synopsis);
}
mu_stream_unref (out);
return 0;
}
else
{
int status = 0;
struct mail_command *cp = util_find_entry (table, nmemb, size, word);
if (cp && cp->synopsis)
mu_printf ("%s\n", cp->synopsis);
else
{
status = 1;
mu_printf (_("Unknown command: %s\n"), word);
}
return status;
}
return 1;
}
int
util_command_list (void *table, size_t nmemb, size_t size)
{
int i;
char *p;
int cols = util_screen_columns ();
int pos = 0;
for (p = table, i = 0; i < nmemb; i++, p += size)
{
const char *cmd;
struct mail_command *cp = (struct mail_command *)p;
int len = strlen (cp->longname);
if (len < 1)
{
cmd = cp->shortname;
len = strlen (cmd);
}
else
cmd = cp->longname;
pos += len + 1;
if (pos >= cols)
{
pos = len + 1;
mu_printf ("\n%s ", cmd);
}
else
mu_printf ("%s ", cmd);
}
mu_printf ("\n");
return 0;
}
/*
* Get the number of columns on the screen
* First try an ioctl() call not all shells set the COLUMNS environ.
*/
int
util_getcols (void)
{
struct winsize ws;
ws.ws_col = ws.ws_row = 0;
if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0)
return 80; /* FIXME: Should we exit()/abort() if col <= 0 ? */
return ws.ws_col;
}
/*
* Get the number of lines on the screen
* First try an ioctl() call not all shells set the LINES environ.
*/
int
util_getlines (void)
{
struct winsize ws;
ws.ws_col = ws.ws_row = 0;
if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row <= 2)
ws.ws_row = 24; /* FIXME: Should we exit()/abort() if row <= 2 ? */
/* Reserve at least 2 line for the prompt. */
return ws.ws_row - 2;
}
int
util_screen_lines ()
{
int screen;
size_t n;
if (mailvar_get (&screen, mailvar_name_screen, mailvar_type_number, 0) == 0)
return screen;
n = util_getlines();
util_do_command ("set screen=%lu", (unsigned long) n);
return n;
}
int
util_screen_columns ()
{
int cols;
size_t n;
if (mailvar_get (&cols, mailvar_name_columns, mailvar_type_number, 0) == 0)
return cols;
n = util_getcols();
util_do_command ("set columns=%lu", (unsigned long) n);
return n;
}
int
util_get_crt ()
{
int lines;
if (mailvar_get (&lines, mailvar_name_crt, mailvar_type_number, 0) == 0)
return lines;
else if (mailvar_is_true (mailvar_name_crt))
return util_getlines ();
return 0;
}
const char *
util_reply_prefix ()
{
char *prefix = "Re: ";
mailvar_get (&prefix, mailvar_name_replyprefix, mailvar_type_string, 0);
return prefix;
}
/* ************************* */
/*
* return 1 if a message is deleted
*/
int
util_isdeleted (size_t n)
{
mu_message_t msg = NULL;
mu_attribute_t attr = NULL;
mu_mailbox_get_message (mbox, n, &msg);
mu_message_get_attribute (msg, &attr);
return mu_attribute_is_deleted (attr);
}
void
util_mark_read (mu_message_t msg)
{
mu_attribute_t attr;
mu_message_get_attribute (msg, &attr);
mu_attribute_set_read (attr);
mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SHOWN);
}
char *
util_get_homedir ()
{
char *homedir = mu_get_homedir ();
if (!homedir)
{
/* Shouldn't happen, but one never knows */
mu_error (_("Cannot get homedir"));
exit (EXIT_FAILURE);
}
return homedir;
}
char *
util_fullpath (const char *inpath)
{
return mu_tilde_expansion (inpath, MU_HIERARCHY_DELIMITER, NULL);
}
char *
util_folder_path (const char *name)
{
char *folder;
int rc;
if (mailvar_get (&folder, mailvar_name_folder, mailvar_type_string, 1))
return NULL;
if (!name)
return NULL;
rc = mu_mailbox_expand_name (name, &folder);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mailbox_expand_name", name, rc);
return NULL;
}
return folder;
}
int outfilename_mode;
char *
util_outfilename (mu_address_t addr)
{
char *buf, *p;
int rc;
if ((rc = mu_address_aget_email (addr, 1, &buf)) != 0)
{
mu_error (_("Cannot determine sender name: %s"), mu_strerror (rc));
return NULL;
}
switch (mailvar_is_true (mailvar_name_mailx)
? outfilename_local : outfilename_mode)
{
case outfilename_local:
p = strchr (buf, '@');
if (p)
*p = 0;
break;
case outfilename_email:
break;
case outfilename_domain:
p = strchr (buf, '@');
if (p)
{
p++;
memmove (buf, p, strlen (p) + 1);
}
else
{
free (buf);
buf = mu_strdup ("localdomain");
}
break;
}
return buf;
}
char *
util_message_sender (mu_message_t msg, int strip)
{
mu_address_t addr = get_sender_address (msg);
char *buf;
int rc;
if (!addr)
{
mu_envelope_t env = NULL;
const char *buffer;
mu_message_get_envelope (msg, &env);
if (mu_envelope_sget_sender (env, &buffer)
|| mu_address_create (&addr, buffer))
{
mu_error (_("Cannot determine sender name"));
return NULL;
}
}
if (strip)
{
buf = util_outfilename (addr);
}
else if ((rc = mu_address_aget_printable (addr, &buf)) != 0)
{
mu_error (_("Cannot determine sender name: %s"), mu_strerror (rc));
buf = NULL;
}
mu_address_destroy (&addr);
return buf;
}
char *
util_get_sender (int msgno, int strip)
{
mu_message_t msg;
if (mu_mailbox_get_message (mbox, msgno, &msg) == 0)
{
return util_message_sender (msg, strip);
}
return 0;
}
void
util_slist_print (mu_list_t list, int nl)
{
mu_iterator_t itr;
char *name;
if (!list || mu_list_get_iterator (list, &itr))
return;
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
mu_iterator_current (itr, (void **)&name);
mu_printf ("%s%c", name, nl ? '\n' : ' ');
}
mu_iterator_destroy (&itr);
}
int
util_slist_lookup (mu_list_t list, const char *str)
{
mu_iterator_t itr;
char *name;
int rc = 0;
if (!list || mu_list_get_iterator (list, &itr))
return 0;
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
mu_iterator_current (itr, (void **)&name);
if (mu_c_strcasecmp (name, str) == 0)
{
rc = 1;
break;
}
}
mu_iterator_destroy (&itr);
return rc;
}
static int
util_slist_compare (const void *a, const void *b)
{
return mu_c_strcasecmp (a, b);
}
void
util_slist_add (mu_list_t *plist, char *value)
{
mu_list_t list;
if (!*plist)
{
if (mu_list_create (&list))
return;
mu_list_set_destroy_item (list, mu_list_free_item);
mu_list_set_comparator (list, util_slist_compare);
*plist = list;
}
else
list = *plist;
mu_list_append (list, mu_strdup (value));
}
void
util_slist_remove (mu_list_t *list, char *value)
{
mu_list_remove (*list, value);
}
void
util_slist_destroy (mu_list_t *list)
{
mu_list_destroy (list);
}
char *
util_slist_to_string (mu_list_t list, const char *delim)
{
mu_iterator_t itr;
char *name;
char *str = NULL;
if (!list || mu_list_get_iterator (list, &itr))
return NULL;
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
mu_iterator_current (itr, (void **)&name);
if (str && delim)
util_strcat (&str, delim);
util_strcat (&str, name);
}
mu_iterator_destroy (&itr);
return str;
}
void
util_strcat (char **dest, const char *str)
{
if (!*dest)
*dest = mu_strdup (str);
else
{
int dlen = strlen (*dest) + 1;
int slen = strlen (str) + 1;
char *newp = realloc (*dest, dlen + slen);
if (!newp)
return;
*dest = newp;
memcpy (newp + dlen - 1, str, slen);
}
}
char *
util_outfolder_name (char *str)
{
char *template = NULL;
char *folder;
char *outfolder;
int rc;
if (!str)
{
mailvar_get (&template, mailvar_name_record, mailvar_type_string, 0);
str = template;
}
else
{
switch (*str)
{
case '/':
case '~':
case '+':
break;
default:
if (mailvar_is_true (mailvar_name_mailx))
{
if (mailvar_get (NULL, mailvar_name_outfolder,
mailvar_type_whatever, 0) == 0)
{
if (mailvar_get (&folder, mailvar_name_folder,
mailvar_type_string, 0) == 0)
template = mu_make_file_name (folder, str);
}
str = template;
}
else if (mailvar_get (&outfolder, mailvar_name_outfolder,
mailvar_type_string, 0) == 0)
{
str = template = mu_make_file_name (outfolder, str);
}
else if (mailvar_is_true (mailvar_name_outfolder))
{
if (mailvar_get (&folder, mailvar_name_folder,
mailvar_type_string, 0) == 0)
template = mu_make_file_name (folder, str);
str = template;
}
else
str = NULL;
break;
}
}
if (str)
{
char *tilde_template = NULL;
char *exp;
if (mailvar_is_true (mailvar_name_mailx))
{
switch (*str)
{
case '/':
case '~':
case '+':
break;
default:
if (mu_asprintf (&tilde_template, "~/%s", str))
{
mu_alloc_die ();
}
str = tilde_template;
}
}
rc = mu_mailbox_expand_name (str, &exp);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_expand_name", str, rc);
str = NULL;
}
else
str = exp;
free (tilde_template);
}
free (template);
return str;
}
/* Save an outgoing message. The SAVEFILE argument overrides the setting
of the "record" variable. */
void
util_save_outgoing (mu_message_t msg, char *savefile)
{
char *filename = util_outfolder_name (savefile);
if (filename)
{
int rc;
mu_mailbox_t outbox;
rc = mu_mailbox_create_default (&outbox, filename);
if (rc)
{
mu_error (_("Cannot create output mailbox `%s': %s"),
filename, mu_strerror (rc));
}
else
{
rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT);
if (rc)
mu_error (_("Cannot open output mailbox `%s': %s"),
filename, mu_strerror (rc));
else
{
rc = mu_mailbox_append_message (outbox, msg);
if (rc)
mu_error (_("Cannot append message to `%s': %s"),
filename, mu_strerror (rc));
}
mu_mailbox_close (outbox);
mu_mailbox_destroy (&outbox);
}
free (filename);
}
}
//FIXME
static int
util_descend_subparts (mu_message_t mesg, msgset_t *msgset, mu_message_t *part)
{
unsigned int i;
for (i = 2; i <= msgset_length (msgset); i++)
{
mu_message_t submsg = NULL;
size_t nparts = 0;
char *type = NULL;
mu_header_t hdr = NULL;
mu_message_get_header (mesg, &hdr);
util_get_content_type (hdr, &type, NULL);
if (mu_c_strncasecmp (type, "message/rfc822", strlen (type)) == 0)
{
if (mu_message_unencapsulate (mesg, &submsg, NULL))
{
mu_error (_("Cannot unencapsulate message/part"));
return 1;
}
mesg = submsg;
}
mu_message_get_num_parts (mesg, &nparts);
if (nparts < msgset->crd[i])
{
mu_error (_("No such (sub)part in the message: %lu"),
(unsigned long) msgset->crd[i]);
return 1;
}
if (mu_message_get_part (mesg, msgset->crd[i], &submsg))
{
mu_error (_("Cannot get (sub)part from the message: %lu"),
(unsigned long) msgset->crd[i]);
return 1;
}
mesg = submsg;
}
*part = mesg;
return 0;
}
void
util_msgset_iterate (msgset_t *msgset,
int (*fun) (mu_message_t, msgset_t *, void *),
void *closure)
{
for (; msgset; msgset = msgset->next)
{
mu_message_t mesg;
if (mu_mailbox_get_message (mbox, msgset_msgno (msgset), &mesg) != 0)
return;
if (util_descend_subparts (mesg, msgset, &mesg) == 0)
(*fun) (mesg, msgset, closure);
}
}
void
util_get_content_type (mu_header_t hdr, char **value, char **args)
{
char *type = NULL;
util_get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type);
if (type == NULL || *type == '\0')
{
if (type)
free (type);
type = mu_strdup ("text/plain"); /* Default. */
}
else
{
char *p;
p = strchr (type, ';');
if (p)
{
*p++ = 0;
if (args)
*args = p;
}
}
*value = type;
}
void
util_get_hdr_value (mu_header_t hdr, const char *name, char **value)
{
int status = mu_header_aget_value_unfold (hdr, name, value);
if (status == 0)
mu_rtrim_class (*value, MU_CTYPE_SPACE);
else
{
if (status != MU_ERR_NOENT)
mu_diag_funcall (MU_DIAG_ERROR, "mu_header_aget_value_unfold", name,
status);
*value = NULL;
}
}
int
util_merge_addresses (char **addr_str, const char *value)
{
mu_address_t addr, new_addr;
int rc;
if ((rc = mu_address_create (&new_addr, value)) != 0)
return rc;
if ((rc = mu_address_create (&addr, *addr_str)) != 0)
{
mu_address_destroy (&new_addr);
return rc;
}
rc = mu_address_union (&addr, new_addr);
if (rc == 0)
{
char *val;
rc = mu_address_aget_printable (addr, &val);
if (rc == 0)
{
if (!val)
return MU_ERR_NOENT;
free (*addr_str);
*addr_str = val;
}
}
mu_address_destroy (&addr);
mu_address_destroy (&new_addr);
return rc;
}
int
is_address_field (const char *name)
{
static char *address_fields[] = {
MU_HEADER_TO,
MU_HEADER_CC,
MU_HEADER_BCC,
0
};
char **p;
for (p = address_fields; *p; p++)
if (mu_c_strcasecmp (*p, name) == 0)
return 1;
return 0;
}
void
util_address_expand_aliases (mu_address_t *paddr)
{
struct mu_address hint;
mu_address_t addr = *paddr;
mu_address_t new_addr = NULL;
int rc;
memset (&hint, 0, sizeof (hint));
while (addr)
{
struct mu_address *next = addr->next;
addr->next = NULL;
if (addr->domain == NULL)
{
char *exp = alias_expand (addr->local_part);
if (exp)
{
mu_address_destroy (&addr);
rc = mu_address_create_hint (&addr, exp, &hint, 0);
if (rc)
{
mu_error (_("Cannot parse address `%s': %s"),
exp, mu_strerror (rc));
free (exp);
continue;
}
free (exp);
}
}
mu_address_union (&new_addr, addr);
mu_address_destroy (&addr);
addr = next;
}
*paddr = new_addr;
}
int
util_header_expand_aliases (mu_header_t *phdr)
{
size_t i, nfields = 0;
mu_header_t hdr;
int errcnt = 0, rc;
if (mailvar_is_true (mailvar_name_inplacealiases))
/* If inplacealiases was set, aliases have been already expanded */
return 0;
rc = mu_header_create (&hdr, "", 0);
if (rc)
{
mu_error (_("Cannot create temporary header: %s"), mu_strerror (rc));
return 1;
}
mu_header_get_field_count (*phdr, &nfields);
for (i = 1; i <= nfields; i++)
{
const char *name;
char *value;
if (mu_header_sget_field_name (*phdr, i, &name))
continue;
if (mu_header_aget_field_value (*phdr, i, &value))
continue;
if (is_address_field (name))
{
struct mu_address hint;
const char *s;
mu_address_t a = NULL;
mu_string_unfold (value, NULL);
memset (&hint, 0, sizeof (hint));
rc = mu_address_create_hint (&a, value, &hint, 0);
if (rc == 0)
{
mu_address_t ha = NULL;
util_address_expand_aliases (&a);
if (mu_header_sget_value (hdr, name, &s) == 0)
mu_address_create_hint (&ha, s, &hint, 0);
mu_address_union (&ha, a);
mu_address_destroy (&a);
a = ha;
}
else
{
mu_error (_("Cannot parse address `%s': %s"),
value, mu_strerror (rc));
}
if (a)
{
const char *newvalue;
rc = mu_address_sget_printable (a, &newvalue);
if (rc == 0 && newvalue)
mu_header_set_value (hdr, name, newvalue, 1);
mu_address_destroy (&a);
}
}
else
mu_header_append (hdr, name, value);
free (value);
}
if (errcnt == 0)
{
mu_header_destroy (phdr);
*phdr = hdr;
}
else
mu_header_destroy (&hdr);
return errcnt;
}
int
util_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *msg)
{
int status;
if (msgno > total)
{
util_error_range (msgno);
return MU_ERR_NOENT;
}
status = mu_mailbox_get_message (mbox, msgno, msg);
if (status)
{
mu_error (_("Cannot get message %lu: %s"),
(unsigned long) msgno, mu_strerror (status));
return status;
}
return 0;
}
int
util_get_message_part (mu_mailbox_t mbox, msgset_t *msgset,
mu_message_t *ret_msg)
{
int status, ismime;
mu_message_t msg;
size_t i;
status = mu_mailbox_get_message (mbox, msgset_msgno (msgset), &msg);
if (status)
{
mu_error (_("Cannot get message %lu: %s"),
(unsigned long) msgset_msgno (msgset), mu_strerror (status));
return status;
}
for (i = 2; i <= msgset_length (msgset); i++)
{
status = mu_message_is_multipart (msg, &ismime);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_message_is_multipart",
NULL, status);
return status;
}
if (!ismime)
{
char *s = msgset_part_str (msgset, i);
mu_error (_("%s: not a multipart message"), s);
free (s);
return status;
}
status = mu_message_get_part (msg, msgset->crd[i], &msg);
if (status)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_part",
NULL, status);
return status;
}
}
*ret_msg = msg;
return 0;
}
int
util_error_range (size_t msgno)
{
mu_error (_("%lu: invalid message number"), (unsigned long) msgno);
return 1;
}
void
util_noapp ()
{
mu_error (_("No applicable messages"));
}
void
util_cache_command (mu_list_t *list, const char *fmt, ...)
{
char *cmd = NULL;
size_t size = 0;
va_list ap;
va_start (ap, fmt);
mu_vasnprintf (&cmd, &size, fmt, ap);
va_end (ap);
if (!*list)
mu_list_create (list);
mu_list_append (*list, cmd);
}
static int
_run_and_free (void *item, void *data)
{
util_do_command ("%s", (char *) item);
free (item);
return 0;
}
void
util_run_cached_commands (mu_list_t *list)
{
mu_list_foreach (*list, _run_and_free, NULL);
mu_list_destroy (list);
}
char *
util_get_charset (void)
{
char *charset;
if (mailvar_get (&charset, mailvar_name_charset, mailvar_type_string, 0))
return NULL;
if (mu_c_strcasecmp (charset, "auto") == 0)
{
struct mu_lc_all lc_all = { .flags = 0 };
char *tmp = getenv ("LC_ALL");
if (!tmp)
tmp = getenv ("LANG");
if (tmp && mu_parse_lc_all (tmp, &lc_all, MU_LC_CSET) == 0)
{
charset = mu_strdup (lc_all.charset);
mu_lc_all_free (&lc_all);
}
else
charset = NULL;
}
else
charset = mu_strdup (charset);
return charset;
}
void
util_rfc2047_decode (char **value)
{
char *charset, *tmp;
int rc;
if (!*value)
return;
charset = util_get_charset ();
if (!charset)
return;
rc = mu_rfc2047_decode (charset, *value, &tmp);
free (charset);
if (rc)
{
if (mailvar_is_true (mailvar_name_verbose))
mu_error (_("Cannot decode line `%s': %s"), *value, mu_strerror (rc));
}
else
{
free (*value);
*value = tmp;
}
}
const char *
util_url_to_string (mu_url_t url)
{
const char *scheme;
if (mu_url_sget_scheme (url, &scheme) == 0)
{
if (strcmp (scheme, "file") == 0 || strcmp (scheme, "mbox") == 0)
{
const char *path;
if (mu_url_sget_path (url, &path) == 0)
return path;
}
}
return mu_url_to_string (url);
}
mu_stream_t
open_pager (size_t lines)
{
const char *pager;
unsigned pagelines = util_get_crt ();
mu_stream_t str;
if (pagelines && lines > pagelines && (pager = getenv ("PAGER")))
{
int rc = mu_command_stream_create (&str, pager, MU_STREAM_WRITE);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create",
pager, rc);
str = mu_strout;
mu_stream_ref (str);
}
}
else
{
str = mu_strout;
mu_stream_ref (str);
}
return str;
}
int
util_get_folder (mu_folder_t *pfolder, mu_url_t url, int type)
{
mu_folder_t folder;
int rc;
rc = mu_folder_create_from_record (&folder, url, NULL);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_create",
mu_url_to_string (url), rc);
return -1;
}
if (!mu_folder_is_local (folder))
{
if (type == local_folder)
{
/* TRANSLATORS: The subject of this sentence ("folder") is the
name of the variable. Don't translate it. */
mu_error ("%s", _("folder must be set to a local folder"));
mu_folder_destroy (&folder);
return -1;
}
/* Set ticket for a remote folder */
rc = mu_folder_attach_ticket (folder);
if (rc)
{
mu_authority_t auth = NULL;
if (mu_folder_get_authority (folder, &auth) == 0 && auth)
{
mu_ticket_t tct;
mu_noauth_ticket_create (&tct);
rc = mu_authority_set_ticket (auth, tct);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_authority_set_ticket",
NULL, rc);
}
}
}
rc = mu_folder_open (folder, MU_STREAM_READ);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_open",
mu_url_to_string (url), rc);
mu_folder_destroy (&folder);
return -1;
}
*pfolder = folder;
return 0;
}