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