(mbx_delimit_begin): Also write the current time.
[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,
f5d0ac07 4 2005, 2006, 2007, 2008, 2009 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;
bba104c1 176 int 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);
097e9c90
CY
344
345#ifdef EPERM
346 if (tem < 0 && errno == EPERM)
347 fatal ("Unable to create hard link between %s and %s",
348 tempname, lockname);
349#endif
350
a4deff3c
RS
351 unlink (tempname);
352 if (tem >= 0)
353 break;
354 sleep (1);
355
356 /* If lock file is five minutes old, unlock it.
357 Five minutes should be good enough to cope with crashes
358 and wedgitude, and long enough to avoid being fooled
359 by time differences between machines. */
360 if (stat (lockname, &st) >= 0)
361 {
362 now = time (0);
363 if (st.st_ctime < now - 300)
364 unlink (lockname);
365 }
237e0016 366 }
237e0016 367
a4deff3c
RS
368 delete_lockname = lockname;
369 }
63cf923d
RS
370#endif /* not MAIL_USE_SYSTEM_LOCK */
371#endif /* not MAIL_USE_MMDF */
237e0016 372
8ca83cfd
RS
373 if (fork () == 0)
374 {
25025815 375 int lockcount = 0;
a4deff3c
RS
376 int status = 0;
377#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
378 long touched_lock, now;
379#endif
25025815 380
d5216c20 381 setuid (getuid ());
8ca83cfd 382
63cf923d
RS
383#ifndef MAIL_USE_MMDF
384#ifdef MAIL_USE_SYSTEM_LOCK
8ca83cfd 385 indesc = open (inname, O_RDWR);
63cf923d 386#else /* if not MAIL_USE_SYSTEM_LOCK */
8ca83cfd 387 indesc = open (inname, O_RDONLY);
63cf923d 388#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd
RS
389#else /* MAIL_USE_MMDF */
390 indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
4293ba7f
RS
391#endif /* MAIL_USE_MMDF */
392
8ca83cfd
RS
393 if (indesc < 0)
394 pfatal_with_name (inname);
237e0016 395
76ed5e01 396#ifdef BSD_SYSTEM
8ca83cfd
RS
397 /* In case movemail is setuid to root, make sure the user can
398 read the output file. */
399 /* This is desirable for all systems
400 but I don't want to assume all have the umask system call */
401 umask (umask (0) & 0333);
76ed5e01 402#endif /* BSD_SYSTEM */
8ca83cfd
RS
403 outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
404 if (outdesc < 0)
405 pfatal_with_name (outname);
25025815
RS
406
407 /* This label exists so we can retry locking
408 after a delay, if it got EAGAIN or EBUSY. */
409 retry_lock:
410
411 /* Try to lock it. */
a4deff3c
RS
412#ifdef MAIL_USE_MAILLOCK
413 if (spool_name)
414 {
415 /* The "0 - " is to make it a negative number if maillock returns
416 non-zero. */
417 status = 0 - maillock (spool_name, 1);
418#ifdef HAVE_TOUCHLOCK
419 touched_lock = time (0);
420#endif
421 lockcount = 5;
422 }
423 else
424#endif /* MAIL_USE_MAILLOCK */
425 {
63cf923d
RS
426#ifdef MAIL_USE_SYSTEM_LOCK
427#ifdef MAIL_USE_LOCKF
a4deff3c 428 status = lockf (indesc, F_LOCK, 0);
63cf923d 429#else /* not MAIL_USE_LOCKF */
7f75d5c6 430#ifdef WINDOWSNT
a4deff3c 431 status = locking (indesc, LK_RLCK, -1L);
237e0016 432#else
a4deff3c 433 status = flock (indesc, LOCK_EX);
237e0016 434#endif
63cf923d
RS
435#endif /* not MAIL_USE_LOCKF */
436#endif /* MAIL_USE_SYSTEM_LOCK */
a4deff3c 437 }
237e0016 438
25025815
RS
439 /* If it fails, retry up to 5 times
440 for certain failure codes. */
441 if (status < 0)
442 {
443 if (++lockcount <= 5)
444 {
445#ifdef EAGAIN
446 if (errno == EAGAIN)
447 {
448 sleep (1);
449 goto retry_lock;
450 }
451#endif
452#ifdef EBUSY
453 if (errno == EBUSY)
454 {
455 sleep (1);
456 goto retry_lock;
457 }
458#endif
459 }
460
461 pfatal_with_name (inname);
462 }
177c0ea7 463
08564963 464 {
8ca83cfd
RS
465 char buf[1024];
466
467 while (1)
08564963 468 {
8ca83cfd 469 nread = read (indesc, buf, sizeof buf);
5e5b35c7
RS
470 if (nread < 0)
471 pfatal_with_name (inname);
8ca83cfd
RS
472 if (nread != write (outdesc, buf, nread))
473 {
474 int saved_errno = errno;
475 unlink (outname);
476 errno = saved_errno;
477 pfatal_with_name (outname);
478 }
479 if (nread < sizeof buf)
480 break;
a4deff3c
RS
481#if defined (MAIL_USE_MAILLOCK) && defined (HAVE_TOUCHLOCK)
482 if (spool_name)
483 {
484 now = time (0);
485 if (now - touched_lock > 60)
486 {
487 touchlock ();
488 touched_lock = now;
489 }
490 }
491#endif /* MAIL_USE_MAILLOCK */
08564963 492 }
08564963 493 }
237e0016 494
e397a017 495#ifdef BSD_SYSTEM
8ca83cfd
RS
496 if (fsync (outdesc) < 0)
497 pfatal_and_delete (outname);
237e0016
RS
498#endif
499
8ca83cfd
RS
500 /* Check to make sure no errors before we zap the inbox. */
501 if (close (outdesc) != 0)
502 pfatal_and_delete (outname);
237e0016 503
63cf923d 504#ifdef MAIL_USE_SYSTEM_LOCK
fea4325c
RS
505 if (! preserve_mail)
506 {
fea4325c 507 ftruncate (indesc, 0L);
b1cb2966 508 }
63cf923d 509#endif /* MAIL_USE_SYSTEM_LOCK */
4293ba7f
RS
510
511#ifdef MAIL_USE_MMDF
8ca83cfd 512 lk_close (indesc, 0, 0, 0);
4293ba7f 513#else
8ca83cfd 514 close (indesc);
4293ba7f 515#endif
237e0016 516
63cf923d 517#ifndef MAIL_USE_SYSTEM_LOCK
fea4325c
RS
518 if (! preserve_mail)
519 {
520 /* Delete the input file; if we can't, at least get rid of its
521 contents. */
e97dd183 522#ifdef MAIL_UNLINK_SPOOL
fea4325c
RS
523 /* This is generally bad to do, because it destroys the permissions
524 that were set on the file. Better to just empty the file. */
525 if (unlink (inname) < 0 && errno != ENOENT)
e97dd183 526#endif /* MAIL_UNLINK_SPOOL */
fea4325c
RS
527 creat (inname, 0600);
528 }
63cf923d 529#endif /* not MAIL_USE_SYSTEM_LOCK */
8ca83cfd 530
a4deff3c
RS
531#ifdef MAIL_USE_MAILLOCK
532 /* This has to occur in the child, i.e., in the process that
533 acquired the lock! */
534 if (spool_name)
535 mailunlock ();
536#endif
65396510 537 exit (EXIT_SUCCESS);
8ca83cfd
RS
538 }
539
540 wait (&status);
541 if (!WIFEXITED (status))
65396510 542 exit (EXIT_FAILURE);
8ca83cfd
RS
543 else if (WRETCODE (status) != 0)
544 exit (WRETCODE (status));
545
63cf923d 546#if !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
a4deff3c
RS
547#ifdef MAIL_USE_MAILLOCK
548 if (! spool_name)
549#endif /* MAIL_USE_MAILLOCK */
550 unlink (lockname);
63cf923d 551#endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
7f75d5c6
RS
552
553#endif /* ! DISABLE_DIRECT_ACCESS */
554
65396510 555 return EXIT_SUCCESS;
237e0016 556}
a4deff3c
RS
557
558#ifdef MAIL_USE_MAILLOCK
559/* This function uses stat to confirm that the mail directory is
560 identical to the directory of the input file, rather than just
561 string-comparing the two paths, because one or both of them might
562 be symbolic links pointing to some other directory. */
563static char *
564mail_spool_name (inname)
565 char *inname;
566{
567 struct stat stat1, stat2;
568 char *indir, *fname;
569 int status;
570
571 if (! (fname = rindex (inname, '/')))
572 return NULL;
573
574 fname++;
575
576 if (stat (MAILDIR, &stat1) < 0)
577 return NULL;
578
579 indir = (char *) xmalloc (fname - inname + 1);
580 strncpy (indir, inname, fname - inname);
581 indir[fname-inname] = '\0';
582
583
584 status = stat (indir, &stat2);
585
586 free (indir);
587
588 if (status < 0)
589 return NULL;
590
c4009c1f
RS
591 if (stat1.st_dev == stat2.st_dev
592 && stat1.st_ino == stat2.st_ino)
a4deff3c
RS
593 return fname;
594
595 return NULL;
596}
597#endif /* MAIL_USE_MAILLOCK */
237e0016
RS
598\f
599/* Print error message and exit. */
600
e2f9d9af 601void
a9eedf40
AS
602fatal (s1, s2, s3)
603 char *s1, *s2, *s3;
237e0016
RS
604{
605 if (delete_lockname)
606 unlink (delete_lockname);
a9eedf40 607 error (s1, s2, s3);
65396510 608 exit (EXIT_FAILURE);
237e0016
RS
609}
610
cc3b64e8
DL
611/* Print error message. `s1' is printf control string, `s2' and `s3'
612 are args for it or null. */
237e0016 613
e2f9d9af 614void
b1ce62a8
RS
615error (s1, s2, s3)
616 char *s1, *s2, *s3;
237e0016 617{
e2f9d9af 618 fprintf (stderr, "movemail: ");
cc3b64e8
DL
619 if (s3)
620 fprintf (stderr, s1, s2, s3);
621 else if (s2)
622 fprintf (stderr, s1, s2);
623 else
624 fprintf (stderr, s1);
e2f9d9af 625 fprintf (stderr, "\n");
237e0016
RS
626}
627
e2f9d9af 628void
237e0016
RS
629pfatal_with_name (name)
630 char *name;
631{
a9eedf40 632 fatal ("%s for %s", strerror (errno), name);
237e0016
RS
633}
634
e2f9d9af 635void
cfa191ff
RS
636pfatal_and_delete (name)
637 char *name;
638{
a9eedf40 639 char *s = strerror (errno);
cfa191ff 640 unlink (name);
a9eedf40 641 fatal ("%s for %s", s, name);
cfa191ff
RS
642}
643
237e0016
RS
644/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */
645
646char *
647concat (s1, s2, s3)
648 char *s1, *s2, *s3;
649{
650 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
651 char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
652
653 strcpy (result, s1);
654 strcpy (result + len1, s2);
655 strcpy (result + len1 + len2, s3);
656 *(result + len1 + len2 + len3) = 0;
657
658 return result;
659}
660
661/* Like malloc but get fatal error if memory is exhausted. */
662
2583d6d7 663long *
237e0016 664xmalloc (size)
e97dd183 665 unsigned size;
237e0016 666{
2583d6d7 667 long *result = (long *) malloc (size);
237e0016 668 if (!result)
a9eedf40 669 fatal ("virtual memory exhausted", 0, 0);
237e0016
RS
670 return result;
671}
672\f
673/* This is the guts of the interface to the Post Office Protocol. */
674
675#ifdef MAIL_USE_POP
676
7f75d5c6 677#ifndef WINDOWSNT
237e0016
RS
678#include <sys/socket.h>
679#include <netinet/in.h>
680#include <netdb.h>
7f75d5c6
RS
681#else
682#undef _WINSOCKAPI_
683#include <winsock.h>
684#endif
cecf0f21 685#include <pwd.h>
d228a23c
GM
686#include <string.h>
687#include <time.h>
237e0016 688
237e0016
RS
689#define NOTOK (-1)
690#define OK 0
691#define DONE 1
692
693char *progname;
694FILE *sfi;
695FILE *sfo;
2e82e3c3
RS
696char ibuffer[BUFSIZ];
697char obuffer[BUFSIZ];
752fb472
DL
698char Errmsg[200]; /* POP errors, at least, can exceed
699 the original length of 80. */
237e0016 700
476b2799 701/*
4d90eee4 702 * The full valid syntax for a POP mailbox specification for movemail
476b2799
GM
703 * is "po:username:hostname". The ":hostname" is optional; if it is
704 * omitted, the MAILHOST environment variable will be consulted. Note
705 * that by the time popmail() is called the "po:" has been stripped
706 * off of the front of the mailbox name.
707 *
708 * If the mailbox is in the form "po:username:hostname", then it is
709 * modified by this function -- the second colon is replaced by a
710 * null.
65396510
TTN
711 *
712 * Return a value suitable for passing to `exit'.
476b2799
GM
713 */
714
cc3b64e8 715int
476b2799
GM
716popmail (mailbox, outfile, preserve, password, reverse_order)
717 char *mailbox;
b1ce62a8 718 char *outfile;
fea4325c 719 int preserve;
7f75d5c6 720 char *password;
a2997b0f 721 int reverse_order;
237e0016 722{
b1ce62a8 723 int nmsgs, nbytes;
b1ce62a8
RS
724 register int i;
725 int mbfi;
726 FILE *mbf;
2e82e3c3 727 char *getenv ();
b32701a7 728 popserver server;
a2997b0f 729 int start, end, increment;
476b2799
GM
730 char *user, *hostname;
731
732 user = mailbox;
733 if ((hostname = index(mailbox, ':')))
734 *hostname++ = '\0';
237e0016 735
476b2799 736 server = pop_open (hostname, user, password, POP_NO_GETPASS);
2e82e3c3 737 if (! server)
b1ce62a8 738 {
cc3b64e8 739 error ("Error connecting to POP server: %s", pop_error, 0);
65396510 740 return EXIT_FAILURE;
237e0016
RS
741 }
742
2e82e3c3 743 if (pop_stat (server, &nmsgs, &nbytes))
b1ce62a8 744 {
cc3b64e8 745 error ("Error getting message count from POP server: %s", pop_error, 0);
65396510 746 return EXIT_FAILURE;
237e0016
RS
747 }
748
b1ce62a8
RS
749 if (!nmsgs)
750 {
2e82e3c3 751 pop_close (server);
65396510 752 return EXIT_SUCCESS;
b1ce62a8
RS
753 }
754
755 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
756 if (mbfi < 0)
757 {
2e82e3c3
RS
758 pop_close (server);
759 error ("Error in open: %s, %s", strerror (errno), outfile);
65396510 760 return EXIT_FAILURE;
b1ce62a8
RS
761 }
762 fchown (mbfi, getuid (), -1);
763
7f75d5c6 764 if ((mbf = fdopen (mbfi, "wb")) == NULL)
b1ce62a8 765 {
2e82e3c3 766 pop_close (server);
cc3b64e8 767 error ("Error in fdopen: %s", strerror (errno), 0);
2e82e3c3
RS
768 close (mbfi);
769 unlink (outfile);
65396510 770 return EXIT_FAILURE;
b1ce62a8
RS
771 }
772
a2997b0f
KH
773 if (reverse_order)
774 {
775 start = nmsgs;
776 end = 1;
777 increment = -1;
778 }
779 else
780 {
781 start = 1;
782 end = nmsgs;
783 increment = 1;
784 }
785
786 for (i = start; i * increment <= end * increment; i += increment)
b1ce62a8
RS
787 {
788 mbx_delimit_begin (mbf);
ff804ff5 789 if (pop_retr (server, i, mbf) != OK)
b1ce62a8 790 {
17a60964 791 error ("%s", Errmsg, 0);
b1ce62a8 792 close (mbfi);
65396510 793 return EXIT_FAILURE;
237e0016 794 }
b1ce62a8
RS
795 mbx_delimit_end (mbf);
796 fflush (mbf);
2e82e3c3
RS
797 if (ferror (mbf))
798 {
cc3b64e8 799 error ("Error in fflush: %s", strerror (errno), 0);
2e82e3c3
RS
800 pop_close (server);
801 close (mbfi);
65396510 802 return EXIT_FAILURE;
2e82e3c3 803 }
237e0016
RS
804 }
805
2e82e3c3
RS
806 /* On AFS, a call to write only modifies the file in the local
807 * workstation's AFS cache. The changes are not written to the server
808 * until a call to fsync or close is made. Users with AFS home
809 * directories have lost mail when over quota because these checks were
810 * not made in previous versions of movemail. */
811
e397a017 812#ifdef BSD_SYSTEM
cfa191ff
RS
813 if (fsync (mbfi) < 0)
814 {
08fa58c9 815 error ("Error in fsync: %s", strerror (errno), 0);
65396510 816 return EXIT_FAILURE;
cfa191ff 817 }
340ff9de 818#endif
cfa191ff
RS
819
820 if (close (mbfi) == -1)
821 {
cc3b64e8 822 error ("Error in close: %s", strerror (errno), 0);
65396510 823 return EXIT_FAILURE;
cfa191ff
RS
824 }
825
fea4325c
RS
826 if (! preserve)
827 for (i = 1; i <= nmsgs; i++)
828 {
829 if (pop_delete (server, i))
830 {
cc3b64e8 831 error ("Error from POP server: %s", pop_error, 0);
fea4325c 832 pop_close (server);
65396510 833 return EXIT_FAILURE;
fea4325c
RS
834 }
835 }
237e0016 836
2e82e3c3 837 if (pop_quit (server))
b1ce62a8 838 {
cc3b64e8 839 error ("Error from POP server: %s", pop_error, 0);
65396510 840 return EXIT_FAILURE;
237e0016 841 }
177c0ea7 842
65396510 843 return EXIT_SUCCESS;
237e0016
RS
844}
845
ff804ff5
KH
846int
847pop_retr (server, msgno, arg)
b32701a7 848 popserver server;
b553981f 849 int msgno;
ff804ff5 850 FILE *arg;
237e0016 851{
2e82e3c3
RS
852 extern char *strerror ();
853 char *line;
854 int ret;
237e0016 855
2e82e3c3 856 if (pop_retrieve_first (server, msgno, &line))
b1ce62a8 857 {
9b956af2
KH
858 char *error = concat ("Error from POP server: ", pop_error, "");
859 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 860 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 861 free(error);
2e82e3c3 862 return (NOTOK);
237e0016
RS
863 }
864
d89d0243 865 while ((ret = pop_retrieve_next (server, &line)) >= 0)
b1ce62a8 866 {
2e82e3c3
RS
867 if (! line)
868 break;
869
d89d0243 870 if (mbx_write (line, ret, arg) != OK)
b1ce62a8 871 {
2e82e3c3
RS
872 strcpy (Errmsg, strerror (errno));
873 pop_close (server);
874 return (NOTOK);
237e0016
RS
875 }
876 }
237e0016 877
2e82e3c3 878 if (ret)
b1ce62a8 879 {
9b956af2
KH
880 char *error = concat ("Error from POP server: ", pop_error, "");
881 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 882 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 883 free(error);
2e82e3c3 884 return (NOTOK);
237e0016
RS
885 }
886
2e82e3c3 887 return (OK);
237e0016
RS
888}
889
2e82e3c3
RS
890/* Do this as a macro instead of using strcmp to save on execution time. */
891#define IS_FROM_LINE(a) ((a[0] == 'F') \
892 && (a[1] == 'r') \
893 && (a[2] == 'o') \
894 && (a[3] == 'm') \
895 && (a[4] == ' '))
237e0016 896
2e82e3c3 897int
d89d0243 898mbx_write (line, len, mbf)
b1ce62a8 899 char *line;
d89d0243 900 int len;
b1ce62a8 901 FILE *mbf;
237e0016 902{
d04f5031 903#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
2e82e3c3
RS
904 if (IS_FROM_LINE (line))
905 {
906 if (fputc ('>', mbf) == EOF)
907 return (NOTOK);
908 }
d04f5031
PE
909#endif
910 if (line[0] == '\037')
911 {
912 if (fputs ("^_", mbf) == EOF)
913 return (NOTOK);
914 line++;
915 len--;
916 }
177c0ea7 917 if (fwrite (line, 1, len, mbf) != len)
2e82e3c3
RS
918 return (NOTOK);
919 if (fputc (0x0a, mbf) == EOF)
920 return (NOTOK);
921 return (OK);
237e0016
RS
922}
923
2e82e3c3 924int
b1ce62a8
RS
925mbx_delimit_begin (mbf)
926 FILE *mbf;
237e0016 927{
d228a23c
GM
928 time_t now;
929 struct tm *ltime;
930 char fromline[40] = "From movemail ";
931
932 now = time (NULL);
933 ltime = localtime (&now);
934
935 strcat (fromline, asctime (ltime));
936
937 if (fputs (fromline, mbf) == EOF)
2e82e3c3
RS
938 return (NOTOK);
939 return (OK);
237e0016
RS
940}
941
cc3b64e8 942int
b1ce62a8
RS
943mbx_delimit_end (mbf)
944 FILE *mbf;
237e0016 945{
3f32be22 946 if (putc ('\n', mbf) == EOF)
2e82e3c3
RS
947 return (NOTOK);
948 return (OK);
237e0016
RS
949}
950
951#endif /* MAIL_USE_POP */
e5f7ea68
RM
952\f
953#ifndef HAVE_STRERROR
954char *
955strerror (errnum)
956 int errnum;
957{
958 extern char *sys_errlist[];
959 extern int sys_nerr;
960
961 if (errnum >= 0 && errnum < sys_nerr)
962 return sys_errlist[errnum];
963 return (char *) "Unknown error";
964}
965
966#endif /* ! HAVE_STRERROR */
ab5796a9
MB
967
968/* arch-tag: 1c323112-41fe-4fe5-8de9-494de631f73f
969 (do not change this comment) */
65396510
TTN
970
971/* movemail.c ends here */