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