XEmacs 21.2.5
[chise/xemacs-chise.git.1] / lib-src / movemail.c
1 /* movemail foo bar -- move file foo to file bar,
2    locking file foo the way /bin/mail respects.
3    Copyright (C) 1986, 1992, 1993, 1994, 1996 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; 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 /* Important notice: defining MAIL_USE_FLOCK or MAIL_USE_LOCKF *will
23    cause loss of mail* if you do it on a system that does not normally
24    use flock as its way of interlocking access to inbox files.  The
25    setting of MAIL_USE_FLOCK and MAIL_USE_LOCKF *must agree* with the
26    system's own conventions.  It is not a choice that is up to you.
27
28    So, if your system uses lock files rather than flock, then the only way
29    you can get proper operation is to enable movemail to write lockfiles there.
30    This means you must either give that directory access modes
31    that permit everyone to write lockfiles in it, or you must make movemail
32    a setuid or setgid program.  */
33
34 /*
35  * Modified January, 1986 by Michael R. Gretzinger (Project Athena)
36  *
37  * Added POP (Post Office Protocol) service.  When compiled -DMAIL_USE_POP
38  * movemail will accept input filename arguments of the form
39  * "po:username".  This will cause movemail to open a connection to
40  * a pop server running on $MAILHOST (environment variable).  Movemail
41  * must be setuid to root in order to work with POP.
42  * 
43  * New module: popmail.c
44  * Modified routines:
45  *      main - added code within #ifdef MAIL_USE_POP; added setuid (getuid ())
46  *              after POP code. 
47  * New routines in movemail.c:
48  *      get_errmsg - return pointer to system error message
49  *
50  * Modified August, 1993 by Jonathan Kamens (OpenVision Technologies)
51  *
52  * Move all of the POP code into a separate file, "pop.c".
53  * Use strerror instead of get_errmsg.
54  *
55  */
56
57 #define NO_SHORTNAMES   /* Tell config not to load remap.h */
58 #include <../src/config.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <sys/file.h>
62 #include <stdio.h>
63 #include <errno.h>
64 #include "../src/syswait.h"
65 #include "../src/systime.h"
66 #include <stdlib.h>
67 #include <string.h>
68 #include "getopt.h"
69 #ifdef MAIL_USE_POP
70 #include "pop.h"
71 #include "../src/regex.h"
72 #endif
73
74 extern char *optarg;
75 extern int optind, opterr;
76
77 #ifndef HAVE_STRERROR
78 static char * strerror (int errnum);
79 #endif /* HAVE_STRERROR */
80
81 #ifdef MSDOS
82 #undef access
83 #endif /* MSDOS */
84
85 #ifndef DIRECTORY_SEP
86 #define DIRECTORY_SEP '/'
87 #endif
88 #ifndef IS_DIRECTORY_SEP
89 #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
90 #endif
91
92 #ifdef WINDOWSNT
93 #undef access
94 #undef unlink
95 #define fork() 0
96 #define sys_wait(var) (*(var) = 0)
97 /* Unfortunately, Samba doesn't seem to properly lock Unix files even
98    though the locking call succeeds (and indeed blocks local access from
99    other NT programs).  If you have direct file access using an NFS
100    client or something other than Samba, the locking call might work
101    properly - make sure it does before you enable this! */
102 #define DISABLE_DIRECT_ACCESS
103 #include <io.h>
104 #endif /* WINDOWSNT */
105
106 #if defined (HAVE_UNISTD_H) || defined (USG)
107 #include <unistd.h>
108 #endif /* unistd.h */
109 #ifndef F_OK
110 #define F_OK 0
111 #define X_OK 1
112 #define W_OK 2
113 #define R_OK 4
114 #endif /* No F_OK */
115
116 #if defined (HAVE_FCNTL_H) || defined (USG)
117 #include <fcntl.h>
118 #endif /* fcntl.h */
119
120 #if defined (XENIX) || defined (WINDOWSNT)
121 #include <sys/locking.h>
122 #endif
123
124 #ifdef MAIL_USE_LOCKF
125 #define MAIL_USE_SYSTEM_LOCK
126 #endif
127
128 #ifdef MAIL_USE_FLOCK
129 #define MAIL_USE_SYSTEM_LOCK
130 #endif
131
132 #ifdef MAIL_USE_MMDF
133 extern int lk_open (), lk_close ();
134 #endif
135
136 /* Cancel substitutions made by config.h for Emacs.  */
137 #undef open
138 #undef read
139 #undef write
140 #undef close
141
142 static void fatal (char *, char*);
143 static void error (char *, char *, char *);
144 static void pfatal_with_name (char *);
145 static void pfatal_and_delete (char *);
146 static char *concat (char *, char *, char *);
147 static long *xmalloc (unsigned int);
148 #ifdef MAIL_USE_POP
149 static int popmail (char *, char *, char *);
150 static int pop_retr (popserver server, int msgno, int (*action)(), void *arg);
151 static int mbx_write (char *, FILE *);
152 static int mbx_delimit_begin (FILE *);
153 static int mbx_delimit_end (FILE *);
154 static struct re_pattern_buffer* compile_regex (char* regexp_pattern);
155 static int pop_search_top (popserver server, int msgno, int lines, 
156                            struct re_pattern_buffer* regexp);
157 #endif
158
159 /* Nonzero means this is name of a lock file to delete on fatal error.  */
160 char *delete_lockname;
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   { "verbose",                  no_argument,               NULL,        'v'     },
184   { 0 }
185 };
186
187 int
188 main (int argc, char *argv[])
189 {
190   char *inname=0, *outname=0, *poppass=0;
191 #ifndef DISABLE_DIRECT_ACCESS
192   int indesc, outdesc;
193   int nread;
194   int status;
195 #endif
196
197 #ifndef MAIL_USE_SYSTEM_LOCK
198   struct stat st;
199   long now;
200   int tem;
201   char *lockname, *p;
202   char *tempname;
203   int desc;
204 #endif /* not MAIL_USE_SYSTEM_LOCK */
205
206   delete_lockname = 0;
207
208   while (1)
209     {
210 #ifdef MAIL_USE_POP
211       char* optstring = "i:o:p:l:r:xvk";
212 #else
213       char* optstring = "i:o:v";
214 #endif
215       int opt = getopt_long (argc, argv, optstring, longopts, 0);
216   
217       if (opt == EOF)
218         break;
219
220       switch (opt)
221         {
222         case 0:
223           break;
224         case 1:                 /* one of the standard arguments seen */
225           if (!inname)
226             inname = optarg;
227           else if (!outname)
228             outname = optarg;
229           else
230             poppass = optarg;
231           break;
232
233         case 'i':               /* infile */
234           inname = optarg;
235           break;
236           
237         case 'o':               /* outfile */
238           outname = optarg;
239           break;
240 #ifdef MAIL_USE_POP
241         case 'p':               /* pop password */
242           poppass = optarg;     
243           break;
244         case 'k':               keep_messages=1;        break;
245         case 'x':               reverse = 1;            break;
246         case 'l':               /* lines to match */
247           match_lines = atoi (optarg);
248           break;
249
250         case 'r':               /* regular expression */
251           regexp_pattern = compile_regex (optarg);
252           break;
253 #endif
254         case 'v':               verbose = 1;    break;
255         }
256     }
257
258   while (optind < argc)
259       {
260           if (!inname)
261               inname = argv[optind];
262           else if (!outname)
263               outname = argv[optind];
264           else
265               poppass = argv[optind];
266           optind++;
267       }
268     
269   if (!inname || !outname)
270     {
271       fprintf (stderr, "Usage: movemail [-rvxk] [-l lines ] [-i] inbox [-o] destfile [[-p] POP-password]\n");
272       exit(1);
273     }
274
275 #ifdef MAIL_USE_MMDF
276   mmdf_init (argv[0]);
277 #endif
278
279   if (*outname == 0)
280     fatal ("Destination file name is empty", 0);
281
282   /* Check access to output file.  */
283   if (access (outname, F_OK) == 0 && access (outname, W_OK) != 0)
284     pfatal_with_name (outname);
285
286   /* Also check that outname's directory is writable to the real uid.  */
287   {
288     char *buf = (char *) xmalloc (strlen (outname) + 1);
289     char *cp;
290     strcpy (buf, outname);
291     cp = buf + strlen (buf);
292     while (cp > buf && !IS_DIRECTORY_SEP (cp[-1]))
293       *--cp = 0;
294     if (cp == buf)
295       *cp++ = '.';
296     if (access (buf, W_OK) != 0)
297       pfatal_with_name (buf);
298     free (buf);
299   }
300
301 #ifdef MAIL_USE_POP
302   if (!strncmp (inname, "po:", 3))
303     {
304       int retcode = popmail (inname + 3, outname, poppass);
305       exit (retcode);
306     }
307
308   setuid (getuid ());
309 #endif /* MAIL_USE_POP */
310
311 #ifndef DISABLE_DIRECT_ACCESS
312
313   /* Check access to input file.  */
314   if (access (inname, R_OK | W_OK) != 0)
315     pfatal_with_name (inname);
316
317 #ifndef MAIL_USE_MMDF
318 #ifndef MAIL_USE_SYSTEM_LOCK
319   /* Use a lock file named after our first argument with .lock appended:
320      If it exists, the mail file is locked.  */
321   /* Note: this locking mechanism is *required* by the mailer
322      (on systems which use it) to prevent loss of mail.
323
324      On systems that use a lock file, extracting the mail without locking
325      WILL occasionally cause loss of mail due to timing errors!
326
327      So, if creation of the lock file fails
328      due to access permission on the mail spool directory,
329      you simply MUST change the permission
330      and/or make movemail a setgid program
331      so it can create lock files properly.
332
333      You might also wish to verify that your system is one
334      which uses lock files for this purpose.  Some systems use other methods.
335
336      If your system uses the `flock' system call for mail locking,
337      define MAIL_USE_SYSTEM_LOCK in config.h or the s-*.h file
338      and recompile movemail.  If the s- file for your system
339      should define MAIL_USE_SYSTEM_LOCK but does not, send a bug report
340      to bug-gnu-emacs@prep.ai.mit.edu so we can fix it.  */
341
342   lockname = concat (inname, ".lock", "");
343   tempname = (char *) xmalloc (strlen (inname) + strlen ("EXXXXXX") + 1);
344   strcpy (tempname, inname);
345   p = tempname + strlen (tempname);
346   while (p != tempname && !IS_DIRECTORY_SEP (p[-1]))
347     p--;
348   *p = 0;
349   strcpy (p, "EXXXXXX");
350   mktemp (tempname);
351   unlink (tempname);
352
353   while (1)
354     {
355       /* Create the lock file, but not under the lock file name.  */
356       /* Give up if cannot do that.  */
357       desc = open (tempname, O_WRONLY | O_CREAT | O_EXCL, 0666);
358       if (desc < 0)
359         {
360           char *message = (char *) xmalloc (strlen (tempname) + 50);
361           sprintf (message, "%s--see source file lib-src/movemail.c",
362                    tempname);
363           pfatal_with_name (message);
364         }
365       close (desc);
366
367       tem = link (tempname, lockname);
368       unlink (tempname);
369       if (tem >= 0)
370         break;
371       sleep (1);
372
373       /* If lock file is five minutes old, unlock it.
374          Five minutes should be good enough to cope with crashes
375          and wedgitude, and long enough to avoid being fooled
376          by time differences between machines.  */
377       if (stat (lockname, &st) >= 0)
378         {
379           now = time (0);
380           if (st.st_ctime < now - 300)
381             unlink (lockname);
382         }
383     }
384
385   delete_lockname = lockname;
386 #endif /* not MAIL_USE_SYSTEM_LOCK */
387 #endif /* not MAIL_USE_MMDF */
388
389   if (fork () == 0)
390     {
391       setuid (getuid ());
392
393 #ifndef MAIL_USE_MMDF
394 #ifdef MAIL_USE_SYSTEM_LOCK
395       indesc = open (inname, O_RDWR);
396 #else  /* if not MAIL_USE_SYSTEM_LOCK */
397       indesc = open (inname, O_RDONLY);
398 #endif /* not MAIL_USE_SYSTEM_LOCK */
399 #else  /* MAIL_USE_MMDF */
400       indesc = lk_open (inname, O_RDONLY, 0, 0, 10);
401 #endif /* MAIL_USE_MMDF */
402
403       if (indesc < 0)
404         pfatal_with_name (inname);
405
406 #if defined (BSD) || defined (XENIX)
407       /* In case movemail is setuid to root, make sure the user can
408          read the output file.  */
409       /* This is desirable for all systems
410          but I don't want to assume all have the umask system call */
411       umask (umask (0) & 0333);
412 #endif /* BSD or Xenix */
413       outdesc = open (outname, O_WRONLY | O_CREAT | O_EXCL, 0666);
414       if (outdesc < 0)
415         pfatal_with_name (outname);
416 #ifdef MAIL_USE_SYSTEM_LOCK
417 #ifdef MAIL_USE_LOCKF
418       if (lockf (indesc, F_LOCK, 0) < 0) pfatal_with_name (inname);
419 #else /* not MAIL_USE_LOCKF */
420 #ifdef XENIX
421       if (locking (indesc, LK_RLCK, 0L) < 0) pfatal_with_name (inname);
422 #else
423 #ifdef WINDOWSNT
424       if (locking (indesc, LK_RLCK, -1L) < 0) pfatal_with_name (inname);
425 #else
426       if (flock (indesc, LOCK_EX) < 0) pfatal_with_name (inname);
427 #endif
428 #endif
429 #endif /* not MAIL_USE_LOCKF */
430 #endif /* MAIL_USE_SYSTEM_LOCK */
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 BSD
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 #ifdef MAIL_USE_SYSTEM_LOCK
460 #if defined (STRIDE) || defined (XENIX) || defined (WINDOWSNT)
461       /* Stride, xenix have file locking, but no ftruncate.  This mess will do. */
462       close (open (inname, O_CREAT | O_TRUNC | O_RDWR, 0666));
463 #else
464       ftruncate (indesc, 0L);
465 #endif /* STRIDE or XENIX */
466 #endif /* MAIL_USE_SYSTEM_LOCK */
467
468 #ifdef MAIL_USE_MMDF
469       lk_close (indesc, 0, 0, 0);
470 #else
471       close (indesc);
472 #endif
473
474 #ifndef MAIL_USE_SYSTEM_LOCK
475       /* Delete the input file; if we can't, at least get rid of its
476          contents.  */
477 #ifdef MAIL_UNLINK_SPOOL
478       /* This is generally bad to do, because it destroys the permissions
479          that were set on the file.  Better to just empty the file.  */
480       if (unlink (inname) < 0 && errno != ENOENT)
481 #endif /* MAIL_UNLINK_SPOOL */
482         creat (inname, 0600);
483 #endif /* not MAIL_USE_SYSTEM_LOCK */
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 !defined (MAIL_USE_MMDF) && !defined (MAIL_USE_SYSTEM_LOCK)
495   unlink (lockname);
496 #endif /* not MAIL_USE_MMDF and not MAIL_USE_SYSTEM_LOCK */
497
498 #endif /* ! DISABLE_DIRECT_ACCESS */
499
500   return 0;
501 }
502 \f
503 /* Print error message and exit.  */
504
505 static void
506 fatal (char *s1, char *s2)
507 {
508   if (delete_lockname)
509     unlink (delete_lockname);
510   error (s1, s2, NULL);
511   exit (1);
512 }
513
514 /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
515
516 static void
517 error (char *s1, char *s2, char *s3)
518 {
519   fprintf (stderr, "movemail: ");
520   fprintf (stderr, s1, s2, s3);
521   fprintf (stderr, "\n");
522 }
523
524 static void
525 pfatal_with_name (char *name)
526 {
527   char *s = concat ("", strerror (errno), " for %s");
528   fatal (s, name);
529 }
530
531 static void
532 pfatal_and_delete (char *name)
533 {
534   char *s = concat ("", strerror (errno), " for %s");
535   unlink (name);
536   fatal (s, name);
537 }
538
539 /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
540
541 static char *
542 concat (char *s1, char *s2, char *s3)
543 {
544   int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
545   char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
546
547   strcpy (result, s1);
548   strcpy (result + len1, s2);
549   strcpy (result + len1 + len2, s3);
550   *(result + len1 + len2 + len3) = 0;
551
552   return result;
553 }
554
555 /* Like malloc but get fatal error if memory is exhausted.  */
556
557 static long *
558 xmalloc (unsigned int size)
559 {
560   long *result = (long *) malloc (size);
561   if (!result)
562     fatal ("virtual memory exhausted", 0);
563   return result;
564 }
565 \f
566 /* This is the guts of the interface to the Post Office Protocol.  */
567
568 #ifdef MAIL_USE_POP
569
570 #ifndef WINDOWSNT
571 #include <sys/socket.h>
572 #include <netinet/in.h>
573 #include <netdb.h>
574 #else
575 #undef _WINSOCKAPI_
576 #include <winsock.h>
577 #endif
578 #include <stdio.h>
579 #include <pwd.h>
580
581 #define POP_ERROR       (-1)
582 #define POP_RETRIEVED (0)
583 #define POP_DONE (1)
584
585 char *progname;
586 FILE *sfi;
587 FILE *sfo;
588 char ibuffer[BUFSIZ];
589 char obuffer[BUFSIZ];
590 char Errmsg[80];
591
592 static int
593 popmail (char *user, char *outfile, char *password)
594 {
595   int nmsgs, nbytes;
596   register int i, idx;
597   int mbfi;
598   short* retrieved_list;
599   FILE *mbf;
600   popserver server;
601
602   VERBOSE(("opening server\r"));
603   server = pop_open (0, user, password, POP_NO_GETPASS);
604   if (! server)
605     {
606       error (pop_error, NULL, NULL);
607       return (1);
608     }
609
610   VERBOSE(("stat'ing messages\r"));
611   if (pop_stat (server, &nmsgs, &nbytes))
612     {
613       error (pop_error, NULL, NULL);
614       return (1);
615     }
616
617   if (!nmsgs)
618     {
619       VERBOSE(("closing server\n"));
620       pop_close (server);
621       return (0);
622     }
623
624   /* build a retrieved table */
625   retrieved_list = (short*) xmalloc (sizeof (short) * (nmsgs+1));
626   memset (retrieved_list, 0, sizeof (short) * (nmsgs+1));
627
628   mbfi = open (outfile, O_WRONLY | O_CREAT | O_EXCL, 0666);
629   if (mbfi < 0)
630     {
631       pop_close (server);
632       error ("Error in open: %s, %s", strerror (errno), outfile);
633       return (1);
634     }
635 #ifndef __CYGWIN32__
636   fchown (mbfi, getuid (), -1);
637 #endif
638
639   if ((mbf = fdopen (mbfi, "wb")) == NULL)
640     {
641       pop_close (server);
642       error ("Error in fdopen: %s", strerror (errno), NULL);
643       close (mbfi);
644       unlink (outfile);
645       return (1);
646     }
647
648   for (idx = 0; idx < nmsgs; idx++)
649     {
650       i = reverse ? nmsgs - idx : idx + 1;
651       VERBOSE(("checking message %d     \r", i));
652       
653       if (!regexp_pattern 
654           || 
655           pop_search_top (server, i, match_lines, regexp_pattern) == POP_RETRIEVED)
656         {
657           VERBOSE(("retrieving message %d     \r", i));
658           mbx_delimit_begin (mbf);
659           if (pop_retr (server, i, mbx_write, mbf) != POP_RETRIEVED)
660             {
661               error (Errmsg, NULL, NULL);
662               close (mbfi);
663               return (1);
664             }
665
666           retrieved_list[i]=1;
667
668           mbx_delimit_end (mbf);
669           fflush (mbf);
670           if (ferror (mbf))
671             {
672               error ("Error in fflush: %s", strerror (errno), NULL);
673               pop_close (server);
674               close (mbfi);
675               return (1);
676             }
677         }
678     }
679
680   /* On AFS, a call to write only modifies the file in the local
681    *     workstation's AFS cache.  The changes are not written to the server
682    *      until a call to fsync or close is made.  Users with AFS home
683    *      directories have lost mail when over quota because these checks were
684    *      not made in previous versions of movemail. */
685
686 #ifdef BSD
687   if (fsync (mbfi) < 0)
688     {
689       error ("Error in fsync: %s", strerror (errno), NULL);
690       return (1);
691     }
692 #endif
693
694   if (close (mbfi) == -1)
695     {
696       error ("Error in close: %s", strerror (errno), NULL);
697       return (1);
698     }
699
700   if (!keep_messages)
701     {
702       for (i = 1; i <= nmsgs; i++)
703         {
704           if (retrieved_list[i] == 1)
705             {
706               VERBOSE(("deleting message %d     \r", i));
707               if (pop_delete (server, i))
708                 {
709                   error (pop_error, NULL, NULL);
710                   pop_close (server);
711                   return (1);
712                 }
713             }
714         }
715     }
716
717   VERBOSE(("closing server             \n"));
718   if (pop_quit (server))
719     {
720       error (pop_error, NULL, NULL);
721       return (1);
722     }
723     
724   return (0);
725 }
726
727 static int
728 pop_retr (popserver server, int msgno, int (*action)(), void *arg)
729 {
730   char *line;
731   int ret;
732
733   if (pop_retrieve_first (server, msgno, &line))
734     {
735       strncpy (Errmsg, pop_error, sizeof (Errmsg));
736       Errmsg[sizeof (Errmsg)-1] = '\0';
737       return (POP_ERROR);
738     }
739
740   while (! (ret = pop_retrieve_next (server, &line)))
741     {
742       if (! line)
743         break;
744
745       if ((*action)(line, arg) != POP_RETRIEVED)
746         {
747           strcpy (Errmsg, strerror (errno));
748           pop_close (server);
749           return (POP_ERROR);
750         }
751     }
752
753   if (ret)
754     {
755       strncpy (Errmsg, pop_error, sizeof (Errmsg));
756       Errmsg[sizeof (Errmsg)-1] = '\0';
757       return (POP_ERROR);
758     }
759
760   return (POP_RETRIEVED);
761 }
762
763 /* search the top lines of each message looking for a match */
764 static int
765 pop_search_top (popserver server, int msgno, int lines, struct re_pattern_buffer* regexp)
766 {
767   char *line;
768   int ret;
769   int match = POP_DONE;
770
771   if (pop_top_first (server, msgno, lines, &line))
772     {
773       strncpy (Errmsg, pop_error, sizeof (Errmsg));
774       Errmsg[sizeof (Errmsg)-1] = '\0';
775       return (POP_ERROR);
776     }
777
778   while (! (ret = pop_top_next (server, &line)))
779     {
780       if (! line)
781         break;
782
783       /*      VERBOSE (("checking %s\n", line));*/
784       if (match != POP_RETRIEVED)
785         {
786           if ((ret = re_match (regexp, line, strlen (line), 0, 0)) == -2 )
787             {
788               strcpy (Errmsg, "error in regular expression");
789               pop_close (server);
790               return (POP_ERROR);
791             }
792           else if (ret >=0)
793             {
794               match = POP_RETRIEVED;
795             }
796         }
797     }
798
799   if (ret)
800     {
801       strncpy (Errmsg, pop_error, sizeof (Errmsg));
802       Errmsg[sizeof (Errmsg)-1] = '\0';
803       return (POP_ERROR);
804     }
805
806   return match;
807 }
808
809 /* Do this as a macro instead of using strcmp to save on execution time. */
810 #define IS_FROM_LINE(a) ((a[0] == 'F') \
811                          && (a[1] == 'r') \
812                          && (a[2] == 'o') \
813                          && (a[3] == 'm') \
814                          && (a[4] == ' '))
815
816 static int
817 mbx_write (char *line, FILE *mbf)
818 {
819   if (IS_FROM_LINE (line))
820     {
821       if (fputc ('>', mbf) == EOF)
822         return (POP_ERROR);
823     }
824   if (fputs (line, mbf) == EOF) 
825     return (POP_ERROR);
826   if (fputc (0x0a, mbf) == EOF)
827     return (POP_ERROR);
828   return (POP_RETRIEVED);
829 }
830
831 static int
832 mbx_delimit_begin (FILE *mbf)
833 {
834   if (fputs ("\f\n0, unseen,,\n", mbf) == EOF)
835     return (POP_ERROR);
836   return (POP_RETRIEVED);
837 }
838
839 static int
840 mbx_delimit_end (FILE *mbf)
841 {
842   if (putc ('\037', mbf) == EOF)
843     return (POP_ERROR);
844   return (POP_RETRIEVED);
845 }
846
847 /* Turn a name, which is an ed-style (but Emacs syntax) regular
848    expression, into a real regular expression by compiling it. */
849 static struct re_pattern_buffer*
850 compile_regex (char* pattern)
851 {
852   char *err;
853   struct re_pattern_buffer *patbuf=0;
854   
855   patbuf = (struct re_pattern_buffer*) xmalloc (sizeof (struct re_pattern_buffer));
856   patbuf->translate = NULL;
857   patbuf->fastmap = NULL;
858   patbuf->buffer = NULL;
859   patbuf->allocated = 0;
860
861   err = (char*) re_compile_pattern (pattern, strlen (pattern), patbuf);
862   if (err != NULL)
863     {
864       error ("%s while compiling pattern", err, NULL);
865       return 0;
866     }
867
868   return patbuf;
869 }
870
871
872
873 #endif /* MAIL_USE_POP */
874 \f
875 #ifndef HAVE_STRERROR
876 static char *
877 strerror (int errnum)
878 {
879   extern char *sys_errlist[];
880   extern int sys_nerr;
881
882   if (errnum >= 0 && errnum < sys_nerr)
883     return sys_errlist[errnum];
884   return (char *) "Unknown error";
885 }
886
887 #endif /* ! HAVE_STRERROR */