]> diplodocus.org Git - nmh/blob - uip/inc.c
Reverted commit 9a4b4a3d3b27fe4a7ff6d0b8724ce1c06b5917eb.
[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 if ((cp = strdup(newmail)) == NULL)
528 adios (NULL, "error allocating memory to copy newmail");
529
530 newmail = cp;
531 }
532
533 /* skip the folder setup */
534 if ((inc_type == INC_POP) && packfile)
535 goto go_to_it;
536
537 if (!context_find ("path"))
538 free (path ("./", TFOLDER));
539 if (!folder)
540 folder = getfolder (0);
541 maildir = m_maildir (folder);
542
543 if ((maildir_copy = strdup(maildir)) == NULL)
544 adios (maildir, "error allocating memory to copy maildir");
545
546 if (!folder_exists(maildir)) {
547 /* If the folder doesn't exist, and we're given the -silent flag,
548 * just fail.
549 */
550 if (noisy)
551 create_folder(maildir, 0, done);
552 else
553 done (1);
554 }
555
556 if (chdir (maildir) == NOTOK)
557 adios (maildir, "unable to change directory to");
558
559 /* read folder and create message structure */
560 if (!(mp = folder_read (folder, 0)))
561 adios (NULL, "unable to read folder %s", folder);
562
563 go_to_it:
564
565 if (inc_type == INC_FILE && Maildir == NULL) {
566 if (access (newmail, W_OK) != NOTOK) {
567 locked++;
568 if (trnflag) {
569 SIGNAL (SIGHUP, SIG_IGN);
570 SIGNAL (SIGINT, SIG_IGN);
571 SIGNAL (SIGQUIT, SIG_IGN);
572 SIGNAL (SIGTERM, SIG_IGN);
573 }
574
575 GETGROUPPRIVS(); /* Reset gid to lock mail file */
576 in = lkfopenspool (newmail, "r");
577 DROPGROUPPRIVS();
578 if (in == NULL)
579 adios (NULL, "unable to lock and fopen %s", newmail);
580 fstat (fileno(in), &s1);
581 } else {
582 trnflag = 0;
583 if ((in = fopen (newmail, "r")) == NULL)
584 adios (newmail, "unable to read");
585 }
586 }
587
588 /* This shouldn't be necessary but it can't hurt. */
589 DROPGROUPPRIVS();
590
591 if (audfile) {
592 int i;
593 if ((i = stat (audfile, &st)) == NOTOK)
594 advise (NULL, "Creating Receive-Audit: %s", audfile);
595 if ((aud = fopen (audfile, "a")) == NULL)
596 adios (audfile, "unable to append to");
597 else if (i == NOTOK)
598 chmod (audfile, m_gmprot ());
599
600 if (from)
601 fprintf (aud, "<<inc>> %s -ms %s\n", dtimenow(0), from);
602 else {
603 if (host)
604 fprintf (aud, "<<inc>> %s -host %s -user %s\n", dtimenow(0),
605 host, user);
606 else
607 fprintf (aud, "<<inc>> %s\n", dtimenow (0));
608 }
609 }
610
611 /* Get new format string */
612 nfs = new_fs (form, format, FORMAT);
613
614 if (noisy) {
615 printf ("Incorporating new mail into %s...\n\n", folder);
616 fflush (stdout);
617 }
618
619
620 /*
621 * Get the mail from a POP server
622 */
623 if (inc_type == INC_POP) {
624 int i;
625 if (packfile) {
626 packfile = path (packfile, TFILE);
627 if (stat (packfile, &st) == NOTOK) {
628 if (errno != ENOENT)
629 adios (packfile, "error on file");
630 cp = concat ("Create file \"", packfile, "\"? ", NULL);
631 if (noisy && !read_yes_or_no_if_tty (cp))
632 done (1);
633 free (cp);
634 }
635 msgnum = map_count ();
636 if ((pd = mbx_open (packfile, mbx_style, getuid(), getgid(), m_gmprot()))
637 == NOTOK)
638 adios (packfile, "unable to open");
639 if ((pf = fdopen (pd, "w+")) == NULL)
640 adios (NULL, "unable to fdopen %s", packfile);
641 } else {
642 hghnum = msgnum = mp->hghmsg;
643 }
644
645 for (i = 1; i <= nmsgs; i++) {
646 charstring_t scanl = NULL;
647
648 msgnum++;
649 if (packfile) {
650 size_t len;
651
652 fseek (pf, 0L, SEEK_CUR);
653 pos = ftell (pf);
654 size = 0;
655 len = strlen(mmdlm1);
656 if (fwrite(mmdlm1, 1, len, pf) < len)
657 advise (mmdlm1, "fwrite");
658 start = ftell (pf);
659
660 if (pop_retr (i, pop_pack) == NOTOK)
661 adios (NULL, "%s", response);
662
663 fseek (pf, 0L, SEEK_CUR);
664 stop = ftell (pf);
665 if (fflush (pf))
666 adios (packfile, "write error on");
667 fseek (pf, start, SEEK_SET);
668 } else {
669 cp = mh_xstrdup(m_name (msgnum));
670 if ((pf = fopen (cp, "w+")) == NULL)
671 adios (cp, "unable to write");
672 chmod (cp, m_gmprot ());
673 start = stop = 0L;
674
675 if (pop_retr (i, pop_action) == NOTOK)
676 adios (NULL, "%s", response);
677
678 if (fflush (pf))
679 adios (cp, "write error on");
680 fseek (pf, 0L, SEEK_SET);
681 }
682 switch (incerr = scan (pf, msgnum, 0, nfs, width,
683 packfile ? 0 : msgnum == mp->hghmsg + 1 && chgflag,
684 1, NULL, stop - start, noisy, &scanl)) {
685 case SCNEOF:
686 printf ("%*d empty\n", DMAXFOLDER, msgnum);
687 break;
688
689 case SCNFAT:
690 trnflag = 0;
691 noisy++;
692 /* advise (cp, "unable to read"); already advised */
693 /* fall thru */
694
695 case SCNERR:
696 case SCNNUM:
697 break;
698
699 case SCNMSG:
700 case SCNENC:
701 default:
702 if (aud)
703 fputs (charstring_buffer (scanl), aud);
704 if (noisy)
705 fflush (stdout);
706 break;
707 }
708 charstring_free (scanl);
709
710 if (packfile) {
711 size_t len;
712
713 fseek (pf, stop, SEEK_SET);
714 len = strlen(mmdlm2);
715 if (fwrite(mmdlm2, 1, len, pf) < len)
716 advise (mmdlm2, "fwrite");
717 if (fflush (pf) || ferror (pf)) {
718 int e = errno;
719 pop_quit ();
720 errno = e;
721 adios (packfile, "write error on");
722 }
723 map_write (packfile, pd, 0, 0L, start, stop, pos, size, noisy);
724 } else {
725 if (ferror(pf) || fclose (pf)) {
726 int e = errno;
727 (void) m_unlink (cp);
728 pop_quit ();
729 errno = e;
730 adios (cp, "write error on");
731 }
732 free (cp);
733 }
734
735 if (trnflag && pop_dele (i) == NOTOK)
736 adios (NULL, "%s", response);
737
738 scan_finished();
739 }
740
741 if (pop_quit () == NOTOK)
742 adios (NULL, "%s", response);
743 if (packfile) {
744 mbx_close (packfile, pd);
745 pd = NOTOK;
746 }
747 }
748
749 /*
750 * Get the mail from file (usually mail spool)
751 */
752 if (inc_type == INC_FILE && Maildir == NULL) {
753 scan_detect_mbox_style (in); /* the MAGIC invocation... */
754 hghnum = msgnum = mp->hghmsg;
755 for (;;) {
756 charstring_t scanl = NULL;
757
758 /* create scanline for new message */
759 switch (incerr = scan (in, msgnum + 1, msgnum + 1, nfs, width,
760 msgnum == hghnum && chgflag, 1, NULL, 0L, noisy,
761 &scanl)) {
762 case SCNFAT:
763 case SCNEOF:
764 break;
765
766 case SCNERR:
767 if (aud)
768 fputs ("inc aborted!\n", aud);
769 advise (NULL, "aborted!"); /* doesn't clean up locks! */
770 break;
771
772 case SCNNUM:
773 advise (NULL, "BUG in %s, number out of range", invo_name);
774 break;
775
776 default:
777 advise (NULL, "BUG in %s, scan() botch (%d)", invo_name, incerr);
778 break;
779
780 case SCNMSG:
781 case SCNENC:
782 /*
783 * Run the external program hook on the message.
784 */
785
786 (void)snprintf(b, sizeof (b), "%s/%d", maildir_copy, msgnum + 1);
787 (void)ext_hook("add-hook", b, NULL);
788
789 if (aud)
790 fputs (charstring_buffer (scanl), aud);
791 if (noisy)
792 fflush (stdout);
793
794 msgnum++;
795 continue;
796 }
797 charstring_free (scanl);
798
799 /* If we get here there was some sort of error from scan(),
800 * so stop processing anything more from the spool.
801 */
802 break;
803 }
804 } else if (inc_type == INC_FILE) { /* Maildir inbox to process */
805 char *sp;
806 FILE *sf;
807 int i;
808
809 hghnum = msgnum = mp->hghmsg;
810 for (i = 0; i < num_maildir_entries; i++) {
811 charstring_t scanl = NULL;
812
813 msgnum++;
814
815 sp = Maildir[i].filename;
816 cp = mh_xstrdup(m_name (msgnum));
817 pf = NULL;
818 if (!trnflag || link(sp, cp) == -1) {
819 static char buf[65536];
820 size_t nrd;
821
822 if ((sf = fopen (sp, "r")) == NULL)
823 adios (sp, "unable to read for copy");
824 if ((pf = fopen (cp, "w+")) == NULL)
825 adios (cp, "unable to write for copy");
826 while ((nrd = fread(buf, 1, sizeof(buf), sf)) > 0)
827 if (fwrite(buf, 1, nrd, pf) != nrd)
828 break;
829 if (ferror(sf) || fflush(pf) || ferror(pf)) {
830 int e = errno;
831 fclose(pf); fclose(sf); (void) m_unlink(cp);
832 errno = e;
833 adios(cp, "copy error %s -> %s", sp, cp);
834 }
835 fclose (sf);
836 sf = NULL;
837 }
838 if (pf == NULL && (pf = fopen (cp, "r")) == NULL)
839 adios (cp, "not available");
840 chmod (cp, m_gmprot ());
841
842 fseek (pf, 0L, SEEK_SET);
843 switch (incerr = scan (pf, msgnum, 0, nfs, width,
844 msgnum == mp->hghmsg + 1 && chgflag,
845 1, NULL, stop - start, noisy, &scanl)) {
846 case SCNEOF:
847 printf ("%*d empty\n", DMAXFOLDER, msgnum);
848 break;
849
850 case SCNFAT:
851 trnflag = 0;
852 noisy++;
853 /* advise (cp, "unable to read"); already advised */
854 /* fall thru */
855
856 case SCNERR:
857 case SCNNUM:
858 break;
859
860 case SCNMSG:
861 case SCNENC:
862 default:
863 /*
864 * Run the external program hook on the message.
865 */
866
867 (void)snprintf(b, sizeof (b), "%s/%d", maildir_copy, msgnum + 1);
868 (void)ext_hook("add-hook", b, NULL);
869
870 if (aud)
871 fputs (charstring_buffer (scanl), aud);
872 if (noisy)
873 fflush (stdout);
874 break;
875 }
876 charstring_free (scanl);
877
878 if (ferror(pf) || fclose (pf)) {
879 int e = errno;
880 (void) m_unlink (cp);
881 errno = e;
882 adios (cp, "write error on");
883 }
884 pf = NULL;
885 free (cp);
886
887 if (trnflag && m_unlink (sp) == NOTOK)
888 adios (sp, "couldn't unlink");
889 free (sp); /* Free Maildir[i]->filename */
890
891 scan_finished();
892 }
893 free (Maildir); /* From now on Maildir is just a flag - don't dref! */
894 }
895
896 scan_finished ();
897
898 if (incerr < 0) { /* error */
899 if (locked) {
900 GETGROUPPRIVS(); /* Be sure we can unlock mail file */
901 (void) lkfclosespool (in, newmail); in = NULL;
902 DROPGROUPPRIVS(); /* And then return us to normal privileges */
903 } else {
904 fclose (in); in = NULL;
905 }
906 adios (NULL, "failed");
907 }
908
909 if (aud)
910 fclose (aud);
911
912 if (noisy)
913 fflush (stdout);
914
915 if ((inc_type == INC_POP) && packfile)
916 done (0);
917
918 /*
919 * truncate file we are incorporating from
920 */
921 if (inc_type == INC_FILE && Maildir == NULL) {
922 if (trnflag) {
923 if (stat (newmail, &st) != NOTOK && s1.st_mtime != st.st_mtime)
924 advise (NULL, "new messages have arrived!\007");
925 else {
926 int newfd;
927 if ((newfd = creat (newmail, 0600)) != NOTOK)
928 close (newfd);
929 else
930 admonish (newmail, "error zero'ing");
931 (void) m_unlink(map_name(newmail));
932 }
933 } else {
934 if (noisy)
935 printf ("%s not zero'd\n", newmail);
936 }
937 }
938
939 if (msgnum == hghnum) {
940 admonish (NULL, "no messages incorporated");
941 } else {
942 /*
943 * Lock the sequence file now, and loop to set the right flags
944 * in the folder structure
945 */
946
947 struct msgs *mp2;
948 int i;
949
950 context_replace (pfolder, folder); /* update current folder */
951
952 if ((mp2 = folder_read(folder, 1)) == NULL) {
953 admonish(NULL, "Unable to reread folder %s", folder);
954 goto skip;
955 }
956
957 /*
958 * Shouldn't happen, but just in case ...
959 */
960
961 if (msgnum >= mp2->hghoff
962 && !(mp2 = folder_realloc (mp2, mp2->lowoff, msgnum + 1))) {
963 advise (NULL, "unable to reallocate folder storage");
964 goto skip;
965 }
966
967 if (chgflag)
968 mp2->curmsg = hghnum + 1;
969 mp2->hghmsg = msgnum;
970
971 if (mp2->lowmsg == 0)
972 mp2->lowmsg = 1;
973 if (chgflag) /* sigh... */
974 seq_setcur (mp2, mp2->curmsg);
975
976 for (i = hghnum + 1; i <= msgnum; i++) {
977 clear_msg_flags (mp2, i);
978 set_exists (mp2, i);
979 set_unseen (mp2, i);
980 }
981 mp2->msgflags |= SEQMOD;
982 seq_setunseen(mp2, 0); /* Set the Unseen-Sequence */
983 seq_save(mp2); /* Save the sequence file */
984 folder_free(mp2);
985 }
986 skip:
987
988 /*
989 * unlock the mail spool
990 */
991 if (inc_type == INC_FILE && Maildir == NULL) {
992 if (locked) {
993 GETGROUPPRIVS(); /* Be sure we can unlock mail file */
994 (void) lkfclosespool (in, newmail); in = NULL;
995 DROPGROUPPRIVS(); /* And then return us to normal privileges */
996 } else {
997 fclose (in); in = NULL;
998 }
999 }
1000
1001 context_save (); /* save the context file */
1002 done (0);
1003 return 1;
1004 }
1005
1006
1007 static void
1008 inc_done (int status)
1009 {
1010 done = exit;
1011 if (packfile && pd != NOTOK)
1012 mbx_close (packfile, pd);
1013 if (locked)
1014 {
1015 GETGROUPPRIVS();
1016 lkfclosespool(in, newmail);
1017 DROPGROUPPRIVS();
1018 }
1019 exit (status);
1020 }
1021
1022 static int
1023 pop_action (char *s)
1024 {
1025 fprintf (pf, "%s\n", s);
1026 stop += strlen (s) + 1;
1027 return 0; /* Is return value used? This was missing before 1999-07-15. */
1028 }
1029
1030 static int
1031 pop_pack (char *s)
1032 {
1033 int j;
1034 char buffer[BUFSIZ];
1035
1036 snprintf (buffer, sizeof(buffer), "%s\n", s);
1037 for ( ; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
1038 continue;
1039 for ( ; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
1040 continue;
1041 fputs (buffer, pf);
1042 size += strlen (buffer) + 1;
1043 return 0; /* Is return value used? This was missing before 1999-07-15. */
1044 }
1045
1046 static int
1047 map_count (void)
1048 {
1049 int md;
1050 char *cp;
1051 struct drop d;
1052 struct stat st;
1053
1054 if (stat (packfile, &st) == NOTOK)
1055 return 0;
1056 if ((md = open (cp = map_name (packfile), O_RDONLY)) == NOTOK
1057 || map_chk (cp, md, &d, (long) st.st_size, 1)) {
1058 if (md != NOTOK)
1059 close (md);
1060 return 0;
1061 }
1062 close (md);
1063 return (d.d_id);
1064 }