Build clean up, etc. from Sebastian
[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                 desc = mkstemp(tempname);
603 #else
604                 desc = open(tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
605 #endif
606
607                 if (desc < 0) {
608                         int msz = strlen(tempname) + 50;
609                         char *message = (char *)xmalloc(msz);
610                         int sz = snprintf(message, msz,
611                                           "%s--see source file lib-src/movemail.c",
612                                           tempname);
613                         assert(sz>=0 && sz < msz);
614                         pfatal_with_name(message);
615                 }
616                 close(desc);
617
618                 tem = link(tempname, lockname);
619                 unlink(tempname);
620                 if (tem >= 0)
621                         break;
622                 sleep(1);
623
624                 /* If lock file is five minutes old, unlock it.
625                    Five minutes should be good enough to cope with crashes
626                    and wedgitude, and long enough to avoid being fooled
627                    by time differences between machines.  */
628                 if (stat(lockname, &st) >= 0) {
629                         now = time(0);
630                         if (st.st_ctime < now - 300)
631                                 unlink(lockname);
632                 }
633         }
634         strcpy(dotlock_filename, filename);
635 }
636 #endif                          /* not DISABLE_DIRECT_ACCESS */
637
638 static void unlock_dot(char *filename)
639 {
640         unlink(dot_filename(filename));
641 }
642
643 static void maybe_unlock_dot(void)
644 {
645         if (dotlock_filename)
646                 unlock_dot(dotlock_filename);
647 }
648
649 /* Print error message and exit.  */
650
651 static void fatal(char *s1, char *s2)
652 {
653         maybe_unlock_dot();
654         error(s1, s2, NULL);
655         exit(1);
656 }
657
658 /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
659
660 static void error(const char *s1, const char *s2, const char *s3)
661 {
662         fprintf(stderr, "movemail: ");
663         fprintf(stderr, s1, s2, s3);
664         fprintf(stderr, "\n");
665 }
666
667 static void pfatal_with_name(char *name)
668 {
669         char *s = concat("", strerror(errno), " for %s");
670         fatal(s, name);
671 }
672
673 static void pfatal_and_delete(char *name)
674 {
675         char *s = concat("", strerror(errno), " for %s");
676         unlink(name);
677         fatal(s, name);
678 }
679
680 /* Return a newly-allocated string whose contents concatenate those of
681    s1, s2, s3.  */
682
683 static char *concat(char *s1, char *s2, char *s3)
684 {
685         int len1 = strlen(s1), len2 = strlen(s2), len3 = strlen(s3);
686         char *result = (char *)xmalloc(len1 + len2 + len3 + 1);
687
688         strncpy(result, s1, len1+1);
689         strncpy(result + len1, s2, len2+1);
690         strncpy(result + len1 + len2, s3, len3+1);
691         *(result + len1 + len2 + len3) = '\0';
692
693         return result;
694 }
695
696 /* Like malloc but get fatal error if memory is exhausted.  */
697
698 static long *xmalloc(unsigned int size)
699 {
700         long *result = (long *)malloc(size);
701         if (!result)
702                 fatal("virtual memory exhausted", 0);
703         return result;
704 }
705
706 /* This is the guts of the interface to the Post Office Protocol.  */
707
708 #ifdef MAIL_USE_POP
709
710 #include <sys/socket.h>
711 #include <netinet/in.h>
712 #include <netdb.h>
713 #include <stdio.h>
714 #include "../src/syspwd.h"
715
716 #define POP_ERROR       (-1)
717 #define POP_RETRIEVED (0)
718 #define POP_DONE (1)
719
720 char *progname;
721 FILE *sfi;
722 FILE *sfo;
723 char ibuffer[BUFSIZ];
724 char obuffer[BUFSIZ];
725 char Errmsg[80];
726
727 static int popmail(char *user, char *outfile, char *password)
728 {
729         int nmsgs, nbytes;
730         register int i, idx;
731         int mbfi;
732         short *retrieved_list;
733         FILE *mbf;
734         popserver server;
735
736         VERBOSE(("opening server\n"));
737         server = pop_open(0, user, password, POP_NO_GETPASS);
738         if (!server) {
739                 error("%s", pop_error, NULL);
740                 return (1);
741         }
742
743         VERBOSE(("stat'ing messages\n"));
744         if (pop_stat(server, &nmsgs, &nbytes)) {
745                 error("%s", pop_error, NULL);
746                 return (1);
747         }
748
749         if (!nmsgs) {
750                 VERBOSE(("closing server\n"));
751                 pop_close(server);
752                 return (0);
753         }
754
755         /* build a retrieved table */
756         retrieved_list = (short *)xmalloc(sizeof(short) * (nmsgs + 1));
757         memset(retrieved_list, 0, sizeof(short) * (nmsgs + 1));
758
759         mbfi = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
760         if (mbfi < 0) {
761                 pop_close(server);
762                 error("Error in open: %s, %s", strerror(errno), outfile);
763                 return (1);
764         }
765         fchown(mbfi, getuid(), (gid_t) - 1);
766
767         if ((mbf = fdopen(mbfi, "wb")) == NULL) {
768                 pop_close(server);
769                 error("Error in fdopen: %s", strerror(errno), NULL);
770                 close(mbfi);
771                 unlink(outfile);
772                 return (1);
773         }
774
775         for (idx = 0; idx < nmsgs; idx++) {
776                 i = reverse ? nmsgs - idx : idx + 1;
777                 VERBOSE(("checking message %d     \n", i));
778
779                 if (!regexp_pattern
780                     ||
781                     pop_search_top(server, i, match_lines,
782                                    regexp_pattern) == POP_RETRIEVED) {
783                         VERBOSE(("retrieving message %d     \n", i));
784                         mbx_delimit_begin(mbf);
785                         if (pop_retr(server, i, mbx_write, mbf) !=
786                             POP_RETRIEVED) {
787                                 error("%s", Errmsg, NULL);
788                                 close(mbfi);
789                                 return (1);
790                         }
791
792                         retrieved_list[i] = 1;
793
794                         mbx_delimit_end(mbf);
795                         fflush(mbf);
796                         if (ferror(mbf)) {
797                                 error("Error in fflush: %s", strerror(errno),
798                                       NULL);
799                                 pop_close(server);
800                                 close(mbfi);
801                                 return (1);
802                         }
803                 }
804         }
805
806         /* On AFS, a call to write only modifies the file in the local
807          *     workstation's AFS cache.  The changes are not written to the server
808          *      until a call to fsync or close is made.  Users with AFS home
809          *      directories have lost mail when over quota because these checks were
810          *      not made in previous versions of movemail. */
811
812 #ifdef HAVE_FSYNC
813         if (fsync(mbfi) < 0) {
814                 error("Error in fsync: %s", strerror(errno), NULL);
815                 return (1);
816         }
817 #endif
818
819         if (close(mbfi) == -1) {
820                 error("Error in close: %s", strerror(errno), NULL);
821                 return (1);
822         }
823
824         if (!keep_messages) {
825                 for (i = 1; i <= nmsgs; i++) {
826                         if (retrieved_list[i] == 1) {
827                                 VERBOSE(("deleting message %d     \n", i));
828                                 if (pop_delete(server, i)) {
829                                         error("%s", pop_error, NULL);
830                                         pop_close(server);
831                                         return (1);
832                                 }
833                         }
834                 }
835         }
836
837         VERBOSE(("closing server             \n"));
838         if (pop_quit(server)) {
839                 error("%s", pop_error, NULL);
840                 return (1);
841         }
842
843         return (0);
844 }
845
846 static int
847 pop_retr(popserver server, int msgno, int (*action) (char *, FILE *),
848          FILE * arg)
849 {
850         char *line;
851         int ret;
852
853         if (pop_retrieve_first(server, msgno, &line)) {
854                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
855                 Errmsg[sizeof(Errmsg) - 1] = '\0';
856                 return (POP_ERROR);
857         }
858
859         while (!(ret = pop_retrieve_next(server, &line))) {
860                 if (!line)
861                         break;
862
863                 if ((*action) (line, arg) != POP_RETRIEVED) {
864                         strcpy(Errmsg, strerror(errno));
865                         pop_close(server);
866                         return (POP_ERROR);
867                 }
868         }
869
870         if (ret) {
871                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
872                 Errmsg[sizeof(Errmsg) - 1] = '\0';
873                 return (POP_ERROR);
874         }
875
876         return (POP_RETRIEVED);
877 }
878
879 /* search the top lines of each message looking for a match */
880 static int
881 pop_search_top(popserver server, int msgno, int lines,
882                struct re_pattern_buffer *regexp)
883 {
884         char *line;
885         int ret;
886         int match = POP_DONE;
887
888         if (pop_top_first(server, msgno, lines, &line)) {
889                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
890                 Errmsg[sizeof(Errmsg) - 1] = '\0';
891                 return (POP_ERROR);
892         }
893
894         while (!(ret = pop_top_next(server, &line))) {
895                 if (!line)
896                         break;
897
898                 /*      VERBOSE (("checking %s\n", line)); */
899                 if (match != POP_RETRIEVED) {
900                         if ((ret =
901                              re_match(regexp, line, strlen(line), 0,
902                                       0)) == -2) {
903                                 strcpy(Errmsg, "error in regular expression");
904                                 pop_close(server);
905                                 return (POP_ERROR);
906                         } else if (ret >= 0) {
907                                 match = POP_RETRIEVED;
908                         }
909                 }
910         }
911
912         if (ret) {
913                 strncpy(Errmsg, pop_error, sizeof(Errmsg));
914                 Errmsg[sizeof(Errmsg) - 1] = '\0';
915                 return (POP_ERROR);
916         }
917
918         return match;
919 }
920
921 /* Do this as a macro instead of using strcmp to save on execution time. */
922 #define IS_FROM_LINE(a) ((a[0] == 'F') \
923                          && (a[1] == 'r') \
924                          && (a[2] == 'o') \
925                          && (a[3] == 'm') \
926                          && (a[4] == ' '))
927
928 static int mbx_write(char *line, FILE * mbf)
929 {
930         if (IS_FROM_LINE(line)) {
931                 if (fputc('>', mbf) == EOF)
932                         return (POP_ERROR);
933         }
934         if (fputs(line, mbf) == EOF)
935                 return (POP_ERROR);
936         if (fputc(0x0a, mbf) == EOF)
937                 return (POP_ERROR);
938         return (POP_RETRIEVED);
939 }
940
941 static int mbx_delimit_begin(FILE * mbf)
942 {
943         if (fputs("\f\n0, unseen,,\n", mbf) == EOF)
944                 return (POP_ERROR);
945         return (POP_RETRIEVED);
946 }
947
948 static int mbx_delimit_end(FILE * mbf)
949 {
950         if (putc('\037', mbf) == EOF)
951                 return (POP_ERROR);
952         return (POP_RETRIEVED);
953 }
954
955 /* Turn a name, which is an ed-style (but Emacs syntax) regular
956    expression, into a real regular expression by compiling it. */
957 static struct re_pattern_buffer *compile_regex(char *pattern)
958 {
959         const char *err;
960         struct re_pattern_buffer *patbuf = 0;
961
962         patbuf =
963             (struct re_pattern_buffer *)
964             xmalloc(sizeof(struct re_pattern_buffer));
965         patbuf->translate = NULL;
966         patbuf->fastmap = NULL;
967         patbuf->buffer = NULL;
968         patbuf->allocated = 0;
969
970         err = re_compile_pattern(pattern, strlen(pattern), patbuf);
971         if (err != NULL) {
972                 error("%s while compiling pattern", err, NULL);
973                 return 0;
974         }
975
976         return patbuf;
977 }
978
979 #endif                          /* MAIL_USE_POP */
980
981 #ifndef HAVE_STRERROR
982 char *strerror(int errnum)
983 {
984         extern char *sys_errlist[];
985         extern int sys_nerr;
986
987         if (errnum >= 0 && errnum < sys_nerr)
988                 return sys_errlist[errnum];
989         return (char *)"Unknown error";
990 }
991
992 #endif                          /* ! HAVE_STRERROR */