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