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