Assume freestanding C89 headers, string.h, stdlib.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
GM
3
4Copyright (C) 1986, 1992-1994, 1996, 1999, 2001-2011
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
25 use flock as its way of interlocking access to inbox files. The
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
DL
68#ifdef HAVE_FCNTL_H
69#include <fcntl.h>
70#endif
1725ae55 71#include <string.h>
f72adc12 72#include "syswait.h"
2e82e3c3
RS
73#ifdef MAIL_USE_POP
74#include "pop.h"
75#endif
237e0016 76
91cf09ac
RS
77#ifdef MSDOS
78#undef access
79#endif /* MSDOS */
80
7f75d5c6 81#ifdef WINDOWSNT
677a7bcf 82#include "ntlib.h"
7f75d5c6
RS
83#undef access
84#undef unlink
85#define fork() 0
4822b2e5 86#define wait(var) (*(var) = 0)
7f75d5c6
RS
87/* Unfortunately, Samba doesn't seem to properly lock Unix files even
88 though the locking call succeeds (and indeed blocks local access from
89 other NT programs). If you have direct file access using an NFS
90 client or something other than Samba, the locking call might work
677a7bcf
RS
91 properly - make sure it does before you enable this!
92
93 [18-Feb-97 andrewi] I now believe my comment above to be incorrect,
94 since it was based on a misunderstanding of how locking calls are
95 implemented and used on Unix. */
96//#define DISABLE_DIRECT_ACCESS
97
677a7bcf 98#include <fcntl.h>
7f75d5c6
RS
99#endif /* WINDOWSNT */
100
4ec9a77a
RS
101#ifndef F_OK
102#define F_OK 0
103#define X_OK 1
104#define W_OK 2
105#define R_OK 4
106#endif
237e0016 107
76ed5e01 108#ifdef WINDOWSNT
237e0016
RS
109#include <sys/locking.h>
110#endif
111
63cf923d
RS
112#ifdef MAIL_USE_LOCKF
113#define MAIL_USE_SYSTEM_LOCK
114#endif
115
116#ifdef MAIL_USE_FLOCK
117#define MAIL_USE_SYSTEM_LOCK
118#endif
119
4293ba7f
RS
120#ifdef MAIL_USE_MMDF
121extern int lk_open (), lk_close ();
122#endif
123
a4deff3c 124#if !defined (MAIL_USE_SYSTEM_LOCK) && !defined (MAIL_USE_MMDF) && \
dd843b6a
DL
125 (defined (HAVE_LIBMAIL) || defined (HAVE_LIBLOCKFILE)) && \
126 defined (HAVE_MAILLOCK_H)
a4deff3c
RS
127#include <maillock.h>
128/* We can't use maillock unless we know what directory system mail
129 files appear in. */
130#ifdef MAILDIR
131#define MAIL_USE_MAILLOCK
5a9c1e26 132static char *mail_spool_name (char *);
a4deff3c
RS
133#endif
134#endif
135
1725ae55 136#ifndef HAVE_STRERROR
873fbd0b 137char *strerror (int);
1725ae55 138#endif
e2f9d9af 139
988e88ab
J
140static void fatal (const char *s1, const char *s2, const char *s3) NO_RETURN;
141static void error (const char *s1, const char *s2, const char *s3);
68441b90
DN
142static void pfatal_with_name (char *name) NO_RETURN;
143static void pfatal_and_delete (char *name) NO_RETURN;
988e88ab 144static char *concat (const char *s1, const char *s2, const char *s3);
1725ae55 145static long *xmalloc (unsigned int size);
e2ad23ef 146#ifdef MAIL_USE_POP
1725ae55
AS
147static int popmail (char *mailbox, char *outfile, int preserve, char *password, int reverse_order);
148static int pop_retr (popserver server, int msgno, FILE *arg);
149static int mbx_write (char *line, int len, FILE *mbf);
150static int mbx_delimit_begin (FILE *mbf);
151static int mbx_delimit_end (FILE *mbf);
e2ad23ef 152#endif
237e0016
RS
153
154/* Nonzero means this is name of a lock file to delete on fatal error. */
b23b5a5b 155static char *delete_lockname;
237e0016 156
e2f9d9af 157int
873fbd0b 158main (int argc, char **argv)
237e0016
RS
159{
160 char *inname, *outname;
161 int indesc, outdesc;
728a982d 162 ssize_t nread;
27d41fb4 163 int wait_status;
fea4325c 164 int c, preserve_mail = 0;
237e0016 165
63cf923d 166#ifndef MAIL_USE_SYSTEM_LOCK
237e0016 167 struct stat st;
237e0016 168 int tem;
529a133c 169 char *lockname;
906ad89d 170 char *tempname;
529a133c 171 size_t inname_dirlen;
237e0016 172 int desc;
63cf923d 173#endif /* not MAIL_USE_SYSTEM_LOCK */
237e0016 174
a4deff3c
RS
175#ifdef MAIL_USE_MAILLOCK
176 char *spool_name;
177#endif
178
a2997b0f
KH
179#ifdef MAIL_USE_POP
180 int pop_reverse_order = 0;
181# define ARGSTR "pr"
182#else /* ! MAIL_USE_POP */
183# define ARGSTR "p"
184#endif /* MAIL_USE_POP */
185
51a91f9d
CY
186 uid_t real_gid = getgid();
187 uid_t priv_gid = getegid();
188
9112a2a9
AI
189#ifdef WINDOWSNT
190 /* Ensure all file i/o is in binary mode. */
191 _fmode = _O_BINARY;
192#endif
193
237e0016
RS
194 delete_lockname = 0;
195
a2997b0f 196 while ((c = getopt (argc, argv, ARGSTR)) != EOF)
e2f9d9af 197 {
fea4325c 198 switch (c) {
a2997b0f
KH
199#ifdef MAIL_USE_POP
200 case 'r':
201 pop_reverse_order = 1;
202 break;
203#endif
fea4325c
RS
204 case 'p':
205 preserve_mail++;
206 break;
207 default:
65396510 208 exit (EXIT_FAILURE);
fea4325c
RS
209 }
210 }
211
212 if (
213#ifdef MAIL_USE_POP
214 (argc - optind < 2) || (argc - optind > 3)
215#else
216 (argc - optind != 2)
217#endif
218 )
219 {
fea4325c 220#ifdef MAIL_USE_POP
f213f2c0 221 fprintf (stderr, "Usage: movemail [-p] [-r] inbox destfile%s\n",
bb5618fe 222 " [POP-password]");
fea4325c 223#else
bb5618fe 224 fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n", "");
fea4325c 225#endif
65396510 226 exit (EXIT_FAILURE);
e2f9d9af 227 }
237e0016 228
fea4325c
RS
229 inname = argv[optind];
230 outname = argv[optind+1];
237e0016 231
4293ba7f
RS
232#ifdef MAIL_USE_MMDF
233 mmdf_init (argv[0]);
234#endif
235
af7bd34e 236 if (*outname == 0)
a9eedf40 237 fatal ("Destination file name is empty", 0, 0);
af7bd34e 238
237e0016 239#ifdef MAIL_USE_POP
12a0565a 240 if (!strncmp (inname, "po:", 3))
237e0016 241 {
b3112191 242 int status;
237e0016 243
fea4325c 244 status = popmail (inname + 3, outname, preserve_mail,
a2997b0f
KH
245 (argc - optind == 3) ? argv[optind+2] : NULL,
246 pop_reverse_order);
237e0016
RS
247 exit (status);
248 }
249
51a91f9d
CY
250 if (setuid (getuid ()) < 0)
251 fatal ("Failed to drop privileges", 0, 0);
252
237e0016
RS
253#endif /* MAIL_USE_POP */
254
7f75d5c6 255#ifndef DISABLE_DIRECT_ACCESS
4293ba7f 256#ifndef MAIL_USE_MMDF
63cf923d 257#ifndef MAIL_USE_SYSTEM_LOCK
a4deff3c
RS
258#ifdef MAIL_USE_MAILLOCK
259 spool_name = mail_spool_name (inname);
5a9c1e26
PE
260 if (spool_name)
261 {
262#ifdef lint
263 lockname = 0;
264#endif
265 }
266 else
a4deff3c 267#endif
237e0016 268 {
5ecec6a7
PE
269 #ifndef DIRECTORY_SEP
270 #define DIRECTORY_SEP '/'
271 #endif
272 #ifndef IS_DIRECTORY_SEP
273 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
274 #endif
275
a4deff3c
RS
276 /* Use a lock file named after our first argument with .lock appended:
277 If it exists, the mail file is locked. */
278 /* Note: this locking mechanism is *required* by the mailer
279 (on systems which use it) to prevent loss of mail.
280
281 On systems that use a lock file, extracting the mail without locking
282 WILL occasionally cause loss of mail due to timing errors!
283
284 So, if creation of the lock file fails
285 due to access permission on the mail spool directory,
286 you simply MUST change the permission
287 and/or make movemail a setgid program
288 so it can create lock files properly.
289
290 You might also wish to verify that your system is one
291 which uses lock files for this purpose. Some systems use other methods.
292
293 If your system uses the `flock' system call for mail locking,
294 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
295 and recompile movemail. If the s- file for your system
296 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
297 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
298
299 lockname = concat (inname, ".lock", "");
529a133c
PE
300 for (inname_dirlen = strlen (inname);
301 inname_dirlen && !IS_DIRECTORY_SEP (inname[inname_dirlen - 1]);
302 inname_dirlen--)
303 continue;
304 tempname = (char *) xmalloc (inname_dirlen + sizeof "EXXXXXX");
237e0016 305
a4deff3c 306 while (1)
237e0016 307 {
a4deff3c
RS
308 /* Create the lock file, but not under the lock file name. */
309 /* Give up if cannot do that. */
529a133c
PE
310
311 memcpy (tempname, inname, inname_dirlen);
312 strcpy (tempname + inname_dirlen, "EXXXXXX");
313#ifdef HAVE_MKSTEMP
314 desc = mkstemp (tempname);
315#else
316 mktemp (tempname);
317 if (!*tempname)
318 desc = -1;
319 else
320 {
321 unlink (tempname);
322 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0600);
323 }
324#endif
a4deff3c
RS
325 if (desc < 0)
326 {
529a133c 327 int mkstemp_errno = errno;
a4deff3c 328 char *message = (char *) xmalloc (strlen (tempname) + 50);
4b265472 329 sprintf (message, "creating %s, which would become the lock file",
a4deff3c 330 tempname);
529a133c 331 errno = mkstemp_errno;
a4deff3c
RS
332 pfatal_with_name (message);
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);
27d41fb4
PE
552 else if (WRETCODE (wait_status) != 0)
553 exit (WRETCODE (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
587 indir = (char *) xmalloc (fname - inname + 1);
588 strncpy (indir, inname, fname - inname);
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
RS
646}
647
237e0016
RS
648/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
649
1725ae55 650static char *
988e88ab 651concat (const char *s1, const char *s2, const char *s3)
237e0016 652{
728a982d 653 size_t len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
237e0016
RS
654 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
655
656 strcpy (result, s1);
657 strcpy (result + len1, s2);
658 strcpy (result + len1 + len2, s3);
659 *(result + len1 + len2 + len3) = 0;
660
661 return result;
662}
663
664/* Like malloc but get fatal error if memory is exhausted. */
665
1725ae55 666static long *
873fbd0b 667xmalloc (unsigned int size)
237e0016 668{
2583d6d7 669 long *result = (long *) malloc (size);
237e0016 670 if (!result)
a9eedf40 671 fatal ("virtual memory exhausted", 0, 0);
237e0016
RS
672 return result;
673}
674\f
675/* This is the guts of the interface to the Post Office Protocol. */
676
677#ifdef MAIL_USE_POP
678
7f75d5c6 679#ifndef WINDOWSNT
237e0016
RS
680#include <sys/socket.h>
681#include <netinet/in.h>
682#include <netdb.h>
7f75d5c6
RS
683#else
684#undef _WINSOCKAPI_
685#include <winsock.h>
686#endif
cecf0f21 687#include <pwd.h>
d228a23c 688#include <string.h>
237e0016 689
237e0016
RS
690#define NOTOK (-1)
691#define OK 0
237e0016 692
b23b5a5b 693static char Errmsg[200]; /* POP errors, at least, can exceed
752fb472 694 the original length of 80. */
237e0016 695
476b2799 696/*
4d90eee4 697 * The full valid syntax for a POP mailbox specification for movemail
476b2799
GM
698 * is "po:username:hostname". The ":hostname" is optional; if it is
699 * omitted, the MAILHOST environment variable will be consulted. Note
700 * that by the time popmail() is called the "po:" has been stripped
701 * off of the front of the mailbox name.
702 *
703 * If the mailbox is in the form "po:username:hostname", then it is
704 * modified by this function -- the second colon is replaced by a
705 * null.
65396510
TTN
706 *
707 * Return a value suitable for passing to `exit'.
476b2799
GM
708 */
709
1725ae55 710static int
873fbd0b 711popmail (char *mailbox, char *outfile, int preserve, char *password, int reverse_order)
237e0016 712{
b1ce62a8 713 int nmsgs, nbytes;
b1ce62a8
RS
714 register int i;
715 int mbfi;
716 FILE *mbf;
873fbd0b 717 char *getenv (const char *);
b32701a7 718 popserver server;
a2997b0f 719 int start, end, increment;
476b2799
GM
720 char *user, *hostname;
721
722 user = mailbox;
8966b757 723 if ((hostname = strchr (mailbox, ':')))
476b2799 724 *hostname++ = '\0';
237e0016 725
476b2799 726 server = pop_open (hostname, user, password, POP_NO_GETPASS);
2e82e3c3 727 if (! server)
b1ce62a8 728 {
cc3b64e8 729 error ("Error connecting to POP server: %s", pop_error, 0);
65396510 730 return EXIT_FAILURE;
237e0016
RS
731 }
732
2e82e3c3 733 if (pop_stat (server, &nmsgs, &nbytes))
b1ce62a8 734 {
cc3b64e8 735 error ("Error getting message count from POP server: %s", pop_error, 0);
65396510 736 return EXIT_FAILURE;
237e0016
RS
737 }
738
b1ce62a8
RS
739 if (!nmsgs)
740 {
2e82e3c3 741 pop_close (server);
65396510 742 return EXIT_SUCCESS;
b1ce62a8
RS
743 }
744
745 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
746 if (mbfi < 0)
747 {
2e82e3c3
RS
748 pop_close (server);
749 error ("Error in open: %s, %s", strerror (errno), outfile);
65396510 750 return EXIT_FAILURE;
b1ce62a8 751 }
f0939c31
PE
752
753 if (fchown (mbfi, getuid (), -1) != 0)
754 {
755 int fchown_errno = errno;
756 struct stat st;
757 if (fstat (mbfi, &st) != 0 || st.st_uid != getuid ())
758 {
759 pop_close (server);
760 error ("Error in fchown: %s, %s", strerror (fchown_errno), outfile);
761 return EXIT_FAILURE;
762 }
763 }
b1ce62a8 764
7f75d5c6 765 if ((mbf = fdopen (mbfi, "wb")) == NULL)
b1ce62a8 766 {
2e82e3c3 767 pop_close (server);
cc3b64e8 768 error ("Error in fdopen: %s", strerror (errno), 0);
2e82e3c3
RS
769 close (mbfi);
770 unlink (outfile);
65396510 771 return EXIT_FAILURE;
b1ce62a8
RS
772 }
773
a2997b0f
KH
774 if (reverse_order)
775 {
776 start = nmsgs;
777 end = 1;
778 increment = -1;
779 }
780 else
781 {
782 start = 1;
783 end = nmsgs;
784 increment = 1;
785 }
786
787 for (i = start; i * increment <= end * increment; i += increment)
b1ce62a8
RS
788 {
789 mbx_delimit_begin (mbf);
ff804ff5 790 if (pop_retr (server, i, mbf) != OK)
b1ce62a8 791 {
17a60964 792 error ("%s", Errmsg, 0);
b1ce62a8 793 close (mbfi);
65396510 794 return EXIT_FAILURE;
237e0016 795 }
b1ce62a8
RS
796 mbx_delimit_end (mbf);
797 fflush (mbf);
2e82e3c3
RS
798 if (ferror (mbf))
799 {
cc3b64e8 800 error ("Error in fflush: %s", strerror (errno), 0);
2e82e3c3
RS
801 pop_close (server);
802 close (mbfi);
65396510 803 return EXIT_FAILURE;
2e82e3c3 804 }
237e0016
RS
805 }
806
2e82e3c3
RS
807 /* On AFS, a call to write only modifies the file in the local
808 * workstation's AFS cache. The changes are not written to the server
809 * until a call to fsync or close is made. Users with AFS home
810 * directories have lost mail when over quota because these checks were
811 * not made in previous versions of movemail. */
812
e397a017 813#ifdef BSD_SYSTEM
cfa191ff
RS
814 if (fsync (mbfi) < 0)
815 {
08fa58c9 816 error ("Error in fsync: %s", strerror (errno), 0);
65396510 817 return EXIT_FAILURE;
cfa191ff 818 }
340ff9de 819#endif
cfa191ff
RS
820
821 if (close (mbfi) == -1)
822 {
cc3b64e8 823 error ("Error in close: %s", strerror (errno), 0);
65396510 824 return EXIT_FAILURE;
cfa191ff
RS
825 }
826
fea4325c
RS
827 if (! preserve)
828 for (i = 1; i <= nmsgs; i++)
829 {
830 if (pop_delete (server, i))
831 {
cc3b64e8 832 error ("Error from POP server: %s", pop_error, 0);
fea4325c 833 pop_close (server);
65396510 834 return EXIT_FAILURE;
fea4325c
RS
835 }
836 }
237e0016 837
2e82e3c3 838 if (pop_quit (server))
b1ce62a8 839 {
cc3b64e8 840 error ("Error from POP server: %s", pop_error, 0);
65396510 841 return EXIT_FAILURE;
237e0016 842 }
177c0ea7 843
65396510 844 return EXIT_SUCCESS;
237e0016
RS
845}
846
1725ae55 847static int
873fbd0b 848pop_retr (popserver server, int msgno, FILE *arg)
237e0016 849{
2e82e3c3
RS
850 char *line;
851 int ret;
237e0016 852
2e82e3c3 853 if (pop_retrieve_first (server, msgno, &line))
b1ce62a8 854 {
27d41fb4
PE
855 char *msg = concat ("Error from POP server: ", pop_error, "");
856 strncpy (Errmsg, msg, sizeof (Errmsg));
2e82e3c3 857 Errmsg[sizeof (Errmsg)-1] = '\0';
27d41fb4 858 free (msg);
2e82e3c3 859 return (NOTOK);
237e0016
RS
860 }
861
d89d0243 862 while ((ret = pop_retrieve_next (server, &line)) >= 0)
b1ce62a8 863 {
2e82e3c3
RS
864 if (! line)
865 break;
866
d89d0243 867 if (mbx_write (line, ret, arg) != OK)
b1ce62a8 868 {
2e82e3c3
RS
869 strcpy (Errmsg, strerror (errno));
870 pop_close (server);
871 return (NOTOK);
237e0016
RS
872 }
873 }
237e0016 874
2e82e3c3 875 if (ret)
b1ce62a8 876 {
27d41fb4
PE
877 char *msg = concat ("Error from POP server: ", pop_error, "");
878 strncpy (Errmsg, msg, sizeof (Errmsg));
2e82e3c3 879 Errmsg[sizeof (Errmsg)-1] = '\0';
27d41fb4 880 free (msg);
2e82e3c3 881 return (NOTOK);
237e0016
RS
882 }
883
2e82e3c3 884 return (OK);
237e0016
RS
885}
886
1725ae55 887static int
873fbd0b 888mbx_write (char *line, int len, FILE *mbf)
237e0016 889{
d04f5031 890#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
5ecec6a7
PE
891 /* Do this as a macro instead of using strcmp to save on execution time. */
892 # define IS_FROM_LINE(a) ((a[0] == 'F') \
893 && (a[1] == 'r') \
894 && (a[2] == 'o') \
895 && (a[3] == 'm') \
896 && (a[4] == ' '))
2e82e3c3
RS
897 if (IS_FROM_LINE (line))
898 {
899 if (fputc ('>', mbf) == EOF)
900 return (NOTOK);
901 }
d04f5031
PE
902#endif
903 if (line[0] == '\037')
904 {
905 if (fputs ("^_", mbf) == EOF)
906 return (NOTOK);
907 line++;
908 len--;
909 }
177c0ea7 910 if (fwrite (line, 1, len, mbf) != len)
2e82e3c3
RS
911 return (NOTOK);
912 if (fputc (0x0a, mbf) == EOF)
913 return (NOTOK);
914 return (OK);
237e0016
RS
915}
916
1725ae55 917static int
873fbd0b 918mbx_delimit_begin (FILE *mbf)
237e0016 919{
d228a23c
GM
920 time_t now;
921 struct tm *ltime;
922 char fromline[40] = "From movemail ";
923
924 now = time (NULL);
925 ltime = localtime (&now);
926
927 strcat (fromline, asctime (ltime));
928
929 if (fputs (fromline, mbf) == EOF)
2e82e3c3
RS
930 return (NOTOK);
931 return (OK);
237e0016
RS
932}
933
1725ae55 934static int
873fbd0b 935mbx_delimit_end (FILE *mbf)
237e0016 936{
3f32be22 937 if (putc ('\n', mbf) == EOF)
2e82e3c3
RS
938 return (NOTOK);
939 return (OK);
237e0016
RS
940}
941
942#endif /* MAIL_USE_POP */
e5f7ea68
RM
943\f
944#ifndef HAVE_STRERROR
945char *
946strerror (errnum)
947 int errnum;
948{
949 extern char *sys_errlist[];
950 extern int sys_nerr;
951
952 if (errnum >= 0 && errnum < sys_nerr)
953 return sys_errlist[errnum];
954 return (char *) "Unknown error";
955}
956
957#endif /* ! HAVE_STRERROR */
ab5796a9 958
65396510
TTN
959
960/* movemail.c ends here */