*** empty log message ***
[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.
4ee9629e 3 Copyright (C) 1986, 92, 93, 94, 96, 1999 Free Software Foundation, Inc.
237e0016
RS
4
5This file is part of GNU Emacs.
6
93320c23
JA
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
39356651 9the Free Software Foundation; either version 2, or (at your option)
93320c23
JA
10any later version.
11
237e0016 12GNU Emacs is distributed in the hope that it will be useful,
93320c23
JA
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
237e0016 16
93320c23
JA
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
19the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20Boston, MA 02111-1307, USA. */
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.
42 *
43 * New module: popmail.c
44 * Modified routines:
cfa191ff 45 * main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
237e0016
RS
46 * after POP code.
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
3b9ee819 57#define NO_SHORTNAMES /* Tell config not to load remap.h */
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>
8ca83cfd 64#include <../src/syswait.h>
fea4325c 65#include <getopt.h>
2e82e3c3
RS
66#ifdef MAIL_USE_POP
67#include "pop.h"
68#endif
237e0016 69
91cf09ac
RS
70#ifdef MSDOS
71#undef access
72#endif /* MSDOS */
73
61946d67
RS
74#ifndef DIRECTORY_SEP
75#define DIRECTORY_SEP '/'
76#endif
77#ifndef IS_DIRECTORY_SEP
78#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
79#endif
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
98/* Ensure all file i/o is in binary mode. */
99#include <fcntl.h>
100int _fmode = _O_BINARY;
7f75d5c6
RS
101#endif /* WINDOWSNT */
102
237e0016
RS
103#ifdef USG
104#include <fcntl.h>
105#include <unistd.h>
4ec9a77a
RS
106#ifndef F_OK
107#define F_OK 0
108#define X_OK 1
109#define W_OK 2
110#define R_OK 4
111#endif
237e0016
RS
112#endif /* USG */
113
29beb080
RS
114#ifdef HAVE_UNISTD_H
115#include <unistd.h>
116#endif
117
7f75d5c6 118#if defined (XENIX) || defined (WINDOWSNT)
237e0016
RS
119#include <sys/locking.h>
120#endif
121
63cf923d
RS
122#ifdef MAIL_USE_LOCKF
123#define MAIL_USE_SYSTEM_LOCK
124#endif
125
126#ifdef MAIL_USE_FLOCK
127#define MAIL_USE_SYSTEM_LOCK
128#endif
129
4293ba7f
RS
130#ifdef MAIL_USE_MMDF
131extern int lk_open (), lk_close ();
132#endif
133
a4deff3c
RS
134#if !defined (MAIL_USE_SYSTEM_LOCK) && !defined (MAIL_USE_MMDF) && \
135 defined (HAVE_LIBMAIL) && defined (HAVE_MAILLOCK_H)
136#include <maillock.h>
137/* We can't use maillock unless we know what directory system mail
138 files appear in. */
139#ifdef MAILDIR
140#define MAIL_USE_MAILLOCK
141static char *mail_spool_name ();
142#endif
143#endif
144
e97dd183 145#ifndef errno
237e0016 146extern int errno;
e97dd183 147#endif
e2f9d9af 148char *strerror ();
c4009c1f 149extern char *rindex ();
e2f9d9af
DM
150
151void fatal ();
152void error ();
153void pfatal_with_name ();
154void pfatal_and_delete ();
155char *concat ();
2583d6d7 156long *xmalloc ();
e2f9d9af
DM
157int popmail ();
158int pop_retr ();
159int mbx_write ();
160int mbx_delimit_begin ();
161int mbx_delimit_end ();
237e0016
RS
162
163/* Nonzero means this is name of a lock file to delete on fatal error. */
164char *delete_lockname;
165
e2f9d9af 166int
237e0016
RS
167main (argc, argv)
168 int argc;
169 char **argv;
170{
171 char *inname, *outname;
172 int indesc, outdesc;
237e0016 173 int nread;
8ca83cfd 174 WAITTYPE status;
fea4325c 175 int c, preserve_mail = 0;
237e0016 176
63cf923d 177#ifndef MAIL_USE_SYSTEM_LOCK
237e0016
RS
178 struct stat st;
179 long now;
180 int tem;
181 char *lockname, *p;
906ad89d 182 char *tempname;
237e0016 183 int desc;
63cf923d 184#endif /* not MAIL_USE_SYSTEM_LOCK */
237e0016 185
a4deff3c
RS
186#ifdef MAIL_USE_MAILLOCK
187 char *spool_name;
188#endif
189
a2997b0f
KH
190#ifdef MAIL_USE_POP
191 int pop_reverse_order = 0;
192# define ARGSTR "pr"
193#else /* ! MAIL_USE_POP */
194# define ARGSTR "p"
195#endif /* MAIL_USE_POP */
196
237e0016
RS
197 delete_lockname = 0;
198
a2997b0f 199 while ((c = getopt (argc, argv, ARGSTR)) != EOF)
e2f9d9af 200 {
fea4325c 201 switch (c) {
a2997b0f
KH
202#ifdef MAIL_USE_POP
203 case 'r':
204 pop_reverse_order = 1;
205 break;
206#endif
fea4325c
RS
207 case 'p':
208 preserve_mail++;
209 break;
210 default:
0d66b6f3 211 exit(1);
fea4325c
RS
212 }
213 }
214
215 if (
216#ifdef MAIL_USE_POP
217 (argc - optind < 2) || (argc - optind > 3)
218#else
219 (argc - optind != 2)
220#endif
221 )
222 {
0d66b6f3 223 fprintf (stderr, "Usage: movemail [-p] inbox destfile%s\n",
fea4325c 224#ifdef MAIL_USE_POP
0d66b6f3 225 " [POP-password]"
fea4325c 226#else
0d66b6f3 227 ""
fea4325c
RS
228#endif
229 );
a4deff3c 230 exit (1);
e2f9d9af 231 }
237e0016 232
fea4325c
RS
233 inname = argv[optind];
234 outname = argv[optind+1];
237e0016 235
4293ba7f
RS
236#ifdef MAIL_USE_MMDF
237 mmdf_init (argv[0]);
238#endif
239
af7bd34e
RS
240 if (*outname == 0)
241 fatal ("Destination file name is empty", 0);
242
b1ce62a8 243 /* Check access to output file. */
237e0016
RS
244 if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
245 pfatal_with_name (outname);
246
8e6208c5 247 /* Also check that outname's directory is writable to the real uid. */
237e0016 248 {
91cf09ac 249 char *buf = (char *) xmalloc (strlen (outname) + 1);
e2f9d9af 250 char *p;
237e0016
RS
251 strcpy (buf, outname);
252 p = buf + strlen (buf);
7f75d5c6 253 while (p > buf && !IS_DIRECTORY_SEP (p[-1]))
237e0016
RS
254 *--p = 0;
255 if (p == buf)
256 *p++ = '.';
257 if (access (buf, W_OK) != 0)
258 pfatal_with_name (buf);
259 free (buf);
260 }
261
262#ifdef MAIL_USE_POP
12a0565a 263 if (!strncmp (inname, "po:", 3))
237e0016 264 {
b3112191 265 int status;
237e0016 266
fea4325c 267 status = popmail (inname + 3, outname, preserve_mail,
a2997b0f
KH
268 (argc - optind == 3) ? argv[optind+2] : NULL,
269 pop_reverse_order);
237e0016
RS
270 exit (status);
271 }
272
cfa191ff 273 setuid (getuid ());
237e0016
RS
274#endif /* MAIL_USE_POP */
275
7f75d5c6
RS
276#ifndef DISABLE_DIRECT_ACCESS
277
b1ce62a8
RS
278 /* Check access to input file. */
279 if (access (inname, R_OK | W_OK) != 0)
280 pfatal_with_name (inname);
281
4293ba7f 282#ifndef MAIL_USE_MMDF
63cf923d 283#ifndef MAIL_USE_SYSTEM_LOCK
a4deff3c
RS
284#ifdef MAIL_USE_MAILLOCK
285 spool_name = mail_spool_name (inname);
286 if (! spool_name)
287#endif
237e0016 288 {
a4deff3c
RS
289 /* Use a lock file named after our first argument with .lock appended:
290 If it exists, the mail file is locked. */
291 /* Note: this locking mechanism is *required* by the mailer
292 (on systems which use it) to prevent loss of mail.
293
294 On systems that use a lock file, extracting the mail without locking
295 WILL occasionally cause loss of mail due to timing errors!
296
297 So, if creation of the lock file fails
298 due to access permission on the mail spool directory,
299 you simply MUST change the permission
300 and/or make movemail a setgid program
301 so it can create lock files properly.
302
303 You might also wish to verify that your system is one
304 which uses lock files for this purpose. Some systems use other methods.
305
306 If your system uses the `flock' system call for mail locking,
307 define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
308 and recompile movemail. If the s- file for your system
309 should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
310 to bug-gnu-emacs@prep.ai.mit.edu so we can fix it. */
311
312 lockname = concat (inname, ".lock", "");
313 tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
314 strcpy (tempname, inname);
315 p = tempname + strlen (tempname);
316 while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
317 p--;
318 *p = 0;
319 strcpy (p, "EXXXXXX");
320 mktemp (tempname);
cfa191ff 321 unlink (tempname);
237e0016 322
a4deff3c 323 while (1)
237e0016 324 {
a4deff3c
RS
325 /* Create the lock file, but not under the lock file name. */
326 /* Give up if cannot do that. */
327 desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
328 if (desc < 0)
329 {
330 char *message = (char *) xmalloc (strlen (tempname) + 50);
4b265472 331 sprintf (message, "creating %s, which would become the lock file",
a4deff3c
RS
332 tempname);
333 pfatal_with_name (message);
334 }
335 close (desc);
336
337 tem = link (tempname, lockname);
338 unlink (tempname);
339 if (tem >= 0)
340 break;
341 sleep (1);
342
343 /* If lock file is five minutes old, unlock it.
344 Five minutes should be good enough to cope with crashes
345 and wedgitude, and long enough to avoid being fooled
346 by time differences between machines. */
347 if (stat (lockname, &st) >= 0)
348 {
349 now = time (0);
350 if (st.st_ctime < now - 300)
351 unlink (lockname);
352 }
237e0016 353 }
237e0016 354
a4deff3c
RS
355 delete_lockname = lockname;
356 }
63cf923d
RS
357#endif /* not MAIL_USE_SYSTEM_LOCK */
358#endif /* not MAIL_USE_MMDF */
237e0016 359
8ca83cfd
RS
360 if (fork () == 0)
361 {
25025815 362 int lockcount = 0;
a4deff3c
RS
363 int status = 0;
364#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
365 long touched_lock, now;
366#endif
25025815 367
d5216c20 368 setuid (getuid ());
8ca83cfd 369
63cf923d
RS
370#ifndef MAIL_USE_MMDF
371#ifdef MAIL_USE_SYSTEM_LOCK
8ca83cfd 372 indesc = open (inname, O_RDWR);
63cf923d 373#else /* if not MAIL_USE_SYSTEM_LOCK */
8ca83cfd 374 indesc = open (inname, O_RDONLY);
63cf923d 375#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd
RS
376#else /* MAIL_USE_MMDF */
377 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
4293ba7f
RS
378#endif /* MAIL_USE_MMDF */
379
8ca83cfd
RS
380 if (indesc < 0)
381 pfatal_with_name (inname);
237e0016 382
e397a017 383#if defined (BSD_SYSTEM) || defined (XENIX)
8ca83cfd
RS
384 /* In case movemail is setuid to root, make sure the user can
385 read the output file. */
386 /* This is desirable for all systems
387 but I don't want to assume all have the umask system call */
388 umask (umask (0) & 0333);
e397a017 389#endif /* BSD_SYSTEM || XENIX */
8ca83cfd
RS
390 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
391 if (outdesc < 0)
392 pfatal_with_name (outname);
25025815
RS
393
394 /* This label exists so we can retry locking
395 after a delay, if it got EAGAIN or EBUSY. */
396 retry_lock:
397
398 /* Try to lock it. */
a4deff3c
RS
399#ifdef MAIL_USE_MAILLOCK
400 if (spool_name)
401 {
402 /* The "0 - " is to make it a negative number if maillock returns
403 non-zero. */
404 status = 0 - maillock (spool_name, 1);
405#ifdef HAVE_TOUCHLOCK
406 touched_lock = time (0);
407#endif
408 lockcount = 5;
409 }
410 else
411#endif /* MAIL_USE_MAILLOCK */
412 {
63cf923d
RS
413#ifdef MAIL_USE_SYSTEM_LOCK
414#ifdef MAIL_USE_LOCKF
a4deff3c 415 status = lockf (indesc, F_LOCK, 0);
63cf923d 416#else /* not MAIL_USE_LOCKF */
237e0016 417#ifdef XENIX
a4deff3c 418 status = locking (indesc, LK_RLCK, 0L);
7f75d5c6
RS
419#else
420#ifdef WINDOWSNT
a4deff3c 421 status = locking (indesc, LK_RLCK, -1L);
237e0016 422#else
a4deff3c 423 status = flock (indesc, LOCK_EX);
237e0016 424#endif
7f75d5c6 425#endif
63cf923d
RS
426#endif /* not MAIL_USE_LOCKF */
427#endif /* MAIL_USE_SYSTEM_LOCK */
a4deff3c 428 }
237e0016 429
25025815
RS
430 /* If it fails, retry up to 5 times
431 for certain failure codes. */
432 if (status < 0)
433 {
434 if (++lockcount <= 5)
435 {
436#ifdef EAGAIN
437 if (errno == EAGAIN)
438 {
439 sleep (1);
440 goto retry_lock;
441 }
442#endif
443#ifdef EBUSY
444 if (errno == EBUSY)
445 {
446 sleep (1);
447 goto retry_lock;
448 }
449#endif
450 }
451
452 pfatal_with_name (inname);
453 }
454
08564963 455 {
8ca83cfd
RS
456 char buf[1024];
457
458 while (1)
08564963 459 {
8ca83cfd
RS
460 nread = read (indesc, buf, sizeof buf);
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 {
473 now = time (0);
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
8ca83cfd
RS
489 /* Check to make sure no errors before we zap the inbox. */
490 if (close (outdesc) != 0)
491 pfatal_and_delete (outname);
237e0016 492
63cf923d 493#ifdef MAIL_USE_SYSTEM_LOCK
fea4325c
RS
494 if (! preserve_mail)
495 {
3288d108 496#if defined (STRIDE) || defined (XENIX)
fea4325c
RS
497 /* Stride, xenix have file locking, but no ftruncate.
498 This mess will do. */
499 close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
237e0016 500#else
fea4325c 501 ftruncate (indesc, 0L);
237e0016 502#endif /* STRIDE or XENIX */
b1cb2966 503 }
63cf923d 504#endif /* MAIL_USE_SYSTEM_LOCK */
4293ba7f
RS
505
506#ifdef MAIL_USE_MMDF
8ca83cfd 507 lk_close (indesc, 0, 0, 0);
4293ba7f 508#else
8ca83cfd 509 close (indesc);
4293ba7f 510#endif
237e0016 511
63cf923d 512#ifndef MAIL_USE_SYSTEM_LOCK
fea4325c
RS
513 if (! preserve_mail)
514 {
515 /* Delete the input file; if we can't, at least get rid of its
516 contents. */
e97dd183 517#ifdef MAIL_UNLINK_SPOOL
fea4325c
RS
518 /* This is generally bad to do, because it destroys the permissions
519 that were set on the file. Better to just empty the file. */
520 if (unlink (inname) < 0 && errno != ENOENT)
e97dd183 521#endif /* MAIL_UNLINK_SPOOL */
fea4325c
RS
522 creat (inname, 0600);
523 }
63cf923d 524#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd 525
a4deff3c
RS
526#ifdef MAIL_USE_MAILLOCK
527 /* This has to occur in the child, i.e., in the process that
528 acquired the lock! */
529 if (spool_name)
530 mailunlock ();
531#endif
8ca83cfd
RS
532 exit (0);
533 }
534
535 wait (&status);
536 if (!WIFEXITED (status))
537 exit (1);
538 else if (WRETCODE (status) != 0)
539 exit (WRETCODE (status));
540
63cf923d 541#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
a4deff3c
RS
542#ifdef MAIL_USE_MAILLOCK
543 if (! spool_name)
544#endif /* MAIL_USE_MAILLOCK */
545 unlink (lockname);
63cf923d 546#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
7f75d5c6
RS
547
548#endif /* ! DISABLE_DIRECT_ACCESS */
549
e2f9d9af 550 return 0;
237e0016 551}
a4deff3c
RS
552
553#ifdef MAIL_USE_MAILLOCK
554/* This function uses stat to confirm that the mail directory is
555 identical to the directory of the input file, rather than just
556 string-comparing the two paths, because one or both of them might
557 be symbolic links pointing to some other directory. */
558static char *
559mail_spool_name (inname)
560 char *inname;
561{
562 struct stat stat1, stat2;
563 char *indir, *fname;
564 int status;
565
566 if (! (fname = rindex (inname, '/')))
567 return NULL;
568
569 fname++;
570
571 if (stat (MAILDIR, &stat1) < 0)
572 return NULL;
573
574 indir = (char *) xmalloc (fname - inname + 1);
575 strncpy (indir, inname, fname - inname);
576 indir[fname-inname] = '\0';
577
578
579 status = stat (indir, &stat2);
580
581 free (indir);
582
583 if (status < 0)
584 return NULL;
585
c4009c1f
RS
586 if (stat1.st_dev == stat2.st_dev
587 && stat1.st_ino == stat2.st_ino)
a4deff3c
RS
588 return fname;
589
590 return NULL;
591}
592#endif /* MAIL_USE_MAILLOCK */
237e0016
RS
593\f
594/* Print error message and exit. */
595
e2f9d9af 596void
237e0016
RS
597fatal (s1, s2)
598 char *s1, *s2;
599{
600 if (delete_lockname)
601 unlink (delete_lockname);
602 error (s1, s2);
603 exit (1);
604}
605
606/* Print error message. `s1' is printf control string, `s2' is arg for it. */
607
e2f9d9af 608void
b1ce62a8
RS
609error (s1, s2, s3)
610 char *s1, *s2, *s3;
237e0016 611{
e2f9d9af
DM
612 fprintf (stderr, "movemail: ");
613 fprintf (stderr, s1, s2, s3);
614 fprintf (stderr, "\n");
237e0016
RS
615}
616
e2f9d9af 617void
237e0016
RS
618pfatal_with_name (name)
619 char *name;
620{
e2f9d9af 621 char *s = concat ("", strerror (errno), " for %s");
237e0016
RS
622 fatal (s, name);
623}
624
e2f9d9af 625void
cfa191ff
RS
626pfatal_and_delete (name)
627 char *name;
628{
e2f9d9af 629 char *s = concat ("", strerror (errno), " for %s");
cfa191ff
RS
630 unlink (name);
631 fatal (s, name);
632}
633
237e0016
RS
634/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
635
636char *
637concat (s1, s2, s3)
638 char *s1, *s2, *s3;
639{
640 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
641 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
642
643 strcpy (result, s1);
644 strcpy (result + len1, s2);
645 strcpy (result + len1 + len2, s3);
646 *(result + len1 + len2 + len3) = 0;
647
648 return result;
649}
650
651/* Like malloc but get fatal error if memory is exhausted. */
652
2583d6d7 653long *
237e0016 654xmalloc (size)
e97dd183 655 unsigned size;
237e0016 656{
2583d6d7 657 long *result = (long *) malloc (size);
237e0016
RS
658 if (!result)
659 fatal ("virtual memory exhausted", 0);
660 return result;
661}
662\f
663/* This is the guts of the interface to the Post Office Protocol. */
664
665#ifdef MAIL_USE_POP
666
7f75d5c6 667#ifndef WINDOWSNT
237e0016
RS
668#include <sys/socket.h>
669#include <netinet/in.h>
670#include <netdb.h>
7f75d5c6
RS
671#else
672#undef _WINSOCKAPI_
673#include <winsock.h>
674#endif
cecf0f21 675#include <pwd.h>
237e0016 676
237e0016
RS
677#define NOTOK (-1)
678#define OK 0
679#define DONE 1
680
681char *progname;
682FILE *sfi;
683FILE *sfo;
2e82e3c3
RS
684char ibuffer[BUFSIZ];
685char obuffer[BUFSIZ];
752fb472
DL
686char Errmsg[200]; /* POP errors, at least, can exceed
687 the original length of 80. */
237e0016 688
476b2799
GM
689/*
690 * The full legal syntax for a POP mailbox specification for movemail
691 * is "po:username:hostname". The ":hostname" is optional; if it is
692 * omitted, the MAILHOST environment variable will be consulted. Note
693 * that by the time popmail() is called the "po:" has been stripped
694 * off of the front of the mailbox name.
695 *
696 * If the mailbox is in the form "po:username:hostname", then it is
697 * modified by this function -- the second colon is replaced by a
698 * null.
699 */
700
701popmail (mailbox, outfile, preserve, password, reverse_order)
702 char *mailbox;
b1ce62a8 703 char *outfile;
fea4325c 704 int preserve;
7f75d5c6 705 char *password;
a2997b0f 706 int reverse_order;
237e0016 707{
b1ce62a8 708 int nmsgs, nbytes;
b1ce62a8
RS
709 register int i;
710 int mbfi;
711 FILE *mbf;
2e82e3c3 712 char *getenv ();
b32701a7 713 popserver server;
a2997b0f 714 int start, end, increment;
476b2799
GM
715 char *user, *hostname;
716
717 user = mailbox;
718 if ((hostname = index(mailbox, ':')))
719 *hostname++ = '\0';
237e0016 720
476b2799 721 server = pop_open (hostname, user, password, POP_NO_GETPASS);
2e82e3c3 722 if (! server)
b1ce62a8 723 {
9b956af2 724 error ("Error connecting to POP server: %s", pop_error);
2e82e3c3 725 return (1);
237e0016
RS
726 }
727
2e82e3c3 728 if (pop_stat (server, &nmsgs, &nbytes))
b1ce62a8 729 {
9b956af2 730 error ("Error getting message count from POP server: %s", pop_error);
2e82e3c3 731 return (1);
237e0016
RS
732 }
733
b1ce62a8
RS
734 if (!nmsgs)
735 {
2e82e3c3
RS
736 pop_close (server);
737 return (0);
b1ce62a8
RS
738 }
739
740 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
741 if (mbfi < 0)
742 {
2e82e3c3
RS
743 pop_close (server);
744 error ("Error in open: %s, %s", strerror (errno), outfile);
745 return (1);
b1ce62a8
RS
746 }
747 fchown (mbfi, getuid (), -1);
748
7f75d5c6 749 if ((mbf = fdopen (mbfi, "wb")) == NULL)
b1ce62a8 750 {
2e82e3c3
RS
751 pop_close (server);
752 error ("Error in fdopen: %s", strerror (errno));
753 close (mbfi);
754 unlink (outfile);
755 return (1);
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 {
2e82e3c3 776 error (Errmsg);
b1ce62a8 777 close (mbfi);
2e82e3c3 778 return (1);
237e0016 779 }
b1ce62a8
RS
780 mbx_delimit_end (mbf);
781 fflush (mbf);
2e82e3c3
RS
782 if (ferror (mbf))
783 {
784 error ("Error in fflush: %s", strerror (errno));
785 pop_close (server);
786 close (mbfi);
787 return (1);
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 {
2e82e3c3
RS
800 error ("Error in fsync: %s", strerror (errno));
801 return (1);
cfa191ff 802 }
340ff9de 803#endif
cfa191ff
RS
804
805 if (close (mbfi) == -1)
806 {
2e82e3c3
RS
807 error ("Error in close: %s", strerror (errno));
808 return (1);
cfa191ff
RS
809 }
810
fea4325c
RS
811 if (! preserve)
812 for (i = 1; i <= nmsgs; i++)
813 {
814 if (pop_delete (server, i))
815 {
9b956af2 816 error ("Error from POP server: %s", pop_error);
fea4325c
RS
817 pop_close (server);
818 return (1);
819 }
820 }
237e0016 821
2e82e3c3 822 if (pop_quit (server))
b1ce62a8 823 {
9b956af2 824 error ("Error from POP server: %s", pop_error);
2e82e3c3 825 return (1);
237e0016 826 }
237e0016 827
2e82e3c3 828 return (0);
237e0016
RS
829}
830
ff804ff5
KH
831int
832pop_retr (server, msgno, arg)
b32701a7 833 popserver server;
ff804ff5 834 FILE *arg;
237e0016 835{
2e82e3c3
RS
836 extern char *strerror ();
837 char *line;
838 int ret;
237e0016 839
2e82e3c3 840 if (pop_retrieve_first (server, msgno, &line))
b1ce62a8 841 {
9b956af2
KH
842 char *error = concat ("Error from POP server: ", pop_error, "");
843 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 844 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 845 free(error);
2e82e3c3 846 return (NOTOK);
237e0016
RS
847 }
848
d89d0243 849 while ((ret = pop_retrieve_next (server, &line)) >= 0)
b1ce62a8 850 {
2e82e3c3
RS
851 if (! line)
852 break;
853
d89d0243 854 if (mbx_write (line, ret, arg) != OK)
b1ce62a8 855 {
2e82e3c3
RS
856 strcpy (Errmsg, strerror (errno));
857 pop_close (server);
858 return (NOTOK);
237e0016
RS
859 }
860 }
237e0016 861
2e82e3c3 862 if (ret)
b1ce62a8 863 {
9b956af2
KH
864 char *error = concat ("Error from POP server: ", pop_error, "");
865 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 866 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 867 free(error);
2e82e3c3 868 return (NOTOK);
237e0016
RS
869 }
870
2e82e3c3 871 return (OK);
237e0016
RS
872}
873
2e82e3c3
RS
874/* Do this as a macro instead of using strcmp to save on execution time. */
875#define IS_FROM_LINE(a) ((a[0] == 'F') \
876 && (a[1] == 'r') \
877 && (a[2] == 'o') \
878 && (a[3] == 'm') \
879 && (a[4] == ' '))
237e0016 880
2e82e3c3 881int
d89d0243 882mbx_write (line, len, mbf)
b1ce62a8 883 char *line;
d89d0243 884 int len;
b1ce62a8 885 FILE *mbf;
237e0016 886{
d04f5031 887#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
2e82e3c3
RS
888 if (IS_FROM_LINE (line))
889 {
890 if (fputc ('>', mbf) == EOF)
891 return (NOTOK);
892 }
d04f5031
PE
893#endif
894 if (line[0] == '\037')
895 {
896 if (fputs ("^_", mbf) == EOF)
897 return (NOTOK);
898 line++;
899 len--;
900 }
d89d0243 901 if (fwrite (line, 1, len, mbf) != len)
2e82e3c3
RS
902 return (NOTOK);
903 if (fputc (0x0a, mbf) == EOF)
904 return (NOTOK);
905 return (OK);
237e0016
RS
906}
907
2e82e3c3 908int
b1ce62a8
RS
909mbx_delimit_begin (mbf)
910 FILE *mbf;
237e0016 911{
2e82e3c3
RS
912 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
913 return (NOTOK);
914 return (OK);
237e0016
RS
915}
916
b1ce62a8
RS
917mbx_delimit_end (mbf)
918 FILE *mbf;
237e0016 919{
2e82e3c3
RS
920 if (putc ('\037', mbf) == EOF)
921 return (NOTOK);
922 return (OK);
237e0016
RS
923}
924
925#endif /* MAIL_USE_POP */
e5f7ea68
RM
926\f
927#ifndef HAVE_STRERROR
928char *
929strerror (errnum)
930 int errnum;
931{
932 extern char *sys_errlist[];
933 extern int sys_nerr;
934
935 if (errnum >= 0 && errnum < sys_nerr)
936 return sys_errlist[errnum];
937 return (char *) "Unknown error";
938}
939
940#endif /* ! HAVE_STRERROR */