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