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