Add support to inc for incorporting email from Maildir dropboxes.
[mmh] / uip / inc.c
1
2 /*
3  * inc.c -- incorporate messages from a maildrop into a folder
4  *
5  * This code is Copyright (c) 2002, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  */
9
10 #ifdef MAILGROUP
11 /* Revised: Sat Apr 14 17:08:17 PDT 1990 (marvit@hplabs)
12  *    Added hpux hacks to set and reset gid to be "mail" as needed. The reset
13  *    is necessary so inc'ed mail is the group of the inc'er, rather than
14  *    "mail". We setgid to egid only when [un]locking the mail file. This
15  *    is also a major security precaution which will not be explained here.
16  *
17  * Fri Feb  7 16:04:57 PST 1992         John Romine <bug-mh@ics.uci.edu>
18  *   NB: I'm not 100% sure that this setgid stuff is secure even now.
19  *
20  * See the *GROUPPRIVS() macros later. I'm reasonably happy with the setgid
21  * attribute. Running setuid root is probably not a terribly good idea, though.
22  *       -- Peter Maydell <pmaydell@chiark.greenend.org.uk>, 04/1998
23  *
24  * Peter Maydell's patch slightly modified for nmh 0.28-pre2.
25  * Ruud de Rooij <ruud@debian.org>  Wed, 22 Jul 1998 13:24:22 +0200
26  */
27 #endif
28
29 #include <h/mh.h>
30 #include <h/utils.h>
31 #include <fcntl.h>
32
33 #ifdef POP
34 # include <h/dropsbr.h>
35 # include <h/popsbr.h>
36 #endif
37
38 #include <h/fmt_scan.h>
39 #include <h/scansbr.h>
40 #include <h/signals.h>
41 #include <h/tws.h>
42 #include <h/mts.h>
43 #include <errno.h>
44 #include <signal.h>
45
46 #ifndef POP
47 # define POPminc(a) (a)
48 #else
49 # define POPminc(a)  0
50 #endif
51
52 #ifndef CYRUS_SASL
53 # define SASLminc(a) (a)
54 #else
55 # define SASLminc(a)  0
56 #endif
57
58 static struct swit switches[] = {
59 #define AUDSW                      0
60     { "audit audit-file", 0 },
61 #define NAUDSW                     1
62     { "noaudit", 0 },
63 #define CHGSW                      2
64     { "changecur", 0 },
65 #define NCHGSW                     3
66     { "nochangecur", 0 },
67 #define FILESW                     4
68     { "file name", 0 },
69 #define FORMSW                     5
70     { "form formatfile", 0 },
71 #define FMTSW                      6
72     { "format string", 5 },
73 #define HOSTSW                     7
74     { "host hostname", POPminc (-4) },
75 #define USERSW                     8
76     { "user username", POPminc (-4) },
77 #define PACKSW                     9
78     { "pack file", POPminc (-4) },
79 #define NPACKSW                   10
80     { "nopack", POPminc (-6) },
81 #define PORTSW                    11
82     { "port name/number", POPminc (-4) },
83 #define SILSW                     12
84     { "silent", 0 },
85 #define NSILSW                    13
86     { "nosilent", 0 },
87 #define TRNCSW                    14
88     { "truncate", 0 },
89 #define NTRNCSW                   15
90     { "notruncate", 0 },
91 #define WIDTHSW                   16
92     { "width columns", 0 },
93 #define VERSIONSW                 17
94     { "version", 0 },
95 #define HELPSW                    18
96     { "help", 0 },
97 #define SNOOPSW                   19
98     { "snoop", -5 },
99 #define SASLSW                    20
100     { "sasl", SASLminc(-4) },
101 #define SASLMECHSW                21
102     { "saslmech", SASLminc(-8) },
103 #define PROXYSW                   22
104     { "proxy command", POPminc(-5) },
105     { NULL, 0 }
106 };
107
108 /*
109  * flags for the mail source
110  */
111 #define INC_FILE  0
112 #define INC_POP   1
113
114 static int inc_type;
115 static struct Maildir_entry {
116         char *filename;
117         time_t mtime;
118 } *Maildir = NULL;
119 static int num_maildir_entries = 0;
120 static int snoop = 0;
121
122 #ifdef POP
123 extern char response[];
124
125 static int size;
126 static long pos;
127
128 static int mbx_style = MMDF_FORMAT;
129 static int pd = NOTOK;
130 #endif /* POP */
131
132 static long start;
133 static long stop;
134
135 static char *packfile = NULL;
136 static FILE *pf = NULL;
137
138 /* This is an attempt to simplify things by putting all the
139  * privilege ops into macros.
140  * *GROUPPRIVS() is related to handling the setgid MAIL property,
141  * and only applies if MAILGROUP is defined.
142  * *USERPRIVS() is related to handling the setuid root property,
143  * and only applies if POP is defined [why does POP => setuid root?]
144  * Basically, SAVEGROUPPRIVS() is called right at the top of main()
145  * to initialise things, and then DROPGROUPPRIVS() and GETGROUPPRIVS()
146  * do the obvious thing. TRYDROPGROUPPRIVS() has to be safe to call
147  * before DROPUSERPRIVS() is called [this is needed because setgid()
148  * sets both effective and real uids if euid is root.]
149  *
150  * There's probably a better implementation if we're allowed to use
151  * BSD-style setreuid() rather than using POSIX saved-ids.
152  * Anyway, if you're euid root it's a bit pointless to drop the group
153  * permissions...
154  *
155  * I'm pretty happy that the security is good provided we aren't setuid root.
156  * The only things we trust with group=mail privilege are lkfopen()
157  * and lkfclose().
158  */
159
160 /*
161  * For setting and returning to "mail" gid
162  */
163 #ifdef MAILGROUP
164 static int return_gid;
165 #ifndef POP
166 /* easy case; we're not setuid root, so can drop group privs
167  * immediately.
168  */
169 #define TRYDROPGROUPPRIVS() DROPGROUPPRIVS()
170 #else /* POP ie we are setuid root */
171 #define TRYDROPGROUPPRIVS() \
172 if (geteuid() != 0) DROPGROUPPRIVS()
173 #endif
174 #define DROPGROUPPRIVS() setgid(getgid())
175 #define GETGROUPPRIVS() setgid(return_gid)
176 #define SAVEGROUPPRIVS() return_gid = getegid()
177 #else
178 /* define *GROUPPRIVS() as null; this avoids having lots of "#ifdef MAILGROUP"s */
179 #define TRYDROPGROUPPRIVS()
180 #define DROPGROUPPRIVS()
181 #define GETGROUPPRIVS()
182 #define SAVEGROUPPRIVS()
183 #endif /* not MAILGROUP */
184
185 /* these variables have to be globals so that done() can correctly clean up the lockfile */
186 static int locked = 0;
187 static char *newmail;
188 static FILE *in;
189
190 /*
191  * prototypes
192  */
193 char *map_name(char *);
194
195 static void inc_done(int) NORETURN;
196 #ifdef POP
197 static int pop_action(char *);
198 static int pop_pack(char *);
199 static int map_count(void);
200 #endif
201
202 int
203 maildir_srt(const void *va, const void *vb)
204 {
205     const struct Maildir_entry *a = va, *b = vb;
206     if (a->mtime > b->mtime)
207       return 1;
208     else if (a->mtime < b->mtime)
209       return -1;
210     else
211       return 0;
212 }
213
214 int
215 main (int argc, char **argv)
216 {
217     int chgflag = 1, trnflag = 1;
218     int noisy = 1, width = 0;
219     int hghnum = 0, msgnum = 0;
220     int sasl = 0;
221     int incerr = 0; /* <0 if inc hits an error which means it should not truncate mailspool */
222     char *cp, *maildir = NULL, *folder = NULL;
223     char *format = NULL, *form = NULL;
224     char *host = NULL, *port = NULL, *user = NULL, *proxy = NULL;
225     char *audfile = NULL, *from = NULL, *saslmech = NULL;
226     char buf[BUFSIZ], **argp, *nfs, **arguments;
227     struct msgs *mp = NULL;
228     struct stat st, s1;
229     FILE *aud = NULL;
230     char b[MAXPATHLEN + 1];
231     char *maildir_copy = NULL;  /* copy of mail directory because the static gets overwritten */
232
233 #ifdef POP
234     int nmsgs, nbytes;
235     char *pass = NULL;
236     char *MAILHOST_env_variable;
237 #endif
238
239 #ifdef MHE
240     FILE *mhe = NULL;
241 #endif
242
243     done=inc_done;
244
245 /* absolutely the first thing we do is save our privileges,
246  * and drop them if we can.
247  */
248     SAVEGROUPPRIVS();
249     TRYDROPGROUPPRIVS();
250
251 #ifdef LOCALE
252     setlocale(LC_ALL, "");
253 #endif
254     invo_name = r1bindex (argv[0], '/');
255
256     /* read user profile/context */
257     context_read();
258
259     mts_init (invo_name);
260     arguments = getarguments (invo_name, argc, argv, 1);
261     argp = arguments;
262
263 #ifdef POP
264     /*
265      * Scheme is:
266      *        use MAILHOST environment variable if present,
267      *  else try Hesiod.
268      *  If that fails, use the default (if any)
269      *  provided by mts.conf in mts_init()
270      */
271     if ((MAILHOST_env_variable = getenv("MAILHOST")) != NULL)
272         pophost = MAILHOST_env_variable;
273     /*
274      * If there is a valid "pophost" entry in mts.conf,
275      * then use it as the default host.
276      */
277     if (pophost && *pophost)
278         host = pophost;
279
280     if ((cp = getenv ("MHPOPDEBUG")) && *cp)
281         snoop++;
282 #endif /* POP */
283
284     while ((cp = *argp++)) {
285         if (*cp == '-') {
286             switch (smatch (++cp, switches)) {
287             case AMBIGSW: 
288                 ambigsw (cp, switches);
289                 done (1);
290             case UNKWNSW: 
291                 adios (NULL, "-%s unknown", cp);
292
293             case HELPSW: 
294                 snprintf (buf, sizeof(buf), "%s [+folder] [switches]", invo_name);
295                 print_help (buf, switches, 1);
296                 done (1);
297             case VERSIONSW:
298                 print_version(invo_name);
299                 done (1);
300
301             case AUDSW: 
302                 if (!(cp = *argp++) || *cp == '-')
303                     adios (NULL, "missing argument to %s", argp[-2]);
304                 audfile = getcpy (m_maildir (cp));
305                 continue;
306             case NAUDSW: 
307                 audfile = NULL;
308                 continue;
309
310             case CHGSW: 
311                 chgflag++;
312                 continue;
313             case NCHGSW: 
314                 chgflag = 0;
315                 continue;
316
317             /*
318              * The flag `trnflag' has the value:
319              *
320              * 2 if -truncate is given
321              * 1 by default (truncating is default)
322              * 0 if -notruncate is given
323              */
324             case TRNCSW: 
325                 trnflag = 2;
326                 continue;
327             case NTRNCSW: 
328                 trnflag = 0;
329                 continue;
330
331             case FILESW: 
332                 if (!(cp = *argp++) || *cp == '-')
333                     adios (NULL, "missing argument to %s", argp[-2]);
334                 from = path (cp, TFILE);
335
336                 /*
337                  * If the truncate file is in default state,
338                  * change to not truncate.
339                  */
340                 if (trnflag == 1)
341                     trnflag = 0;
342                 continue;
343
344             case SILSW: 
345                 noisy = 0;
346                 continue;
347             case NSILSW: 
348                 noisy++;
349                 continue;
350
351             case FORMSW: 
352                 if (!(form = *argp++) || *form == '-')
353                     adios (NULL, "missing argument to %s", argp[-2]);
354                 format = NULL;
355                 continue;
356             case FMTSW: 
357                 if (!(format = *argp++) || *format == '-')
358                     adios (NULL, "missing argument to %s", argp[-2]);
359                 form = NULL;
360                 continue;
361
362             case WIDTHSW: 
363                 if (!(cp = *argp++) || *cp == '-')
364                     adios (NULL, "missing argument to %s", argp[-2]);
365                 width = atoi (cp);
366                 continue;
367
368             case HOSTSW:
369                 if (!(host = *argp++) || *host == '-')
370                     adios (NULL, "missing argument to %s", argp[-2]);
371                 continue;
372
373             case PORTSW:
374                 if (!(host = *argp++) || *port == '-')
375                     adios (NULL, "missing argument to %s", argp[-2]);
376                 continue;
377
378             case USERSW:
379                 if (!(user = *argp++) || *user == '-')
380                     adios (NULL, "missing argument to %s", argp[-2]);
381                 continue;
382
383             case PACKSW:
384 #ifndef POP
385                 if (!(cp = *argp++) || *cp == '-')
386                     adios (NULL, "missing argument to %s", argp[-2]);
387 #else /* POP */
388                 if (!(packfile = *argp++) || *packfile == '-')
389                     adios (NULL, "missing argument to %s", argp[-2]);
390 #endif /* POP */
391                 continue;
392             case NPACKSW:
393 #ifdef POP
394                 packfile = NULL;
395 #endif /* POP */
396                 continue;
397
398             case SNOOPSW:
399                 snoop++;
400                 continue;
401         
402             case SASLSW:
403                 sasl++;
404                 continue;
405         
406             case SASLMECHSW:
407                 if (!(saslmech = *argp++) || *saslmech == '-')
408                     adios (NULL, "missing argument to %s", argp[-2]);
409                 continue;
410             case PROXYSW:
411                 if (!(proxy = *argp++) || *proxy == '-')
412                     adios (NULL, "missing argument to %s", argp[-2]);
413                 continue;
414             }
415         }
416         if (*cp == '+' || *cp == '@') {
417             if (folder)
418                 adios (NULL, "only one folder at a time!");
419             else
420                 folder = pluspath (cp);
421         } else {
422             adios (NULL, "usage: %s [+folder] [switches]", invo_name);
423         }
424     }
425
426     /* NOTE: above this point you should use TRYDROPGROUPPRIVS(),
427      * not DROPGROUPPRIVS().
428      */
429 #ifdef POP
430     if (host && !*host)
431         host = NULL;
432 #endif /* POP */
433
434     /* guarantee dropping group priveleges; we might not have done so earlier */
435     DROPGROUPPRIVS();
436
437     /*
438      * Where are we getting the new mail?
439      */
440     if (from)
441         inc_type = INC_FILE;
442 #ifdef POP
443     else if (host)
444         inc_type = INC_POP;
445 #endif
446     else
447         inc_type = INC_FILE;
448
449 #ifdef POP
450     /*
451      * Are we getting the mail from
452      * a POP server?
453      */
454     if (inc_type == INC_POP) {
455         if (user == NULL)
456             user = getusername ();
457         if (sasl)
458             pass = getusername ();
459         else
460             ruserpass (host, &user, &pass);
461
462         /*
463          * initialize POP connection
464          */
465         if (pop_init (host, port, user, pass, proxy, snoop, sasl,
466                       saslmech) == NOTOK)
467             adios (NULL, "%s", response);
468
469         /* Check if there are any messages */
470         if (pop_stat (&nmsgs, &nbytes) == NOTOK)
471             adios (NULL, "%s", response);
472
473         if (nmsgs == 0) {
474             pop_quit();
475             adios (NULL, "no mail to incorporate");
476         }
477     }
478 #endif /* POP */
479
480     /*
481      * We will get the mail from a file
482      * (typically the standard maildrop)
483      */
484
485     if (inc_type == INC_FILE) {
486         if (from)
487             newmail = from;
488         else if ((newmail = getenv ("MAILDROP")) && *newmail)
489             newmail = m_mailpath (newmail);
490         else if ((newmail = context_find ("maildrop")) && *newmail)
491             newmail = m_mailpath (newmail);
492         else {
493             newmail = concat (MAILDIR, "/", MAILFIL, NULL);
494         }
495         if (stat (newmail, &s1) == NOTOK || s1.st_size == 0)
496             adios (NULL, "no mail to incorporate");
497         if (s1.st_mode & S_IFDIR) {
498             DIR *md;
499             struct dirent *de;
500             struct stat ms;
501             int i;
502             i = 0;
503             cp = concat (newmail, "/new", NULL);
504             if ((md = opendir(cp)) == NULL)
505                 adios (NULL, "unable to open %s", cp);
506             while ((de = readdir (md)) != NULL) {
507                 if (de->d_name[0] == '.')
508                     continue;
509                 if (i >= num_maildir_entries) {
510                     if ((Maildir = realloc(Maildir, sizeof(*Maildir) * (2*i+16))) == NULL)
511                         adios(NULL, "not enough memory for %d messages", 2*i+16);
512                     num_maildir_entries = 2*i+16;
513                 }
514                 Maildir[i].filename = concat (cp, "/", de->d_name, NULL);
515                 if (stat(Maildir[i].filename, &ms) != 0)
516                    adios (Maildir[i].filename, "couldn't get delivery time");
517                 Maildir[i].mtime = ms.st_mtime;
518                 i++;
519             }
520             free (cp);
521             closedir (md);
522             cp = concat (newmail, "/cur", NULL);
523             if ((md = opendir(cp)) == NULL)
524                 adios (NULL, "unable to open %s", cp);
525             while ((de = readdir (md)) != NULL) {
526                 if (de->d_name[0] == '.')
527                     continue;
528                 if (i >= num_maildir_entries) {
529                     if ((Maildir = realloc(Maildir, sizeof(*Maildir) * (2*i+16))) == NULL)
530                         adios(NULL, "not enough memory for %d messages", 2*i+16);
531                     num_maildir_entries = 2*i+16;
532                 }
533                 Maildir[i].filename = concat (cp, "/", de->d_name, NULL);
534                 if (stat(Maildir[i].filename, &ms) != 0)
535                    adios (Maildir[i].filename, "couldn't get delivery time");
536                 Maildir[i].mtime = ms.st_mtime;
537                 i++;
538             }
539             free (cp);
540             closedir (md);
541             if (i == 0)
542                 adios (NULL, "no mail to incorporate");
543             num_maildir_entries = i;
544             qsort (Maildir, num_maildir_entries, sizeof(*Maildir), maildir_srt);
545         }
546
547         if ((cp = strdup(newmail)) == (char *)0)
548             adios (NULL, "error allocating memory to copy newmail");
549
550         newmail = cp;
551     }
552
553 #ifdef POP
554     /* skip the folder setup */
555     if ((inc_type == INC_POP) && packfile)
556         goto go_to_it;
557 #endif /* POP */
558
559     if (!context_find ("path"))
560         free (path ("./", TFOLDER));
561     if (!folder)
562         folder = getfolder (0);
563     maildir = m_maildir (folder);
564
565     if ((maildir_copy = strdup(maildir)) == (char *)0)
566         adios (maildir, "error allocating memory to copy maildir");
567
568     if (!folder_exists(maildir)) {
569         /* If the folder doesn't exist, and we're given the -silent flag,
570          * just fail.
571          */
572         if (noisy)
573             create_folder(maildir, 0, done);
574         else
575             done (1);
576     }
577
578     if (chdir (maildir) == NOTOK)
579         adios (maildir, "unable to change directory to");
580
581     /* read folder and create message structure */
582     if (!(mp = folder_read (folder)))
583         adios (NULL, "unable to read folder %s", folder);
584
585 #ifdef POP
586 go_to_it:
587 #endif /* POP */
588
589     if (inc_type == INC_FILE && Maildir == NULL) {
590         if (access (newmail, W_OK) != NOTOK) {
591             locked++;
592             if (trnflag) {
593                 SIGNAL (SIGHUP, SIG_IGN);
594                 SIGNAL (SIGINT, SIG_IGN);
595                 SIGNAL (SIGQUIT, SIG_IGN);
596                 SIGNAL (SIGTERM, SIG_IGN);
597             }
598
599             GETGROUPPRIVS();       /* Reset gid to lock mail file */
600             in = lkfopen (newmail, "r");
601             DROPGROUPPRIVS();
602             if (in == NULL)
603                 adios (NULL, "unable to lock and fopen %s", newmail);
604             fstat (fileno(in), &s1);
605         } else {
606             trnflag = 0;
607             if ((in = fopen (newmail, "r")) == NULL)
608                 adios (newmail, "unable to read");
609         }
610     }
611
612     /* This shouldn't be necessary but it can't hurt. */
613     DROPGROUPPRIVS();
614
615     if (audfile) {
616         int i;
617         if ((i = stat (audfile, &st)) == NOTOK)
618             advise (NULL, "Creating Receive-Audit: %s", audfile);
619         if ((aud = fopen (audfile, "a")) == NULL)
620             adios (audfile, "unable to append to");
621         else if (i == NOTOK)
622             chmod (audfile, m_gmprot ());
623
624 #ifdef POP
625         fprintf (aud, from ? "<<inc>> %s -ms %s\n"
626                  : host ? "<<inc>> %s -host %s -user %s\n"
627                  : "<<inc>> %s\n",
628                  dtimenow (0), from ? from : host, user);
629 #else /* POP */
630         fprintf (aud, from ? "<<inc>> %s  -ms %s\n" : "<<inc>> %s\n",
631                  dtimenow (0), from);
632 #endif /* POP */
633     }
634
635 #ifdef MHE
636     if (context_find ("mhe")) {
637         int i;
638         cp = concat (maildir, "/++", NULL);
639         i = stat (cp, &st);
640         if ((mhe = fopen (cp, "a")) == NULL)
641             admonish (cp, "unable to append to");
642         else
643             if (i == NOTOK)
644                 chmod (cp, m_gmprot ());
645         free (cp);
646     }
647 #endif /* MHE */
648
649     /* Get new format string */
650     nfs = new_fs (form, format, FORMAT);
651
652     if (noisy) {
653         printf ("Incorporating new mail into %s...\n\n", folder);
654         fflush (stdout);
655     }
656
657 #ifdef POP
658     /*
659      * Get the mail from a POP server
660      */
661     if (inc_type == INC_POP) {
662         int i;
663         if (packfile) {
664             packfile = path (packfile, TFILE);
665             if (stat (packfile, &st) == NOTOK) {
666                 if (errno != ENOENT)
667                     adios (packfile, "error on file");
668                 cp = concat ("Create file \"", packfile, "\"? ", NULL);
669                 if (noisy && !getanswer (cp))
670                     done (1);
671                 free (cp);
672             }
673             msgnum = map_count ();
674             if ((pd = mbx_open (packfile, mbx_style, getuid(), getgid(), m_gmprot()))
675                 == NOTOK)
676                 adios (packfile, "unable to open");
677             if ((pf = fdopen (pd, "w+")) == NULL)
678                 adios (NULL, "unable to fdopen %s", packfile);
679         } else {
680             hghnum = msgnum = mp->hghmsg;
681             /*
682              * Check if we have enough message space for all the new
683              * messages.  If not, then realloc the folder and add enough
684              * space for all new messages plus 10 additional slots.
685              */
686             if (mp->hghmsg + nmsgs >= mp->hghoff
687                 && !(mp = folder_realloc (mp, mp->lowoff, mp->hghmsg + nmsgs + 10)))
688                 adios (NULL, "unable to allocate folder storage");
689         }
690
691         for (i = 1; i <= nmsgs; i++) {
692             msgnum++;
693             if (packfile) {
694                 fseek (pf, 0L, SEEK_CUR);
695                 pos = ftell (pf);
696                 size = 0;
697                 fwrite (mmdlm1, 1, strlen (mmdlm1), pf);
698                 start = ftell (pf);
699
700                 if (pop_retr (i, pop_pack) == NOTOK)
701                     adios (NULL, "%s", response);
702
703                 fseek (pf, 0L, SEEK_CUR);
704                 stop = ftell (pf);
705                 if (fflush (pf))
706                     adios (packfile, "write error on");
707                 fseek (pf, start, SEEK_SET);
708             } else {
709                 cp = getcpy (m_name (msgnum));
710                 if ((pf = fopen (cp, "w+")) == NULL)
711                     adios (cp, "unable to write");
712                 chmod (cp, m_gmprot ());
713                 start = stop = 0L;
714
715                 if (pop_retr (i, pop_action) == NOTOK)
716                     adios (NULL, "%s", response);
717
718                 if (fflush (pf))
719                     adios (cp, "write error on");
720                 fseek (pf, 0L, SEEK_SET);
721             }
722             switch (incerr = scan (pf, msgnum, 0, nfs, width,
723                               packfile ? 0 : msgnum == mp->hghmsg + 1 && chgflag,
724                               1, NULL, stop - start, noisy)) {
725             case SCNEOF: 
726                 printf ("%*d  empty\n", DMAXFOLDER, msgnum);
727                 break;
728
729             case SCNFAT:
730                 trnflag = 0;
731                 noisy++;
732                 /* advise (cp, "unable to read"); already advised */
733                 /* fall thru */
734
735             case SCNERR:
736             case SCNNUM: 
737                 break;
738
739             case SCNMSG: 
740             case SCNENC:
741             default: 
742                 if (aud)
743                     fputs (scanl, aud);
744 # ifdef MHE
745                 if (mhe)
746                     fputs (scanl, mhe);
747 # endif /* MHE */
748                 if (noisy)
749                     fflush (stdout);
750                 if (!packfile) {
751                     clear_msg_flags (mp, msgnum);
752                     set_exists (mp, msgnum);
753                     set_unseen (mp, msgnum);
754                     mp->msgflags |= SEQMOD;
755                 }
756                 break;
757             }
758             if (packfile) {
759                 fseek (pf, stop, SEEK_SET);
760                 fwrite (mmdlm2, 1, strlen (mmdlm2), pf);
761                 if (fflush (pf) || ferror (pf)) {
762                     int e = errno;
763                     pop_quit ();
764                     errno = e;
765                     adios (packfile, "write error on");
766                 }
767                 map_write (packfile, pd, 0, 0L, start, stop, pos, size, noisy);
768             } else {
769                 if (ferror(pf) || fclose (pf)) {
770                     int e = errno;
771                     unlink (cp);
772                     pop_quit ();
773                     errno = e;
774                     adios (cp, "write error on");
775                 }
776                 free (cp);
777             }
778
779             if (trnflag && pop_dele (i) == NOTOK)
780                 adios (NULL, "%s", response);
781         }
782
783         if (pop_quit () == NOTOK)
784             adios (NULL, "%s", response);
785         if (packfile) {
786             mbx_close (packfile, pd);
787             pd = NOTOK;
788         }
789     }
790 #endif /* POP */
791
792     /*
793      * Get the mail from file (usually mail spool)
794      */
795     if (inc_type == INC_FILE && Maildir == NULL) {
796         m_unknown (in);         /* the MAGIC invocation... */
797         hghnum = msgnum = mp->hghmsg;
798         for (;;) {
799             /*
800              * Check if we need to allocate more space for message status.
801              * If so, then add space for an additional 100 messages.
802              */
803             if (msgnum >= mp->hghoff
804                 && !(mp = folder_realloc (mp, mp->lowoff, mp->hghoff + 100))) {
805                 advise (NULL, "unable to allocate folder storage");
806                 incerr = NOTOK;
807                 break;
808             }
809
810 #if 0
811             /* copy file from spool to tmp file */
812             tmpfilenam = m_scratch ("", invo_name);
813             if ((fd = creat (tmpfilenam, m_gmprot ())) == NOTOK)
814                 adios (tmpfilenam, "unable to create");
815             chmod (tmpfilenam, m_gmprot ());
816             if (!(in2 = fdopen (fd, "r+")))
817                 adios (tmpfilenam, "unable to access");
818             cpymsg (in, in2);
819
820             /* link message into folder */
821             newmsg = folder_addmsg(mp, tmpfilenam);
822 #endif
823             /* create scanline for new message */
824             switch (incerr = scan (in, msgnum + 1, msgnum + 1, nfs, width,
825                               msgnum == hghnum && chgflag, 1, NULL, 0L, noisy)) {
826             case SCNFAT:
827             case SCNEOF: 
828                 break;
829
830             case SCNERR:
831                 if (aud)
832                     fputs ("inc aborted!\n", aud);
833                 advise (NULL, "aborted!");      /* doesn't clean up locks! */
834                 break;
835
836             case SCNNUM: 
837                 advise (NULL, "BUG in %s, number out of range", invo_name);
838                 break;
839
840             default: 
841                 advise (NULL, "BUG in %s, scan() botch (%d)", invo_name, incerr);
842                 break;
843
844             case SCNMSG:
845             case SCNENC:
846                 /*
847                  *  Run the external program hook on the message.
848                  */
849
850                 (void)snprintf(b, sizeof (b), "%s/%d", maildir_copy, msgnum + 1);
851                 (void)ext_hook("add-hook", b, (char *)0);
852
853                 if (aud)
854                     fputs (scanl, aud);
855 #ifdef MHE
856                 if (mhe)
857                     fputs (scanl, mhe);
858 #endif /* MHE */
859                 if (noisy)
860                     fflush (stdout);
861
862                 msgnum++;
863                 mp->hghmsg++;
864                 mp->nummsg++;
865                 if (mp->lowmsg == 0) mp->lowmsg = 1;
866
867                 clear_msg_flags (mp, msgnum);
868                 set_exists (mp, msgnum);
869                 set_unseen (mp, msgnum);
870                 mp->msgflags |= SEQMOD;
871                 continue;
872             }
873             /* If we get here there was some sort of error from scan(),
874              * so stop processing anything more from the spool.
875              */
876             break;
877         }
878     } else { /* Maildir inbox to process */
879         char *sp;
880         FILE *sf;
881         int i;
882
883         hghnum = msgnum = mp->hghmsg;
884         for (i = 0; i < num_maildir_entries; i++) {
885             msgnum++;
886             /*
887              * Check if we need to allocate more space for message status.
888              * If so, then add space for an additional 100 messages.
889              */
890             if (msgnum >= mp->hghoff
891                 && !(mp = folder_realloc (mp, mp->lowoff, mp->hghoff + 100))) {
892                 advise (NULL, "unable to allocate folder storage");
893                 incerr = NOTOK;
894                 break;
895             }
896
897             sp = Maildir[i].filename;
898             cp = getcpy (m_name (msgnum));
899             pf = NULL;
900             if (!trnflag || link(sp, cp) == -1) {
901                 static char buf[65536];
902                 size_t nrd;
903
904                 if ((sf = fopen (sp, "r")) == NULL)
905                     adios (sp, "unable to read for copy");
906                 if ((pf = fopen (cp, "w+")) == NULL)
907                     adios (cp, "unable to write for copy");
908                 while ((nrd = fread(buf, 1, sizeof(buf), sf)) > 0)
909                     if (fwrite(buf, 1, nrd, pf) != nrd)
910                         break;
911                 if (ferror(sf) || fflush(pf) || ferror(pf)) {
912                         int e = errno;
913                         fclose(pf); fclose(sf); unlink(cp);
914                         errno = e;
915                         adios(cp, "copy error %s -> %s", sp, cp);
916                 }
917                 fclose (sf);
918                 sf = NULL;
919             } 
920             if (pf == NULL && (pf = fopen (cp, "r")) == NULL)
921                 adios (cp, "not available");
922             chmod (cp, m_gmprot ());
923
924             fseek (pf, 0L, SEEK_SET);
925             switch (incerr = scan (pf, msgnum, 0, nfs, width,
926                               msgnum == mp->hghmsg + 1 && chgflag,
927                               1, NULL, stop - start, noisy)) {
928             case SCNEOF: 
929                 printf ("%*d  empty\n", DMAXFOLDER, msgnum);
930                 break;
931
932             case SCNFAT:
933                 trnflag = 0;
934                 noisy++;
935                 /* advise (cp, "unable to read"); already advised */
936                 /* fall thru */
937
938             case SCNERR:
939             case SCNNUM: 
940                 break;
941
942             case SCNMSG: 
943             case SCNENC:
944             default: 
945                 /*
946                  *  Run the external program hook on the message.
947                  */
948
949                 (void)snprintf(b, sizeof (b), "%s/%d", maildir_copy, msgnum + 1);
950                 (void)ext_hook("add-hook", b, (char *)0);
951
952                 if (aud)
953                     fputs (scanl, aud);
954 # ifdef MHE
955                 if (mhe)
956                     fputs (scanl, mhe);
957 # endif /* MHE */
958                 if (noisy)
959                     fflush (stdout);
960                 if (!packfile) {
961                     clear_msg_flags (mp, msgnum);
962                     set_exists (mp, msgnum);
963                     set_unseen (mp, msgnum);
964                     mp->msgflags |= SEQMOD;
965                 }
966                 break;
967             }
968             if (ferror(pf) || fclose (pf)) {
969                 int e = errno;
970                 unlink (cp);
971                 errno = e;
972                 adios (cp, "write error on");
973             }
974             pf = NULL;
975             free (cp);
976
977             if (trnflag && unlink (sp) == NOTOK)
978                 adios (sp, "couldn't unlink");
979             free (sp); /* Free Maildir[i]->filename */
980         }
981         free (Maildir); /* From now on Maildir is just a flag - don't dref! */
982     }
983
984     if (incerr < 0) {           /* error */
985         if (locked) {
986             GETGROUPPRIVS();      /* Be sure we can unlock mail file */
987             (void) lkfclose (in, newmail); in = NULL;
988             DROPGROUPPRIVS();    /* And then return us to normal privileges */
989         } else {
990             fclose (in); in = NULL;
991         }
992         adios (NULL, "failed");
993     }
994
995     if (aud)
996         fclose (aud);
997
998 #ifdef MHE
999     if (mhe)
1000         fclose (mhe);
1001 #endif /* MHE */
1002
1003     if (noisy)
1004         fflush (stdout);
1005
1006 #ifdef POP
1007     if ((inc_type == INC_POP) && packfile)
1008         done (0);
1009 #endif /* POP */
1010
1011     /*
1012      * truncate file we are incorporating from
1013      */
1014     if (inc_type == INC_FILE && Maildir == NULL) {
1015         if (trnflag) {
1016             if (stat (newmail, &st) != NOTOK && s1.st_mtime != st.st_mtime)
1017                 advise (NULL, "new messages have arrived!\007");
1018             else {
1019                 int newfd;
1020                 if ((newfd = creat (newmail, 0600)) != NOTOK)
1021                     close (newfd);
1022                 else
1023                     admonish (newmail, "error zero'ing");
1024                 unlink(map_name(newmail));
1025             }
1026         } else {
1027             if (noisy)
1028                 printf ("%s not zero'd\n", newmail);
1029         }
1030     }
1031
1032     if (msgnum == hghnum) {
1033         admonish (NULL, "no messages incorporated");
1034     } else {
1035         context_replace (pfolder, folder);      /* update current folder */
1036         if (chgflag)
1037             mp->curmsg = hghnum + 1;
1038         mp->hghmsg = msgnum;
1039         if (mp->lowmsg == 0)
1040             mp->lowmsg = 1;
1041         if (chgflag)            /* sigh... */
1042             seq_setcur (mp, mp->curmsg);
1043     }
1044
1045     /*
1046      * unlock the mail spool
1047      */
1048     if (inc_type == INC_FILE && Maildir == NULL) {
1049         if (locked) {
1050            GETGROUPPRIVS();        /* Be sure we can unlock mail file */
1051            (void) lkfclose (in, newmail); in = NULL;
1052            DROPGROUPPRIVS();       /* And then return us to normal privileges */
1053         } else {
1054             fclose (in); in = NULL;
1055         }
1056     }
1057
1058     seq_setunseen (mp, 0);      /* set the Unseen-Sequence */
1059     seq_save (mp);              /* synchronize sequences   */
1060     context_save ();            /* save the context file   */
1061     done (0);
1062     return 1;
1063 }
1064
1065
1066 #if 0
1067
1068 /*
1069  * Copy message message from spool into
1070  * temporary file.  Massage the "From " line
1071  * while copying.
1072  */
1073
1074 cpymsg (FILE *in, FILE *out)
1075 {
1076     int state;
1077     char *tmpbuf, name[NAMESZ];
1078
1079     for (;;) {
1080         state = m_getfld (state, name, tmpbuf, rlwidth, in);
1081         switch (state) {
1082         case FLD:
1083         case FLDPLUS:
1084             break;
1085         case BODY:
1086             break;
1087         case LENERR:
1088         case FMTERR:
1089             break;
1090         case FILEEOF:
1091             break;
1092         default:
1093         }
1094     }
1095 }
1096 #endif /* if 0 */
1097
1098
1099 static void
1100 inc_done (int status)
1101 {
1102 #ifdef POP
1103     if (packfile && pd != NOTOK)
1104         mbx_close (packfile, pd);
1105 #endif /* POP */
1106     if (locked)
1107     {
1108         GETGROUPPRIVS();
1109         lkfclose(in, newmail);
1110         DROPGROUPPRIVS();
1111     }
1112     exit (status);
1113 }
1114
1115 #ifdef POP
1116 static int
1117 pop_action (char *s)
1118 {
1119     fprintf (pf, "%s\n", s);
1120     stop += strlen (s) + 1;
1121     return 0;  /* Is return value used?  This was missing before 1999-07-15. */
1122 }
1123
1124 static int
1125 pop_pack (char *s)
1126 {
1127     int j;
1128     char buffer[BUFSIZ];
1129
1130     snprintf (buffer, sizeof(buffer), "%s\n", s);
1131     for (j = 0; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
1132         continue;
1133     for (j = 0; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
1134         continue;
1135     fputs (buffer, pf);
1136     size += strlen (buffer) + 1;
1137     return 0;  /* Is return value used?  This was missing before 1999-07-15. */
1138 }
1139
1140 static int
1141 map_count (void)
1142 {
1143     int md;
1144     char *cp;
1145     struct drop d;
1146     struct stat st;
1147
1148     if (stat (packfile, &st) == NOTOK)
1149         return 0;
1150     if ((md = open (cp = map_name (packfile), O_RDONLY)) == NOTOK
1151             || map_chk (cp, md, &d, (long) st.st_size, 1)) {
1152         if (md != NOTOK)
1153             close (md);
1154         return 0;
1155     }
1156     close (md);
1157     return (d.d_id);
1158 }
1159 #endif /* POP */