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