Blame view

mail/util.c 22.9 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
   2009, 2010 Free Software Foundation, Inc.
4

5
   GNU Mailutils is free software; you can redistribute it and/or modify
6
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 3, or (at your option)
8 9
   any later version.

10
   GNU Mailutils is distributed in the hope that it will be useful,
11 12 13 14 15
   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
16
   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
17 18

#include "mail.h"
19
#include <mailutils/util.h>
20
#include <mailutils/mime.h>
21
#include <pwd.h>
22 23 24
#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif
25
#include <sys/ioctl.h>
26
#include <sys/stat.h>
27 28 29 30 31 32

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#else
# include <sys/fcntl.h>
#endif
33
#include <mailutils/io.h>
34 35

int
36
util_do_command (const char *fmt, ...)
37
{
38 39 40
  struct mu_wordsplit ws;
  int argc;
  char **argv;
41
  int status = 0;
42
  const struct mail_command_entry *entry = NULL;
43
  char *cmd = NULL;
44
  size_t size = 0;
45
  va_list ap;
46
  
47 48
  va_start (ap, fmt);
  status = mu_vasnprintf (&cmd, &size, fmt, ap);
49
  va_end (ap);
50 51
  if (status)
    return status;
52

53 54
  if (cmd)
    {
55
      /*  Ignore comments */
56 57 58 59 60
      if (cmd[0] == '#')
	{
	  free (cmd);
	  return 0;
	}
61

62 63 64
      if (cmd[0] == '\0')
	{
	  free (cmd);
65 66 67 68 69 70 71 72
	  
	  /* 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 = strdup ("next");
	  else
	    return 0;
73
	}
74

75 76 77 78 79 80 81 82
      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 failed: %s"), "mu_wordsplit",
		    mu_wordsplit_strerror (&ws));
	}
      else
83
	{
84 85
	  char *p;

86 87 88
	  argc = ws.ws_wordc;
	  argv = ws.ws_wordv + 1;
	  
89
	  /* Special case: a number alone implies "print" */
90 91 92
	  if (argc == 1
	      && ((strtoul (argv[0], &p, 10) > 0 && *p == 0)
		  || (argv[0][1] == 0 && strchr("^$", argv[0][0]))))
93
	    {
94 95 96 97
	      /* Use the extra slot for "print" command */
	      argc++;
	      argv--;
	      argv[0] = "print";
98
	    }
99

100
	  entry = mail_find_command (argv[0]);
101
	}
102
      free (cmd);
103 104
    }
  else
105
    entry = mail_find_command ("quit");
106

107
  if (!entry)
108
    {
109 110 111 112 113 114
      /* 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;
115
	   p > argv[0] && !mu_isalpha (*p);
116 117 118 119 120 121 122
	   p--)
	;

      p++;
      
      if (strlen (p))
	{
123 124
	  /* Expand contracted form.  That's what we have kept an extra
	     ws slot for. */
125
	  argc++;
126 127
	  argv--;
	  argv[0] = argv[1];
128 129
	  argv[1] = xstrdup (p);
	  *p = 0;
130 131 132
	  /* Register the new entry in WS */
	  ws.ws_wordc++;
	  ws.ws_offs = 0;
133 134 135 136 137 138 139 140 141 142
	}
      
      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);
143
    }
144 145
  else
    {
146
      if (argc)
147
	mu_error (_("Unknown command: %s"), argv[0]);
148
      else
149
	mu_error (_("Invalid command"));
150 151
      status = 1;
    }
152

153
  mu_wordsplit_free (&ws);
154 155 156 157
  return status;
}

int
158 159
util_foreach_msg (int argc, char **argv, int flags,
		  msg_handler_t func, void *data)
160
{
161
  msgset_t *list = NULL, *mp;
162
  int status = 0;
163

164
  if (msgset_parse (argc, argv, flags, &list))
165 166 167
      return 1;

  for (mp = list; mp; mp = mp->next)
168
    {
169
      mu_message_t mesg;
170

171
      if (util_get_message (mbox, mp->msg_part[0], &mesg) == 0)
172 173 174 175 176 177 178
	{
	  if (func (mp, mesg, data) != 0)
	    status = 1;
	  /* Bail out if we receive an interrupt.  */
	  if (ml_got_interrupt () != 0)
	    break;
	}
179
    }
180
  msgset_free (list);
181 182

  return status;
183 184
}

185
size_t
186 187
util_range_msg (size_t low, size_t high, int flags, 
		msg_handler_t func, void *data)
188
{
189
  msgset_t msgspec = { 0 };
190
  size_t count, expect_count;
191 192 193 194
  
  msgspec.next = NULL;
  msgspec.npart = 0;
  msgspec.msg_part = &low;
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
  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++)
210
    {
211
     mu_message_t mesg;
212

213 214 215
     if ((flags & MSG_NODELETED) && util_isdeleted (low))
       {
	 if (!(flags & MSG_SILENT))
216
	   mu_error (_("%lu: Inappropriate message (has been deleted)"),
217 218 219 220 221
		       (unsigned long) low);
	 continue;
       }
     
     if (util_get_message (mbox, low, &mesg) == 0)
222 223
       {
	 count ++;
224 225
	 if (func)
	   func (&msgspec, mesg, data) ;
226 227 228 229
	 /* Bail out if we receive an interrupt.  */
	 if (ml_got_interrupt () != 0)
	   break;
       }
230
    }
231
  return count;
232 233
}

234 235 236
/*
 * returns the function to run for command
 */
237
function_t *
238
util_command_get (const char *cmd)
239
{
240 241
  const struct mail_command_entry *entry = mail_find_command (cmd);
  return entry ? entry->func : NULL;
242 243 244 245 246
}

/*
 * returns the mail_command_entry structure for the command matching cmd
 */
247 248
void *
util_find_entry (void *table, size_t nmemb, size_t size, const char *cmd)
249
{
250
  int i;
251
  int len = strlen (cmd);
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
  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;
276
      mu_stream_t out;
277 278
      char *p;

279
      out = open_pager (util_screen_lines () + 1);
280 281 282 283

      for (p = table, i = 0; i < nmemb; i++, p += size)
	{
	  struct mail_command *cp = (struct mail_command *)p;
284 285
	  if (cp->synopsis == NULL)
	    continue;
286
	  mu_stream_printf (out, "%s\n", cp->synopsis);
287 288
	}
      
289
      mu_stream_unref (out);
290 291 292 293 294 295 296

      return 0;
    }
  else
    {
      int status = 0;
      struct mail_command *cp = util_find_entry (table, nmemb, size, word);
297
      if (cp && cp->synopsis)
298
	mu_printf ("%s\n", cp->synopsis);
299 300 301
      else
	{
	  status = 1;
302
	  mu_printf (_("Unknown command: %s\n"), word);
303 304 305 306 307
	}
      return status;
    }
  return 1;
}
308

309 310 311 312 313 314 315 316 317
int
util_command_list (void *table, size_t nmemb, size_t size)
{
  int i;
  char *p;
  int cols = util_getcols ();
  int pos;
  
  for (p = table, i = 0; i < nmemb; i++, p += size)
318
    {
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
      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;
335
	  mu_printf ("\n%s ", cmd);
336 337
	}
      else
338
        mu_printf ("%s ", cmd);
339
    }
340
  mu_printf ("\n");
341
  return 0;
342 343 344
}

/*
345 346
 * Get the number of columns on the screen
 * First try an ioctl() call not all shells set the COLUMNS environ.
347 348 349 350
 */
int
util_getcols (void)
{
351 352 353
  struct winsize ws;

  ws.ws_col = ws.ws_row = 0;
354 355 356 357 358 359
  if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row == 0)
    {
      const char *columns = getenv ("COLUMNS");
      if (columns)
	ws.ws_col = strtol (columns, NULL, 10);
    }
360 361 362

  /* FIXME: Should we exit()/abort() if col <= 0 ?  */
  return ws.ws_col;
363
}
364

365
/*
366 367
 * Get the number of lines on the screen
 * First try an ioctl() call not all shells set the LINES environ.
368 369 370 371
 */
int
util_getlines (void)
{
372 373 374
  struct winsize ws;

  ws.ws_col = ws.ws_row = 0;
375 376 377 378 379 380
  if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row == 0)
    {
      const char *lines = getenv ("LINES");
      if (lines)
	ws.ws_row = strtol (lines, NULL, 10);
    }
381 382 383 384 385

  /* FIXME: Should we exit()/abort() if row <= 0 ?  */

  /* Reserve at least 2 line for the prompt.  */
  return ws.ws_row - 2;
386
}
387

388
int
389
util_screen_lines ()
390
{
391
  int screen;
392
  size_t n;
393

394
  if (mailvar_get (&screen, "screen", mailvar_type_number, 0) == 0)
395
    return screen;
396
  n = util_getlines();
397
  util_do_command ("set screen=%lu", (unsigned long) n);
398 399 400 401
  return n;
}

int
402
util_screen_columns ()
403
{
404
  int cols;
405 406
  size_t n;

407
  if (mailvar_get (&cols, "columns", mailvar_type_number, 0) == 0)
408
    return cols;
409
  n = util_getcols();
410
  util_do_command ("set columns=%lu", (unsigned long) n);
411
  return n;
412 413
}

414 415 416 417 418
int
util_get_crt ()
{
  int lines;

419
  if (mailvar_get (&lines, "crt", mailvar_type_number, 0) == 0)
420
    return lines;
421
  else if (mailvar_get (NULL, "crt", mailvar_type_boolean, 0) == 0)
422 423 424 425
    return util_getlines ();
  return 0;
}

426 427 428 429
const char *
util_reply_prefix ()
{
  char *prefix = "Re: ";
430
  mailvar_get (&prefix, "replyprefix", mailvar_type_string, 0);
431 432 433 434
  return prefix;
}


435 436
/* ************************* */

437
/*
438 439
 * return 1 if a message is deleted
 */
440

441
int
442
util_isdeleted (size_t n)
443
{
444 445
  mu_message_t msg = NULL;
  mu_attribute_t attr = NULL;
446

447
  mu_mailbox_get_message (mbox, n, &msg);
448
  mu_message_get_attribute (msg, &attr);
449
  return mu_attribute_is_deleted (attr);
450
}
451

452
void
453
util_mark_read (mu_message_t msg)
454
{
455
  mu_attribute_t attr;
456

457
  mu_message_get_attribute (msg, &attr);
458 459
  mu_attribute_set_read (attr);
  mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SHOWN);
460 461
}

462
char *
463
util_get_homedir ()
464
{
465
  char *homedir = mu_get_homedir ();
466 467 468
  if (!homedir)
    {
      /* Shouldn't happen, but one never knows */
469
      mu_error (_("Cannot get homedir"));
470 471
      exit (EXIT_FAILURE);
    }
472
  return homedir;
473
}
474

475
char *
476
util_fullpath (const char *inpath)
477
{
478
  return mu_tilde_expansion (inpath, "/", NULL);
479 480
}

481
char *
482 483 484 485
util_folder_path (const char *name)
{
  char *folder;
  char *tmp;
486 487
  char *p;
  
488
  if (mailvar_get (&folder, "folder", mailvar_type_string, 1))
489 490 491 492 493 494 495
    return NULL;
      
  if (!name)
    return NULL;
  if (name[0] == '+')
    name++;
  
496
  if (folder[0] != '/' && folder[0] != '~')
497 498 499 500 501 502 503 504 505 506 507 508 509
    {
      char *home = mu_get_homedir ();
      tmp  = xmalloc (strlen (home) + 1 +
		      strlen (folder) + 1 +
		      strlen (name) + 1);
      sprintf (tmp, "%s/%s/%s", home, folder, name);
    }
  else
    {
      tmp  = xmalloc (strlen (folder) + 1 +
		      strlen (name) + 1);
      sprintf (tmp, "%s/%s", folder, name);
    }
510 511 512 513
  p = util_fullpath (tmp);
  free (tmp);
  
  return p;
514 515 516
}

char *
517
util_get_sender (int msgno, int strip)
518
{
519 520
  mu_message_t msg = NULL;
  mu_address_t addr = NULL;
521
  char *buf = NULL, *p;
522

523
  mu_mailbox_get_message (mbox, msgno, &msg);
524 525
  addr = get_sender_address (msg);
  if (!addr)
526
    {
527
      mu_envelope_t env = NULL;
528
      const char *buffer;
529
      mu_message_get_envelope (msg, &env);
530
      if (mu_envelope_sget_sender (env, &buffer)
531
	  || mu_address_create (&addr, buffer))
532
	{
533
	  mu_error (_("Cannot determine sender name (msg %d)"), msgno);
534 535 536 537
	  return NULL;
	}
    }

538
  if (mu_address_aget_email (addr, 1, &buf))
539
    {
540
      mu_error (_("Cannot determine sender name (msg %d)"), msgno);
541
      mu_address_destroy (&addr);
542 543
      return NULL;
    }
544

545 546
  if (strip)
    {
547
      p = strchr (buf, '@');
548 549 550
      if (p)
	*p = 0;
    }
551

552
  mu_address_destroy (&addr);
553
  return buf;
554 555 556
}

void
557
util_slist_print (mu_list_t list, int nl)
558
{
559
  mu_iterator_t itr;
560
  char *name;
561

562
  if (!list || mu_list_get_iterator (list, &itr))
563
    return;
564

565
  for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
566
    {
567
      mu_iterator_current (itr, (void **)&name);
568
      mu_printf ("%s%c", name, nl ? '\n' : ' ');
569
    }
570
  mu_iterator_destroy (&itr);
571 572 573
}

int
574
util_slist_lookup (mu_list_t list, const char *str)
575
{
576
  mu_iterator_t itr;
577 578
  char *name;
  int rc = 0;
579

580
  if (!list || mu_list_get_iterator (list, &itr))
581
    return 0;
582

583
  for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
584
    {
585
      mu_iterator_current (itr, (void **)&name);
586
      if (mu_c_strcasecmp (name, str) == 0)
587 588 589 590 591
	{
	  rc = 1;
	  break;
	}
    }
592
  mu_iterator_destroy (&itr);
593 594 595
  return rc;
}

596 597
static int
util_slist_compare (const void *a, const void *b)
598
{
599 600
  return mu_c_strcasecmp (a, b);
}
601

602 603 604 605
void
util_slist_add (mu_list_t *plist, char *value)
{
  mu_list_t list;
606

607
  if (!*plist)
608
    {
609 610 611 612 613
      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;
614
    }
615 616 617
  else
    list = *plist;
  mu_list_append (list, xstrdup (value));
618 619 620
}

void
621
util_slist_remove (mu_list_t *list, char *value)
622
{
623
  mu_list_remove (*list, value);
624 625
}

626
void
627
util_slist_destroy (mu_list_t *list)
628
{
629
  mu_list_destroy (list);
630 631 632
}

char *
633
util_slist_to_string (mu_list_t list, const char *delim)
634
{
635
  mu_iterator_t itr;
636 637
  char *name;
  char *str = NULL;
638

639
  if (!list || mu_list_get_iterator (list, &itr))
640
    return NULL;
641

642 643
  for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
       mu_iterator_next (itr))
644
    {
645
      mu_iterator_current (itr, (void **)&name);
646
      if (str && delim)
647 648
	util_strcat (&str, delim);
      util_strcat (&str, name);
649
    }
650
  mu_iterator_destroy (&itr);
651 652 653 654
  return str;
}

void
655
util_strcat (char **dest, const char *str)
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
{
  if (!*dest)
    *dest = 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);
    }
}
672

673 674 675
char *
util_outfolder_name (char *str)
{
676
  char *outfolder;
677

678 679 680
  if (!str)
    return NULL;
  
681 682 683 684 685 686
  switch (*str)
    {
    case '/':
    case '~':
      str = util_fullpath (str);
      break;
687 688 689 690
      
    case '+':
      str = util_folder_path (str);
      break;
691

692
    default:
693
      if (mailvar_get (&outfolder, "outfolder", mailvar_type_string, 0) == 0)
694 695
	{
	  char *ns = NULL;
696
	  mu_asprintf (&ns, "%s/%s", outfolder, str);
697 698 699 700 701 702 703
	  str = util_fullpath (ns);
	  free (ns);
	}
      break;

    }

704
  return strdup (str);
705 706
}

707
/* Save an outgoing message. The SAVEFILE argument overrides the setting
708 709
   of the "record" variable. */
void
710
util_save_outgoing (mu_message_t msg, char *savefile)
711
{
712 713
  char *record;
  
714
  if (mailvar_get (&record, "record", mailvar_type_string, 0) == 0)
715
    {
716
      int rc;
717
      mu_mailbox_t outbox;
718
      char *filename = util_outfolder_name (savefile ? savefile : record);
719

720
      rc = mu_mailbox_create_default (&outbox, filename);
721
      if (rc)
722
	{
723
	  mu_error (_("Cannot create output mailbox `%s': %s"),
724 725 726
		      filename, strerror (rc));
	  free (filename);
	  return;
727
	}
728

729
      rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT);
730
      if (rc)
731
	mu_error (_("Cannot open output mailbox `%s': %s"),
732
		    filename, strerror (rc));
733 734
      else
	{
735
	  rc = mu_mailbox_append_message (outbox, msg);
736
	  if (rc)
737
	    mu_error (_("Cannot append message to `%s': %s"),
738
			filename, strerror (rc));
739
	}
740

741 742
      mu_mailbox_close (outbox);
      mu_mailbox_destroy (&outbox);
743
      
744 745 746
      free (filename);
    }
}
747

748
static int
749
util_descend_subparts (mu_message_t mesg, msgset_t *msgset, mu_message_t *part)
750
{
751
  unsigned int i;
752 753 754

  for (i = 1; i < msgset->npart; i++)
    {
755
      mu_message_t submsg = NULL;
756
      size_t nparts = 0;
757
      char *type = NULL;
758
      mu_header_t hdr = NULL;
759

760
      mu_message_get_header (mesg, &hdr);
761
      util_get_content_type (hdr, &type, NULL);
762
      if (mu_c_strncasecmp (type, "message/rfc822", strlen (type)) == 0)
763
	{
764
	  if (mu_message_unencapsulate (mesg, &submsg, NULL))
765
	    {
766
	      mu_error (_("Cannot unencapsulate message/part"));
767 768 769 770
	      return 1;
	    }
	  mesg = submsg;
	}
771

772
      mu_message_get_num_parts (mesg, &nparts);
773 774
      if (nparts < msgset->msg_part[i])
	{
775
	  mu_error (_("No such (sub)part in the message: %lu"),
776
		      (unsigned long) msgset->msg_part[i]);
777 778
	  return 1;
	}
779

780
      if (mu_message_get_part (mesg, msgset->msg_part[i], &submsg))
781
	{
782
	  mu_error (_("Cannot get (sub)part from the message: %lu"),
783
		      (unsigned long) msgset->msg_part[i]);
784 785 786 787 788 789 790 791 792
	  return 1;
	}

      mesg = submsg;
    }

  *part = mesg;
  return 0;
}
793

794
void
795
util_msgset_iterate (msgset_t *msgset,
796
		     int (*fun) (mu_message_t, msgset_t *, void *),
797
		     void *closure)
798 799 800
{
  for (; msgset; msgset = msgset->next)
    {
801
      mu_message_t mesg;
802

803
      if (mu_mailbox_get_message (mbox, msgset->msg_part[0], &mesg) != 0)
804 805 806
	return;

      if (util_descend_subparts (mesg, msgset, &mesg) == 0)
807
	(*fun) (mesg, msgset, closure);
808
    }
809 810 811
}

int
812
util_get_content_type (mu_header_t hdr, char **value, char **args)
813 814 815 816 817 818 819 820 821
{
  char *type = NULL;
  util_get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type);
  if (type == NULL || *type == '\0')
    {
      if (type)
	free (type);
      type = strdup ("text/plain"); /* Default.  */
    }
822 823 824 825 826 827 828 829 830 831 832
  else
    {
      char *p;
      p = strchr (type, ';');
      if (p)
	{
	  *p++ = 0;
	  if (args)
	    *args = p;
	}
    }
833 834 835 836 837
  *value = type;
  return 0;
}

int
838
util_get_hdr_value (mu_header_t hdr, const char *name, char **value)
839
{
840
  int status = mu_header_aget_value_unfold (hdr, name, value);
841
  if (status == 0)
842
    mu_rtrim_class (*value, MU_CTYPE_SPACE);
843 844
  return status;
}
845 846 847 848

int
util_merge_addresses (char **addr_str, const char *value)
{
849
  mu_address_t addr, new_addr;
850 851
  int rc;

852
  if ((rc = mu_address_create (&new_addr, value)) != 0)
853 854
    return rc;
      
855
  if ((rc = mu_address_create (&addr, *addr_str)) != 0)
856
    {
857
      mu_address_destroy (&new_addr);
858 859 860
      return rc;
    }

861
  rc = mu_address_union (&addr, new_addr);
862 863 864 865
  if (rc == 0)
    {
      size_t n;

866
      rc = mu_address_to_string (addr, NULL, 0, &n);
867 868 869 870 871 872 873
      if (rc == 0)
	{
	  free (*addr_str);
	  *addr_str = malloc (n + 1);
	  if (!*addr_str)
	    rc = ENOMEM;
	  else
874
	    mu_address_to_string (addr, *addr_str, n + 1, &n);
875 876 877
	}
    }

878 879
  mu_address_destroy (&addr);
  mu_address_destroy (&new_addr);
880 881
  return rc;
}
882

883
int
884 885 886 887 888 889 890 891 892 893 894
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++)
895
    if (mu_c_strcasecmp (*p, name) == 0)
896 897 898 899 900
      return 1;
  return 0;
}

int
901
util_header_expand (mu_header_t *phdr)
902 903
{
  size_t i, nfields = 0;
904
  mu_header_t hdr;
905
  int errcnt = 0, rc;
906
  
907
  rc = mu_header_create (&hdr, "", 0);
908 909
  if (rc)
    {
910
      mu_error (_("Cannot create temporary header: %s"), mu_strerror (rc));
911 912 913
      return 1;
    }
      
914
  mu_header_get_field_count (*phdr, &nfields);
915 916
  for (i = 1; i <= nfields; i++)
    {
917
      const char *name, *value;
918
      
919
      if (mu_header_sget_field_name (*phdr, i, &name))
920 921
	continue;

922 923
      if (mu_header_sget_field_value (*phdr, i, &value))
	continue;
924 925 926
      
      if (is_address_field (name))
	{
927
	  const char *s;
928
	  mu_address_t addr = NULL;
929 930 931 932 933 934 935 936 937 938 939
	  struct mu_wordsplit ws;
	  size_t j;
	  
	  if (mu_header_sget_value (hdr, name, &s) == 0)
	    mu_address_create (&addr, s);

	  ws.ws_delim = ",";
	  if (mu_wordsplit (value, &ws,
			    MU_WRDSF_DELIM|MU_WRDSF_SQUEEZE_DELIMS|
			    MU_WRDSF_WS|
			    MU_WRDSF_NOVAR|MU_WRDSF_NOCMD))
940
	    {
941 942 943 944
	      errcnt++;
	      mu_error (_("cannot split line `%s': %s"), value,
			mu_wordsplit_strerror (&ws));
	      break;
945
	    }
946 947

	  for (j = 0; j < ws.ws_wordc; j++)
948
	    {
949
	      const char *exp;
950
	      mu_address_t new_addr;
951
	      char *p = ws.ws_wordv[j];
952
	      
953
	      /* If inplacealiases was set, the value was already expanded */
954 955
	      if (mailvar_get (NULL, "inplacealiases",
			       mailvar_type_boolean, 0))
956
		exp = alias_expand (p);
957
	      rc = mu_address_create (&new_addr, exp ? exp : p);
958 959 960 961
	      if (rc)
		{
		  errcnt++;
		  if (exp)
962
		    mu_error (_("Cannot parse address `%s' (while expanding `%s'): %s"),
963
				exp, p, mu_strerror (rc));
964
		  else
965
		    mu_error (_("Cannot parse address `%s': %s"),
966
				p, mu_strerror (rc));
967 968
		}
	      
969 970
	      mu_address_union (&addr, new_addr);
	      mu_address_destroy (&new_addr);
971 972 973 974
	    }
	  
	  if (addr)
	    {
975
	      char *newvalue;
976 977
	      size_t n = 0;
	      
978
	      mu_address_to_string (addr, NULL, 0, &n);
979 980
	      newvalue = xmalloc (n + 1);
	      mu_address_to_string (addr, newvalue, n + 1, NULL);
981
	      mu_address_destroy (&addr);
982 983
	      mu_header_set_value (hdr, name, newvalue, 1);
	      free (newvalue);
984 985 986
	    }
	}
      else
987
	mu_header_set_value (hdr, name, value, 0);
988 989 990 991
    }

  if (errcnt == 0)
    {
992
      mu_header_destroy (phdr);
993 994 995
      *phdr = hdr;
    }
  else
996
    mu_header_destroy (&hdr);
997 998 999

  return errcnt;
}
1000 1001

int
1002
util_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *msg)
1003 1004 1005 1006 1007 1008
{
  int status;

  if (msgno > total)
    {
      util_error_range (msgno);
1009
      return MU_ERR_NOENT;
1010 1011
    }
  
1012
  status = mu_mailbox_get_message (mbox, msgno, msg);
1013 1014
  if (status)
    {
1015
      mu_error (_("Cannot get message %lu: %s"),
1016
		  (unsigned long) msgno, mu_strerror (status));
1017 1018 1019 1020 1021 1022 1023 1024 1025
      return status;
    }

  return 0;
}

int
util_error_range (size_t msgno)
{
1026
  mu_error (_("%lu: invalid message number"), (unsigned long) msgno);
1027 1028
  return 1;
}
1029 1030 1031 1032

void
util_noapp ()
{
1033
  mu_error (_("No applicable messages"));
1034
}
1035 1036

void
1037
util_cache_command (mu_list_t *list, const char *fmt, ...)
1038
{
1039 1040
  char *cmd = NULL;
  size_t size = 0;
1041 1042 1043
  va_list ap;

  va_start (ap, fmt);
1044
  mu_vasnprintf (&cmd, &size, fmt, ap);
1045 1046 1047
  va_end (ap);

  if (!*list)
1048
    mu_list_create (list);
1049

1050
  mu_list_append (*list, cmd);
1051 1052 1053 1054 1055
}

static int
_run_and_free (void *item, void *data)
{
Sergey Poznyakoff authored
1056
  util_do_command ("%s", (char *) item);
1057 1058 1059 1060 1061
  free (item);
  return 0;
}

void
1062
util_run_cached_commands (mu_list_t *list)
1063
{
1064 1065
  mu_list_do (*list, _run_and_free, NULL);
  mu_list_destroy (list);
1066
}
1067 1068 1069 1070

void
util_rfc2047_decode (char **value)
{
1071
  char *charset = NULL;
1072 1073 1074
  char *tmp;
  int rc;

1075
  if (!*value || mailvar_get (&charset, "charset", mailvar_type_string, 0))
1076
    return;
Wojciech Polak authored
1077

1078
  if (mu_c_strcasecmp (charset, "auto") == 0)
1079
    {
1080
      static char *saved_charset;
1081

1082
      if (!saved_charset)
1083
	{
1084
	  /* Try to deduce the charset from LC_ALL or LANG variables */
Wojciech Polak authored
1085

1086 1087 1088
	  tmp = getenv ("LC_ALL");
	  if (!tmp)
	    tmp = getenv ("LANG");
1089

1090 1091 1092 1093 1094 1095 1096
	  if (tmp)
	    {
	      struct mu_lc_all lc_all;
	      
	      if (mu_parse_lc_all (tmp, &lc_all, MU_LC_CSET) == 0)
		saved_charset = lc_all.charset;
	    }
1097
	}
1098
      charset = saved_charset; /* NOTE: a minor memory leak */
1099 1100 1101 1102 1103
    }

  if (!charset)
    return;
  
1104
  rc = mu_rfc2047_decode (charset, *value, &tmp);
1105 1106
  if (rc)
    {
1107
      if (mailvar_get (NULL, "verbose", mailvar_type_boolean, 0) == 0)
1108
	mu_error (_("Cannot decode line `%s': %s"), *value, mu_strerror (rc));
1109 1110 1111 1112 1113 1114 1115
    }
  else
    {
      free (*value);
      *value = tmp;
    }
}
1116

1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
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);
}
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146

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);
1147
	  str = mu_strout;
1148 1149 1150 1151 1152
	  mu_stream_ref (str);
	}
    }
  else
    {
1153
      str = mu_strout;
1154 1155 1156 1157
      mu_stream_ref (str);
    }
  return str;
}