]> diplodocus.org Git - nmh/blob - uip/inc.c
Use va_copy() to get a copy of va_list, instead of using original.
[nmh] / uip / inc.c
1 /* inc.c -- incorporate messages from a maildrop into a folder
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #ifdef MAILGROUP
9 /*
10 * Thu Feb 12 21:00 CST 2015 Marcin Cieslak <saper@saper.info>
11 * Replaced setgid() calls with setegid() so that it works with dot
12 * locking on FreeBSD. setegid() should be supported on modern POSIX
13 * systems.
14 *
15 * Revised: Sat Apr 14 17:08:17 PDT 1990 (marvit@hplabs)
16 * Added hpux hacks to set and reset gid to be "mail" as needed. The reset
17 * is necessary so inc'ed mail is the group of the inc'er, rather than
18 * "mail". We setgid to egid only when [un]locking the mail file. This
19 * is also a major security precaution which will not be explained here.
20 *
21 * Fri Feb 7 16:04:57 PST 1992 John Romine <bug-mh@ics.uci.edu>
22 * NB: I'm not 100% sure that this setgid stuff is secure even now.
23 *
24 * See the *GROUPPRIVS() macros later. I'm reasonably happy with the setgid
25 * attribute. Running setuid root is probably not a terribly good idea, though.
26 * -- Peter Maydell <pmaydell@chiark.greenend.org.uk>, 04/1998
27 *
28 * Peter Maydell's patch slightly modified for nmh 0.28-pre2.
29 * Ruud de Rooij <ruud@debian.org> Wed, 22 Jul 1998 13:24:22 +0200
30 */
31 #endif
32
33 #include <h/mh.h>
34 #include <h/utils.h>
35 #include <fcntl.h>
36 #include <h/dropsbr.h>
37 #include <h/popsbr.h>
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 "h/done.h"
44 #include "sbr/lock_file.h"
45 #include "sbr/m_maildir.h"
46 #include "sbr/m_mktemp.h"
47
48 #ifndef TLS_SUPPORT
49 # define TLSminc(a) (a)
50 #else
51 # define TLSminc(a) 0
52 #endif
53
54 #define INC_SWITCHES \
55 X("audit audit-file", 0, AUDSW) \
56 X("noaudit", 0, NAUDSW) \
57 X("changecur", 0, CHGSW) \
58 X("nochangecur", 0, NCHGSW) \
59 X("file name", 0, FILESW) \
60 X("form formatfile", 0, FORMSW) \
61 X("format string", 5, FMTSW) \
62 X("host hostname", 0, HOSTSW) \
63 X("user username", 0, USERSW) \
64 X("port name/number", 0, PORTSW) \
65 X("silent", 0, SILSW) \
66 X("nosilent", 0, NSILSW) \
67 X("truncate", 0, TRNCSW) \
68 X("notruncate", 0, NTRNCSW) \
69 X("width columns", 0, WIDTHSW) \
70 X("version", 0, VERSIONSW) \
71 X("help", 0, HELPSW) \
72 X("snoop", 0, SNOOPSW) \
73 X("sasl", 0, SASLSW) \
74 X("nosasl", 0, NOSASLSW) \
75 X("saslmech", 0, SASLMECHSW) \
76 X("initialtls", TLSminc(-10), INITTLSSW) \
77 X("notls", TLSminc(-5), NOTLSSW) \
78 X("certverify", TLSminc(-10), CERTVERSW) \
79 X("nocertverify", TLSminc(-12), NOCERTVERSW) \
80 X("authservice", 0, AUTHSERVICESW) \
81 X("proxy command", 0, PROXYSW) \
82
83 #define X(sw, minchars, id) id,
84 DEFINE_SWITCH_ENUM(INC);
85 #undef X
86
87 #define X(sw, minchars, id) { sw, minchars, id },
88 DEFINE_SWITCH_ARRAY(INC, switches);
89 #undef X
90
91 /*
92 * flags for the mail source
93 */
94 #define INC_FILE 0
95 #define INC_POP 1
96
97 static struct Maildir_entry {
98 char *filename;
99 time_t mtime;
100 } *Maildir = NULL;
101 static int num_maildir_entries = 0;
102 static bool snoop;
103
104 typedef struct {
105 FILE *mailout;
106 long written;
107 } pop_closure;
108
109 extern char response[];
110
111 /* This is an attempt to simplify things by putting all the
112 * privilege ops into macros.
113 * *GROUPPRIVS() is related to handling the setgid MAIL property,
114 * and only applies if MAILGROUP is defined.
115 * Basically, SAVEGROUPPRIVS() is called right at the top of main()
116 * to initialise things, and then DROPGROUPPRIVS() and GETGROUPPRIVS()
117 * do the obvious thing.
118 *
119 * There's probably a better implementation if we're allowed to use
120 * BSD-style setreuid() rather than using POSIX saved-ids.
121 * Anyway, if you're euid root it's a bit pointless to drop the group
122 * permissions...
123 *
124 * I'm pretty happy that the security is good provided we aren't setuid root.
125 * The only things we trust with group=mail privilege are lkfopen()
126 * and lkfclose().
127 */
128
129 /*
130 * For setting and returning to "mail" gid
131 */
132 #ifdef MAILGROUP
133 static gid_t return_gid;
134 #define TRYDROPGROUPPRIVS() DROPGROUPPRIVS()
135 #define DROPGROUPPRIVS() \
136 if (setegid(getgid()) != 0) { \
137 adios ("setegid", "unable to restore group to %ld", (long) getgid()); \
138 }
139 #define GETGROUPPRIVS() \
140 if (setegid(return_gid) != 0) { \
141 adios ("setegid", "unable to set group to %ld", (long) return_gid); \
142 }
143 #define SAVEGROUPPRIVS() return_gid = getegid()
144 #else
145 /* define *GROUPPRIVS() as null; this avoids having lots of "#ifdef MAILGROUP"s */
146 #define TRYDROPGROUPPRIVS()
147 #define DROPGROUPPRIVS()
148 #define GETGROUPPRIVS()
149 #define SAVEGROUPPRIVS()
150 #endif /* not MAILGROUP */
151
152 /* these variables have to be globals so that done() can correctly clean up the lockfile */
153 static bool locked;
154 static char *newmail;
155 static FILE *in;
156
157 /*
158 * prototypes
159 */
160 static int maildir_srt(const void *va, const void *vb) PURE;
161 static void inc_done(int) NORETURN;
162 static int pop_action(void *closure, char *);
163
164 static int
165 maildir_srt(const void *va, const void *vb)
166 {
167 const struct Maildir_entry *a = va, *b = vb;
168 if (a->mtime > b->mtime)
169 return 1;
170 if (a->mtime < b->mtime)
171 return -1;
172 return 0;
173 }
174
175 int
176 main (int argc, char **argv)
177 {
178 static int inc_type;
179 bool chgflag;
180 int trnflag = 1;
181 bool noisy;
182 int width = -1;
183 int hghnum = 0, msgnum = 0;
184 FILE *pf = NULL;
185 bool sasl, tls, noverify;
186 int incerr = 0; /* <0 if inc hits an error which means it should not truncate mailspool */
187 char *cp, *maildir = NULL, *folder = NULL;
188 char *format = NULL, *form = NULL;
189 char *host = NULL, *port = NULL, *user = NULL, *proxy = NULL;
190 char *audfile = NULL, *from = NULL, *saslmech = NULL, *auth_svc = NULL;
191 char buf[BUFSIZ], **argp, *nfs, **arguments;
192 struct msgs *mp = NULL;
193 struct stat st, s1;
194 FILE *aud = NULL;
195 char b[PATH_MAX + 1];
196 char *maildir_copy = NULL; /* copy of mail directory because the static gets overwritten */
197
198 int nmsgs, nbytes;
199 char *MAILHOST_env_variable;
200 set_done(inc_done);
201
202 /* absolutely the first thing we do is save our privileges,
203 * and drop them if we can.
204 */
205 SAVEGROUPPRIVS();
206 TRYDROPGROUPPRIVS();
207
208 if (nmh_init(argv[0], true, true)) { return 1; }
209
210 mts_init ();
211 arguments = getarguments (invo_name, argc, argv, 1);
212 argp = arguments;
213
214 /*
215 * Scheme is:
216 * use MAILHOST environment variable if present,
217 * else try Hesiod.
218 * If that fails, use the default (if any)
219 * provided by mts.conf in mts_init()
220 */
221 if ((MAILHOST_env_variable = getenv("MAILHOST")) != NULL)
222 pophost = MAILHOST_env_variable;
223 /*
224 * If there is a valid "pophost" entry in mts.conf,
225 * then use it as the default host.
226 */
227 if (pophost && *pophost)
228 host = pophost;
229
230 sasl = tls = false;
231 chgflag = noisy = noverify = true;
232 while ((cp = *argp++)) {
233 if (*cp == '-') {
234 switch (smatch (++cp, switches)) {
235 case AMBIGSW:
236 ambigsw (cp, switches);
237 done (1);
238 case UNKWNSW:
239 die("-%s unknown", cp);
240
241 case HELPSW:
242 snprintf (buf, sizeof(buf), "%s [+folder] [switches]", invo_name);
243 print_help (buf, switches, 1);
244 done (0);
245 case VERSIONSW:
246 print_version(invo_name);
247 done (0);
248
249 case AUDSW:
250 if (!(cp = *argp++) || *cp == '-')
251 die("missing argument to %s", argp[-2]);
252 audfile = mh_xstrdup(m_maildir(cp));
253 continue;
254 case NAUDSW:
255 audfile = NULL;
256 continue;
257
258 case CHGSW:
259 chgflag = true;
260 continue;
261 case NCHGSW:
262 chgflag = false;
263 continue;
264
265 /*
266 * The flag `trnflag' has the value:
267 *
268 * 2 if -truncate is given
269 * 1 by default (truncating is default)
270 * 0 if -notruncate is given
271 */
272 case TRNCSW:
273 trnflag = 2;
274 continue;
275 case NTRNCSW:
276 trnflag = 0;
277 continue;
278
279 case FILESW:
280 if (!(cp = *argp++) || *cp == '-')
281 die("missing argument to %s", argp[-2]);
282 from = path (cp, TFILE);
283
284 /*
285 * If the truncate file is in default state,
286 * change to not truncate.
287 */
288 if (trnflag == 1)
289 trnflag = 0;
290 continue;
291
292 case SILSW:
293 noisy = false;
294 continue;
295 case NSILSW:
296 noisy = true;
297 continue;
298
299 case FORMSW:
300 if (!(form = *argp++) || *form == '-')
301 die("missing argument to %s", argp[-2]);
302 format = NULL;
303 continue;
304 case FMTSW:
305 if (!(format = *argp++) || *format == '-')
306 die("missing argument to %s", argp[-2]);
307 form = NULL;
308 continue;
309
310 case WIDTHSW:
311 if (!(cp = *argp++) || *cp == '-')
312 die("missing argument to %s", argp[-2]);
313 width = atoi (cp);
314 continue;
315
316 case HOSTSW:
317 if (!(host = *argp++) || *host == '-')
318 die("missing argument to %s", argp[-2]);
319 continue;
320
321 case PORTSW:
322 if (!(port = *argp++) || *port == '-')
323 die("missing argument to %s", argp[-2]);
324 continue;
325
326 case USERSW:
327 if (!(user = *argp++) || *user == '-')
328 die("missing argument to %s", argp[-2]);
329 continue;
330
331 case SNOOPSW:
332 snoop = true;
333 continue;
334
335 case SASLSW:
336 sasl = true;
337 continue;
338 case NOSASLSW:
339 sasl = false;
340 continue;
341
342 case SASLMECHSW:
343 if (!(saslmech = *argp++) || *saslmech == '-')
344 die("missing argument to %s", argp[-2]);
345 continue;
346
347 case INITTLSSW:
348 tls = true;
349 continue;
350
351 case NOTLSSW:
352 tls = false;
353 continue;
354
355 case CERTVERSW:
356 noverify = false;
357 continue;
358
359 case NOCERTVERSW:
360 noverify = true;
361 continue;
362
363 case AUTHSERVICESW:
364 #ifdef OAUTH_SUPPORT
365 if (!(auth_svc = *argp++) || *auth_svc == '-')
366 die("missing argument to %s", argp[-2]);
367 #else
368 die("not built with OAuth support");
369 #endif
370 continue;
371
372 case PROXYSW:
373 if (!(proxy = *argp++) || *proxy == '-')
374 die("missing argument to %s", argp[-2]);
375 continue;
376 }
377 }
378 if (*cp == '+' || *cp == '@') {
379 if (folder)
380 die("only one folder at a time!");
381 folder = pluspath (cp);
382 } else {
383 die("usage: %s [+folder] [switches]", invo_name);
384 }
385 }
386
387 /* NOTE: above this point you should use TRYDROPGROUPPRIVS(),
388 * not DROPGROUPPRIVS().
389 */
390 if (host && !*host)
391 host = NULL;
392
393 /* guarantee dropping group privileges; we might not have done so earlier */
394 DROPGROUPPRIVS();
395
396 /* Source of mail; -from overrides any -host. */
397 inc_type = host && !from ? INC_POP : INC_FILE;
398
399 if (inc_type == INC_POP) {
400 /* Mail from a POP server. */
401 int tlsflag = 0;
402
403 if (auth_svc == NULL) {
404 if (saslmech && ! strcasecmp(saslmech, "xoauth2")) {
405 die("must specify -authservice with -saslmech xoauth2");
406 }
407 } else {
408 if (user == NULL) {
409 die("must specify -user with -saslmech xoauth2");
410 }
411 }
412
413 if (tls)
414 tlsflag |= P_INITTLS;
415
416 if (noverify)
417 tlsflag |= P_NOVERIFY;
418
419 /*
420 * initialize POP connection
421 */
422 if (pop_init (host, port, user, proxy, snoop, sasl, saslmech,
423 tlsflag, auth_svc) == NOTOK)
424 die("%s", response);
425
426 /* Check if there are any messages */
427 if (pop_stat (&nmsgs, &nbytes) == NOTOK)
428 die("%s", response);
429
430 if (nmsgs == 0) {
431 pop_quit();
432 die("no mail to incorporate");
433 }
434
435 } else if (inc_type == INC_FILE) {
436 /* Mail from a spool file, or Maildir. */
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 die("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 die("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 die("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 die("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 die("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 die("no mail to incorporate");
494 num_maildir_entries = i;
495 qsort (Maildir, num_maildir_entries, sizeof(*Maildir), maildir_srt);
496 }
497
498 cp = mh_xstrdup(newmail);
499 newmail = cp;
500 }
501
502 if (!context_find ("path"))
503 free (path ("./", TFOLDER));
504 if (!folder)
505 folder = getfolder (0);
506 maildir = m_maildir (folder);
507 maildir_copy = mh_xstrdup(maildir);
508
509 if (!folder_exists(maildir)) {
510 /* If the folder doesn't exist, and we're given the -silent flag,
511 * just fail.
512 */
513 if (noisy)
514 create_folder(maildir, 0, done);
515 else
516 done (1);
517 }
518
519 if (chdir (maildir) == NOTOK)
520 adios (maildir, "unable to change directory to");
521
522 /* read folder and create message structure */
523 if (!(mp = folder_read (folder, 0)))
524 die("unable to read folder %s", folder);
525
526 if (inc_type == INC_FILE && Maildir == NULL) {
527 /* Mail from a spool file. */
528
529 if (access (newmail, W_OK) != NOTOK) {
530 locked = true;
531 if (trnflag) {
532 SIGNAL (SIGHUP, SIG_IGN);
533 SIGNAL (SIGINT, SIG_IGN);
534 SIGNAL (SIGQUIT, SIG_IGN);
535 SIGNAL (SIGTERM, SIG_IGN);
536 }
537
538 GETGROUPPRIVS(); /* Reset gid to lock mail file */
539 in = lkfopenspool (newmail, "r");
540 DROPGROUPPRIVS();
541 if (in == NULL)
542 die("unable to lock and fopen %s", newmail);
543 fstat (fileno(in), &s1);
544 } else {
545 trnflag = 0;
546 if ((in = fopen (newmail, "r")) == NULL)
547 adios (newmail, "unable to read");
548 }
549 }
550
551 /* This shouldn't be necessary but it can't hurt. */
552 DROPGROUPPRIVS();
553
554 if (audfile) {
555 int i;
556 if ((i = stat (audfile, &st)) == NOTOK)
557 inform("Creating Receive-Audit: %s", audfile);
558 if ((aud = fopen (audfile, "a")) == NULL)
559 adios (audfile, "unable to append to");
560 if (i == NOTOK)
561 chmod (audfile, m_gmprot ());
562
563 if (from)
564 fprintf (aud, "<<inc>> %s -ms %s\n", dtimenow(0), from);
565 else {
566 if (host)
567 fprintf (aud, "<<inc>> %s -host %s -user %s\n", dtimenow(0),
568 host, user);
569 else
570 fprintf (aud, "<<inc>> %s\n", dtimenow (0));
571 }
572 }
573
574 /* Get new format string */
575 nfs = new_fs (form, format, FORMAT);
576
577 if (noisy) {
578 printf ("Incorporating new mail into %s...\n\n", folder);
579 fflush (stdout);
580 }
581
582
583 /*
584 * Get the mail from a POP server
585 */
586 if (inc_type == INC_POP) {
587 /* Mail from a POP server. */
588 int i;
589 pop_closure pc;
590
591 hghnum = msgnum = mp->hghmsg;
592 for (i = 1; i <= nmsgs; i++) {
593 charstring_t scanl = NULL;
594
595 msgnum++;
596 cp = mh_xstrdup(m_name (msgnum));
597 if ((pf = fopen (cp, "w+")) == NULL)
598 adios (cp, "unable to write");
599 chmod (cp, m_gmprot ());
600
601 pc.written = 0;
602 pc.mailout = pf;
603 if (pop_retr(i, pop_action, &pc) == NOTOK)
604 die("%s", response);
605
606 if (fflush (pf))
607 adios (cp, "write error on");
608 fseek (pf, 0L, SEEK_SET);
609 switch (incerr = scan (pf, msgnum, 0, nfs, width,
610 msgnum == mp->hghmsg + 1 && chgflag,
611 1, NULL, pc.written, noisy, &scanl)) {
612 case SCNEOF:
613 printf ("%*d empty\n", DMAXFOLDER, msgnum);
614 break;
615
616 case SCNFAT:
617 trnflag = 0;
618 noisy = true;
619 /* advise (cp, "unable to read"); already advised */
620 break;
621
622 case SCNERR:
623 case SCNNUM:
624 break;
625
626 case SCNMSG:
627 case SCNENC:
628 default:
629 if (aud)
630 fputs (charstring_buffer (scanl), aud);
631 if (noisy)
632 fflush (stdout);
633 break;
634 }
635 charstring_free (scanl);
636
637 if (ferror(pf) || fclose (pf)) {
638 int e = errno;
639 (void) m_unlink (cp);
640 pop_quit ();
641 errno = e;
642 adios (cp, "write error on");
643 }
644 free (cp);
645
646 if (trnflag && pop_dele (i) == NOTOK)
647 die("%s", response);
648
649 scan_finished();
650 }
651
652 if (pop_quit () == NOTOK)
653 die("%s", response);
654
655 } else if (inc_type == INC_FILE && Maildir == NULL) {
656 /* Mail from a spool file. */
657
658 scan_detect_mbox_style (in); /* the MAGIC invocation... */
659 hghnum = msgnum = mp->hghmsg;
660 for (;;) {
661 charstring_t scanl = NULL;
662
663 /* create scanline for new message */
664 switch (incerr = scan (in, msgnum + 1, msgnum + 1, nfs, width,
665 msgnum == hghnum && chgflag, 1, NULL, 0L, noisy,
666 &scanl)) {
667 case SCNFAT:
668 case SCNEOF:
669 break;
670
671 case SCNERR:
672 if (aud)
673 fputs ("inc aborted!\n", aud);
674 inform("aborted!"); /* doesn't clean up locks! */
675 break;
676
677 case SCNNUM:
678 inform("BUG in %s, number out of range", invo_name);
679 break;
680
681 default:
682 inform("BUG in %s, scan() botch (%d)", invo_name, incerr);
683 break;
684
685 case SCNMSG:
686 case SCNENC:
687 /*
688 * Run the external program hook on the message.
689 */
690
691 (void)snprintf(b, sizeof (b), "%s/%d", maildir_copy, msgnum + 1);
692 (void)ext_hook("add-hook", b, NULL);
693
694 if (aud)
695 fputs (charstring_buffer (scanl), aud);
696 if (noisy)
697 fflush (stdout);
698
699 msgnum++;
700 continue;
701 }
702 charstring_free (scanl);
703
704 /* If we get here there was some sort of error from scan(),
705 * so stop processing anything more from the spool.
706 */
707 break;
708 }
709
710 } else {
711 /* Mail from Maildir. */
712 char *sp;
713 FILE *sf;
714 int i;
715
716 hghnum = msgnum = mp->hghmsg;
717 for (i = 0; i < num_maildir_entries; i++) {
718 charstring_t scanl = NULL;
719
720 msgnum++;
721
722 sp = Maildir[i].filename;
723 cp = mh_xstrdup(m_name (msgnum));
724 pf = NULL;
725 if (!trnflag || link(sp, cp) == -1) {
726 static char buf[65536];
727 size_t nrd;
728
729 if ((sf = fopen (sp, "r")) == NULL)
730 adios (sp, "unable to read for copy");
731 if ((pf = fopen (cp, "w+")) == NULL)
732 adios (cp, "unable to write for copy");
733 while ((nrd = fread(buf, 1, sizeof(buf), sf)) > 0)
734 if (fwrite(buf, 1, nrd, pf) != nrd)
735 break;
736 if (ferror(sf) || fflush(pf) || ferror(pf)) {
737 int e = errno;
738 fclose(pf); fclose(sf); (void) m_unlink(cp);
739 errno = e;
740 adios(cp, "copy error %s -> %s", sp, cp);
741 }
742 fclose (sf);
743 sf = NULL;
744 }
745 if (pf == NULL && (pf = fopen (cp, "r")) == NULL)
746 adios (cp, "not available");
747 chmod (cp, m_gmprot ());
748
749 fseek (pf, 0L, SEEK_SET);
750 switch (incerr = scan (pf, msgnum, 0, nfs, width,
751 msgnum == mp->hghmsg + 1 && chgflag,
752 1, NULL, 0, noisy, &scanl)) {
753 case SCNEOF:
754 printf ("%*d empty\n", DMAXFOLDER, msgnum);
755 break;
756
757 case SCNFAT:
758 trnflag = 0;
759 noisy = true;
760 /* advise (cp, "unable to read"); already advised */
761 break;
762
763 case SCNERR:
764 case SCNNUM:
765 break;
766
767 case SCNMSG:
768 case SCNENC:
769 default:
770 /*
771 * Run the external program hook on the message.
772 */
773
774 (void)snprintf(b, sizeof (b), "%s/%d", maildir_copy, msgnum + 1);
775 (void)ext_hook("add-hook", b, NULL);
776
777 if (aud)
778 fputs (charstring_buffer (scanl), aud);
779 if (noisy)
780 fflush (stdout);
781 break;
782 }
783 charstring_free (scanl);
784
785 if (ferror(pf) || fclose (pf)) {
786 int e = errno;
787 (void) m_unlink (cp);
788 errno = e;
789 adios (cp, "write error on");
790 }
791 pf = NULL;
792 free (cp);
793
794 if (trnflag && m_unlink (sp) == NOTOK)
795 adios (sp, "couldn't unlink");
796 free (sp); /* Free Maildir[i]->filename */
797
798 scan_finished();
799 }
800 free (Maildir); /* From now on Maildir is just a flag - don't dref! */
801 }
802
803 scan_finished ();
804
805 if (incerr < 0) { /* error */
806 if (locked) {
807 GETGROUPPRIVS(); /* Be sure we can unlock mail file */
808 (void) lkfclosespool (in, newmail); in = NULL;
809 DROPGROUPPRIVS(); /* And then return us to normal privileges */
810 } else {
811 fclose (in); in = NULL;
812 }
813 die("failed");
814 }
815
816 if (aud)
817 fclose (aud);
818
819 if (noisy)
820 fflush (stdout);
821
822 if (inc_type == INC_FILE && Maildir == NULL) {
823 /* Mail from a spool file; truncate it. */
824
825 if (trnflag) {
826 if (stat (newmail, &st) != NOTOK && s1.st_mtime != st.st_mtime)
827 inform("new messages have arrived!\007");
828 else {
829 int newfd;
830 if ((newfd = creat (newmail, 0600)) != NOTOK)
831 close (newfd);
832 else
833 admonish (newmail, "error zero'ing");
834 }
835 } else {
836 if (noisy)
837 printf ("%s not zero'd\n", newmail);
838 }
839 }
840
841 if (msgnum == hghnum) {
842 inform("no messages incorporated, continuing...");
843 } else {
844 /*
845 * Lock the sequence file now, and loop to set the right flags
846 * in the folder structure
847 */
848
849 struct msgs *mp2;
850 int i;
851
852 context_replace (pfolder, folder); /* update current folder */
853
854 if ((mp2 = folder_read(folder, 1)) == NULL) {
855 inform("Unable to reread folder %s, continuing...", folder);
856 goto skip;
857 }
858
859 /*
860 * Shouldn't happen, but just in case ...
861 */
862
863 if (msgnum >= mp2->hghoff
864 && !(mp2 = folder_realloc (mp2, mp2->lowoff, msgnum + 1))) {
865 inform("unable to reallocate folder storage");
866 goto skip;
867 }
868
869 if (chgflag)
870 mp2->curmsg = hghnum + 1;
871 mp2->hghmsg = msgnum;
872
873 if (mp2->lowmsg == 0)
874 mp2->lowmsg = 1;
875 if (chgflag) /* sigh... */
876 seq_setcur (mp2, mp2->curmsg);
877
878 for (i = hghnum + 1; i <= msgnum; i++) {
879 clear_msg_flags (mp2, i);
880 set_exists (mp2, i);
881 set_unseen (mp2, i);
882 }
883 mp2->msgflags |= SEQMOD;
884 seq_setunseen(mp2, 0); /* Set the Unseen-Sequence */
885 seq_save(mp2); /* Save the sequence file */
886 folder_free(mp2);
887 }
888
889 skip:
890 if (inc_type == INC_FILE && Maildir == NULL) {
891 /* Mail from a spool file; unlock it. */
892
893 if (locked) {
894 GETGROUPPRIVS(); /* Be sure we can unlock mail file */
895 (void) lkfclosespool (in, newmail); in = NULL;
896 DROPGROUPPRIVS(); /* And then return us to normal privileges */
897 } else {
898 fclose (in); in = NULL;
899 }
900 }
901
902 context_save (); /* save the context file */
903 done (0);
904 return 1;
905 }
906
907
908 static void NORETURN
909 inc_done (int status)
910 {
911 set_done(exit);
912 if (locked)
913 {
914 GETGROUPPRIVS();
915 lkfclosespool(in, newmail);
916 DROPGROUPPRIVS();
917 }
918 exit (status);
919 }
920
921 static int
922 pop_action(void *closure, char *s)
923 {
924 pop_closure *pc;
925 int n;
926
927 pc = closure;
928 n = fprintf(pc->mailout, "%s\n", s);
929 if (n < 0)
930 return NOTOK;
931 pc->written += n; /* Count linefeed too. */
932
933 return OK;
934 }