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