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