]> diplodocus.org Git - nmh/blob - uip/dropsbr.c
Use existing macros min() and max() more.
[nmh] / uip / dropsbr.c
1 /* dropsbr.c -- create/read/manipulate mail drops
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include <h/nmh.h>
9 #include <h/utils.h>
10
11 #include <h/mh.h>
12 #include <h/dropsbr.h>
13 #include <h/mts.h>
14 #include <h/tws.h>
15
16 #ifdef NTOHLSWAP
17 # include <netinet/in.h>
18 #else
19 # undef ntohl
20 # define ntohl(n) (n)
21 #endif
22
23 #include <fcntl.h>
24
25 /*
26 * static prototypes
27 */
28 static int mbx_chk_mbox (int);
29 static int mbx_chk_mmdf (int);
30 static int map_open (char *, int);
31
32
33 /*
34 * Main entry point to open/create and lock
35 * a file or maildrop.
36 */
37
38 int
39 mbx_open (char *file, int mbx_style, uid_t uid, gid_t gid, mode_t mode)
40 {
41 int j, count, fd = NOTOK;
42 struct stat st;
43
44 j = 0;
45
46 /* attempt to open and lock file */
47 for (count = 4; count > 0; count--) {
48 int failed_to_lock = 0;
49
50 if ((fd = lkopenspool (file, O_RDWR | O_CREAT | O_NONBLOCK,
51 mode, &failed_to_lock)) != NOTOK)
52 break;
53
54 if (!failed_to_lock)
55 return NOTOK;
56
57 j = errno;
58 sleep (5);
59 }
60
61 errno = j;
62
63 /*
64 * Return if we still failed after 4 attempts,
65 * or we just want to skip the sanity checks.
66 */
67 if (fd == NOTOK || mbx_style == OTHER_FORMAT)
68 return fd;
69
70 /*
71 * Do sanity checks on maildrop.
72 */
73 if (fstat (fd, &st) == NOTOK) {
74 /*
75 * The stat failed. So we make sure file
76 * has right ownership/modes
77 */
78 if (chown (file, uid, gid) < 0) {
79 advise (file, "chown");
80 }
81 if (chmod (file, mode) < 0) {
82 advise (file, "chmod");
83 }
84 } else if (st.st_size > (off_t) 0) {
85 int status;
86
87 /* check the maildrop */
88 switch (mbx_style) {
89 case MMDF_FORMAT:
90 default:
91 status = mbx_chk_mmdf (fd);
92 break;
93
94 case MBOX_FORMAT:
95 status = mbx_chk_mbox (fd);
96 break;
97 }
98
99 /* if error, attempt to close it */
100 if (status == NOTOK) {
101 close (fd);
102 return NOTOK;
103 }
104 }
105
106 return fd;
107 }
108
109
110 /*
111 * Check/prepare MBOX style maildrop for appending.
112 */
113
114 static int
115 mbx_chk_mbox (int fd)
116 {
117 /* just seek to the end */
118 if (lseek (fd, (off_t) 0, SEEK_END) == (off_t) NOTOK)
119 return NOTOK;
120
121 return OK;
122 }
123
124
125 /*
126 * Check/prepare MMDF style maildrop for appending.
127 */
128
129 static int
130 mbx_chk_mmdf (int fd)
131 {
132 ssize_t count;
133 char ldelim[BUFSIZ];
134
135 count = strlen (mmdlm2);
136
137 if (lseek (fd, -count, SEEK_END) == (off_t) NOTOK)
138 return NOTOK;
139 if (read (fd, ldelim, count) != count)
140 return NOTOK;
141
142 ldelim[count] = 0;
143
144 if (strcmp (ldelim, mmdlm2)
145 && write (fd, "\n", 1) != 1
146 && write (fd, mmdlm2, count) != count)
147 return NOTOK;
148
149 return OK;
150 }
151
152
153 int
154 mbx_read (FILE *fp, long pos, struct drop **drops)
155 {
156 int len, size;
157 long ld1, ld2;
158 char *bp;
159 char buffer[BUFSIZ];
160 struct drop *cp, *dp, *ep, *pp;
161
162 len = MAXFOLDER;
163 pp = mh_xcalloc(len, sizeof *pp);
164
165 ld1 = (long) strlen (mmdlm1);
166 ld2 = (long) strlen (mmdlm2);
167
168 fseek (fp, pos, SEEK_SET);
169 for (ep = (dp = pp) + len - 1; fgets (buffer, sizeof(buffer), fp);) {
170 size = 0;
171 if (strcmp (buffer, mmdlm1) == 0)
172 pos += ld1, dp->d_start = (long) pos;
173 else {
174 dp->d_start = (long)pos , pos += (long) strlen (buffer);
175 for (bp = buffer; *bp; bp++, size++)
176 if (*bp == '\n')
177 size++;
178 }
179
180 while (fgets (buffer, sizeof(buffer), fp) != NULL)
181 if (strcmp (buffer, mmdlm2) == 0)
182 break;
183 else {
184 pos += (long) strlen (buffer);
185 for (bp = buffer; *bp; bp++, size++)
186 if (*bp == '\n')
187 size++;
188 }
189
190 if (dp->d_start != (long) pos) {
191 dp->d_id = 0;
192 dp->d_size = (long) size;
193 dp->d_stop = pos;
194 dp++;
195 }
196 pos += ld2;
197
198 if (dp >= ep) {
199 int curlen = dp - pp;
200
201 cp = (struct drop *) mh_xrealloc ((char *) pp,
202 (size_t) (len += MAXFOLDER) * sizeof(*pp));
203 dp = cp + curlen, ep = (pp = cp) + len - 1;
204 }
205 }
206
207 if (dp == pp)
208 free(pp);
209 else
210 *drops = pp;
211 return (dp - pp);
212 }
213
214
215 int
216 mbx_write(char *mailbox, int md, FILE *fp, int id, long last,
217 long pos, off_t stop, int mapping, int noisy)
218 {
219 int i, j, size;
220 off_t start;
221 long off;
222 char *cp;
223 char buffer[BUFSIZ];
224
225 off = (long) lseek (md, (off_t) 0, SEEK_CUR);
226 j = strlen (mmdlm1);
227 if (write (md, mmdlm1, j) != j)
228 return NOTOK;
229 start = lseek (md, (off_t) 0, SEEK_CUR);
230 size = 0;
231
232 fseek (fp, pos, SEEK_SET);
233 while (fgets (buffer, sizeof(buffer), fp) && (pos < stop)) {
234 i = strlen (buffer);
235 for ( ; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
236 continue;
237 for ( ; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
238 continue;
239 if (write (md, buffer, i) != i)
240 return NOTOK;
241 pos += (long) i;
242 if (mapping)
243 for (cp = buffer; i-- > 0; size++)
244 if (*cp++ == '\n')
245 size++;
246 }
247
248 stop = lseek (md, (off_t) 0, SEEK_CUR);
249 j = strlen (mmdlm2);
250 if (write (md, mmdlm2, j) != j)
251 return NOTOK;
252 if (mapping)
253 map_write (mailbox, md, id, last, start, stop, off, size, noisy);
254
255 return OK;
256 }
257
258
259 /*
260 * Append message to end of file or maildrop.
261 */
262
263 int
264 mbx_copy (char *mailbox, int mbx_style, int md, int fd,
265 int mapping, char *text, int noisy)
266 {
267 int i, j, size;
268 off_t start, stop;
269 long pos;
270 char *cp, buffer[BUFSIZ + 1]; /* Space for NUL. */
271 FILE *fp;
272
273 pos = (long) lseek (md, (off_t) 0, SEEK_CUR);
274 size = 0;
275
276 switch (mbx_style) {
277 case MMDF_FORMAT:
278 default:
279 j = strlen (mmdlm1);
280 if (write (md, mmdlm1, j) != j)
281 return NOTOK;
282 start = lseek (md, (off_t) 0, SEEK_CUR);
283
284 if (text) {
285 i = strlen (text);
286 if (write (md, text, i) != i)
287 return NOTOK;
288 for (cp = text; *cp++; size++)
289 if (*cp == '\n')
290 size++;
291 }
292
293 while ((i = read (fd, buffer, sizeof buffer - 1)) > 0) {
294 buffer[i] = '\0'; /* Terminate for stringdex(). */
295
296 for ( ; (j = stringdex (mmdlm1, buffer)) >= 0; buffer[j]++)
297 continue;
298 for ( ; (j = stringdex (mmdlm2, buffer)) >= 0; buffer[j]++)
299 continue;
300 if (write (md, buffer, i) != i)
301 return NOTOK;
302 if (mapping)
303 for (cp = buffer; i-- > 0; size++)
304 if (*cp++ == '\n')
305 size++;
306 }
307
308 stop = lseek (md, (off_t) 0, SEEK_CUR);
309 j = strlen (mmdlm2);
310 if (write (md, mmdlm2, j) != j)
311 return NOTOK;
312 if (mapping)
313 map_write (mailbox, md, 0, (long) 0, start, stop, pos, size, noisy);
314
315 return (i != NOTOK ? OK : NOTOK);
316
317 case MBOX_FORMAT:
318 if ((j = dup (fd)) == NOTOK)
319 return NOTOK;
320 if ((fp = fdopen (j, "r")) == NULL) {
321 close (j);
322 return NOTOK;
323 }
324 start = lseek (md, (off_t) 0, SEEK_CUR);
325
326 /* If text is given, we add it to top of message */
327 if (text) {
328 i = strlen (text);
329 if (write (md, text, i) != i)
330 return NOTOK;
331 for (cp = text; *cp++; size++)
332 if (*cp == '\n')
333 size++;
334 }
335
336 for (j = 0; fgets (buffer, sizeof(buffer), fp) != NULL; j++) {
337
338 /*
339 * Check the first line, and make some changes.
340 */
341 if (j == 0 && !text) {
342 /*
343 * Change the "Return-Path:" field (if in first line)
344 * back to "From ".
345 */
346 if (has_prefix(buffer, "Return-Path:")) {
347 char tmpbuffer[sizeof buffer];
348 char *tp, *ep, *fp;
349
350 strncpy(tmpbuffer, buffer, sizeof(tmpbuffer));
351 ep = tmpbuffer + 13;
352 if (!(fp = strchr(ep + 1, ' ')))
353 fp = strchr(ep + 1, '\n');
354 tp = dctime(dlocaltimenow());
355 snprintf (buffer, sizeof(buffer), "From %.*s %s",
356 (int)(fp - ep), ep, tp);
357 } else if (has_prefix(buffer, "X-Envelope-From:")) {
358 /*
359 * Change the "X-Envelope-From:" field
360 * (if first line) back to "From ".
361 */
362 char tmpbuffer[sizeof buffer];
363 char *ep;
364
365 strncpy(tmpbuffer, buffer, sizeof(tmpbuffer));
366 ep = tmpbuffer + 17;
367 snprintf (buffer, sizeof(buffer), "From %s", ep);
368 } else if (!has_prefix(buffer, "From ")) {
369 /*
370 * If there is already a "From " line,
371 * then leave it alone. Else we add one.
372 */
373 char tmpbuffer[sizeof buffer];
374 char *tp, *ep;
375
376 strncpy(tmpbuffer, buffer, sizeof(tmpbuffer));
377 ep = "nobody@nowhere";
378 tp = dctime(dlocaltimenow());
379 snprintf (buffer, sizeof(buffer), "From %s %s%s", ep, tp, tmpbuffer);
380 }
381 }
382
383 /*
384 * If this is not first line, and begins with
385 * "From ", then prepend line with ">".
386 */
387 if (j != 0 && has_prefix(buffer, "From ")) {
388 if (write (md, ">", 1) < 0) {
389 advise (mailbox, "write");
390 }
391 size++;
392 }
393 i = strlen (buffer);
394 if (write (md, buffer, i) != i) {
395 fclose (fp);
396 return NOTOK;
397 }
398 if (mapping)
399 for (cp = buffer; i-- > 0; size++)
400 if (*cp++ == '\n')
401 size++;
402 }
403 if (write (md, "\n", 1) != 1) {
404 fclose (fp);
405 return NOTOK;
406 }
407 if (mapping)
408 size += 2;
409
410 fclose (fp);
411 lseek (fd, (off_t) 0, SEEK_END);
412 stop = lseek (md, (off_t) 0, SEEK_CUR);
413 if (mapping)
414 map_write (mailbox, md, 0, (long) 0, start, stop, pos, size, noisy);
415
416 return OK;
417 }
418 }
419
420
421 int
422 mbx_size (int md, off_t start, off_t stop)
423 {
424 int i, fd;
425 long pos;
426 FILE *fp;
427
428 if ((fd = dup (md)) == NOTOK || (fp = fdopen (fd, "r")) == NULL) {
429 if (fd != NOTOK)
430 close (fd);
431 return NOTOK;
432 }
433
434 fseek (fp, start, SEEK_SET);
435 for (i = 0, pos = stop - start; pos-- > 0; i++)
436 if (fgetc (fp) == '\n')
437 i++;
438
439 fclose (fp);
440 return i;
441 }
442
443
444 /*
445 * Close and unlock file/maildrop.
446 */
447
448 int
449 mbx_close (char *mailbox, int md)
450 {
451 if (lkclosespool (md, mailbox) == 0)
452 return OK;
453 return NOTOK;
454 }
455
456
457 /*
458 * This function is performed implicitly by getbbent.c:
459 * bb->bb_map = map_name (bb->bb_file);
460 */
461
462 char *
463 map_name (char *file)
464 {
465 char *cp, *dp;
466 static char buffer[BUFSIZ];
467
468 if ((dp = strchr(cp = r1bindex (file, '/'), '.')) == NULL)
469 dp = cp + strlen (cp);
470 if (cp == file)
471 snprintf (buffer, sizeof(buffer), ".%.*s%s", (int)(dp - cp), cp, ".map");
472 else
473 snprintf (buffer, sizeof(buffer), "%.*s.%.*s%s",
474 (int)(cp - file), file, (int)(dp - cp), cp, ".map");
475
476 return buffer;
477 }
478
479
480 int
481 map_read (char *file, long pos, struct drop **drops, int noisy)
482 {
483 int i, md, msgp;
484 char *cp;
485 struct drop d;
486 struct drop *mp, *dp;
487
488 if ((md = open (cp = map_name (file), O_RDONLY)) == NOTOK
489 || map_chk (cp, md, mp = &d, pos, noisy)) {
490 if (md != NOTOK)
491 close (md);
492 return 0;
493 }
494
495 msgp = mp->d_id;
496 dp = mh_xcalloc(msgp + 1, sizeof *dp);
497 memcpy((char *) dp, (char *) mp, sizeof(*dp));
498
499 lseek (md, (off_t) sizeof(*mp), SEEK_SET);
500 if ((i = read (md, (char *) (dp + 1), msgp * sizeof(*dp))) <
501 (int) sizeof(*dp)) {
502 i = 0;
503 free(dp);
504 } else {
505 #ifdef NTOHLSWAP
506 struct drop *tdp;
507 int j;
508
509 for (j = 0, tdp = dp; j < i / sizeof(*dp); j++, tdp++) {
510 tdp->d_id = ntohl(tdp->d_id);
511 tdp->d_size = ntohl(tdp->d_size);
512 tdp->d_start = ntohl(tdp->d_start);
513 tdp->d_stop = ntohl(tdp->d_stop);
514 }
515 #endif
516 *drops = dp;
517 }
518
519 close (md);
520
521 return (i / sizeof(*dp));
522 }
523
524
525 int
526 map_write (char *mailbox, int md, int id, long last, off_t start,
527 off_t stop, long pos, int size, int noisy)
528 {
529 int i;
530 int clear, fd, td;
531 char *file;
532 struct drop *dp;
533 struct drop d1, d2, *rp;
534 FILE *fp;
535 struct stat st;
536
537 if ((fd = map_open (file = map_name (mailbox), md)) == NOTOK)
538 return NOTOK;
539
540 if ((fstat (fd, &st) == OK) && (st.st_size > 0))
541 clear = 0;
542 else
543 clear = 1;
544
545 if (!clear && map_chk (file, fd, &d1, pos, noisy)) {
546 (void) m_unlink (file);
547 mbx_close (file, fd);
548 if ((fd = map_open (file, md)) == NOTOK)
549 return NOTOK;
550 clear++;
551 }
552
553 if (clear) {
554 if ((td = dup (md)) == NOTOK || (fp = fdopen (td, "r")) == NULL) {
555 if (noisy)
556 admonish (file, "unable to %s", td != NOTOK ? "fdopen" : "dup");
557 if (td != NOTOK)
558 close (td);
559 mbx_close (file, fd);
560 return NOTOK;
561 }
562
563 switch (i = mbx_read (fp, 0, &rp)) {
564 case NOTOK:
565 fclose (fp);
566 mbx_close (file, fd);
567 return NOTOK;
568
569 case OK:
570 fclose (fp);
571 break;
572
573 default:
574 d1.d_id = 0;
575 for (dp = rp; i-- >0; dp++) {
576 if (dp->d_start == start)
577 dp->d_id = id;
578 lseek (fd, (off_t) (++d1.d_id * sizeof(*dp)), SEEK_SET);
579 if (write (fd, (char *) dp, sizeof(*dp)) != sizeof(*dp)) {
580 if (noisy)
581 admonish (file, "write error");
582 mbx_close (file, fd);
583 fclose (fp);
584 return NOTOK;
585 }
586 }
587 free(rp);
588 fclose (fp);
589 break;
590 }
591 }
592 else {
593 if (last == 0)
594 last = d1.d_start;
595 dp = &d2;
596 dp->d_id = id;
597 dp->d_size = (long) (size ? size : mbx_size (fd, start, stop));
598 dp->d_start = start;
599 dp->d_stop = stop;
600 lseek (fd, (off_t) (++d1.d_id * sizeof(*dp)), SEEK_SET);
601 if (write (fd, (char *) dp, sizeof(*dp)) != sizeof(*dp)) {
602 if (noisy)
603 admonish (file, "write error");
604 mbx_close (file, fd);
605 return NOTOK;
606 }
607 }
608
609 dp = &d1;
610 dp->d_size = DRVRSN;
611 dp->d_start = (long) last;
612 dp->d_stop = lseek (md, (off_t) 0, SEEK_CUR);
613
614 lseek (fd, (off_t) 0, SEEK_SET);
615 if (write (fd, (char *) dp, sizeof(*dp)) != sizeof(*dp)) {
616 if (noisy)
617 admonish (file, "write error");
618 mbx_close (file, fd);
619 return NOTOK;
620 }
621
622 mbx_close (file, fd);
623
624 return OK;
625 }
626
627
628 static int
629 map_open (char *file, int md)
630 {
631 mode_t mode;
632 struct stat st;
633
634 mode = fstat (md, &st) != NOTOK ? (int) (st.st_mode & 0777) : m_gmprot ();
635 return mbx_open (file, OTHER_FORMAT, st.st_uid, st.st_gid, mode);
636 }
637
638
639 int
640 map_chk (char *file, int fd, struct drop *dp, long pos, int noisy)
641 {
642 ssize_t count;
643 struct drop d, tmpd;
644 struct drop *dl;
645
646 if (read (fd, (char *) &tmpd, sizeof(*dp)) != sizeof(*dp)) {
647 #ifdef notdef
648 inform("%s: missing or partial index, continuing...", file);
649 #endif /* notdef */
650 return NOTOK;
651 }
652 #ifndef NTOHLSWAP
653 *dp = tmpd; /* if ntohl(n)=(n), can use struct assign */
654 #else
655 dp->d_id = ntohl(tmpd.d_id);
656 dp->d_size = ntohl(tmpd.d_size);
657 dp->d_start = ntohl(tmpd.d_start);
658 dp->d_stop = ntohl(tmpd.d_stop);
659 #endif
660
661 if (dp->d_size != DRVRSN) {
662 if (noisy)
663 inform("%s: version mismatch (%d != %d), continuing...", file,
664 dp->d_size, DRVRSN);
665 return NOTOK;
666 }
667
668 if (dp->d_stop != pos) {
669 if (noisy && pos != (long) 0)
670 inform("%s: pointer mismatch or incomplete index (%ld!=%ld), "
671 "continuing...", file, dp->d_stop, (long) pos);
672 return NOTOK;
673 }
674
675 if ((long) ((dp->d_id + 1) * sizeof(*dp)) != (long) lseek (fd, (off_t) 0, SEEK_END)) {
676 if (noisy)
677 inform("%s: corrupt index(1), continuing...", file);
678 return NOTOK;
679 }
680
681 dl = &d;
682 count = strlen (mmdlm2);
683 lseek (fd, (off_t) (dp->d_id * sizeof(*dp)), SEEK_SET);
684 if (read (fd, (char *) dl, sizeof(*dl)) != sizeof(*dl)
685 || (ntohl(dl->d_stop) != dp->d_stop
686 && ntohl(dl->d_stop) + count != dp->d_stop)) {
687 if (noisy)
688 inform("%s: corrupt index(2), continuing...", file);
689 return NOTOK;
690 }
691
692 return OK;
693 }