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