Add 2009 to copyright years.
[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;
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>
237e0016 686
237e0016
RS
687#define NOTOK (-1)
688#define OK 0
689#define DONE 1
690
691char *progname;
692FILE *sfi;
693FILE *sfo;
2e82e3c3
RS
694char ibuffer[BUFSIZ];
695char obuffer[BUFSIZ];
752fb472
DL
696char Errmsg[200]; /* POP errors, at least, can exceed
697 the original length of 80. */
237e0016 698
476b2799 699/*
4d90eee4 700 * The full valid syntax for a POP mailbox specification for movemail
476b2799
GM
701 * is "po:username:hostname". The ":hostname" is optional; if it is
702 * omitted, the MAILHOST environment variable will be consulted. Note
703 * that by the time popmail() is called the "po:" has been stripped
704 * off of the front of the mailbox name.
705 *
706 * If the mailbox is in the form "po:username:hostname", then it is
707 * modified by this function -- the second colon is replaced by a
708 * null.
65396510
TTN
709 *
710 * Return a value suitable for passing to `exit'.
476b2799
GM
711 */
712
cc3b64e8 713int
476b2799
GM
714popmail (mailbox, outfile, preserve, password, reverse_order)
715 char *mailbox;
b1ce62a8 716 char *outfile;
fea4325c 717 int preserve;
7f75d5c6 718 char *password;
a2997b0f 719 int reverse_order;
237e0016 720{
b1ce62a8 721 int nmsgs, nbytes;
b1ce62a8
RS
722 register int i;
723 int mbfi;
724 FILE *mbf;
2e82e3c3 725 char *getenv ();
b32701a7 726 popserver server;
a2997b0f 727 int start, end, increment;
476b2799
GM
728 char *user, *hostname;
729
730 user = mailbox;
731 if ((hostname = index(mailbox, ':')))
732 *hostname++ = '\0';
237e0016 733
476b2799 734 server = pop_open (hostname, user, password, POP_NO_GETPASS);
2e82e3c3 735 if (! server)
b1ce62a8 736 {
cc3b64e8 737 error ("Error connecting to POP server: %s", pop_error, 0);
65396510 738 return EXIT_FAILURE;
237e0016
RS
739 }
740
2e82e3c3 741 if (pop_stat (server, &nmsgs, &nbytes))
b1ce62a8 742 {
cc3b64e8 743 error ("Error getting message count from POP server: %s", pop_error, 0);
65396510 744 return EXIT_FAILURE;
237e0016
RS
745 }
746
b1ce62a8
RS
747 if (!nmsgs)
748 {
2e82e3c3 749 pop_close (server);
65396510 750 return EXIT_SUCCESS;
b1ce62a8
RS
751 }
752
753 mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
754 if (mbfi < 0)
755 {
2e82e3c3
RS
756 pop_close (server);
757 error ("Error in open: %s, %s", strerror (errno), outfile);
65396510 758 return EXIT_FAILURE;
b1ce62a8
RS
759 }
760 fchown (mbfi, getuid (), -1);
761
7f75d5c6 762 if ((mbf = fdopen (mbfi, "wb")) == NULL)
b1ce62a8 763 {
2e82e3c3 764 pop_close (server);
cc3b64e8 765 error ("Error in fdopen: %s", strerror (errno), 0);
2e82e3c3
RS
766 close (mbfi);
767 unlink (outfile);
65396510 768 return EXIT_FAILURE;
b1ce62a8
RS
769 }
770
a2997b0f
KH
771 if (reverse_order)
772 {
773 start = nmsgs;
774 end = 1;
775 increment = -1;
776 }
777 else
778 {
779 start = 1;
780 end = nmsgs;
781 increment = 1;
782 }
783
784 for (i = start; i * increment <= end * increment; i += increment)
b1ce62a8
RS
785 {
786 mbx_delimit_begin (mbf);
ff804ff5 787 if (pop_retr (server, i, mbf) != OK)
b1ce62a8 788 {
17a60964 789 error ("%s", Errmsg, 0);
b1ce62a8 790 close (mbfi);
65396510 791 return EXIT_FAILURE;
237e0016 792 }
b1ce62a8
RS
793 mbx_delimit_end (mbf);
794 fflush (mbf);
2e82e3c3
RS
795 if (ferror (mbf))
796 {
cc3b64e8 797 error ("Error in fflush: %s", strerror (errno), 0);
2e82e3c3
RS
798 pop_close (server);
799 close (mbfi);
65396510 800 return EXIT_FAILURE;
2e82e3c3 801 }
237e0016
RS
802 }
803
2e82e3c3
RS
804 /* On AFS, a call to write only modifies the file in the local
805 * workstation's AFS cache. The changes are not written to the server
806 * until a call to fsync or close is made. Users with AFS home
807 * directories have lost mail when over quota because these checks were
808 * not made in previous versions of movemail. */
809
e397a017 810#ifdef BSD_SYSTEM
cfa191ff
RS
811 if (fsync (mbfi) < 0)
812 {
08fa58c9 813 error ("Error in fsync: %s", strerror (errno), 0);
65396510 814 return EXIT_FAILURE;
cfa191ff 815 }
340ff9de 816#endif
cfa191ff
RS
817
818 if (close (mbfi) == -1)
819 {
cc3b64e8 820 error ("Error in close: %s", strerror (errno), 0);
65396510 821 return EXIT_FAILURE;
cfa191ff
RS
822 }
823
fea4325c
RS
824 if (! preserve)
825 for (i = 1; i <= nmsgs; i++)
826 {
827 if (pop_delete (server, i))
828 {
cc3b64e8 829 error ("Error from POP server: %s", pop_error, 0);
fea4325c 830 pop_close (server);
65396510 831 return EXIT_FAILURE;
fea4325c
RS
832 }
833 }
237e0016 834
2e82e3c3 835 if (pop_quit (server))
b1ce62a8 836 {
cc3b64e8 837 error ("Error from POP server: %s", pop_error, 0);
65396510 838 return EXIT_FAILURE;
237e0016 839 }
177c0ea7 840
65396510 841 return EXIT_SUCCESS;
237e0016
RS
842}
843
ff804ff5
KH
844int
845pop_retr (server, msgno, arg)
b32701a7 846 popserver server;
b553981f 847 int msgno;
ff804ff5 848 FILE *arg;
237e0016 849{
2e82e3c3
RS
850 extern char *strerror ();
851 char *line;
852 int ret;
237e0016 853
2e82e3c3 854 if (pop_retrieve_first (server, msgno, &line))
b1ce62a8 855 {
9b956af2
KH
856 char *error = concat ("Error from POP server: ", pop_error, "");
857 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 858 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 859 free(error);
2e82e3c3 860 return (NOTOK);
237e0016
RS
861 }
862
d89d0243 863 while ((ret = pop_retrieve_next (server, &line)) >= 0)
b1ce62a8 864 {
2e82e3c3
RS
865 if (! line)
866 break;
867
d89d0243 868 if (mbx_write (line, ret, arg) != OK)
b1ce62a8 869 {
2e82e3c3
RS
870 strcpy (Errmsg, strerror (errno));
871 pop_close (server);
872 return (NOTOK);
237e0016
RS
873 }
874 }
237e0016 875
2e82e3c3 876 if (ret)
b1ce62a8 877 {
9b956af2
KH
878 char *error = concat ("Error from POP server: ", pop_error, "");
879 strncpy (Errmsg, error, sizeof (Errmsg));
2e82e3c3 880 Errmsg[sizeof (Errmsg)-1] = '\0';
9b956af2 881 free(error);
2e82e3c3 882 return (NOTOK);
237e0016
RS
883 }
884
2e82e3c3 885 return (OK);
237e0016
RS
886}
887
2e82e3c3
RS
888/* Do this as a macro instead of using strcmp to save on execution time. */
889#define IS_FROM_LINE(a) ((a[0] == 'F') \
890 && (a[1] == 'r') \
891 && (a[2] == 'o') \
892 && (a[3] == 'm') \
893 && (a[4] == ' '))
237e0016 894
2e82e3c3 895int
d89d0243 896mbx_write (line, len, mbf)
b1ce62a8 897 char *line;
d89d0243 898 int len;
b1ce62a8 899 FILE *mbf;
237e0016 900{
d04f5031 901#ifdef MOVEMAIL_QUOTE_POP_FROM_LINES
2e82e3c3
RS
902 if (IS_FROM_LINE (line))
903 {
904 if (fputc ('>', mbf) == EOF)
905 return (NOTOK);
906 }
d04f5031
PE
907#endif
908 if (line[0] == '\037')
909 {
910 if (fputs ("^_", mbf) == EOF)
911 return (NOTOK);
912 line++;
913 len--;
914 }
177c0ea7 915 if (fwrite (line, 1, len, mbf) != len)
2e82e3c3
RS
916 return (NOTOK);
917 if (fputc (0x0a, mbf) == EOF)
918 return (NOTOK);
919 return (OK);
237e0016
RS
920}
921
2e82e3c3 922int
b1ce62a8
RS
923mbx_delimit_begin (mbf)
924 FILE *mbf;
237e0016 925{
2e82e3c3
RS
926 if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
927 return (NOTOK);
928 return (OK);
237e0016
RS
929}
930
cc3b64e8 931int
b1ce62a8
RS
932mbx_delimit_end (mbf)
933 FILE *mbf;
237e0016 934{
2e82e3c3
RS
935 if (putc ('\037', mbf) == EOF)
936 return (NOTOK);
937 return (OK);
237e0016
RS
938}
939
940#endif /* MAIL_USE_POP */
e5f7ea68
RM
941\f
942#ifndef HAVE_STRERROR
943char *
944strerror (errnum)
945 int errnum;
946{
947 extern char *sys_errlist[];
948 extern int sys_nerr;
949
950 if (errnum >= 0 && errnum < sys_nerr)
951 return sys_errlist[errnum];
952 return (char *) "Unknown error";
953}
954
955#endif /* ! HAVE_STRERROR */
ab5796a9
MB
956
957/* arch-tag: 1c323112-41fe-4fe5-8de9-494de631f73f
958 (do not change this comment) */
65396510
TTN
959
960/* movemail.c ends here */