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