Blame view

mail/mail.c 14.6 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2
   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
3
   2005, 2006, 2007 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, write to the Free Software
17 18
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301 USA */
19

20
#include "mail.h"
21
#include "mailutils/libargp.h"
22

23
/* Global variables and constants*/
Sergey Poznyakoff authored
24
mu_mailbox_t mbox;            /* Mailbox being operated upon */
25 26 27
size_t total;                 /* Total number of messages in the mailbox */
FILE *ofile;                  /* Output file */
int interactive;              /* Is the session interactive */  
28

29
static mu_list_t command_list;   /* List of commands to be executed after parsing
30
				 command line */
31

32
const char *program_version = "mail (" PACKAGE_STRING ")";
33 34
static char doc[] = N_("GNU mail -- the standard /bin/mail interface");
static char args_doc[] = N_("[address...]");
35 36

static struct argp_option options[] = {
37
  {"exist",   'e', 0,      0, N_("Return true if mail exists"), 0},
38 39
  {"file",    'f', N_("URL"), OPTION_ARG_OPTIONAL,
			      N_("Operate on given mailbox URL (default ~/mbox)"), 0},
40 41 42 43 44 45 46 47
  {"byname",  'F', 0,      0, N_("Save messages according to sender"), 0},
  {"headers", 'H', 0,      0, N_("Write a header summary and exit"), 0},
  {"ignore",  'i', 0,      0, N_("Ignore interrupts"), 0},
  {"norc",    'n', 0,      0, N_("Do not read the system mailrc file"), 0},
  {"nosum",   'N', 0,      0, N_("Do not display initial header summary"), 0},
  {"print",   'p', 0,      0, N_("Print all mail to standard output"), 0},
  {"quit",    'q', 0,      0, N_("Cause interrupts to terminate program"), 0},
  {"read",    'r', 0,      0, N_("Same as -p"), 0},
48
  {"subject", 's', N_("SUBJ"), 0, N_("Send a message with a Subject of SUBJ"), 0},
49
  {"to",      't', 0,      0, N_("Precede message by a list of addresses"), 0},
50
  {"user",    'u', N_("USER"), 0, N_("Operate on USER's mailbox"), 0},
51
  {"append",  'a', N_("HEADER: VALUE"), 0,
52
   N_("Append given header to the message being sent"), 0},
53 54
  {"exec",    'E', N_("COMMAND"), 0,
   N_("Execute COMMAND"), 0 },
55
  { NULL,      0, NULL, 0, NULL, 0 }
56
};
Jeff Bailey authored
57

58

59
struct arguments
60
{
61 62 63
  char **args;
  char *file;
  char *user;
64
  int send_mode;
65 66
};

67 68
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
Jeff Bailey authored
69
{
70
  struct arguments *args = state->input;
71

72
  switch (key)
Jeff Bailey authored
73
    {
74 75 76 77 78
    case 'a':
      args->send_mode = 1;
      send_append_header (arg);
      break;
      
79
    case 'e':
80
      util_cache_command (&command_list, "set mode=exist");
81
      break;
82
      
83 84 85
    case 'f':
      if (arg != NULL)
	args->file = arg;
86 87 88 89 90 91
      /* People often tend to separate -f option from its argument
	 with a whitespace. This heuristics tries to catch the
	 error: */
      else if (state->next < state->argc
	       && state->argv[state->next][0] != '-')
	args->file = state->argv[state->next++];
92 93
      else
	{
Alain Magloire authored
94
	  int len;
95
	  char *home = getenv("HOME");
Alain Magloire authored
96
	  len = strlen (home) + strlen ("/mbox") + 1;
97
	  args->file = xmalloc(len * sizeof (char));
98 99 100
	  strcpy (args->file, home);
	  strcat (args->file, "/mbox");
	}
101
      break;
102
      
103
    case 'p':
104
    case 'r':
105
      util_cache_command (&command_list, "set mode=print");
106
      break;
107
      
108
    case 'q':
109
      util_cache_command (&command_list, "set quit");
110
      break;
111
      
112
    case 't':
113
      util_cache_command (&command_list, "set mode=send");
114
      break;
115
      
116
    case 'H':
117
      util_cache_command (&command_list, "set mode=headers");
118
      break;
119
      
120
    case 'i':
121
      util_cache_command (&command_list, "set ignore");
122
      break;
123
      
124
    case 'n':
125
      util_do_command ("set norc");
126
      break;
127
      
128
    case 'N':
129
      util_cache_command (&command_list, "set noheader");
130
      break;
131
      
132
    case 's':
133
      send_append_header2 (MU_HEADER_SUBJECT, arg, COMPOSE_REPLACE);
134
      util_cache_command (&command_list, "set noasksub");
135
      args->send_mode = 1;
136
      break;
137
      
138 139 140
    case 'u':
      args->user = arg;
      break;
141 142 143 144

    case 'E':
      util_cache_command (&command_list, arg);
      break;
145
      
146
    case 'F':
147
      util_cache_command (&command_list, "set byname");
148
      break;
149

150
    case ARGP_KEY_ARG:
151 152 153 154 155
      args->args = realloc (args->args,
			    sizeof (char *) * (state->arg_num + 2));
      args->args[state->arg_num] = arg;
      args->args[state->arg_num + 1] = NULL;
      args->send_mode = 1;
156
      break;
157 158 159 160 161

    case ARGP_KEY_FINI:
      if (args->send_mode)
	util_cache_command (&command_list, "set mode=send");
      break;
162
      
163 164
    default:
      return ARGP_ERR_UNKNOWN;
Jeff Bailey authored
165
    }
166 167
  return 0;
}
Jeff Bailey authored
168

169 170 171 172 173
static struct argp argp = {
  options,
  parse_opt,
  args_doc,
  doc,
Sergey Poznyakoff authored
174
  NULL,
175 176
  NULL, NULL
};
177

178
static const char *mail_capa[] = {
179
  "common",
180
  "debug",
181 182
  "license",
  "mailbox",
183
  "locking",
184
  NULL 
185
};
Sergey Poznyakoff authored
186
			     
187
static char *
188
mail_cmdline (void *closure, int cont MU_ARG_UNUSED)
189
{
190
  char *prompt = (char*) closure;
191
  char *rc;
192

193 194
  while (1)
    {
195
      if (util_getenv (NULL, "autoinc", Mail_env_boolean, 0) == 0
196
          && !mu_mailbox_is_updated (mbox))
197
	{
198
	  mu_mailbox_messages_count (mbox, &total);
199
	  page_invalidate (0);
200
	  fprintf (ofile, _("New mail has arrived.\n"));
201 202
	}

203
      rc = ml_readline (prompt);
204 205
      
      if (ml_got_interrupt ())
206
	{
207
	  util_error (_("Interrupt"));
208 209
	  continue;
	}
210

211
      if (!rc && util_getenv (NULL, "ignoreeof", Mail_env_boolean, 0) == 0)
212
	{
213
	  util_error (_("Use \"quit\" to quit."));
214 215
	  continue;
	}
216

217 218 219
      break;
    }
  return rc;
220 221
}

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
static char *default_setup[] = {
  "set noallnet",
  "set noappend",
  "set asksub",
  "set crt",
  "set noaskbcc",
  "set askcc",
  "set noautoprint",
  "set nobang",
  "set nocmd",
  "set nodebug",
  "set nodot",
  "set escape=~",
  "set noflipr",
  "set nofolder",
  "set header",
  "set nohold",
  "set noignore",
  "set noignoreeof",
  "set indentprefix=\"\t\"",
  "set nokeep",
  "set nokeepsave",
  "set nometoo",
  "set noonehop",
  "set nooutfolder",
  "set nopage",
  "set prompt=\"? \"",
  "set norecord",
  "set save",
  "set nosendmail",
  "set nosendwait",
  "set noshowto",
  "set nosign",
  "set noSign",
  "set toplines=5",
  "set autoinc",
  "set regex",
  "set replyprefix=\"Re: \"",
  "set charset=auto",
  "set xmailer",
  "unfold subject",
  "sender mail-followup-to reply-to from",
264
  "set nocmd",
265
  "set metamail",
266 267
  "set recursivealiases",
  "set noinplacealiases",
268
  
269 270 271
  /* Start in mail reading mode */
  "set mode=read",
  "set noquit",
272 273 274 275 276
  "set rc",

  "set noflipr",
  "set noshowto",
  "set nobang",
277 278 279

  "set nullbody", /* Null message body is traditionally allowed */
  "set nullbodymsg=\"" N_("Null message body; hope that's ok") "\"",
280 281 282 283 284
  
  /* These settings are not yet used */
  "set nodebug",
  "set noonehop",
  "set nosendwait",
285 286
};

287 288 289
int
main (int argc, char **argv)
{
290
  char *mode = NULL, *prompt = NULL;
291
  struct arguments args;
292
  int i, rc;
293
  
294
  ofile = stdout;
295
  set_cursor (1);
296

297
  /* Native Language Support */
Sergey Poznyakoff authored
298 299
  MU_APP_INIT_NLS ();
  
300
  /* Register the desired formats.  */
301
  mu_register_all_formats ();
302

303
  interactive = isatty (fileno(stdin));
304 305 306 307 308 309 310 311 312
#ifdef HAVE_SIGACTION
  {
    struct sigaction act;
    act.sa_handler = SIG_IGN;
    sigemptyset (&act.sa_mask);
    act.sa_flags = 0;
    sigaction (SIGPIPE, &act, NULL);
  }
#else
313
  signal (SIGPIPE, SIG_IGN);
314
#endif
315

316 317
  /* set up the default environment */
  if (!getenv ("HOME"))
318
    {
319
      char *p = util_get_homedir ();
320 321
      setenv ("HOME", p, 0);
    }
322 323

  /* Set up the default environment */
324
  setenv ("DEAD", util_fullpath("~/dead.letter"), 0);
325 326
  setenv ("EDITOR", "ed", 0);
  setenv ("LISTER", "ls", 0);
327
  setenv ("MAILRC", util_fullpath("~/.mailrc"), 0);
328
  setenv ("MBOX", util_fullpath("~/mbox"), 0);
329 330 331 332 333 334
  setenv ("PAGER", "more", 0);
  setenv ("SHELL", "sh", 0);
  setenv ("VISUAL", "vi", 0);
  setenv ("COLUMNS", "80", 0);
  setenv ("LINES", "24", 0);

335
  /* set defaults for execution */
336 337
  for (i = 0; i < sizeof(default_setup)/sizeof(default_setup[0]); i++)
    util_do_command (default_setup[i]);
338 339
  util_do_command ("set screen=%d", util_getlines ());
  util_do_command ("set columns=%d", util_getcols ());
340
  
341 342 343 344 345
  /* Set the default mailer to sendmail.  */
  {
    char *mailer_name = alloca (strlen ("sendmail:")
				+ strlen (_PATH_SENDMAIL) + 1);
    sprintf (mailer_name, "sendmail:%s", _PATH_SENDMAIL);
346
    util_setenv ("sendmail", mailer_name, Mail_env_string, 1);
347 348
  }

349

350
  args.args = NULL;
351 352
  args.file = NULL;
  args.user = NULL;
353 354
  args.send_mode = 0;
  
355
  /* argument parsing */
356
#ifdef WITH_TLS
357
  mu_gocs_register ("tls", mu_tls_module_init);
358
#endif
359 360 361 362
  mu_argp_init (program_version, NULL);
  if (mu_app_init (&argp, mail_capa, NULL, argc, argv, 0, NULL, &args))
    exit (1);
  
363
  /* read system-wide mail.rc and user's .mailrc */
364
  if (util_getenv (NULL, "rc", Mail_env_boolean, 0) == 0)
365
    util_do_command ("source %s", SITE_MAIL_RC);
366
  util_do_command ("source %s", getenv ("MAILRC"));
367 368

  util_run_cached_commands (&command_list);
369

370 371 372 373 374 375 376
  if (!interactive)
    {
      util_do_command ("set nocrt");
      util_do_command ("set noasksub");
      util_do_command ("set noaskcc");
      util_do_command ("set noaskbcc");
    }
Jakob Kaivo authored
377

378
  /* how should we be running? */
379
  if (util_getenv (&mode, "mode", Mail_env_string, 1))
380 381
    exit (EXIT_FAILURE);

382
  /* Interactive mode */
383

384 385 386
  ml_readline_init ();
  mail_set_my_name(args.user);

387
  /* Mode is just sending */
388
  if (strcmp (mode, "send") == 0)
389
    {
Jakob Kaivo authored
390
      /* FIXME: set cmd to "mail [add1...]" */
391 392
      char *buf = NULL;
      int num = 0;
393
      int rc;
394 395 396
      if (args.args != NULL)
	while (args.args[num] != NULL)
	  num++;
397
      mu_argcv_string (num, args.args, &buf);
398 399
      rc = util_do_command ("mail %s", buf);
      return util_getenv (NULL, "mailx", Mail_env_boolean, 0) ? rc : 0;
400
    }
401
  /* Or acting as a normal reader */
402
  else 
Jeff Bailey authored
403
    {
404 405 406
      /* open the mailbox */
      if (args.file == NULL)
	{
407 408 409 410 411
	  if (args.user)
	    {
	      char *p = xmalloc (strlen (args.user) + 2);
	      p[0] = '%';
	      strcpy (p + 1, args.user);
412
	      rc = mu_mailbox_create_default (&mbox, p);
413 414 415
	      free (p);
	    }
	  else
416
	    rc = mu_mailbox_create_default (&mbox, NULL);
417
	  if (rc != 0)
418
	    {
419
	      util_error (_("Cannot create mailbox for %s: %s"), args.user,
420
			  mu_strerror (rc));
421 422
	      exit (EXIT_FAILURE);
	    }
423
	}
424
      else if ((rc = mu_mailbox_create_default (&mbox, args.file)) != 0)
425
	{
426
	  util_error (_("Cannot create mailbox %s: %s"), args.file,
427
		      mu_strerror (rc));
428 429
	  exit (EXIT_FAILURE);
	}
430

431 432 433 434
      /* Could we enable this at runtime, via the a set environment?  */
      if (0)
	{
	  mu_debug_t debug = NULL;
435
	  mu_mailbox_get_debug (mbox, &debug);
436
	  mu_debug_set_level (debug, MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT));
437 438
	}

Sergey Poznyakoff authored
439
      if ((rc = mu_mailbox_open (mbox, MU_STREAM_RDWR|MU_STREAM_CREAT)) != 0)
440
	{
441
	  mu_url_t url = NULL;
442
	  mu_mailbox_get_url (mbox, &url);
443
	  util_error (_("Cannot open mailbox %s: %s"),
444
		      mu_url_to_string (url), mu_strerror (rc));
445
	  mu_mailbox_destroy (&mbox);
446
	}
447

448 449 450
      if (rc)
	total = 0;
      else
451
	{
452
	  if ((rc = mu_mailbox_scan (mbox, 1, &total)) != 0)
453
	    {
454
	      mu_url_t url = NULL;
455
	      mu_mailbox_get_url (mbox, &url);
456
	      util_error (_("Cannot read mailbox %s: %s"),
457
			  mu_url_to_string (url), mu_strerror (rc));
458 459
	      exit (EXIT_FAILURE);
	    }
460

461
	  if (strcmp (mode, "exist") == 0)
462
	    return (total < 1) ? 1 : 0;
463
	  else if (strcmp (mode, "print") == 0)
464
	    return util_do_command ("print *");
465
	  else if (strcmp (mode, "headers") == 0)
466
	    return util_do_command ("from *");
467 468 469 470 471 472
	  else if (strcmp (mode, "read"))
	    {
	      util_error(_("Unknown mode `%s'"), mode);
	      util_do_command ("quit");
	      return 1;
	    }
473 474
	}
      
475 476 477
      if (total == 0
	  && (strcmp (mode, "read")
	      || util_getenv (NULL, "emptystart", Mail_env_boolean, 0)))
478
        {
479
	  if (args.file)
480
	    fprintf (ofile, _("%s: 0 messages\n"), args.file);
481
	  else
482
	    fprintf (ofile, _("No mail for %s\n"),
483
		     args.user ? args.user : mail_whoami ());
484 485 486
          return 1;
        }

487
      /* initial commands */
488
      if (util_getenv (NULL, "header", Mail_env_boolean, 0) == 0)
489 490
	{
	  util_do_command ("summary");
491
	  util_do_command ("z.");
492
	}
493

494 495
      util_getenv (&prompt, "prompt", Mail_env_string, 0);
      mail_mainloop (mail_cmdline, (void*) prompt, 1);
496 497 498 499 500 501 502 503
      fprintf (ofile, "\n");
      util_do_command ("quit");
      return 0;
    }
  /* We should never reach this point */
  return 1;
}

504

505
void
506
mail_mainloop (char *(*input) (void *, int),
507
	       void *closure, int do_history)
508 509
{
  char *command, *cmd;
510

511 512 513 514
  while ((command = (*input)(closure, 0)) != NULL)
    {
      int len = strlen (command);
      while (command[len-1] == '\\')
515
	{
516 517 518 519
	  char *buf;
	  char *command2 = (*input) (closure, 1);

	  if (!command2)
520
	    {
521 522
	      command[len-1] = 0;
	      break;
523
	    }
524
	  command[len-1] = '\0';
525
	  buf = xmalloc ((len + strlen (command2)) * sizeof (char));
526 527 528 529 530 531 532
	  strcpy (buf, command);
	  strcat (buf, command2);
	  free (command);
	  command = buf;
	  len = strlen (command);
	}
      cmd = util_stripwhite (command);
533
      util_do_command ("%s", cmd);
534
#ifdef WITH_READLINE
535
      if (do_history && !(isspace(cmd[0]) || cmd[0] == '#'))
536
	add_history (cmd);
537
#endif
538 539
      if (command)
	free (command);
Jeff Bailey authored
540
    }
541 542
}

543
int
544
mail_warranty (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
545
{
546
  fputs (_("GNU Mailutils -- a suite of utilities for electronic mail\n"
547 548
           "Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,\n"
           "2007 Free Software Foundation, Inc.\n\n"),
549
           ofile);
Wojciech Polak authored
550
  fputs (
Wojciech Polak authored
551 552
  _("   GNU Mailutils is free software; you can redistribute it and/or modify\n"
    "   it under the terms of the GNU General Public License as published by\n"
553
    "   the Free Software Foundation; either version 3 of the License, or\n"
Wojciech Polak authored
554 555 556 557 558 559 560
    "   (at your option) any later version.\n"
    "\n"
    "   GNU Mailutils is distributed in the hope that it will be useful,\n"
    "   but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
    "   GNU General Public License for more details.\n"
    "\n"
561 562 563 564 565 566
    "   You should have received a copy of the GNU General Public License along\n"
    "   with GNU Mailutils; if not, write to the Free Software Foundation,\n"
    "   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
    "\n"
    "\n"
),
Wojciech Polak authored
567
    ofile);
Wojciech Polak authored
568

569 570
  return 0;
}
571