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