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