This commit was generated by cvs2svn to compensate for changes in r1365,
[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 static void lock_dot(char *);
208 static void unlock_dot(char *);
209 static int parse_lock_method(char *);
210 static char *unparse_lock_method(int);
211
212 int
213 main (int argc, char *argv[])
214 {
215   char *inname=0, *outname=0, *poppass=0;
216 #ifndef DISABLE_DIRECT_ACCESS
217   int indesc, outdesc;
218   int nread;
219   int status;
220 #endif
221
222   int lock_method = DEFAULT_LOCKING;
223
224   char *maybe_lock_env;
225
226   maybe_lock_env = getenv("EMACSLOCKMETHOD");
227   if (maybe_lock_env)
228     {
229       printf("maybe-lock_env: %s\n", maybe_lock_env);
230       lock_method = parse_lock_method(maybe_lock_env);
231     }
232
233   for (;;)
234     {
235 #ifdef MAIL_USE_POP
236       char* optstring = "i:o:m:p:l:r:xvhk";
237 #else
238       char* optstring = "i:o:m:vh";
239 #endif
240       int opt = getopt_long (argc, argv, optstring, longopts, 0);
241   
242       if (opt == EOF)
243         break;
244
245       switch (opt)
246         {
247         case 0:
248           break;
249         case 1:                 /* one of the standard arguments seen */
250           if (!inname)
251             inname = optarg;
252           else if (!outname)
253             outname = optarg;
254           else
255             poppass = optarg;
256           break;
257
258         case 'i':               /* infile */
259           inname = optarg;
260           break;
261           
262         case 'o':               /* outfile */
263           outname = optarg;
264           break;
265 #ifdef MAIL_USE_POP
266         case 'p':               /* pop password */
267           poppass = optarg;     
268           break;
269         case 'k':               keep_messages=1;        break;
270         case 'x':               reverse = 1;            break;
271         case 'l':               /* lines to match */
272           match_lines = atoi (optarg);
273           break;
274
275         case 'r':               /* regular expression */
276           regexp_pattern = compile_regex (optarg);
277           break;
278 #endif
279
280         case 'm':
281           lock_method = parse_lock_method(optarg);
282           break;
283         case 'h':
284           usage(lock_method);
285           exit(0);
286         case 'v':
287           verbose = 1;
288           break;
289         }
290     }
291
292   while (optind < argc)
293       {
294           if (!inname)
295               inname = argv[optind];
296           else if (!outname)
297               outname = argv[optind];
298           else
299               poppass = argv[optind];
300           optind++;
301       }
302     
303   if (!inname || !outname)
304     {
305       usage(lock_method);
306       exit(1);
307     }
308
309 #ifdef HAVE_MMDF
310   if (lock_method == MMDF)
311     mmdf_init (argv[0]);
312 #endif
313
314   if (*outname == 0)
315     fatal ("Destination file name is empty", 0);
316
317   VERBOSE(("checking access to output file\n"));
318   /* Check access to output file.  */
319   if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
320     pfatal_with_name (outname);
321
322   /* Also check that outname's directory is writable to the real uid.  */
323   {
324     char *buf = (char *) xmalloc (strlen (outname) + 1);
325     char *cp;
326     strcpy (buf, outname);
327     cp = buf + strlen (buf);
328     while (cp > buf && !IS_DIRECTORY_SEP (cp[-1]))
329       *--cp = 0;
330     if (cp == buf)
331       *cp++ = '.';
332     if (access (buf, W_OK) != 0)
333       pfatal_with_name (buf);
334     free (buf);
335   }
336
337 #ifdef MAIL_USE_POP
338   if (!strncmp (inname, "po:", 3))
339     {
340       int retcode = popmail (inname + 3, outname, poppass);
341       exit (retcode);
342     }
343
344 #ifndef WINDOWSNT
345   setuid (getuid ());
346 #endif
347 #endif /* MAIL_USE_POP */
348
349 #ifndef DISABLE_DIRECT_ACCESS
350
351   /* Check access to input file.  */
352   if (access (inname, R_OK | W_OK) != 0)
353     pfatal_with_name (inname);
354
355
356   if (fork () == 0)
357     {
358       setuid (getuid ());
359
360       VERBOSE(("opening input file\n"));
361
362       switch (lock_method)
363         {
364         case DOTLOCKING:
365           indesc = open (inname, O_RDONLY);
366           break;
367 #ifdef HAVE_LOCKF
368         case LOCKFING:
369           indesc = open (inname, O_RDWR);
370           break;
371 #endif
372 #ifdef HAVE_FLOCK
373         case FLOCKING:
374           indesc = open (inname, O_RDWR);
375           break;
376 #endif
377 #ifdef HAVE_LOCKING
378         case LOCKING:
379           indesc = open (inname, O_RDWR);
380           break;
381 #endif
382 #ifdef HAVE_MMDF
383         case MMDF:
384           indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
385           break;
386 #endif
387         default: abort();
388         }
389
390       if (indesc < 0)
391         pfatal_with_name (inname);
392
393 #ifdef HAVE_UMASK      
394       /* In case movemail is setuid to root, make sure the user can
395          read the output file.  */
396       umask (umask (0) & 0333);
397 #endif
398
399       outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
400       if (outdesc < 0)
401         pfatal_with_name (outname);
402
403       VERBOSE(("locking input file\n"));
404
405       switch (lock_method)
406         {
407 #ifdef HAVE_LOCKF
408         case LOCKFING:
409           if (lockf (indesc, F_LOCK, 0) < 0)
410             pfatal_with_name (inname);
411           break;
412 #endif
413 #ifdef HAVE_FLOCK
414         case FLOCKING:
415           if (flock (indesc, LOCK_EX) < 0)
416             pfatal_with_name (inname);
417           break;
418 #endif
419 #ifdef HAVE_LOCKING
420         case LOCKING:
421           if (locking (indesc, LK_RLCK, -1L) < 0)
422             pfatal_with_name (inname);
423           break;
424 #endif
425         case DOTLOCKING:
426           lock_dot(inname);
427           break;
428         }
429
430       VERBOSE(("copying input file to output file\n"));
431             
432       {
433         char buf[1024];
434
435         while (1)
436           {
437             nread = read (indesc, buf, sizeof buf);
438             if (nread != write (outdesc, buf, nread))
439               {
440                 int saved_errno = errno;
441                 unlink (outname);
442                 errno = saved_errno;
443                 pfatal_with_name (outname);
444               }
445             if (nread < sizeof buf)
446               break;
447           }
448       }
449
450 #ifdef HAVE_FSYNC
451       if (fsync (outdesc) < 0)
452         pfatal_and_delete (outname);
453 #endif
454
455       /* Check to make sure no errors before we zap the inbox.  */
456       if (close (outdesc) != 0)
457         pfatal_and_delete (outname);
458
459       VERBOSE(("deleting or truncating input file\n"));
460
461       switch (lock_method)
462         {
463         case LOCKFING:
464         case FLOCKING:
465         case LOCKING:
466 #ifdef HAVE_FTRUNCATE
467           ftruncate (indesc, 0L);
468 #else
469           close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
470 #endif
471           close (indesc);
472           break;
473 #ifdef HAVE_MMDF
474         case MMDF:
475           lk_close (indesc, 0, 0, 0);
476           break;
477 #endif
478         case DOTLOCKING:
479           creat (inname, 0600);
480           break;
481         }
482
483       exit (0);
484     }
485
486   wait (&status);
487   if (!WIFEXITED (status))
488     exit (1);
489   else if (WEXITSTATUS (status) != 0)
490     exit (WEXITSTATUS (status));
491
492   if (lock_method == DOTLOCKING)
493     unlock_dot(inname);
494
495 #endif /* not DISABLE_DIRECT_ACCESS */
496
497   return 0;
498 }
499
500 static void
501 usage(int lock_method)
502 {
503   printf ("Usage: movemail [-rvxkh] [-l lines ] [-m method ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
504   printf("where method is one of: dot");
505 #ifdef HAVE_LOCKF
506   printf(", lockf");
507 #endif
508 #ifdef HAVE_FLOCK
509   printf(", flock");
510 #endif
511 #ifdef HAVE_MMDF
512   printf(", mmdf");
513 #endif
514 #ifdef HAVE_LOCKING
515   printf(", locking");
516 #endif
517   printf("\nDefault is: %s\n", unparse_lock_method(lock_method));
518   
519 }
520
521 static char *
522 unparse_lock_method(int lock_method)
523 {
524   switch (lock_method)
525     {
526     case DOTLOCKING: return "dot";
527     case FLOCKING:   return "flock";
528     case LOCKFING:   return "lockf";
529     case LOCKING:    return "locking";
530     case MMDF:       return "mmdf";
531     default: abort();return 0;
532     }
533 }
534
535 static int
536 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 *
562 dot_filename(char *filename)
563 {
564   return concat (filename, ".lock", "");
565 }
566
567 static char *dotlock_filename = NULL;
568
569 static void
570 lock_dot(char *filename)
571 {
572   struct stat st;
573   long now;
574   int tem;
575   char *lockname, *p;
576   char *tempname;
577   int desc;
578
579   dotlock_filename = (char *) xmalloc(strlen(filename) + 1);
580
581   /* Use a lock file named after our first argument with .lock appended:
582      If it exists, the mail file is locked. */
583
584   lockname = dot_filename(filename);
585   tempname = (char *) xmalloc (strlen (filename) + strlen ("EXXXXXX") + 1);
586   strcpy (tempname, filename);
587   p = tempname + strlen (tempname);
588   while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
589     p--;
590   *p = 0;
591   strcpy (p, "EXXXXXX");
592   mktemp (tempname);
593   unlink (tempname);
594
595   for (;;)
596     {
597       /* Create the lock file, but not under the lock file name.  */
598       /* Give up if cannot do that.  */
599       desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
600       if (desc < 0)
601         {
602           char *message = (char *) xmalloc (strlen (tempname) + 50);
603           sprintf (message, "%s--see source file lib-src/movemail.c",
604                    tempname);
605           pfatal_with_name (message);
606         }
607       close (desc);
608
609       tem = link (tempname, lockname);
610       unlink (tempname);
611       if (tem >= 0)
612         break;
613       sleep (1);
614
615       /* If lock file is five minutes old, unlock it.
616          Five minutes should be good enough to cope with crashes
617          and wedgitude, and long enough to avoid being fooled
618          by time differences between machines.  */
619       if (stat (lockname, &st) >= 0)
620         {
621           now = time (0);
622           if (st.st_ctime < now - 300)
623             unlink (lockname);
624         }
625     }
626   strcpy(dotlock_filename, filename);
627 }
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 WINDOWSNT
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 <pwd.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 (pop_error, NULL, NULL);
745       return (1);
746     }
747
748   VERBOSE(("stat'ing messages\n"));
749   if (pop_stat (server, &nmsgs, &nbytes))
750     {
751       error (pop_error, NULL, 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(__CYGWIN32__) && !defined(WINDOWSNT)
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 (Errmsg, NULL, 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 (pop_error, NULL, 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 (pop_error, NULL, 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 */