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