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