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