Blame view

movemail/movemail.c 21 KB
uid65697 authored
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
   Foundation, Inc.
uid65697 authored
4 5 6

   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
7
   the Free Software Foundation; either version 3, or (at your option)
uid65697 authored
8 9 10 11 12 13 14 15
   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
16
   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>.  */
uid65697 authored
17 18 19 20 21 22 23

#if defined(HAVE_CONFIG_H)
# include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
24
#include <sys/stat.h>
25
#include <pwd.h>
26 27
#include <grp.h>
#include <unistd.h>
uid65697 authored
28 29
#include <mailutils/mailutils.h>
#include <mailutils/tls.h>
30
#include "mailutils/libargp.h"
Sergey Poznyakoff authored
31
#include <muaux.h>
uid65697 authored
32

33
static char doc[] = N_("GNU movemail -- move messages across mailboxes.");
34
static char args_doc[] = N_("inbox-url destfile [POP-password]");
uid65697 authored
35

Sergey Poznyakoff authored
36 37 38 39 40
enum {
  EMACS_OPTION=256,
  IGNORE_ERRORS_OPTION,
  PROGRAM_ID_OPTION
};
41

uid65697 authored
42
static struct argp_option options[] = {
Sergey Poznyakoff authored
43
  { "preserve", 'p', NULL, 0, N_("preserve the source mailbox") },
44
  { "keep-messages", 0, NULL, OPTION_ALIAS, NULL },
Sergey Poznyakoff authored
45
  { "reverse",  'r', NULL, 0, N_("reverse the sorting order") },
Sergey Poznyakoff authored
46
  { "emacs", EMACS_OPTION, NULL, 0,
Sergey Poznyakoff authored
47
    N_("output information used by Emacs rmail interface") },
48
  { "uidl", 'u', NULL, 0,
Sergey Poznyakoff authored
49
    N_("use UIDLs to avoid downloading the same message twice") },
50
  { "verbose", 'v', NULL, 0,
Sergey Poznyakoff authored
51
    N_("increase verbosity level") },
52
  { "owner", 'P', N_("MODELIST"), 0,
Sergey Poznyakoff authored
53
    N_("control mailbox ownership") },
Sergey Poznyakoff authored
54 55 56 57
  { "ignore-errors", IGNORE_ERRORS_OPTION, NULL, 0,
    N_("try to continue after errors") },
  { "program-id", PROGRAM_ID_OPTION, N_("FMT"), 0,
    N_("set program identifier for diagnostics (default: program name)") },
uid65697 authored
58 59 60 61 62
  { NULL,      0, NULL, 0, NULL, 0 }
};

static int reverse_order;
static int preserve_mail; 
63
static int emacs_mode;
64 65
static int uidl_option;
static int verbose_option;
Sergey Poznyakoff authored
66 67
static int ignore_errors;
static char *program_id_option;
uid65697 authored
68

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
enum set_ownership_mode
  {
    copy_owner_id,
    copy_owner_name,
    set_owner_id,
    set_owner_name
  };
#define SET_OWNERSHIP_MAX 4

struct user_id
{
  uid_t uid;
  gid_t gid;
};

struct set_ownership_method
{
  enum set_ownership_mode mode;
  union
  {
    char *name;
    struct user_id id;
  } owner;
};

static struct set_ownership_method so_methods[SET_OWNERSHIP_MAX];
static int so_method_num;

struct set_ownership_method *
get_next_so_method ()
{
  if (so_method_num == MU_ARRAY_SIZE (so_methods))
    {
      mu_error (_("ownership method table overflow"));
      exit (1);
    }
  return so_methods + so_method_num++;
}

mu_kwd_t method_kwd[] = {
  { "copy-id", copy_owner_id }, 
  { "copy-name", copy_owner_name },
  { "set-name", set_owner_name },
  { "user", set_owner_name },
  { "set-id", set_owner_id },
  { NULL }
};

uid65697 authored
117 118 119
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
120
  static mu_list_t lst;
121

uid65697 authored
122 123 124
  switch (key)
    {
    case 'r':
125
      mu_argp_node_list_new (lst, "reverse", "yes");
uid65697 authored
126 127 128
      break;

    case 'p':
129
      mu_argp_node_list_new (lst, "preserve", "yes");
uid65697 authored
130 131
      break;

132
    case 'P':
133
      mu_argp_node_list_new (lst, "mailbox-ownership", arg);
134
      break;
135 136

    case 'u':
137
      mu_argp_node_list_new (lst, "uidl", "yes");
138 139 140 141 142
      break;

    case 'v':
      verbose_option++;
      break;
143
      
Sergey Poznyakoff authored
144
    case EMACS_OPTION:
145
      mu_argp_node_list_new (lst, "emacs", "yes");
146
      break;
147

Sergey Poznyakoff authored
148 149 150 151 152 153 154 155
    case IGNORE_ERRORS_OPTION:
      mu_argp_node_list_new (lst, "ignore-errors", "yes");
      break;

    case PROGRAM_ID_OPTION:
      mu_argp_node_list_new (lst, "program-id", arg);
      break;
      
156 157 158 159 160
    case ARGP_KEY_INIT:
      mu_argp_node_list_init (&lst);
      break;
      
    case ARGP_KEY_FINI:
161
      mu_argp_node_list_finish (lst, NULL, NULL);
162 163
      break;
      
uid65697 authored
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}

static struct argp argp = {
  options,
  parse_opt,
  args_doc,
  doc,
  NULL,
  NULL, NULL
};

179

180
static int
181
_cb_mailbox_ownership (const char *str)
182 183 184 185 186 187 188 189 190 191 192 193
{
  if (strcmp (str, "clear") == 0)
    so_method_num = 0;
  else
    {
      int code;
      char *p;
      size_t len = strcspn (str, "=");
      struct set_ownership_method *meth;
	  
      if (mu_kwd_xlat_name_len (method_kwd, str, len, &code))
	{
194
	  mu_error (_("invalid ownership method: %s"), str);
195 196 197 198 199 200 201 202 203 204 205 206 207 208
	  return 1;
	}
      
      meth = get_next_so_method ();
      meth->mode = code;
      switch (meth->mode)
	{
	case copy_owner_id:
	case copy_owner_name:
	  break;
	  
	case set_owner_id:
	  if (!str[len])
	    {
209
	      mu_error (_("ownership method %s requires value"), str);
210 211 212 213 214 215 216 217 218 219 220 221
	      return 1;
	    }
	  str += len + 1;
	  meth->owner.id.uid = strtoul (str, &p, 0);
	  if (*p)
	    {
	      if (*p == ':')
		{
		  str = p + 1;
		  meth->owner.id.gid = strtoul (str, &p, 0);
		  if (*p)
		    {
222
		      mu_error (_("expected gid number, but found %s"), str);
223 224 225 226 227
		      return 1;
		    }
		}
	      else
		{
228
		  mu_error (_("expected uid number, but found %s"), str);
229 230 231 232 233 234 235 236 237 238
		  return 1;
		}
	    }
	  else
	    meth->owner.id.gid = (gid_t) -1;
	  break;
	  
	case set_owner_name:
	  if (!str[len])
	    {
239
	      mu_error (_("ownership method %s requires value"), str);
240 241 242 243 244 245 246 247 248
	      return 1;
	    }
	  meth->owner.name = mu_strdup (str + len + 1);
	}
    }
  return 0;
}

static int
249
cb_mailbox_ownership (void *data, mu_config_value_t *val)
250 251 252 253 254 255 256
{
  int i;
  
  if (val->type == MU_CFG_STRING)
    {
      const char *str = val->v.string;
      if (!strchr (str, ','))
257
	return _cb_mailbox_ownership (str);
258 259
      else
	{
260
	  struct mu_wordsplit ws;
261

262 263
	  ws.ws_delim = ",";
	  if (mu_wordsplit (str, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM))
264
	    {
265 266
	      mu_error (_("cannot parse %s: %s"),
			str, mu_wordsplit_strerror (&ws));
267 268 269
	      return 1;
	    }

270
	  for (i = 0; i < ws.ws_wordc; i++)
271
	    if (_cb_mailbox_ownership (ws.ws_wordv[i]))
272
	      return 1;
273
	  mu_wordsplit_free (&ws);
274 275 276 277
	  return 0;
	}
    }
		
278
  if (mu_cfg_assert_value_type (val, MU_CFG_LIST))
279 280 281 282
    return 1;

  for (i = 0; i < val->v.arg.c; i++)
    {
283
      if (mu_cfg_assert_value_type (&val->v.arg.v[i], MU_CFG_STRING))
284
	return 1;
285
      if (_cb_mailbox_ownership (val->v.arg.v[i].v.string))
286 287 288 289 290
	return 1;
    }
  return 0;
}

291
struct mu_cfg_param movemail_cfg_param[] = {
292
  { "preserve", mu_cfg_bool, &preserve_mail, 0, NULL,
293
    N_("Do not remove messages from the source mailbox.") },
294
  { "reverse",  mu_cfg_bool, &reverse_order, 0, NULL,
295
    N_("Reverse message sorting order.") },
296
  { "emacs", mu_cfg_bool, &emacs_mode, 0, NULL,
297
    N_("Output information used by Emacs rmail interface.") },
298 299 300
  { "uidl", mu_cfg_bool, &uidl_option, 0, NULL,
    N_("Use UIDLs to avoid downloading the same message twice.") },
  { "verbose", mu_cfg_int, &verbose_option, 0, NULL,
301
    N_("Set verbosity level.") },
Sergey Poznyakoff authored
302 303 304 305
  { "ignore-errors", mu_cfg_bool, &ignore_errors, 0, NULL,
    N_("Continue after an error.") },
  { "program-id", mu_cfg_string, &program_id_option, 0, NULL,
    N_("Set program identifier string (default: program name)") },
306 307 308 309 310 311 312 313 314
  { "mailbox-ownership", mu_cfg_callback, NULL, 0,
    cb_mailbox_ownership,
    N_("Define a list of methods for setting mailbox ownership. Valid "
       "methods are:\n"
       " copy-id          get owner UID and GID from the source mailbox\n"
       " copy-name        get owner name from the source mailbox URL\n"
       " set-id=UID[:GID] set supplied UID and GID\n"
       " set-name=USER    make destination mailbox owned by USER"),
    N_("methods: list") },
315 316 317 318
  { NULL }
};


319
static const char *movemail_capa[] = {
320
  "common",
321 322
  "debug",
  "locking",
323
  "mailbox",
324
  "auth",
325
  NULL 
uid65697 authored
326 327 328
};

void
Sergey Poznyakoff authored
329
die (mu_mailbox_t mbox, const char *msg, int status)
uid65697 authored
330
{
331
  mu_url_t url = NULL;
uid65697 authored
332
  
333
  mu_mailbox_get_url (mbox, &url);
334
  if (emacs_mode)
335
    mu_error (_("%s:mailbox `%s': %s: %s"),
336
	      mu_errname (status),
337
	      mu_url_to_string (url),
338 339 340
	      msg,
	      mu_strerror (status));
  else
341
    mu_error (_("mailbox `%s': %s: %s"),
342
	      mu_url_to_string (url), msg, mu_strerror (status));
uid65697 authored
343 344 345 346
  exit (1);
}

void
347
lock_mailbox (mu_mailbox_t mbox)
uid65697 authored
348
{
349
  mu_locker_t lock;
uid65697 authored
350 351
  int status;
  
352
  status = mu_mailbox_get_locker (mbox, &lock);
uid65697 authored
353
  if (status)
354
    die (mbox, _("cannot retrieve locker"), status);
uid65697 authored
355 356 357 358 359
      
  if (!lock)
    /* Remote mailboxes have no lockers */
    return;

360
  status = mu_locker_lock (lock);
uid65697 authored
361 362

  if (status)
363
    die (mbox, _("cannot lock"), status);
uid65697 authored
364 365
}

366

uid65697 authored
367
void
368
attach_passwd_ticket (mu_mailbox_t mbx, char *passwd)
369
{
370 371
  mu_folder_t folder = NULL;
  mu_authority_t auth = NULL;
372
  mu_secret_t secret;
373
  mu_ticket_t t;
374
  int rc;
375 376 377 378 379 380 381

  rc = mu_secret_create (&secret, passwd, strlen (passwd));
  if (rc)
    {
      mu_error ("mu_secret_create: %s", mu_strerror (rc));
      exit (1);
    }
382
  
383 384
  mu_ticket_create (&t, NULL);
  mu_ticket_set_secret (t, secret);
385

386 387
  if ((rc = mu_mailbox_get_folder (mbx, &folder)))
    die (mbx, _("mu_mailbox_get_folder failed"), rc);
388

389 390
  if ((rc = mu_folder_get_authority (folder, &auth)))
    die (mbx, _("mu_folder_get_authority failed"), rc);
391

392 393
  if (auth && (rc = mu_authority_set_ticket (auth, t)))
    die (mbx, _("mu_authority_set_ticket failed"), rc);
394 395 396 397 398 399
}


/* Create and open a mailbox associated with the given URL,
   flags and (optionally) password */
void
400
open_mailbox (mu_mailbox_t *mbx, char *name, int flags, char *passwd)
uid65697 authored
401
{
402
  int status = mu_mailbox_create_default (mbx, name);
uid65697 authored
403 404 405

  if (status)
    {
406
      if (name)
407
	mu_error (_("could not create mailbox `%s': %s"),
408 409 410
		  name,
		  mu_strerror (status));
      else
411
	mu_error (_("could not create default mailbox: %s"),
412
		  mu_strerror (status));
uid65697 authored
413 414 415
      exit (1);
    }

416 417
  if (passwd)
    attach_passwd_ticket (*mbx, passwd);
418
  status = mu_mailbox_open (*mbx, flags);
uid65697 authored
419
  if (status)
420
    die (*mbx, _("cannot open"), status);
uid65697 authored
421 422 423 424
  lock_mailbox (*mbx);
}

int
425
move_message (mu_mailbox_t src, mu_mailbox_t dst, size_t msgno)
uid65697 authored
426 427
{
  int rc;
428
  mu_message_t msg;
uid65697 authored
429

430
  if ((rc = mu_mailbox_get_message (src, msgno, &msg)) != 0)
uid65697 authored
431
    {
432
      mu_error (_("cannot read message %lu: %s"),
433
		(unsigned long) msgno, mu_strerror (rc));
uid65697 authored
434 435
      return rc;
    }
436
  if ((rc = mu_mailbox_append_message (dst, msg)) != 0)
uid65697 authored
437
    {
Sergey Poznyakoff authored
438
      mu_error (_("cannot append message %lu: %s"),
439
		(unsigned long) msgno, mu_strerror (rc));
uid65697 authored
440 441 442 443
      return rc;
    }
  if (!preserve_mail)
    {
444 445
      mu_attribute_t attr;
      mu_message_get_attribute (msg, &attr);
446
      mu_attribute_set_deleted (attr);
uid65697 authored
447 448 449 450 451 452 453 454 455 456 457 458
    }
  return rc;
}

/* Open source mailbox using compatibility syntax. Source_name is
   of the form:

     po:USERNAME[:POP-SERVER]

   if POP-SERVER part is omitted, the MAILHOST environment variable
   will be consulted. */
void
459
compatibility_mode (mu_mailbox_t *mbx, char *source_name, char *password,
uid65697 authored
460 461 462 463 464 465 466 467 468
		    int flags)
{
  char *tmp;
  char *user_name = strtok (source_name+3, ":");
  char *host = strtok (NULL, ":");
  if (!host)
    host = getenv ("MAILHOST");
  if (!host)
    {
469
      mu_error (_("hostname of the POP3 server is unknown"));
uid65697 authored
470 471
      exit (1);
    }
472
  mu_asprintf (&tmp, "pop://%s@%s", user_name, host);
473
  open_mailbox (mbx, tmp, flags, password);
uid65697 authored
474 475 476
  free (tmp);
}

477 478 479 480 481 482 483 484
static mu_mailbox_t source, dest;

static void
close_mailboxes (void)
{
  mu_mailbox_close (dest);
  mu_mailbox_close (source);
}  
485 486 487

static int
get_mbox_owner_id (mu_mailbox_t mbox, mu_url_t url, struct user_id *id)
488 489
{
  const char *s;
490
  int rc = mu_url_sget_scheme  (url, &s);
491
  if (rc)
492
    die (mbox, _("cannot get scheme"), rc);
493 494 495 496
  if ((strcmp (s, "/") == 0
       || strcmp (s, "mbox") == 0
       || strcmp (s, "mh") == 0
       || strcmp (s, "maildir") == 0))
497 498 499 500 501
    {
      struct stat st;
      
      rc = mu_url_sget_path  (url, &s);
      if (rc)
502
	die (mbox, _("cannot get path"), rc);
503 504
      if (stat (s, &st))
	{
Sergey Poznyakoff authored
505
	  mu_diag_funcall (MU_DIAG_ERROR, "stat", s, errno);
506 507
	  exit (1);
	}
508 509 510
      id->uid = st.st_uid;
      id->gid = st.st_gid;
      return 0;
511
    }
512 513 514 515 516 517 518 519 520 521 522 523
  else if (verbose_option)
    mu_diag_output (MU_DIAG_WARNING,
		    _("ignoring copy-name: not a local mailbox"));
  return 1;
}

static int
get_user_id (const char *name, struct user_id *id)
{
  struct mu_auth_data *auth = mu_get_auth_by_name (name);
  
  if (!auth)
524
    {
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
      if (verbose_option)
	mu_diag_output (MU_DIAG_WARNING, _("no such user: %s"), name);
      return 1;
    }

  id->uid = auth->uid;
  id->gid = auth->gid;
  mu_auth_data_free (auth);
  return 0;
}  

static int
get_mbox_owner_name (mu_mailbox_t mbox, mu_url_t url, struct user_id *id)
{
  const char *s;
  int rc = mu_url_sget_user (url, &s);
  if (rc)
    /* FIXME */
543
    die (mbox, _("cannot get mailbox owner name"), rc);
544 545 546 547 548 549 550 551 552 553 554 555 556

  return get_user_id (s, id);
}

static int
guess_mbox_owner (mu_mailbox_t mbox, struct user_id *id)
{
  mu_url_t url = NULL;
  int rc;
  struct set_ownership_method *meth;
  
  rc = mu_mailbox_get_url (mbox, &url);
  if (rc)
Sergey Poznyakoff authored
557 558 559 560
    {
      mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_get_url", NULL, rc);
      exit (1);
    }
561 562 563 564 565

  rc = 1;
  for (meth = so_methods; rc == 1 && meth < so_methods + so_method_num; meth++)
    {
      switch (meth->mode)
566
	{
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
	case copy_owner_id:
	  rc = get_mbox_owner_id (mbox, url, id);
	  break;
	  
	case copy_owner_name:
	  rc = get_mbox_owner_name (mbox, url, id);
	  break;
	  
	case set_owner_id:
	  id->uid = meth->owner.id.uid;
	  rc = 0;
	  if (meth->owner.id.gid == (gid_t)-1)
	    {
	      struct passwd *pw = getpwuid (id->uid);
	      if (pw)
		id->gid = pw->pw_gid;
	      else
		{
		  if (verbose_option)
		    mu_diag_output (MU_DIAG_WARNING,
				    _("no user with uid %lu found"),
				    (unsigned long) id->uid);
		  rc = 1;
		}
	    }
Sergey Poznyakoff authored
592 593
	  else
	    id->gid = meth->owner.id.gid;
594 595 596 597 598
	  break;
	  
	case set_owner_name:
	  rc = get_user_id (meth->owner.name, id);
	  break;
599 600
	}
    }
601 602 603
  
  return rc;
}
604

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
static void
switch_owner (mu_mailbox_t mbox)
{
  struct user_id user_id;

  if (so_method_num == 0)
    return;

  if (getuid ())
    {
      if (verbose_option)
	mu_diag_output (MU_DIAG_WARNING,
			_("ignoring mailbox-ownership statement"));
      return;
    }
  
  if (guess_mbox_owner (mbox, &user_id) == 0)
    {
      if (mu_switch_to_privs (user_id.uid, user_id.gid, NULL))
	exit (1);
    }
  else
    {
      mu_error (_("no suitable method for setting mailbox ownership"));
      exit (1);
    }
631 632
}

633 634 635 636 637 638 639 640
static int
_compare_uidls (const void *item, const void *value)
{
  const struct mu_uidl *a = item;
  const struct mu_uidl *b = value;

  return strcmp (a->uidl, b->uidl);
}
641 642 643 644 645 646 647 648

struct movemail_getvar_closure
{
  const char *source_name;
  const char *dest_name;
  mu_url_t source_url;
  mu_url_t dest_url;
};
649

650 651
#define SEQ(s, n, l) \
  (((l) == (sizeof(s) - 1)) && memcmp (s, n, l) == 0)
652

653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
static const char *
get_url_part (mu_url_t url, const char *name, size_t nlen)
{
  int rc = MU_ERR_NOENT;
  const char *s;
  
  if (!url)
    return NULL;
  if (SEQ ("user", name, nlen))
    rc = mu_url_sget_user (url, &s);
  else if (SEQ ("host", name, nlen))
    rc = mu_url_sget_host (url, &s);
  else if (SEQ ("port", name, nlen))
    rc = mu_url_sget_portstr (url, &s);
  else if (SEQ ("path", name, nlen))
    rc = mu_url_sget_path (url, &s);

  if (rc)
    return NULL;
  return s;
}
    
static const char *
movemail_getvar (const char *name, size_t nlen, void *data)
{
  struct movemail_getvar_closure *pc = data;
  
  if (SEQ ("progname", name, nlen))
    return mu_program_name;
  if (SEQ ("source", name, nlen))
    return pc->source_name;
  if (SEQ ("dest", name, nlen))
    return pc->dest_name;

  if (nlen > 7 && memcmp ("source_", name, 7) == 0)
    return get_url_part (pc->source_url, name + 7, nlen - 7);
  if (nlen > 5 && memcmp ("dest_", name, 5) == 0)
    return get_url_part (pc->dest_url, name + 5, nlen - 5);
  
  return NULL;
}
Sergey Poznyakoff authored
694 695 696

static void
set_program_id (const char *source_name, const char *dest_name)
697
{
Sergey Poznyakoff authored
698
  int rc;
699 700 701 702 703 704
  struct mu_wordsplit ws;
  struct movemail_getvar_closure clos;

  clos.source_name = source_name;
  clos.dest_name = dest_name;
  rc = mu_mailbox_get_url (source, &clos.source_url);
Sergey Poznyakoff authored
705 706 707 708
  if (rc)
    mu_diag_output (MU_DIAG_INFO,
		    _("cannot obtain source mailbox URL: %s"),
		    mu_strerror (rc));
709
  rc = mu_mailbox_get_url (dest, &clos.dest_url);
Sergey Poznyakoff authored
710 711 712 713
  if (rc)
    mu_diag_output (MU_DIAG_INFO,
		    _("cannot obtain destination mailbox URL: %s"),
		    mu_strerror (rc));
714 715 716 717 718 719
  
  ws.ws_getvar = movemail_getvar;
  ws.ws_closure = &clos;
  if (mu_wordsplit (program_id_option, &ws,
		    MU_WRDSF_NOSPLIT | MU_WRDSF_NOCMD |
		    MU_WRDSF_GETVAR | MU_WRDSF_CLOSURE))
Sergey Poznyakoff authored
720
    {
721 722 723
      mu_error (_("cannot expand line `%s': %s"), program_id_option,
		mu_wordsplit_strerror (&ws));
      return;
Sergey Poznyakoff authored
724
    }
725
  
Sergey Poznyakoff authored
726 727 728
  /* FIXME: Don't use mu_set_program_name here, because it
     plays wise with its argument. We need a mu_set_diag_prefix
     function. */
729 730 731 732
  mu_program_name = ws.ws_wordv[0];
  ws.ws_wordv[0] = NULL;
  ws.ws_wordc = 0;
  mu_wordsplit_free (&ws);
Sergey Poznyakoff authored
733
}
734

uid65697 authored
735 736 737 738 739 740
int
main (int argc, char **argv)
{
  int index;
  size_t i, total;
  int rc = 0;
Sergey Poznyakoff authored
741
  int errs = 0;
uid65697 authored
742 743
  char *source_name, *dest_name;
  int flags;
744 745
  mu_list_t src_uidl_list = NULL;
  size_t msg_count = 0;
uid65697 authored
746 747
  
  /* Native Language Support */
Sergey Poznyakoff authored
748
  MU_APP_INIT_NLS ();
749 750
  MU_AUTH_REGISTER_ALL_MODULES ();
  
751 752
  /* Register the desired "mailbox" formats.  */
  mu_register_all_formats ();
753

uid65697 authored
754
  /* argument parsing */
755
  
uid65697 authored
756
#ifdef WITH_TLS
757
  mu_gocs_register ("tls", mu_tls_module_init);
uid65697 authored
758
#endif
759
  mu_argp_init (NULL, NULL);
760
  if (mu_app_init (&argp, movemail_capa, movemail_cfg_param, 
761
		   argc, argv, 0, &index, NULL))
762
    exit (1);
uid65697 authored
763 764 765 766 767 768

  argc -= index;
  argv += index;

  if (argc < 2 || argc > 3)
    {
769
      mu_error (_("wrong number of arguments"));
uid65697 authored
770 771 772
      return 1;
    }

773
  if (emacs_mode)
774
    {      
775
      /* Undo the effect of configuration options that may affect
776
	 interaction with Emacs. */
777
      mu_registrar_set_default_record (mu_mbox_record);
778
      mu_stdstream_strerr_setup (MU_STRERR_STDERR);
779 780
    }
  
781 782
  atexit (close_mailboxes);
  
uid65697 authored
783 784 785 786 787 788 789
  source_name = argv[0];
  dest_name = argv[1];

  flags = preserve_mail ? MU_STREAM_READ : MU_STREAM_RDWR;
  
  if (strncmp (source_name, "po:", 3) == 0)
    compatibility_mode (&source, source_name, argv[2], flags);
790 791
  else
    open_mailbox (&source, source_name, flags, argv[2]);
792

793
  switch_owner (source);
794 795
  
  open_mailbox (&dest, dest_name, MU_STREAM_RDWR | MU_STREAM_CREAT, NULL);
796

Sergey Poznyakoff authored
797 798 799
  if (program_id_option)
    set_program_id (source_name, dest_name);

800 801 802
  rc = mu_mailbox_messages_count (source, &total);
  if (rc)
    {
803
      mu_error(_("cannot count messages: %s"), mu_strerror (rc));
804 805 806 807 808
      exit (1);
    }
  
  if (verbose_option)
    mu_diag_output (MU_DIAG_INFO,
809
		    _("number of messages in source mailbox: %lu"),
810 811 812 813 814 815 816 817 818
		    (unsigned long) total);

  if (uidl_option)
    {
      mu_iterator_t itr;
      mu_list_t dst_uidl_list = NULL;

      rc = mu_mailbox_get_uidls (source, &src_uidl_list);
      if (rc)
819
	die (source, _("cannot get UIDLs"), rc);
820 821
      rc = mu_mailbox_get_uidls (dest, &dst_uidl_list);
      if (rc)
822
	die (dest, _("cannot get UIDLs"), rc);
823 824 825 826 827 828 829 830 831 832 833

      mu_list_set_comparator (dst_uidl_list, _compare_uidls);
      
      mu_list_get_iterator (src_uidl_list, &itr);
      for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
	   mu_iterator_next (itr))
	{
	  struct mu_uidl *uidl;
	      
	  mu_iterator_current (itr, (void **)&uidl);
	  if (mu_list_locate (dst_uidl_list, uidl, NULL) == 0)
834
	    mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
835 836 837
	}
      mu_iterator_destroy (&itr);
      mu_list_destroy (&dst_uidl_list);
Sergey Poznyakoff authored
838
      mu_list_set_comparator (src_uidl_list, NULL);
839
    }
Sergey Poznyakoff authored
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875

  /* FIXME: Implementing a mailbox iterator would allow to merge the three
     branches of this conditional. */
  if (src_uidl_list)
    {
      mu_iterator_t itr;

      rc = mu_list_get_iterator (src_uidl_list, &itr);
      if (rc)
	{
	  mu_error(_("cannot get iterator: %s"), mu_strerror (rc));
	  exit (1);
	}
      rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &reverse_order);
      if (rc)
	{
	  mu_error(_("cannot set iteration direction: %s"), mu_strerror (rc));
	  exit (1);
	}
      for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
	   mu_iterator_next (itr))
	{
	  struct mu_uidl *uidl;
	      
	  mu_iterator_current (itr, (void **)&uidl);
	  rc = move_message (source, dest, uidl->msgno);
	  if (rc == 0)
	    msg_count++;
	  else if (!ignore_errors)
	    break;
	  else
	    errs = 1;
	}
      mu_iterator_destroy (&itr);
    }
  else if (reverse_order)
uid65697 authored
876
    {
877 878 879 880 881
      for (i = total; i > 0; i--)
	{
	  rc = move_message (source, dest, i);
	  if (rc == 0)
	    msg_count++;
Sergey Poznyakoff authored
882
	  else if (!ignore_errors)
883
	    break;
Sergey Poznyakoff authored
884 885
	  else
	    errs = 1;
886
	}
uid65697 authored
887 888 889
    }
  else
    {
890 891 892 893 894
      for (i = 1; i <= total; i++)
	{
	  rc = move_message (source, dest, i);
	  if (rc == 0)
	    msg_count++;
Sergey Poznyakoff authored
895
	  else if (!ignore_errors)
896
	    break;
Sergey Poznyakoff authored
897 898
	  else
	    errs = 1;
899
	}
uid65697 authored
900
    }
901
  
902 903
  if (verbose_option)
    mu_diag_output (MU_DIAG_INFO,
904
		    _("number of processed messages: %lu"),
905 906
		    (unsigned long) msg_count);
  
uid65697 authored
907
  if (rc)
Sergey Poznyakoff authored
908
    return !!rc;
909
  
910
  mu_mailbox_sync (dest);
911 912 913
  rc = mu_mailbox_close (dest);
  mu_mailbox_destroy (&dest);
  if (rc)
914
    mu_error (_("cannot close destination mailbox: %s"), mu_strerror (rc));
Sergey Poznyakoff authored
915
  else if (!preserve_mail)
916 917
    mu_mailbox_flush (source, 1);

918 919
  mu_mailbox_close (source);
  mu_mailbox_destroy (&source);
920

Sergey Poznyakoff authored
921
  return !(rc == 0 && errs == 0);
uid65697 authored
922
}