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