source: trunk/third/nmh/uip/dropsbr.c @ 14162

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