8f99b7bdb3191197fa0c21d85ca3b07f7dd2bc8c
[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 int snoop = 0;
116
117 #ifdef POP
118 extern char response[];
119
120 static char *packfile = NULL;
121 static int size;
122 static long pos;
123 static long start;
124 static long stop;
125
126 static int mbx_style = MMDF_FORMAT;
127 static int pd = NOTOK;
128 static FILE *pf = NULL;
129 #endif /* POP */
130
131 /* This is an attempt to simplify things by putting all the
132  * privilege ops into macros.
133  * *GROUPPRIVS() is related to handling the setgid MAIL property,
134  * and only applies if MAILGROUP is defined.
135  * *USERPRIVS() is related to handling the setuid root property,
136  * and only applies if POP is defined [why does POP => setuid root?]
137  * Basically, SAVEGROUPPRIVS() is called right at the top of main()
138  * to initialise things, and then DROPGROUPPRIVS() and GETGROUPPRIVS()
139  * do the obvious thing. TRYDROPGROUPPRIVS() has to be safe to call
140  * before DROPUSERPRIVS() is called [this is needed because setgid()
141  * sets both effective and real uids if euid is root.]
142  *
143  * There's probably a better implementation if we're allowed to use
144  * BSD-style setreuid() rather than using POSIX saved-ids.
145  * Anyway, if you're euid root it's a bit pointless to drop the group
146  * permissions...
147  *
148  * I'm pretty happy that the security is good provided we aren't setuid root.
149  * The only things we trust with group=mail privilege are lkfopen()
150  * and lkfclose().
151  */
152
153 /*
154  * For setting and returning to "mail" gid
155  */
156 #ifdef MAILGROUP
157 static int return_gid;
158 #ifndef POP
159 /* easy case; we're not setuid root, so can drop group privs
160  * immediately.
161  */
162 #define TRYDROPGROUPPRIVS() DROPGROUPPRIVS()
163 #else /* POP ie we are setuid root */
164 #define TRYDROPGROUPPRIVS() \
165 if (geteuid() != 0) DROPGROUPPRIVS()
166 #endif
167 #define DROPGROUPPRIVS() setgid(getgid())
168 #define GETGROUPPRIVS() setgid(return_gid)
169 #define SAVEGROUPPRIVS() return_gid = getegid()
170 #else
171 /* define *GROUPPRIVS() as null; this avoids having lots of "#ifdef MAILGROUP"s */
172 #define TRYDROPGROUPPRIVS()
173 #define DROPGROUPPRIVS()
174 #define GETGROUPPRIVS()
175 #define SAVEGROUPPRIVS()
176 #endif /* not MAILGROUP */
177
178 /* these variables have to be globals so that done() can correctly clean up the lockfile */
179 static int locked = 0;
180 static char *newmail;
181 static FILE *in;
182
183 /*
184  * prototypes
185  */
186 char *map_name(char *);
187
188 static void inc_done(int) NORETURN;
189 #ifdef POP
190 static int pop_action(char *);
191 static int pop_pack(char *);
192 static int map_count(void);
193 #endif
194
195
196 int
197 main (int argc, char **argv)
198 {
199     int chgflag = 1, trnflag = 1;
200     int noisy = 1, width = 0;
201     int hghnum = 0, msgnum = 0;
202     int sasl = 0;
203     int incerr = 0; /* <0 if inc hits an error which means it should not truncate mailspool */
204     char *cp, *maildir = NULL, *folder = NULL;
205     char *format = NULL, *form = NULL;
206     char *host = NULL, *port = NULL, *user = NULL, *proxy = NULL;
207     char *audfile = NULL, *from = NULL, *saslmech = NULL;
208     char buf[BUFSIZ], **argp, *nfs, **arguments;
209     struct msgs *mp = NULL;
210     struct stat st, s1;
211     FILE *aud = NULL;
212     char b[MAXPATHLEN + 1];
213     char *maildir_copy = NULL;  /* copy of mail directory because the static gets overwritten */
214
215 #ifdef POP
216     int nmsgs, nbytes;
217     char *pass = NULL;
218     char *MAILHOST_env_variable;
219 #endif
220
221 #ifdef MHE
222     FILE *mhe = NULL;
223 #endif
224
225     done=inc_done;
226
227 /* absolutely the first thing we do is save our privileges,
228  * and drop them if we can.
229  */
230     SAVEGROUPPRIVS();
231     TRYDROPGROUPPRIVS();
232
233 #ifdef LOCALE
234     setlocale(LC_ALL, "");
235 #endif
236     invo_name = r1bindex (argv[0], '/');
237
238     /* read user profile/context */
239     context_read();
240
241     mts_init (invo_name);
242     arguments = getarguments (invo_name, argc, argv, 1);
243     argp = arguments;
244
245 #ifdef POP
246     /*
247      * Scheme is:
248      *        use MAILHOST environment variable if present,
249      *  else try Hesiod.
250      *  If that fails, use the default (if any)
251      *  provided by mts.conf in mts_init()
252      */
253     if ((MAILHOST_env_variable = getenv("MAILHOST")) != NULL)
254         pophost = MAILHOST_env_variable;
255     /*
256      * If there is a valid "pophost" entry in mts.conf,
257      * then use it as the default host.
258      */
259     if (pophost && *pophost)
260         host = pophost;
261
262     if ((cp = getenv ("MHPOPDEBUG")) && *cp)
263         snoop++;
264 #endif /* POP */
265
266     while ((cp = *argp++)) {
267         if (*cp == '-') {
268             switch (smatch (++cp, switches)) {
269             case AMBIGSW: 
270                 ambigsw (cp, switches);
271                 done (1);
272             case UNKWNSW: 
273                 adios (NULL, "-%s unknown", cp);
274
275             case HELPSW: 
276                 snprintf (buf, sizeof(buf), "%s [+folder] [switches]", invo_name);
277                 print_help (buf, switches, 1);
278                 done (1);
279             case VERSIONSW:
280                 print_version(invo_name);
281                 done (1);
282
283             case AUDSW: 
284                 if (!(cp = *argp++) || *cp == '-')
285                     adios (NULL, "missing argument to %s", argp[-2]);
286                 audfile = getcpy (m_maildir (cp));
287                 continue;
288             case NAUDSW: 
289                 audfile = NULL;
290                 continue;
291
292             case CHGSW: 
293                 chgflag++;
294                 continue;
295             case NCHGSW: 
296                 chgflag = 0;
297                 continue;
298
299             /*
300              * The flag `trnflag' has the value:
301              *
302              * 2 if -truncate is given
303              * 1 by default (truncating is default)
304              * 0 if -notruncate is given
305              */
306             case TRNCSW: 
307                 trnflag = 2;
308                 continue;
309             case NTRNCSW: 
310                 trnflag = 0;
311                 continue;
312
313             case FILESW: 
314                 if (!(cp = *argp++) || *cp == '-')
315                     adios (NULL, "missing argument to %s", argp[-2]);
316                 from = path (cp, TFILE);
317
318                 /*
319                  * If the truncate file is in default state,
320                  * change to not truncate.
321                  */
322                 if (trnflag == 1)
323                     trnflag = 0;
324                 continue;
325
326             case SILSW: 
327                 noisy = 0;
328                 continue;
329             case NSILSW: 
330                 noisy++;
331                 continue;
332
333             case FORMSW: 
334                 if (!(form = *argp++) || *form == '-')
335                     adios (NULL, "missing argument to %s", argp[-2]);
336                 format = NULL;
337                 continue;
338             case FMTSW: 
339                 if (!(format = *argp++) || *format == '-')
340                     adios (NULL, "missing argument to %s", argp[-2]);
341                 form = NULL;
342                 continue;
343
344             case WIDTHSW: 
345                 if (!(cp = *argp++) || *cp == '-')
346                     adios (NULL, "missing argument to %s", argp[-2]);
347                 width = atoi (cp);
348                 continue;
349
350             case HOSTSW:
351                 if (!(host = *argp++) || *host == '-')
352                     adios (NULL, "missing argument to %s", argp[-2]);
353                 continue;
354
355             case PORTSW:
356                 if (!(host = *argp++) || *port == '-')
357                     adios (NULL, "missing argument to %s", argp[-2]);
358                 continue;
359
360             case USERSW:
361                 if (!(user = *argp++) || *user == '-')
362                     adios (NULL, "missing argument to %s", argp[-2]);
363                 continue;
364
365             case PACKSW:
366 #ifndef POP
367                 if (!(cp = *argp++) || *cp == '-')
368                     adios (NULL, "missing argument to %s", argp[-2]);
369 #else /* POP */
370                 if (!(packfile = *argp++) || *packfile == '-')
371                     adios (NULL, "missing argument to %s", argp[-2]);
372 #endif /* POP */
373                 continue;
374             case NPACKSW:
375 #ifdef POP
376                 packfile = NULL;
377 #endif /* POP */
378                 continue;
379
380             case SNOOPSW:
381                 snoop++;
382                 continue;
383         
384             case SASLSW:
385                 sasl++;
386                 continue;
387         
388             case SASLMECHSW:
389                 if (!(saslmech = *argp++) || *saslmech == '-')
390                     adios (NULL, "missing argument to %s", argp[-2]);
391                 continue;
392             case PROXYSW:
393                 if (!(proxy = *argp++) || *proxy == '-')
394                     adios (NULL, "missing argument to %s", argp[-2]);
395                 continue;
396             }
397         }
398         if (*cp == '+' || *cp == '@') {
399             if (folder)
400                 adios (NULL, "only one folder at a time!");
401             else
402                 folder = pluspath (cp);
403         } else {
404             adios (NULL, "usage: %s [+folder] [switches]", invo_name);
405         }
406     }
407
408     /* NOTE: above this point you should use TRYDROPGROUPPRIVS(),
409      * not DROPGROUPPRIVS().
410      */
411 #ifdef POP
412     if (host && !*host)
413         host = NULL;
414 #endif /* POP */
415
416     /* guarantee dropping group priveleges; we might not have done so earlier */
417     DROPGROUPPRIVS();
418
419     /*
420      * Where are we getting the new mail?
421      */
422     if (from)
423         inc_type = INC_FILE;
424 #ifdef POP
425     else if (host)
426         inc_type = INC_POP;
427 #endif
428     else
429         inc_type = INC_FILE;
430
431 #ifdef POP
432     /*
433      * Are we getting the mail from
434      * a POP server?
435      */
436     if (inc_type == INC_POP) {
437         if (user == NULL)
438             user = getusername ();
439         if (sasl)
440             pass = getusername ();
441         else
442             ruserpass (host, &user, &pass);
443
444         /*
445          * initialize POP connection
446          */
447         if (pop_init (host, port, user, pass, proxy, snoop, sasl,
448                       saslmech) == NOTOK)
449             adios (NULL, "%s", response);
450
451         /* Check if there are any messages */
452         if (pop_stat (&nmsgs, &nbytes) == NOTOK)
453             adios (NULL, "%s", response);
454
455         if (nmsgs == 0) {
456             pop_quit();
457             adios (NULL, "no mail to incorporate");
458         }
459     }
460 #endif /* POP */
461
462     /*
463      * We will get the mail from a file
464      * (typically the standard maildrop)
465      */
466
467     if (inc_type == INC_FILE) {
468         if (from)
469             newmail = from;
470         else if ((newmail = getenv ("MAILDROP")) && *newmail)
471             newmail = m_mailpath (newmail);
472         else if ((newmail = context_find ("maildrop")) && *newmail)
473             newmail = m_mailpath (newmail);
474         else {
475             newmail = concat (MAILDIR, "/", MAILFIL, NULL);
476         }
477         if (stat (newmail, &s1) == NOTOK || s1.st_size == 0)
478             adios (NULL, "no mail to incorporate");
479
480         if ((cp = strdup(newmail)) == (char *)0)
481             adios (NULL, "error allocating memory to copy newmail");
482
483         newmail = cp;
484     }
485
486 #ifdef POP
487     /* skip the folder setup */
488     if ((inc_type == INC_POP) && packfile)
489         goto go_to_it;
490 #endif /* POP */
491
492     if (!context_find ("path"))
493         free (path ("./", TFOLDER));
494     if (!folder)
495         folder = getfolder (0);
496     maildir = m_maildir (folder);
497
498     if ((maildir_copy = strdup(maildir)) == (char *)0)
499         adios (maildir, "error allocating memory to copy maildir");
500
501     if (!folder_exists(maildir)) {
502         /* If the folder doesn't exist, and we're given the -silent flag,
503          * just fail.
504          */
505         if (noisy)
506             create_folder(maildir, 0, done);
507         else
508             done (1);
509     }
510
511     if (chdir (maildir) == NOTOK)
512         adios (maildir, "unable to change directory to");
513
514     /* read folder and create message structure */
515     if (!(mp = folder_read (folder)))
516         adios (NULL, "unable to read folder %s", folder);
517
518 #ifdef POP
519 go_to_it:
520 #endif /* POP */
521
522     if (inc_type == INC_FILE) {
523         if (access (newmail, W_OK) != NOTOK) {
524             locked++;
525             if (trnflag) {
526                 SIGNAL (SIGHUP, SIG_IGN);
527                 SIGNAL (SIGINT, SIG_IGN);
528                 SIGNAL (SIGQUIT, SIG_IGN);
529                 SIGNAL (SIGTERM, SIG_IGN);
530             }
531
532             GETGROUPPRIVS();       /* Reset gid to lock mail file */
533             in = lkfopen (newmail, "r");
534             DROPGROUPPRIVS();
535             if (in == NULL)
536                 adios (NULL, "unable to lock and fopen %s", newmail);
537             fstat (fileno(in), &s1);
538         } else {
539             trnflag = 0;
540             if ((in = fopen (newmail, "r")) == NULL)
541                 adios (newmail, "unable to read");
542         }
543     }
544
545     /* This shouldn't be necessary but it can't hurt. */
546     DROPGROUPPRIVS();
547
548     if (audfile) {
549         int i;
550         if ((i = stat (audfile, &st)) == NOTOK)
551             advise (NULL, "Creating Receive-Audit: %s", audfile);
552         if ((aud = fopen (audfile, "a")) == NULL)
553             adios (audfile, "unable to append to");
554         else if (i == NOTOK)
555             chmod (audfile, m_gmprot ());
556
557 #ifdef POP
558         fprintf (aud, from ? "<<inc>> %s -ms %s\n"
559                  : host ? "<<inc>> %s -host %s -user %s\n"
560                  : "<<inc>> %s\n",
561                  dtimenow (0), from ? from : host, user);
562 #else /* POP */
563         fprintf (aud, from ? "<<inc>> %s  -ms %s\n" : "<<inc>> %s\n",
564                  dtimenow (0), from);
565 #endif /* POP */
566     }
567
568 #ifdef MHE
569     if (context_find ("mhe")) {
570         int i;
571         cp = concat (maildir, "/++", NULL);
572         i = stat (cp, &st);
573         if ((mhe = fopen (cp, "a")) == NULL)
574             admonish (cp, "unable to append to");
575         else
576             if (i == NOTOK)
577                 chmod (cp, m_gmprot ());
578         free (cp);
579     }
580 #endif /* MHE */
581
582     /* Get new format string */
583     nfs = new_fs (form, format, FORMAT);
584
585     if (noisy) {
586         printf ("Incorporating new mail into %s...\n\n", folder);
587         fflush (stdout);
588     }
589
590 #ifdef POP
591     /*
592      * Get the mail from a POP server
593      */
594     if (inc_type == INC_POP) {
595         int i;
596         if (packfile) {
597             packfile = path (packfile, TFILE);
598             if (stat (packfile, &st) == NOTOK) {
599                 if (errno != ENOENT)
600                     adios (packfile, "error on file");
601                 cp = concat ("Create file \"", packfile, "\"? ", NULL);
602                 if (noisy && !getanswer (cp))
603                     done (1);
604                 free (cp);
605             }
606             msgnum = map_count ();
607             if ((pd = mbx_open (packfile, mbx_style, getuid(), getgid(), m_gmprot()))
608                 == NOTOK)
609                 adios (packfile, "unable to open");
610             if ((pf = fdopen (pd, "w+")) == NULL)
611                 adios (NULL, "unable to fdopen %s", packfile);
612         } else {
613             hghnum = msgnum = mp->hghmsg;
614             /*
615              * Check if we have enough message space for all the new
616              * messages.  If not, then realloc the folder and add enough
617              * space for all new messages plus 10 additional slots.
618              */
619             if (mp->hghmsg + nmsgs >= mp->hghoff
620                 && !(mp = folder_realloc (mp, mp->lowoff, mp->hghmsg + nmsgs + 10)))
621                 adios (NULL, "unable to allocate folder storage");
622         }
623
624         for (i = 1; i <= nmsgs; i++) {
625             msgnum++;
626             if (packfile) {
627                 fseek (pf, 0L, SEEK_CUR);
628                 pos = ftell (pf);
629                 size = 0;
630                 fwrite (mmdlm1, 1, strlen (mmdlm1), pf);
631                 start = ftell (pf);
632
633                 if (pop_retr (i, pop_pack) == NOTOK)
634                     adios (NULL, "%s", response);
635
636                 fseek (pf, 0L, SEEK_CUR);
637                 stop = ftell (pf);
638                 if (fflush (pf))
639                     adios (packfile, "write error on");
640                 fseek (pf, start, SEEK_SET);
641             } else {
642                 cp = getcpy (m_name (msgnum));
643                 if ((pf = fopen (cp, "w+")) == NULL)
644                     adios (cp, "unable to write");
645                 chmod (cp, m_gmprot ());
646                 start = stop = 0L;
647
648                 if (pop_retr (i, pop_action) == NOTOK)
649                     adios (NULL, "%s", response);
650
651                 if (fflush (pf))
652                     adios (cp, "write error on");
653                 fseek (pf, 0L, SEEK_SET);
654             }
655             switch (incerr = scan (pf, msgnum, 0, nfs, width,
656                               packfile ? 0 : msgnum == mp->hghmsg + 1 && chgflag,
657                               1, NULL, stop - start, noisy)) {
658             case SCNEOF: 
659                 printf ("%*d  empty\n", DMAXFOLDER, msgnum);
660                 break;
661
662             case SCNFAT:
663                 trnflag = 0;
664                 noisy++;
665                 /* advise (cp, "unable to read"); already advised */
666                 /* fall thru */
667
668             case SCNERR:
669             case SCNNUM: 
670                 break;
671
672             case SCNMSG: 
673             case SCNENC:
674             default: 
675                 if (aud)
676                     fputs (scanl, aud);
677 # ifdef MHE
678                 if (mhe)
679                     fputs (scanl, mhe);
680 # endif /* MHE */
681                 if (noisy)
682                     fflush (stdout);
683                 if (!packfile) {
684                     clear_msg_flags (mp, msgnum);
685                     set_exists (mp, msgnum);
686                     set_unseen (mp, msgnum);
687                     mp->msgflags |= SEQMOD;
688                 }
689                 break;
690             }
691             if (packfile) {
692                 fseek (pf, stop, SEEK_SET);
693                 fwrite (mmdlm2, 1, strlen (mmdlm2), pf);
694                 if (fflush (pf) || ferror (pf)) {
695                     int e = errno;
696                     pop_quit ();
697                     errno = e;
698                     adios (packfile, "write error on");
699                 }
700                 map_write (packfile, pd, 0, 0L, start, stop, pos, size, noisy);
701             } else {
702                 if (ferror(pf) || fclose (pf)) {
703                     int e = errno;
704                     unlink (cp);
705                     pop_quit ();
706                     errno = e;
707                     adios (cp, "write error on");
708                 }
709                 free (cp);
710             }
711
712             if (trnflag && pop_dele (i) == NOTOK)
713                 adios (NULL, "%s", response);
714         }
715
716         if (pop_quit () == NOTOK)
717             adios (NULL, "%s", response);
718         if (packfile) {
719             mbx_close (packfile, pd);
720             pd = NOTOK;
721         }
722     }
723 #endif /* POP */
724
725     /*
726      * Get the mail from file (usually mail spool)
727      */
728     if (inc_type == INC_FILE) {
729         m_unknown (in);         /* the MAGIC invocation... */
730         hghnum = msgnum = mp->hghmsg;
731         for (;;) {
732             /*
733              * Check if we need to allocate more space for message status.
734              * If so, then add space for an additional 100 messages.
735              */
736             if (msgnum >= mp->hghoff
737                 && !(mp = folder_realloc (mp, mp->lowoff, mp->hghoff + 100))) {
738                 advise (NULL, "unable to allocate folder storage");
739                 incerr = NOTOK;
740                 break;
741             }
742
743 #if 0
744             /* copy file from spool to tmp file */
745             tmpfilenam = m_scratch ("", invo_name);
746             if ((fd = creat (tmpfilenam, m_gmprot ())) == NOTOK)
747                 adios (tmpfilenam, "unable to create");
748             chmod (tmpfilenam, m_gmprot ());
749             if (!(in2 = fdopen (fd, "r+")))
750                 adios (tmpfilenam, "unable to access");
751             cpymsg (in, in2);
752
753             /* link message into folder */
754             newmsg = folder_addmsg(mp, tmpfilenam);
755 #endif
756             /* create scanline for new message */
757             switch (incerr = scan (in, msgnum + 1, msgnum + 1, nfs, width,
758                               msgnum == hghnum && chgflag, 1, NULL, 0L, noisy)) {
759             case SCNFAT:
760             case SCNEOF: 
761                 break;
762
763             case SCNERR:
764                 if (aud)
765                     fputs ("inc aborted!\n", aud);
766                 advise (NULL, "aborted!");      /* doesn't clean up locks! */
767                 break;
768
769             case SCNNUM: 
770                 advise (NULL, "BUG in %s, number out of range", invo_name);
771                 break;
772
773             default: 
774                 advise (NULL, "BUG in %s, scan() botch (%d)", invo_name, incerr);
775                 break;
776
777             case SCNMSG:
778             case SCNENC:
779                 /*
780                  *  Run the external program hook on the message.
781                  */
782
783                 (void)snprintf(b, sizeof (b), "%s/%d", maildir_copy, msgnum + 1);
784                 (void)ext_hook("add-hook", b, (char *)0);
785
786                 if (aud)
787                     fputs (scanl, aud);
788 #ifdef MHE
789                 if (mhe)
790                     fputs (scanl, mhe);
791 #endif /* MHE */
792                 if (noisy)
793                     fflush (stdout);
794
795                 msgnum++;
796                 mp->hghmsg++;
797                 mp->nummsg++;
798                 if (mp->lowmsg == 0) mp->lowmsg = 1;
799
800                 clear_msg_flags (mp, msgnum);
801                 set_exists (mp, msgnum);
802                 set_unseen (mp, msgnum);
803                 mp->msgflags |= SEQMOD;
804                 continue;
805             }
806             /* If we get here there was some sort of error from scan(),
807              * so stop processing anything more from the spool.
808              */
809             break;
810         }
811     }
812
813     if (incerr < 0) {           /* error */
814         if (locked) {
815             GETGROUPPRIVS();      /* Be sure we can unlock mail file */
816             (void) lkfclose (in, newmail); in = NULL;
817             DROPGROUPPRIVS();    /* And then return us to normal privileges */
818         } else {
819             fclose (in); in = NULL;
820         }
821         adios (NULL, "failed");
822     }
823
824     if (aud)
825         fclose (aud);
826
827 #ifdef MHE
828     if (mhe)
829         fclose (mhe);
830 #endif /* MHE */
831
832     if (noisy)
833         fflush (stdout);
834
835 #ifdef POP
836     if ((inc_type == INC_POP) && packfile)
837         done (0);
838 #endif /* POP */
839
840     /*
841      * truncate file we are incorporating from
842      */
843     if (inc_type == INC_FILE) {
844         if (trnflag) {
845             if (stat (newmail, &st) != NOTOK && s1.st_mtime != st.st_mtime)
846                 advise (NULL, "new messages have arrived!\007");
847             else {
848                 int newfd;
849                 if ((newfd = creat (newmail, 0600)) != NOTOK)
850                     close (newfd);
851                 else
852                     admonish (newmail, "error zero'ing");
853                 unlink(map_name(newmail));
854             }
855         } else {
856             if (noisy)
857                 printf ("%s not zero'd\n", newmail);
858         }
859     }
860
861     if (msgnum == hghnum) {
862         admonish (NULL, "no messages incorporated");
863     } else {
864         context_replace (pfolder, folder);      /* update current folder */
865         if (chgflag)
866             mp->curmsg = hghnum + 1;
867         mp->hghmsg = msgnum;
868         if (mp->lowmsg == 0)
869             mp->lowmsg = 1;
870         if (chgflag)            /* sigh... */
871             seq_setcur (mp, mp->curmsg);
872     }
873
874     /*
875      * unlock the mail spool
876      */
877     if (inc_type == INC_FILE) {
878         if (locked) {
879            GETGROUPPRIVS();        /* Be sure we can unlock mail file */
880            (void) lkfclose (in, newmail); in = NULL;
881            DROPGROUPPRIVS();       /* And then return us to normal privileges */
882         } else {
883             fclose (in); in = NULL;
884         }
885     }
886
887     seq_setunseen (mp, 0);      /* set the Unseen-Sequence */
888     seq_save (mp);              /* synchronize sequences   */
889     context_save ();            /* save the context file   */
890     done (0);
891     return 1;
892 }
893
894
895 #if 0
896
897 /*
898  * Copy message message from spool into
899  * temporary file.  Massage the "From " line
900  * while copying.
901  */
902
903 cpymsg (FILE *in, FILE *out)
904 {
905     int state;
906     char *tmpbuf, name[NAMESZ];
907
908     for (;;) {
909         state = m_getfld (state, name, tmpbuf, rlwidth, in);
910         switch (state) {
911         case FLD:
912         case FLDPLUS:
913             break;
914         case BODY:
915             break;
916         case LENERR:
917         case FMTERR:
918             break;
919         case FILEEOF:
920             break;
921         default:
922         }
923     }
924 }
925 #endif /* if 0 */
926
927
928 static void
929 inc_done (int status)
930 {
931 #ifdef POP
932     if (packfile && pd != NOTOK)
933         mbx_close (packfile, pd);
934 #endif /* POP */
935     if (locked)
936     {
937         GETGROUPPRIVS();
938         lkfclose(in, newmail);
939         DROPGROUPPRIVS();
940     }
941     exit (status);
942 }
943
944 #ifdef POP
945 static int
946 pop_action (char *s)
947 {
948     fprintf (pf, "%s\n", s);
949     stop += strlen (s) + 1;
950     return 0;  /* Is return value used?  This was missing before 1999-07-15. */
951 }
952
953 static int
954 pop_pack (char *s)
955 {
956     int j;
957     char buffer[BUFSIZ];
958
959     snprintf (buffer, sizeof(buffer), "%s\n", s);
960     for (j = 0; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
961         continue;
962     for (j = 0; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
963         continue;
964     fputs (buffer, pf);
965     size += strlen (buffer) + 1;
966     return 0;  /* Is return value used?  This was missing before 1999-07-15. */
967 }
968
969 static int
970 map_count (void)
971 {
972     int md;
973     char *cp;
974     struct drop d;
975     struct stat st;
976
977     if (stat (packfile, &st) == NOTOK)
978         return 0;
979     if ((md = open (cp = map_name (packfile), O_RDONLY)) == NOTOK
980             || map_chk (cp, md, &d, (long) st.st_size, 1)) {
981         if (md != NOTOK)
982             close (md);
983         return 0;
984     }
985     close (md);
986     return (d.d_id);
987 }
988 #endif /* POP */