Coverity fixes, etc from Nelson
[sxemacs] / lib-src / movemail.c
1 /* movemail foo bar -- move file foo to file bar,
2    locking file foo.
3    Copyright (C) 1986, 1992, 1993, 1994, 1996 Free Software Foundation, Inc.
4
5    Copyright (C) 2005 Johann "Myrkraverk" Oskarsson <johann@myrkraverk.com>
6
7 This file is part of SXEmacs.
8
9 SXEmacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 SXEmacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22  Please mail bugs and suggestions to the SXEmacs maintainer.
23 */
24
25 /* Important notice:
26  *
27  *  You *must* coordinate the locking method used by movemail with that
28  *  used by your mail delivery agent, as well as that of the other mail
29  *  user agents on your system.  movemail allows you to do this at run
30  *  time via the -m flag.  Moreover, it uses a default determined by
31  *  the MAIL_LOCK_DOT, MAIL_LOCK_LOCKF, MAIL_LOCK_FLOCK,
32  *  MAIL_LOCK_LOCKING, and MAIL_LOCK_MMDF preprocessor settings.
33  */
34
35 /*
36  * Mike Sperber <sperber@informatik.uni-tuebingen.de> reorganized
37  * everything that has to with locking in December 1999.
38  */
39
40 /*
41  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
42  *
43  * Added POP (Post Office Protocol) service.  When compiled
44  * -DMAIL_USE_POP movemail will accept input filename arguments of the
45  * form "po:username".  This will cause movemail to open a connection
46  * to a pop server running on $MAILHOST (environment variable).
47  * Movemail must be setuid to root in order to work with POP.
48  *
49  * New module: popmail.c
50  * Modified routines:
51  *      main - added code within #ifdef MAIL_USE_POP; added setuid
52  *             (getuid ()) after POP code.
53  * New routines in movemail.c:
54  *      get_errmsg - return pointer to system error message
55  *
56  * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
57  *
58  * Move all of the POP code into a separate file, "pop.c".
59  * Use strerror instead of get_errmsg.
60  *
61  */
62
63 #define NO_SHORTNAMES           /* Tell config not to load remap.h */
64 #define DONT_ENCAPSULATE
65 #include <config.h>
66 #include <sys/types.h>
67 #include <sys/stat.h>
68 #include <stdio.h>
69 #include <errno.h>
70 #include "../src/sysfile.h"
71 #include "../src/syswait.h"
72 #include "../src/systime.h"
73 #include <stdlib.h>
74 #include <string.h>
75 #ifdef HAVE_GETOPT_H
76 #include <getopt.h>
77 #endif
78 #ifdef MAIL_USE_POP
79 #include "pop.h"
80 #include "../src/regex.h"
81 #endif
82
83 #include <assert.h>
84
85 extern char *optarg;
86 extern int optind, opterr;
87
88 #ifndef HAVE_STRERROR
89 char *strerror(int errnum);
90 #endif                          /* HAVE_STRERROR */
91
92 #ifndef DIRECTORY_SEP
93 #define DIRECTORY_SEP '/'
94 #endif
95 #ifndef IS_DIRECTORY_SEP
96 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
97 #endif
98
99 #if defined (HAVE_UNISTD_H)
100 #include <unistd.h>
101 #endif                          /* unistd.h */
102 #ifndef F_OK
103 #define F_OK 0
104 #define X_OK 1
105 #define W_OK 2
106 #define R_OK 4
107 #endif                          /* No F_OK */
108
109 #if defined (HAVE_FCNTL_H)
110 #include <fcntl.h>
111 #endif                          /* fcntl.h */
112
113 #ifdef HAVE_LOCKING
114 #include <sys/locking.h>
115 #endif
116
117 #ifdef HAVE_MMDF
118 extern int lk_open(), lk_close();
119 #endif
120
121 /* Cancel substitutions made by config.h for Emacs.  */
122 #undef open
123 #undef read
124 #undef write
125 #undef close
126
127 static void fatal(char *, char *);
128 static void error(const char *, const char *, const char *);
129 static void usage(int);
130 static void pfatal_with_name(char *);
131 static void pfatal_and_delete(char *);
132 static char *concat(char *, char *, char *);
133 static long *xmalloc(unsigned int);
134 #ifdef MAIL_USE_POP
135 static int popmail(char *, char *, char *);
136 static int pop_retr(popserver server, int msgno,
137                     int (*action) (char *, FILE *), FILE * arg);
138 static int mbx_write(char *, FILE *);
139 static int mbx_delimit_begin(FILE *);
140 static int mbx_delimit_end(FILE *);
141 static struct re_pattern_buffer *compile_regex(char *regexp_pattern);
142 static int pop_search_top(popserver server, int msgno, int lines,
143                           struct re_pattern_buffer *regexp);
144 #endif
145
146 int verbose = 0;
147 #ifdef MAIL_USE_POP
148 int reverse = 0;
149 int keep_messages = 0;
150 struct re_pattern_buffer *regexp_pattern = 0;
151 int match_lines = 10;
152 #endif
153
154 #define VERBOSE(x) if (verbose) { printf x; fflush(stdout); }
155
156 #ifdef HAVE_GETOPT_LONG
157 struct option longopts[] = {
158         {"inbox", required_argument, NULL, 'i'},
159         {"outfile", required_argument, NULL, 'o'},
160 #ifdef MAIL_USE_POP
161         {"password", required_argument, NULL, 'p'},
162         {"reverse-pop-order", no_argument, NULL, 'x'},
163         {"keep-messages", no_argument, NULL, 'k'},
164         {"regex", required_argument, NULL, 'r'},
165         {"match-lines", required_argument, NULL, 'l'},
166 #endif
167         {"lock-method", required_argument, NULL, 'm'},
168         {"help", no_argument, NULL, 'h'},
169         {"verbose", no_argument, NULL, 'v'},
170         {0}
171 };
172 #endif
173
174 #define DOTLOCKING      0
175 #define FLOCKING        1
176 #define LOCKFING        2
177 #define MMDF            3
178 #define LOCKING         4
179
180 #if defined(MAIL_LOCK_FLOCK) && defined(HAVE_FLOCK)
181 #define DEFAULT_LOCKING FLOCKING
182 #elif defined(MAIL_LOCK_LOCKF) && defined(HAVE_LOCKF)
183 #define DEFAULT_LOCKING LOCKFING
184 #elif defined(MAIL_LOCK_MMDF) && defined(HAVE_MMDF)
185 #define DEFAULT_LOCKING MMDF
186 #elif defined(MAIL_LOCK_LOCKING) && defined(HAVE_LOCKING)
187 #define DEFAULT_LOCKING LOCKING
188 #else
189 #define DEFAULT_LOCKING DOTLOCKING
190 #endif
191
192 #ifndef DISABLE_DIRECT_ACCESS
193 static void lock_dot(char *);
194 #endif
195 static void unlock_dot(char *);
196 static int parse_lock_method(char *);
197 static char *unparse_lock_method(int);
198
199 int main(int argc, char *argv[])
200 {
201         char *inname = 0, *outname = 0;
202 #if defined MAIL_USE_POP
203         char *poppass = 0;
204 #endif  /* MAIL_USE_POP */
205 #ifndef DISABLE_DIRECT_ACCESS
206         int indesc, outdesc;
207         int nread;
208         int status;
209 #endif
210
211         int lock_method = DEFAULT_LOCKING;
212
213         char *maybe_lock_env;
214
215         maybe_lock_env = getenv("EMACSLOCKMETHOD");
216         if (maybe_lock_env) {
217                 printf("maybe-lock_env: %s\n", maybe_lock_env);
218                 lock_method = parse_lock_method(maybe_lock_env);
219         }
220
221         for (;;) {
222 #ifdef MAIL_USE_POP
223                 char *optstring = "i:o:m:p:l:r:xvhk";
224 #else
225                 char *optstring = "i:o:m:vh";
226 #endif
227
228 #if HAVE_GETOPT_LONG
229                 int opt = getopt_long(argc, argv, optstring, longopts, 0);
230 #elif HAVE_GETOPT
231                 int opt = getopt(argc, argv, optstring);
232 #else
233 # error         "movemail cannot be built without getopt, preferably getopt_long"
234 #endif
235                 if (opt == EOF)
236                         break;
237
238                 switch (opt) {
239                 case 0:
240                         break;
241                 case 1: /* one of the standard arguments seen */
242                         if (!inname) {
243                                 inname = optarg;
244                         } else if (!outname) {
245                                 outname = optarg;
246 #if defined MAIL_USE_POP
247                         } else {
248                                 poppass = optarg;
249 #endif  /* MAIL_USE_POP */
250                         }
251                         break;
252
253                 case 'i':       /* infile */
254                         inname = optarg;
255                         break;
256
257                 case 'o':       /* outfile */
258                         outname = optarg;
259                         break;
260 #ifdef MAIL_USE_POP
261                 case 'p':       /* pop password */
262                         poppass = optarg;
263                         break;
264                 case 'k':
265                         keep_messages = 1;
266                         break;
267                 case 'x':
268                         reverse = 1;
269                         break;
270                 case 'l':       /* lines to match */
271                         match_lines = atoi(optarg);
272                         break;
273
274                 case 'r':       /* regular expression */
275                         regexp_pattern = compile_regex(optarg);
276                         break;
277 #endif
278
279                 case 'm':
280                         lock_method = parse_lock_method(optarg);
281                         break;
282                 case 'h':
283                         usage(lock_method);
284                         exit(0);
285                 case 'v':
286                         verbose = 1;
287                         break;
288                 default:
289                         break;
290                 }
291         }
292
293         while (optind < argc) {
294                 if (!inname) {
295                         inname = argv[optind];
296                 } else if (!outname) {
297                         outname = argv[optind];
298 #if defined MAIL_USE_POP
299                 } else {
300                         poppass = argv[optind];
301 #endif  /* MAIL_USE_POP */
302                 }
303                 optind++;
304         }
305
306         if (!inname || !outname) {
307                 usage(lock_method);
308                 exit(1);
309         }
310 #ifdef HAVE_MMDF
311         if (lock_method == MMDF)
312                 mmdf_init(argv[0]);
313 #endif
314
315         if (*outname == 0)
316                 fatal("Destination file name is empty", 0);
317
318         /* Also check that outname's directory is writable to the real uid.  */
319         {
320                 char *buf = (char *)xmalloc(strlen(outname) + 1);
321                 char *cp;
322                 strcpy(buf, outname);
323                 cp = buf + strlen(buf);
324                 while (cp > buf && !IS_DIRECTORY_SEP(cp[-1]))
325                         *--cp = 0;
326                 if (cp == buf)
327                         *cp++ = '.';
328                 if (access(buf, W_OK) != 0)
329                         pfatal_with_name(buf);
330                 free(buf);
331         }
332
333 #ifdef MAIL_USE_POP
334         if (!strncmp(inname, "po:", 3)) {
335                 int retcode = popmail(inname + 3, outname, poppass);
336                 exit(retcode);
337         }
338         setuid(getuid());
339 #endif                          /* MAIL_USE_POP */
340
341 #ifndef DISABLE_DIRECT_ACCESS
342
343
344         if (fork() == 0) {
345                 setuid(getuid());
346
347                 VERBOSE(("opening input file\n"));
348
349                 switch (lock_method) {
350                 case DOTLOCKING:
351                         indesc = open(inname, O_RDONLY);
352                         break;
353 #ifdef HAVE_LOCKF
354                 case LOCKFING:
355                         indesc = open(inname, O_RDWR);
356                         break;
357 #endif
358 #ifdef HAVE_FLOCK
359                 case FLOCKING:
360                         indesc = open(inname, O_RDWR);
361                         break;
362 #endif
363 #ifdef HAVE_LOCKING
364                 case LOCKING:
365                         indesc = open(inname, O_RDWR);
366                         break;
367 #endif
368 #ifdef HAVE_MMDF
369                 case MMDF:
370                         indesc = lk_open(inname, O_RDONLY, 0, 0, 10);
371                         break;
372 #endif
373                 default:
374                         abort();
375                 }
376
377                 if (indesc < 0)
378                         pfatal_with_name(inname);
379
380 #ifdef HAVE_UMASK
381                 /* In case movemail is setuid to root, make sure the user can
382                    read the output file.  */
383                 umask(umask(0) & 0333);
384 #endif
385
386                 outdesc = open(outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
387                 if (outdesc < 0)
388                         pfatal_with_name(outname);
389
390                 VERBOSE(("locking input file\n"));
391
392                 switch (lock_method) {
393 #ifdef HAVE_LOCKF
394                 case LOCKFING:
395                         if (lockf(indesc, F_LOCK, 0) < 0)
396                                 pfatal_with_name(inname);
397                         break;
398 #endif
399 #ifdef HAVE_FLOCK
400                 case FLOCKING:
401                         if (flock(indesc, LOCK_EX) < 0)
402                                 pfatal_with_name(inname);
403                         break;
404 #endif
405 #ifdef HAVE_LOCKING
406                 case LOCKING:
407                         if (locking(indesc, LK_RLCK, -1L) < 0)
408                                 pfatal_with_name(inname);
409                         break;
410 #endif
411                 case DOTLOCKING:
412                         lock_dot(inname);
413                         break;
414                 default:
415                         break;
416                 }
417
418                 VERBOSE(("copying input file to output file\n"));
419
420                 {
421                         char buf[1024];
422                         while (1) {
423                                 nread = read(indesc, buf, sizeof buf);
424                                 if (nread < 0 ||
425                                     nread != write(outdesc, buf, nread)) {
426                                         int saved_errno = errno;
427                                         unlink(outname);
428                                         errno = saved_errno;
429                                         pfatal_with_name(outname);
430                                 }
431                                 if (nread < (int)sizeof(buf))
432                                         break;
433                         }
434                 }
435
436 #ifdef HAVE_FSYNC
437                 if (fsync(outdesc) < 0)
438                         pfatal_and_delete(outname);
439 #endif
440
441                 /* Check to make sure no errors before we zap the inbox.  */
442                 if (close(outdesc) != 0)
443                         pfatal_and_delete(outname);
444
445                 VERBOSE(("deleting or truncating input file\n"));
446
447                 switch (lock_method) {
448                 case LOCKFING:
449                 case FLOCKING:
450                 case LOCKING:
451 #ifdef HAVE_FTRUNCATE
452                         if(ftruncate(indesc, 0L)!=0)
453                                 pfatal_and_delete(inname);
454 #else
455                         close(open(inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
456 #endif
457                         close(indesc);
458                         break;
459 #ifdef HAVE_MMDF
460                 case MMDF:
461                         lk_close(indesc, 0, 0, 0);
462                         break;
463 #endif
464                 case DOTLOCKING:
465                         creat(inname, 0600);
466                         break;
467                 default:
468                         break;
469                 }
470
471                 exit(0);
472         }
473
474         wait(&status);
475         if (!WIFEXITED(status))
476                 exit(1);
477         else if (WEXITSTATUS(status) != 0)
478                 exit(WEXITSTATUS(status));
479
480         if (lock_method == DOTLOCKING)
481                 unlock_dot(inname);
482
483 #endif                          /* not DISABLE_DIRECT_ACCESS */
484
485         return 0;
486 }
487
488 static void usage(int lock_method)
489 {
490         printf ("Usage: movemail [-rvxkh] [-l lines ] [-m method ] [-i] "
491                 "inbox [-o] destfile [[-p] POP-password]\n");
492         printf("where method is one of: dot");
493 #ifdef HAVE_LOCKF
494         printf(", lockf");
495 #endif
496 #ifdef HAVE_FLOCK
497         printf(", flock");
498 #endif
499 #ifdef HAVE_MMDF
500         printf(", mmdf");
501 #endif
502 #ifdef HAVE_LOCKING
503         printf(", locking");
504 #endif
505         printf("\nDefault is: %s\n", unparse_lock_method(lock_method));
506
507 }
508
509 static char *unparse_lock_method(int lock_method)
510 {
511         switch (lock_method) {
512         case DOTLOCKING:
513                 return "dot";
514         case FLOCKING:
515                 return "flock";
516         case LOCKFING:
517                 return "lockf";
518         case LOCKING:
519                 return "locking";
520         case MMDF:
521                 return "mmdf";
522         default:
523                 abort();
524                 return 0;
525         }
526 }
527
528 static int parse_lock_method(char *method_name)
529 {
530         if (!strcmp("dot", method_name) || !strcmp("file", method_name))
531                 return DOTLOCKING;
532 #ifdef HAVE_LOCKF
533         else if (!strcmp("lockf", method_name))
534                 return LOCKFING;
535 #endif
536 #ifdef HAVE_FLOCK
537         else if (!strcmp("flock", method_name))
538                 return FLOCKING;
539 #endif
540 #ifdef HAVE_MMDF
541         else if (!strcmp("mmdf", method_name))
542                 return MMDF;
543 #endif
544 #ifdef HAVE_LOCKING
545         else if (!strcmp("locking", method_name))
546                 return LOCKING;
547 #endif
548         else
549                 fatal("invalid lock method: %s", method_name);
550         return 0;               /* unreached */
551 }
552
553 static char *dot_filename(char *filename)
554 {
555         return concat(filename, ".lock", "");
556 }
557
558 static char *dotlock_filename = NULL;
559
560 #ifndef DISABLE_DIRECT_ACCESS
561 static void lock_dot(char *filename)
562 {
563         struct stat st;
564         long now;
565         int tem;
566         char *lockname, *p;
567         char *tempname;
568         int desc;
569
570         dotlock_filename = (char *)xmalloc(strlen(filename) + 1);
571
572         /* Use a lock file named after our first argument with .lock appended:
573            If it exists, the mail file is locked. */
574
575         lockname = dot_filename(filename);
576         tempname = (char *)xmalloc(strlen(filename) + strlen("EXXXXXX") + 1);
577         strcpy(tempname, filename);
578         p = tempname + strlen(tempname);
579         while (p != tempname && !IS_DIRECTORY_SEP(p[-1]))
580                 p--;
581         *p = 0;
582         strcpy(p, "EXXXXXX");
583
584 #ifndef HAVE_MKSTEMP
585         mktemp(tempname);
586         unlink(tempname);
587 #endif
588
589         for (;;) {
590                 /* Create the lock file, but not under the lock file name.  */
591                 /* Give up if cannot do that.  */
592
593 #ifdef HAVE_MKSTEMP
594                 /* Remove all group and other permissions.. */
595                 umask(S_IRWXG|S_IRWXO);
596                 desc = mkstemp(tempname);
597 #else
598                 desc = open(tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
599 #endif
600
601                 if (desc < 0) {
602                         int msz = strlen(tempname) + 50;
603                         char *message = (char *)xmalloc(msz);
604                         int sz = snprintf(message, msz,
605                                           "%s--see source file lib-src/movemail.c",
606                                           tempname);
607                         assert(sz>=0 && sz < msz);
608                         pfatal_with_name(message);
609                 }
610                 close(desc);
611
612                 tem = link(tempname, lockname);
613                 unlink(tempname);
614                 if (tem >= 0)
615                         break;
616                 sleep(1);
617
618                 /* If lock file is five minutes old, unlock it.
619                    Five minutes should be good enough to cope with crashes
620                    and wedgitude, and long enough to avoid being fooled
621                    by time differences between machines.  */
622                 if (stat(lockname, &st) >= 0) {
623                         now = time(0);
624                         if (st.st_ctime < now - 300)
625                                 unlink(lockname);
626                 }
627         }
628         strcpy(dotlock_filename, filename);
629         free(tempname);
630 }
631 #endif                          /* not DISABLE_DIRECT_ACCESS */
632
633 static void unlock_dot(char *filename)
634 {
635         unlink(dot_filename(filename));
636 }
637
638 static void maybe_unlock_dot(void)
639 {
640         if (dotlock_filename)
641                 unlock_dot(dotlock_filename);
642 }
643
644 /* Print error message and exit.  */
645
646 static void fatal(char *s1, char *s2)
647 {
648         maybe_unlock_dot();
649         error(s1, s2, NULL);
650         exit(1);
651 }
652
653 /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
654
655 static void error(const char *s1, const char *s2, const char *s3)
656 {
657         fprintf(stderr, "movemail: ");
658         fprintf(stderr, s1, s2, s3);
659         fprintf(stderr, "\n");
660 }
661
662 static void pfatal_with_name(char *name)
663 {
664         char *s = concat("", strerror(errno), " for %s");
665         fatal(s, name);
666 }
667
668 static void pfatal_and_delete(char *name)
669 {
670         char *s = concat("", strerror(errno), " for %s");
671         unlink(name);
672         fatal(s, name);
673 }
674
675 /* Return a newly-allocated string whose contents concatenate those of
676    s1, s2, s3.  */
677
678 static char *concat(char *s1, char *s2, char *s3)
679 {
680         int len1 = strlen(s1), len2 = strlen(s2), len3 = strlen(s3);
681         char *result = (char *)xmalloc(len1 + len2 + len3 + 1);
682
683         strncpy(result, s1, len1+1);
684         strncpy(result + len1, s2, len2+1);
685         strncpy(result + len1 + len2, s3, len3+1);
686         *(result + len1 + len2 + len3) = '\0';
687
688         return result;
689 }
690
691 /* Like malloc but get fatal error if memory is exhausted.  */
692
693 static long *xmalloc(unsigned int size)
694 {
695         long *result = (long *)malloc(size);
696         if (!result)
697                 fatal("virtual memory exhausted", 0);
698         return result;
699 }
700
701 /* This is the guts of the interface to the Post Office Protocol.  */
702
703 #ifdef MAIL_USE_POP
704
705 #include <sys/socket.h>
706 #include <netinet/in.h>
707 #include <netdb.h>
708 #include <stdio.h>
709 #include "../src/syspwd.h"
710
711 #define POP_ERROR       (-1)
712 #define POP_RETRIEVED (0)
713 #define POP_DONE (1)
714
715 char *progname;
716 FILE *sfi;
717 FILE *sfo;
718 char ibuffer[BUFSIZ];
719 char obuffer[BUFSIZ];
720 char Errmsg[80];
721
722 static int popmail(char *user, char *outfile, char *password)
723 {
724         int nmsgs, nbytes;
725         register int i, idx;
726         int mbfi;
727         short *retrieved_list;
728         FILE *mbf;
729         popserver server;
730
731         VERBOSE(("opening server\n"));
732         server = pop_open(0, user, password, POP_NO_GETPASS);
733         if (!server) {
734                 error("%s", pop_error, NULL);
735                 return (1);
736         }
737
738         VERBOSE(("stat'ing messages\n"));
739         if (pop_stat(server, &nmsgs, &nbytes)) {
740                 error("%s", pop_error, NULL);
741                 return (1);
742         }
743
744         if (!nmsgs) {
745                 VERBOSE(("closing server\n"));
746                 pop_close(server);
747                 return (0);
748         }
749
750         /* build a retrieved table */
751         retrieved_list = (short *)xmalloc(sizeof(short) * (nmsgs + 1));
752         memset(retrieved_list, 0, sizeof(short) * (nmsgs + 1));
753
754         mbfi = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
755         if (mbfi < 0) {
756                 pop_close(server);
757                 error("Error in open: %s, %s", strerror(errno), outfile);
758                 return (1);
759         }
760         fchown(mbfi, getuid(), (gid_t) - 1);
761
762         if ((mbf = fdopen(mbfi, "wb")) == NULL) {
763                 pop_close(server);
764                 error("Error in fdopen: %s", strerror(errno), NULL);
765                 close(mbfi);
766                 unlink(outfile);
767                 return (1);
768         }
769
770         for (idx = 0; idx < nmsgs; idx++) {
771                 i = reverse ? nmsgs - idx : idx + 1;
772                 VERBOSE(("checking message %d     \n", i));
773
774                 if (!regexp_pattern
775                     ||
776                     pop_search_top(server, i, match_lines,
777                                    regexp_pattern) == POP_RETRIEVED) {
778                         VERBOSE(("retrieving message %d     \n", i));
779                         mbx_delimit_begin(mbf);
780                         if (pop_retr(server, i, mbx_write, mbf) !=
781                             POP_RETRIEVED) {
782                                 error("%s", Errmsg, NULL);
783                                 close(mbfi);
784                                 return (1);
785                         }
786
787                         retrieved_list[i] = 1;
788
789                         mbx_delimit_end(mbf);
790                         fflush(mbf);
791                         if (ferror(mbf)) {
792                                 error("Error in fflush: %s", strerror(errno),
793                                       NULL);
794                                 pop_close(server);
795                                 close(mbfi);
796                                 return (1);
797                         }
798                 }
799         }
800
801         /* On AFS, a call to write only modifies the file in the local
802          *     workstation's AFS cache.  The changes are not written to the server
803          *      until a call to fsync or close is made.  Users with AFS home
804          *      directories have lost mail when over quota because these checks were
805          *      not made in previous versions of movemail. */
806
807 #ifdef HAVE_FSYNC
808         if (fsync(mbfi) < 0) {
809                 error("Error in fsync: %s", strerror(errno), NULL);
810                 return (1);
811         }
812 #endif
813
814         if (close(mbfi) == -1) {
815                 error("Error in close: %s", strerror(errno), NULL);
816                 return (1);
817         }
818
819         if (!keep_messages) {
820                 for (i = 1; i <= nmsgs; i++) {
821                         if (retrieved_list[i] == 1) {
822                                 VERBOSE(("deleting message %d     \n", i));
823                                 if (pop_delete(server, i)) {
824                                         error("%s", pop_error, NULL);
825                                         pop_close(server);
826                                         return (1);
827                                 }
828                         }
829                 }
830         }
831
832         VERBOSE(("closing server             \n"));
833         if (pop_quit(server)) {
834                 error("%s", pop_error, NULL);
835                 return (1);
836         }
837
838         return (0);
839 }
840
841 static int
842 pop_retr(popserver server, int msgno, int (*action) (char *, FILE *),
843          FILE * arg)
844 {
845         char *line;
846         int ret;
847
848         if (pop_retrieve_first(server, msgno, &line)) {
849                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
850                 Errmsg[sizeof(Errmsg) - 1] = '\0';
851                 return (POP_ERROR);
852         }
853
854         while (!(ret = pop_retrieve_next(server, &line))) {
855                 if (!line)
856                         break;
857
858                 if ((*action) (line, arg) != POP_RETRIEVED) {
859                         strcpy(Errmsg, strerror(errno));
860                         pop_close(server);
861                         return (POP_ERROR);
862                 }
863         }
864
865         if (ret) {
866                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
867                 Errmsg[sizeof(Errmsg) - 1] = '\0';
868                 return (POP_ERROR);
869         }
870
871         return (POP_RETRIEVED);
872 }
873
874 /* search the top lines of each message looking for a match */
875 static int
876 pop_search_top(popserver server, int msgno, int lines,
877                struct re_pattern_buffer *regexp)
878 {
879         char *line;
880         int ret;
881         int match = POP_DONE;
882
883         if (pop_top_first(server, msgno, lines, &line)) {
884                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
885                 Errmsg[sizeof(Errmsg) - 1] = '\0';
886                 return (POP_ERROR);
887         }
888
889         while (!(ret = pop_top_next(server, &line))) {
890                 if (!line)
891                         break;
892
893                 /*      VERBOSE (("checking %s\n", line)); */
894                 if (match != POP_RETRIEVED) {
895                         if ((ret =
896                              re_match(regexp, line, strlen(line), 0,
897                                       0)) == -2) {
898                                 strcpy(Errmsg, "error in regular expression");
899                                 pop_close(server);
900                                 return (POP_ERROR);
901                         } else if (ret >= 0) {
902                                 match = POP_RETRIEVED;
903                         }
904                 }
905         }
906
907         if (ret) {
908                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
909                 Errmsg[sizeof(Errmsg) - 1] = '\0';
910                 return (POP_ERROR);
911         }
912
913         return match;
914 }
915
916 /* Do this as a macro instead of using strcmp to save on execution time. */
917 #define IS_FROM_LINE(a) ((a[0] == 'F') \
918                          && (a[1] == 'r') \
919                          && (a[2] == 'o') \
920                          && (a[3] == 'm') \
921                          && (a[4] == ' '))
922
923 static int mbx_write(char *line, FILE * mbf)
924 {
925         if (IS_FROM_LINE(line)) {
926                 if (fputc('>', mbf) == EOF)
927                         return (POP_ERROR);
928         }
929         if (fputs(line, mbf) == EOF)
930                 return (POP_ERROR);
931         if (fputc(0x0a, mbf) == EOF)
932                 return (POP_ERROR);
933         return (POP_RETRIEVED);
934 }
935
936 static int mbx_delimit_begin(FILE * mbf)
937 {
938         if (fputs("\f\n0, unseen,,\n", mbf) == EOF)
939                 return (POP_ERROR);
940         return (POP_RETRIEVED);
941 }
942
943 static int mbx_delimit_end(FILE * mbf)
944 {
945         if (putc('\037', mbf) == EOF)
946                 return (POP_ERROR);
947         return (POP_RETRIEVED);
948 }
949
950 /* Turn a name, which is an ed-style (but Emacs syntax) regular
951    expression, into a real regular expression by compiling it. */
952 static struct re_pattern_buffer *compile_regex(char *pattern)
953 {
954         const char *err;
955         struct re_pattern_buffer *patbuf = 0;
956
957         patbuf =
958             (struct re_pattern_buffer *)
959             xmalloc(sizeof(struct re_pattern_buffer));
960         patbuf->translate = NULL;
961         patbuf->fastmap = NULL;
962         patbuf->buffer = NULL;
963         patbuf->allocated = 0;
964
965         err = re_compile_pattern(pattern, strlen(pattern), patbuf);
966         if (err != NULL) {
967                 error("%s while compiling pattern", err, NULL);
968                 return 0;
969         }
970
971         return patbuf;
972 }
973
974 #endif                          /* MAIL_USE_POP */
975
976 #ifndef HAVE_STRERROR
977 char *strerror(int errnum)
978 {
979         extern char *sys_errlist[];
980         extern int sys_nerr;
981
982         if (errnum >= 0 && errnum < sys_nerr)
983                 return sys_errlist[errnum];
984         return (char *)"Unknown error";
985 }
986
987 #endif                          /* ! HAVE_STRERROR */