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