Clean up movemail source
[bpt/emacs.git] / lib-src / movemail.c
CommitLineData
237e0016
RS
1/* movemail foo bar -- move file foo to file bar,
2 locking file foo the way /bin/mail respects.
b3d90e46 3 Copyright (C) 1986, 1992, 1993, 1994, 1996, 1999, 2001, 2002, 2003, 2004,
114f9c96 4 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
237e0016
RS
5
6This file is part of GNU Emacs.
7
294981c7 8GNU Emacs is free software: you can redistribute it and/or modify
93320c23 9it under the terms of the GNU General Public License as published by
294981c7
GM
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
93320c23 12
237e0016 13GNU Emacs is distributed in the hope that it will be useful,
93320c23
JA
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
237e0016 17
93320c23 18You should have received a copy of the GNU General Public License
294981c7
GM
19along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
20
237e0016 21
63cf923d
RS
22/* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
23 cause loss of mail* if you do it on a system that does not normally
24 use flock as its way of interlocking access to inbox files. The
25 setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
26 system's own conventions. It is not a choice that is up to you.
08d0752f
RS
27
28 So, if your system uses lock files rather than flock, then the only way
29 you can get proper operation is to enable movemail to write lockfiles there.
30 This means you must either give that directory access modes
31 that permit everyone to write lockfiles in it, or you must make movemail
32 a setuid or setgid program. */
33
237e0016
RS
34/*
35 * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
36 *
88c40feb 37 * Added POP (Post Office Protocol) service. When compiled -DMAIL_USE_POP
237e0016
RS
38 * movemail will accept input filename arguments of the form
39 * "po:username". This will cause movemail to open a connection to
40 * a pop server running on $MAILHOST (environment variable). Movemail
41 * must be setuid to root in order to work with POP.
177c0ea7 42 *
237e0016
RS
43 * New module: popmail.c
44 * Modified routines:
cfa191ff 45 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
177c0ea7 46 * after POP code.
237e0016
RS
47 * New routines in movemail.c:
48 * get_errmsg - return pointer to system error message
49 *
2e82e3c3
RS
50 * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
51 *
52 * Move all of the POP code into a separate file, "pop.c".
53 * Use strerror instead of get_errmsg.
54 *
237e0016
RS
55 */
56
752fb472 57#include <config.h>
237e0016
RS
58#include <sys/types.h>
59#include <sys/stat.h>
60#include <sys/file.h>
e2f9d9af 61#include <stdio.h>
237e0016 62#include <errno.h>
bd41a17d 63#include <time.h>
cc3b64e8 64
fea4325c 65#include <getopt.h>
f72adc12
EZ
66#ifdef HAVE_UNISTD_H
67#include <unistd.h>
68#endif
cc3b64e8
DL
69#ifdef HAVE_FCNTL_H
70#include <fcntl.h>
71#endif
1725ae55
AS
72#ifdef HAVE_STRING_H
73#include <string.h>
74#endif
f72adc12 75#include "syswait.h"
2e82e3c3
RS
76#ifdef MAIL_USE_POP
77#include "pop.h"
78#endif
237e0016 79
91cf09ac
RS
80#ifdef MSDOS
81#undef access
82#endif /* MSDOS */
83
61946d67
RS
84#ifndef DIRECTORY_SEP
85#define DIRECTORY_SEP '/'
86#endif
87#ifndef IS_DIRECTORY_SEP
88#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
89#endif
90
7f75d5c6 91#ifdef WINDOWSNT
677a7bcf 92#include "ntlib.h"
7f75d5c6
RS
93#undef access
94#undef unlink
95#define fork() 0
4822b2e5 96#define wait(var) (*(var) = 0)
7f75d5c6
RS
97/* Unfortunately, Samba doesn't seem to properly lock Unix files even
98 though the locking call succeeds (and indeed blocks local access from
99 other NT programs). If you have direct file access using an NFS
100 client or something other than Samba, the locking call might work
677a7bcf
RS
101 properly - make sure it does before you enable this!
102
103 [18-Feb-97 andrewi] I now believe my comment above to be incorrect,
104 since it was based on a misunderstanding of how locking calls are
105 implemented and used on Unix. */
106//#define DISABLE_DIRECT_ACCESS
107
677a7bcf 108#include <fcntl.h>
7f75d5c6
RS
109#endif /* WINDOWSNT */
110
4ec9a77a
RS
111#ifndef F_OK
112#define F_OK 0
113#define X_OK 1
114#define W_OK 2
115#define R_OK 4
116#endif
237e0016 117
76ed5e01 118#ifdef WINDOWSNT
237e0016
RS
119#include <sys/locking.h>
120#endif
121
63cf923d
RS
122#ifdef MAIL_USE_LOCKF
123#define MAIL_USE_SYSTEM_LOCK
124#endif
125
126#ifdef MAIL_USE_FLOCK
127#define MAIL_USE_SYSTEM_LOCK
128#endif
129
4293ba7f
RS
130#ifdef MAIL_USE_MMDF
131extern int lk_open (), lk_close ();
132#endif
133
a4deff3c 134#if !defined (MAIL_USE_SYSTEM_LOCK) && !defined (MAIL_USE_MMDF) && \
dd843b6a
DL
135 (defined (HAVE_LIBMAIL) || defined (HAVE_LIBLOCKFILE)) && \
136 defined (HAVE_MAILLOCK_H)
a4deff3c
RS
137#include <maillock.h>
138/* We can't use maillock unless we know what directory system mail
139 files appear in. */
140#ifdef MAILDIR
141#define MAIL_USE_MAILLOCK
142static char *mail_spool_name ();
143#endif
144#endif
145
1725ae55 146#ifndef HAVE_STRERROR
873fbd0b 147char *strerror (int);
1725ae55 148#endif
517699ca 149#ifdef HAVE_INDEX
f57e2426 150extern char *index (const char *, int);
517699ca
DL
151#endif
152#ifdef HAVE_RINDEX
f57e2426 153extern char *rindex (const char *, int);
517699ca 154#endif
e2f9d9af 155
1725ae55
AS
156static void fatal (char *s1, char *s2, char *s3);
157static void error (char *s1, char *s2, char *s3);
158static void pfatal_with_name (char *name);
159static void pfatal_and_delete (char *name);
160static char *concat (char *s1, char *s2, char *s3);
161static long *xmalloc (unsigned int size);
e2ad23ef 162#ifdef MAIL_USE_POP
1725ae55
AS
163static int popmail (char *mailbox, char *outfile, int preserve, char *password, int reverse_order);
164static int pop_retr (popserver server, int msgno, FILE *arg);
165static int mbx_write (char *line, int len, FILE *mbf);
166static int mbx_delimit_begin (FILE *mbf);
167static int mbx_delimit_end (FILE *mbf);
e2ad23ef 168#endif
237e0016
RS
169
170/* Nonzero means this is name of a lock file to delete on fatal error. */
171char *delete_lockname;
172
e2f9d9af 173int
873fbd0b 174main (int argc, char **argv)
237e0016
RS
175{
176 char *inname, *outname;
177 int indesc, outdesc;
237e0016 178 int nread;
bba104c1 179 int status;
fea4325c 180 int c, preserve_mail = 0;
237e0016 181
63cf923d 182#ifndef MAIL_USE_SYSTEM_LOCK
237e0016
RS
183 struct stat st;
184 long now;
185 int tem;
186 char *lockname, *p;
906ad89d 187 char *tempname;
237e0016 188 int desc;
63cf923d 189#endif /* not MAIL_USE_SYSTEM_LOCK */
237e0016 190
a4deff3c
RS
191#ifdef MAIL_USE_MAILLOCK
192 char *spool_name;
193#endif
194
a2997b0f
KH
195#ifdef MAIL_USE_POP
196 int pop_reverse_order = 0;
197# define ARGSTR "pr"
198#else /* ! MAIL_USE_POP */
199# define ARGSTR "p"
200#endif /* MAIL_USE_POP */
201
51a91f9d
CY
202 uid_t real_gid = getgid();
203 uid_t priv_gid = getegid();
204
9112a2a9
AI
205#ifdef WINDOWSNT
206 /* Ensure all file i/o is in binary mode. */
207 _fmode = _O_BINARY;
208#endif
209
237e0016
RS
210 delete_lockname = 0;
211
a2997b0f 212 while ((c = getopt (argc, argv, ARGSTR)) != EOF)
e2f9d9af 213 {
fea4325c 214 switch (c) {
a2997b0f
KH
215#ifdef MAIL_USE_POP
216 case 'r':
217 pop_reverse_order = 1;
218 break;
219#endif
fea4325c
RS
220 case 'p':
221 preserve_mail++;
222 break;
223 default:
65396510 224 exit (EXIT_FAILURE);
fea4325c
RS
225 }
226 }
227
228 if (
229#ifdef MAIL_USE_POP
230 (argc - optind < 2) || (argc - optind > 3)
231#else
232 (argc - optind != 2)
233#endif
234 )
235 {
fea4325c 236#ifdef MAIL_USE_POP
f213f2c0 237 fprintf (stderr, "Usage: movemail [-p] [-r] inbox destfile%s\n",
bb5618fe 238 " [POP-password]");
fea4325c 239#else
bb5618fe 240 fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", "");
fea4325c 241#endif
65396510 242 exit (EXIT_FAILURE);
e2f9d9af 243 }
237e0016 244
fea4325c
RS
245 inname = argv[optind];
246 outname = argv[optind+1];
237e0016 247
4293ba7f
RS
248#ifdef MAIL_USE_MMDF
249 mmdf_init (argv[0]);
250#endif
251
af7bd34e 252 if (*outname == 0)
a9eedf40 253 fatal ("Destination file name is empty", 0, 0);
af7bd34e 254
237e0016 255#ifdef MAIL_USE_POP
12a0565a 256 if (!strncmp (inname, "po:", 3))
237e0016 257 {
b3112191 258 int status;
237e0016 259
fea4325c 260 status = popmail (inname + 3, outname, preserve_mail,
a2997b0f
KH
261 (argc - optind == 3) ? argv[optind+2] : NULL,
262 pop_reverse_order);
237e0016
RS
263 exit (status);
264 }
265
51a91f9d
CY
266 if (setuid (getuid ()) < 0)
267 fatal ("Failed to drop privileges", 0, 0);
268
237e0016
RS
269#endif /* MAIL_USE_POP */
270
7f75d5c6 271#ifndef DISABLE_DIRECT_ACCESS
4293ba7f 272#ifndef MAIL_USE_MMDF
63cf923d 273#ifndef MAIL_USE_SYSTEM_LOCK
a4deff3c
RS
274#ifdef MAIL_USE_MAILLOCK
275 spool_name = mail_spool_name (inname);
276 if (! spool_name)
277#endif
237e0016 278 {
a4deff3c
RS
279 /* Use a lock file named after our first argument with .lock appended:
280 If it exists, the mail file is locked. */
281 /* Note: this locking mechanism is *required* by the mailer
282 (on systems which use it) to prevent loss of mail.
283
284 On systems that use a lock file, extracting the mail without locking
285 WILL occasionally cause loss of mail due to timing errors!
286
287 So, if creation of the lock file fails
288 due to access permission on the mail spool directory,
289 you simply MUST change the permission
290 and/or make movemail a setgid program
291 so it can create lock files properly.
292
293 You might also wish to verify that your system is one
294 which uses lock files for this purpose. Some systems use other methods.
295
296 If your system uses the `flock' system call for mail locking,
297 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
298 and recompile movemail. If the s- file for your system
299 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
300 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
301
302 lockname = concat (inname, ".lock", "");
303 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
304 strcpy (tempname, inname);
305 p = tempname + strlen (tempname);
306 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
307 p--;
308 *p = 0;
309 strcpy (p, "EXXXXXX");
310 mktemp (tempname);
cfa191ff 311 unlink (tempname);
237e0016 312
a4deff3c 313 while (1)
237e0016 314 {
a4deff3c
RS
315 /* Create the lock file, but not under the lock file name. */
316 /* Give up if cannot do that. */
317 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
318 if (desc < 0)
319 {
320 char *message = (char *) xmalloc (strlen (tempname) + 50);
4b265472 321 sprintf (message, "creating %s, which would become the lock file",
a4deff3c
RS
322 tempname);
323 pfatal_with_name (message);
324 }
325 close (desc);
326
327 tem = link (tempname, lockname);
097e9c90
CY
328
329#ifdef EPERM
330 if (tem < 0 && errno == EPERM)
331 fatal ("Unable to create hard link between %s and %s",
332 tempname, lockname);
333#endif
334
a4deff3c
RS
335 unlink (tempname);
336 if (tem >= 0)
337 break;
338 sleep (1);
339
340 /* If lock file is five minutes old, unlock it.
341 Five minutes should be good enough to cope with crashes
342 and wedgitude, and long enough to avoid being fooled
343 by time differences between machines. */
344 if (stat (lockname, &st) >= 0)
345 {
346 now = time (0);
347 if (st.st_ctime < now - 300)
348 unlink (lockname);
349 }
237e0016 350 }
237e0016 351
a4deff3c
RS
352 delete_lockname = lockname;
353 }
63cf923d
RS
354#endif /* not MAIL_USE_SYSTEM_LOCK */
355#endif /* not MAIL_USE_MMDF */
237e0016 356
8ca83cfd
RS
357 if (fork () == 0)
358 {
25025815 359 int lockcount = 0;
a4deff3c
RS
360 int status = 0;
361#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
bd41a17d 362 time_t touched_lock, now;
a4deff3c 363#endif
25025815 364
51a91f9d
CY
365 if (setuid (getuid ()) < 0 || setegid (real_gid) < 0)
366 fatal ("Failed to drop privileges", 0, 0);
8ca83cfd 367
63cf923d
RS
368#ifndef MAIL_USE_MMDF
369#ifdef MAIL_USE_SYSTEM_LOCK
8ca83cfd 370 indesc = open (inname, O_RDWR);
63cf923d 371#else /* if not MAIL_USE_SYSTEM_LOCK */
8ca83cfd 372 indesc = open (inname, O_RDONLY);
63cf923d 373#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd
RS
374#else /* MAIL_USE_MMDF */
375 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
4293ba7f
RS
376#endif /* MAIL_USE_MMDF */
377
8ca83cfd
RS
378 if (indesc < 0)
379 pfatal_with_name (inname);
237e0016 380
76ed5e01 381#ifdef BSD_SYSTEM
8ca83cfd
RS
382 /* In case movemail is setuid to root, make sure the user can
383 read the output file. */
384 /* This is desirable for all systems
385 but I don't want to assume all have the umask system call */
386 umask (umask (0) & 0333);
76ed5e01 387#endif /* BSD_SYSTEM */
8ca83cfd
RS
388 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
389 if (outdesc < 0)
390 pfatal_with_name (outname);
25025815 391
51a91f9d
CY
392 if (setegid (priv_gid) < 0)
393 fatal ("Failed to regain privileges", 0, 0);
394
25025815
RS
395 /* This label exists so we can retry locking
396 after a delay, if it got EAGAIN or EBUSY. */
397 retry_lock:
398
399 /* Try to lock it. */
a4deff3c
RS
400#ifdef MAIL_USE_MAILLOCK
401 if (spool_name)
402 {
403 /* The "0 - " is to make it a negative number if maillock returns
404 non-zero. */
405 status = 0 - maillock (spool_name, 1);
406#ifdef HAVE_TOUCHLOCK
407 touched_lock = time (0);
408#endif
409 lockcount = 5;
410 }
411 else
412#endif /* MAIL_USE_MAILLOCK */
413 {
63cf923d
RS
414#ifdef MAIL_USE_SYSTEM_LOCK
415#ifdef MAIL_USE_LOCKF
a4deff3c 416 status = lockf (indesc, F_LOCK, 0);
63cf923d 417#else /* not MAIL_USE_LOCKF */
7f75d5c6 418#ifdef WINDOWSNT
a4deff3c 419 status = locking (indesc, LK_RLCK, -1L);
237e0016 420#else
a4deff3c 421 status = flock (indesc, LOCK_EX);
237e0016 422#endif
63cf923d
RS
423#endif /* not MAIL_USE_LOCKF */
424#endif /* MAIL_USE_SYSTEM_LOCK */
a4deff3c 425 }
237e0016 426
25025815
RS
427 /* If it fails, retry up to 5 times
428 for certain failure codes. */
429 if (status < 0)
430 {
431 if (++lockcount <= 5)
432 {
433#ifdef EAGAIN
434 if (errno == EAGAIN)
435 {
436 sleep (1);
437 goto retry_lock;
438 }
439#endif
440#ifdef EBUSY
441 if (errno == EBUSY)
442 {
443 sleep (1);
444 goto retry_lock;
445 }
446#endif
447 }
448
449 pfatal_with_name (inname);
450 }
177c0ea7 451
08564963 452 {
8ca83cfd
RS
453 char buf[1024];
454
455 while (1)
08564963 456 {
8ca83cfd 457 nread = read (indesc, buf, sizeof buf);
5e5b35c7
RS
458 if (nread < 0)
459 pfatal_with_name (inname);
8ca83cfd
RS
460 if (nread != write (outdesc, buf, nread))
461 {
462 int saved_errno = errno;
463 unlink (outname);
464 errno = saved_errno;
465 pfatal_with_name (outname);
466 }
467 if (nread < sizeof buf)
468 break;
a4deff3c
RS
469#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
470 if (spool_name)
471 {
472 now = time (0);
473 if (now - touched_lock > 60)
474 {
475 touchlock ();
476 touched_lock = now;
477 }
478 }
479#endif /* MAIL_USE_MAILLOCK */
08564963 480 }
08564963 481 }
237e0016 482
e397a017 483#ifdef BSD_SYSTEM
8ca83cfd
RS
484 if (fsync (outdesc) < 0)
485 pfatal_and_delete (outname);
237e0016
RS
486#endif
487
51a91f9d
CY
488 /* Prevent symlink attacks truncating other users' mailboxes */
489 if (setegid (real_gid) < 0)
490 fatal ("Failed to drop privileges", 0, 0);
491
8ca83cfd
RS
492 /* Check to make sure no errors before we zap the inbox. */
493 if (close (outdesc) != 0)
494 pfatal_and_delete (outname);
237e0016 495
63cf923d 496#ifdef MAIL_USE_SYSTEM_LOCK
fea4325c
RS
497 if (! preserve_mail)
498 {
fea4325c 499 ftruncate (indesc, 0L);
b1cb2966 500 }
63cf923d 501#endif /* MAIL_USE_SYSTEM_LOCK */
4293ba7f
RS
502
503#ifdef MAIL_USE_MMDF
8ca83cfd 504 lk_close (indesc, 0, 0, 0);
4293ba7f 505#else
8ca83cfd 506 close (indesc);
4293ba7f 507#endif
237e0016 508
63cf923d 509#ifndef MAIL_USE_SYSTEM_LOCK
fea4325c
RS
510 if (! preserve_mail)
511 {
512 /* Delete the input file; if we can't, at least get rid of its
513 contents. */
e97dd183 514#ifdef MAIL_UNLINK_SPOOL
fea4325c
RS
515 /* This is generally bad to do, because it destroys the permissions
516 that were set on the file. Better to just empty the file. */
517 if (unlink (inname) < 0 && errno != ENOENT)
e97dd183 518#endif /* MAIL_UNLINK_SPOOL */
fea4325c
RS
519 creat (inname, 0600);
520 }
63cf923d 521#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd 522
51a91f9d
CY
523 /* End of mailbox truncation */
524 if (setegid (priv_gid) < 0)
525 fatal ("Failed to regain privileges", 0, 0);
526
a4deff3c
RS
527#ifdef MAIL_USE_MAILLOCK
528 /* This has to occur in the child, i.e., in the process that
529 acquired the lock! */
530 if (spool_name)
531 mailunlock ();
532#endif
65396510 533 exit (EXIT_SUCCESS);
8ca83cfd
RS
534 }
535
536 wait (&status);
537 if (!WIFEXITED (status))
65396510 538 exit (EXIT_FAILURE);
8ca83cfd
RS
539 else if (WRETCODE (status) != 0)
540 exit (WRETCODE (status));
541
63cf923d 542#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
a4deff3c
RS
543#ifdef MAIL_USE_MAILLOCK
544 if (! spool_name)
545#endif /* MAIL_USE_MAILLOCK */
546 unlink (lockname);
63cf923d 547#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
7f75d5c6
RS
548
549#endif /* ! DISABLE_DIRECT_ACCESS */
550
65396510 551 return EXIT_SUCCESS;
237e0016 552}
a4deff3c
RS
553
554#ifdef MAIL_USE_MAILLOCK
555/* This function uses stat to confirm that the mail directory is
556 identical to the directory of the input file, rather than just
557 string-comparing the two paths, because one or both of them might
558 be symbolic links pointing to some other directory. */
559static char *
560mail_spool_name (inname)
561 char *inname;
562{
563 struct stat stat1, stat2;
564 char *indir, *fname;
565 int status;
566
567 if (! (fname = rindex (inname, '/')))
568 return NULL;
569
570 fname++;
571
572 if (stat (MAILDIR, &stat1) < 0)
573 return NULL;
574
575 indir = (char *) xmalloc (fname - inname + 1);
576 strncpy (indir, inname, fname - inname);
577 indir[fname-inname] = '\0';
578
579
580 status = stat (indir, &stat2);
581
582 free (indir);
583
584 if (status < 0)
585 return NULL;
586
c4009c1f
RS
587 if (stat1.st_dev == stat2.st_dev
588 && stat1.st_ino == stat2.st_ino)
a4deff3c
RS
589 return fname;
590
591 return NULL;
592}
593#endif /* MAIL_USE_MAILLOCK */
237e0016
RS
594\f
595/* Print error message and exit. */
596
1725ae55 597static void
873fbd0b 598fatal (char *s1, char *s2, char *s3)
237e0016
RS
599{
600 if (delete_lockname)
601 unlink (delete_lockname);
a9eedf40 602 error (s1, s2, s3);
65396510 603 exit (EXIT_FAILURE);
237e0016
RS
604}
605
cc3b64e8
DL
606/* Print error message. `s1' is printf control string, `s2' and `s3'
607 are args for it or null. */
237e0016 608
1725ae55 609static void
873fbd0b 610error (char *s1, char *s2, char *s3)
237e0016 611{
e2f9d9af 612 fprintf (stderr, "movemail: ");
cc3b64e8
DL
613 if (s3)
614 fprintf (stderr, s1, s2, s3);
615 else if (s2)
616 fprintf (stderr, s1, s2);
617 else
3b3807f8 618 fprintf (stderr, "%s", s1);
e2f9d9af 619 fprintf (stderr, "\n");
237e0016
RS
620}
621
1725ae55 622static void
873fbd0b 623pfatal_with_name (char *name)
237e0016 624{
a9eedf40 625 fatal ("%s for %s", strerror (errno), name);
237e0016
RS
626}
627
1725ae55 628static void
873fbd0b 629pfatal_and_delete (char *name)
cfa191ff 630{
a9eedf40 631 char *s = strerror (errno);
cfa191ff 632 unlink (name);
a9eedf40 633 fatal ("%s for %s", s, name);
cfa191ff
RS
634}
635
237e0016
RS
636/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
637
1725ae55 638static char *
873fbd0b 639concat (char *s1, char *s2, char *s3)
237e0016
RS
640{
641 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
642 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
643
644 strcpy (result, s1);
645 strcpy (result + len1, s2);
646 strcpy (result + len1 + len2, s3);
647 *(result + len1 + len2 + len3) = 0;
648
649 return result;
650}
651
652/* Like malloc but get fatal error if memory is exhausted. */
653
1725ae55 654static long *
873fbd0b 655xmalloc (unsigned int size)
237e0016 656{
2583d6d7 657 long *result = (long *) malloc (size);
237e0016 658 if (!result)
a9eedf40 659 fatal ("virtual memory exhausted", 0, 0);
237e0016
RS
660 return result;
661}
662\f
663/* This is the guts of the interface to the Post Office Protocol. */
664
665#ifdef MAIL_USE_POP
666
7f75d5c6 667#ifndef WINDOWSNT
237e0016
RS
668#include <sys/socket.h>
669#include <netinet/in.h>
670#include <netdb.h>
7f75d5c6
RS
671#else
672#undef _WINSOCKAPI_
673#include <winsock.h>
674#endif
cecf0f21 675#include <pwd.h>
d228a23c 676#include <string.h>
237e0016 677
237e0016
RS
678#define NOTOK (-1)
679#define OK 0
680#define DONE 1
681
682char *progname;
683FILE *sfi;
684FILE *sfo;
2e82e3c3
RS
685char ibuffer[BUFSIZ];
686char obuffer[BUFSIZ];
752fb472
DL
687char Errmsg[200]; /* POP errors, at least, can exceed
688 the original length of 80. */
237e0016 689
476b2799 690/*
4d90eee4 691 * The full valid syntax for a POP mailbox specification for movemail
476b2799
GM
692 * is "po:username:hostname". The ":hostname" is optional; if it is
693 * omitted, the MAILHOST environment variable will be consulted. Note
694 * that by the time popmail() is called the "po:" has been stripped
695 * off of the front of the mailbox name.
696 *
697 * If the mailbox is in the form "po:username:hostname", then it is
698 * modified by this function -- the second colon is replaced by a
699 * null.
65396510
TTN
700 *
701 * Return a value suitable for passing to `exit'.
476b2799
GM
702 */
703
1725ae55 704static int
873fbd0b 705popmail (char *mailbox, char *outfile, int preserve, char *password, int reverse_order)
237e0016 706{
b1ce62a8 707 int nmsgs, nbytes;
b1ce62a8
RS
708 register int i;
709 int mbfi;
710 FILE *mbf;
873fbd0b 711 char *getenv (const char *);
b32701a7 712 popserver server;
a2997b0f 713 int start, end, increment;
476b2799
GM
714 char *user, *hostname;
715
716 user = mailbox;
717 if ((hostname = index(mailbox, ':')))
718 *hostname++ = '\0';
237e0016 719
476b2799 720 server = pop_open (hostname, user, password, POP_NO_GETPASS);
2e82e3c3 721 if (! server)
b1ce62a8 722 {
cc3b64e8 723 error ("Error connecting to POP server: %s", pop_error, 0);
65396510 724 return EXIT_FAILURE;
237e0016
RS
725 }
726
2e82e3c3 727 if (pop_stat (server, &nmsgs, &nbytes))
b1ce62a8 728 {
cc3b64e8 729 error ("Error getting message count from POP server: %s", pop_error, 0);
65396510 730 return EXIT_FAILURE;
237e0016
RS
731 }
732
b1ce62a8
RS
733 if (!nmsgs)
734 {
2e82e3c3 735 pop_close (server);
65396510 736 return EXIT_SUCCESS;
b1ce62a8
RS
737 }
738
739 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
740 if (mbfi < 0)
741 {
2e82e3c3
RS
742 pop_close (server);
743 error ("Error in open: %s, %s", strerror (errno), outfile);
65396510 744 return EXIT_FAILURE;
b1ce62a8
RS
745 }
746 fchown (mbfi, getuid (), -1);
747
7f75d5c6 748 if ((mbf = fdopen (mbfi, "wb")) == NULL)
b1ce62a8 749 {
2e82e3c3 750 pop_close (server);
cc3b64e8 751 error ("Error in fdopen: %s", strerror (errno), 0);
2e82e3c3
RS
752 close (mbfi);
753 unlink (outfile);
65396510 754 return EXIT_FAILURE;
b1ce62a8
RS
755 }
756
a2997b0f
KH
757 if (reverse_order)
758 {
759 start = nmsgs;
760 end = 1;
761 increment = -1;
762 }
763 else
764 {
765 start = 1;
766 end = nmsgs;
767 increment = 1;
768 }
769
770 for (i = start; i * increment <= end * increment; i += increment)
b1ce62a8
RS
771 {
772 mbx_delimit_begin (mbf);
ff804ff5 773 if (pop_retr (server, i, mbf) != OK)
b1ce62a8 774 {
17a60964 775 error ("%s", Errmsg, 0);
b1ce62a8 776 close (mbfi);
65396510 777 return EXIT_FAILURE;
237e0016 778 }
b1ce62a8
RS
779 mbx_delimit_end (mbf);
780 fflush (mbf);
2e82e3c3
RS
781 if (ferror (mbf))
782 {
cc3b64e8 783 error ("Error in fflush: %s", strerror (errno), 0);
2e82e3c3
RS
784 pop_close (server);
785 close (mbfi);
65396510 786 return EXIT_FAILURE;
2e82e3c3 787 }
237e0016
RS
788 }
789
2e82e3c3
RS
790 /* On AFS, a call to write only modifies the file in the local
791 * workstation's AFS cache. The changes are not written to the server
792 * until a call to fsync or close is made. Users with AFS home
793 * directories have lost mail when over quota because these checks were
794 * not made in previous versions of movemail. */
795
e397a017 796#ifdef BSD_SYSTEM
cfa191ff
RS
797 if (fsync (mbfi) < 0)
798 {
08fa58c9 799 error ("Error in fsync: %s", strerror (errno), 0);
65396510 800 return EXIT_FAILURE;
cfa191ff 801 }
340ff9de 802#endif
cfa191ff
RS
803
804 if (close (mbfi) == -1)
805 {
cc3b64e8 806 error ("Error in close: %s", strerror (errno), 0);
65396510 807 return EXIT_FAILURE;
cfa191ff
RS
808 }
809
fea4325c
RS
810 if (! preserve)
811 for (i = 1; i <= nmsgs; i++)
812 {
813 if (pop_delete (server, i))
814 {
cc3b64e8 815 error ("Error from POP server: %s", pop_error, 0);
fea4325c 816 pop_close (server);
65396510 817 return EXIT_FAILURE;
fea4325c
RS
818 }
819 }
237e0016 820
2e82e3c3 821 if (pop_quit (server))
b1ce62a8 822 {
cc3b64e8 823 error ("Error from POP server: %s", pop_error, 0);
65396510 824 return EXIT_FAILURE;
237e0016 825 }
177c0ea7 826
65396510 827 return EXIT_SUCCESS;
237e0016
RS
828}
829
1725ae55 830static int
873fbd0b 831pop_retr (popserver server, int msgno, FILE *arg)
237e0016 832{
2e82e3c3
RS
833 char *line;
834 int ret;
237e0016 835
2e82e3c3 836 if (pop_retrieve_first (server, msgno, &line))
b1ce62a8 837 {
9b956af2
KH
838 char *error = concat ("Error from POP server: ", pop_error, "");
839 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 840 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 841 free(error);
2e82e3c3 842 return (NOTOK);
237e0016
RS
843 }
844
d89d0243 845 while ((ret = pop_retrieve_next (server, &line)) >= 0)
b1ce62a8 846 {
2e82e3c3
RS
847 if (! line)
848 break;
849
d89d0243 850 if (mbx_write (line, ret, arg) != OK)
b1ce62a8 851 {
2e82e3c3
RS
852 strcpy (Errmsg, strerror (errno));
853 pop_close (server);
854 return (NOTOK);
237e0016
RS
855 }
856 }
237e0016 857
2e82e3c3 858 if (ret)
b1ce62a8 859 {
9b956af2
KH
860 char *error = concat ("Error from POP server: ", pop_error, "");
861 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 862 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 863 free(error);
2e82e3c3 864 return (NOTOK);
237e0016
RS
865 }
866
2e82e3c3 867 return (OK);
237e0016
RS
868}
869
2e82e3c3
RS
870/* Do this as a macro instead of using strcmp to save on execution time. */
871#define IS_FROM_LINE(a) ((a[0] == 'F') \
872 && (a[1] == 'r') \
873 && (a[2] == 'o') \
874 && (a[3] == 'm') \
875 && (a[4] == ' '))
237e0016 876
1725ae55 877static int
873fbd0b 878mbx_write (char *line, int len, FILE *mbf)
237e0016 879{
d04f5031 880#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
2e82e3c3
RS
881 if (IS_FROM_LINE (line))
882 {
883 if (fputc ('>', mbf) == EOF)
884 return (NOTOK);
885 }
d04f5031
PE
886#endif
887 if (line[0] == '\037')
888 {
889 if (fputs ("^_", mbf) == EOF)
890 return (NOTOK);
891 line++;
892 len--;
893 }
177c0ea7 894 if (fwrite (line, 1, len, mbf) != len)
2e82e3c3
RS
895 return (NOTOK);
896 if (fputc (0x0a, mbf) == EOF)
897 return (NOTOK);
898 return (OK);
237e0016
RS
899}
900
1725ae55 901static int
873fbd0b 902mbx_delimit_begin (FILE *mbf)
237e0016 903{
d228a23c
GM
904 time_t now;
905 struct tm *ltime;
906 char fromline[40] = "From movemail ";
907
908 now = time (NULL);
909 ltime = localtime (&now);
910
911 strcat (fromline, asctime (ltime));
912
913 if (fputs (fromline, mbf) == EOF)
2e82e3c3
RS
914 return (NOTOK);
915 return (OK);
237e0016
RS
916}
917
1725ae55 918static int
873fbd0b 919mbx_delimit_end (FILE *mbf)
237e0016 920{
3f32be22 921 if (putc ('\n', mbf) == EOF)
2e82e3c3
RS
922 return (NOTOK);
923 return (OK);
237e0016
RS
924}
925
926#endif /* MAIL_USE_POP */
e5f7ea68
RM
927\f
928#ifndef HAVE_STRERROR
929char *
930strerror (errnum)
931 int errnum;
932{
933 extern char *sys_errlist[];
934 extern int sys_nerr;
935
936 if (errnum >= 0 && errnum < sys_nerr)
937 return sys_errlist[errnum];
938 return (char *) "Unknown error";
939}
940
941#endif /* ! HAVE_STRERROR */
ab5796a9
MB
942
943/* arch-tag: 1c323112-41fe-4fe5-8de9-494de631f73f
944 (do not change this comment) */
65396510
TTN
945
946/* movemail.c ends here */