source: trunk/third/nmh/uip/sendsbr.c @ 12455

Revision 12455, 14.5 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12454, which included commits to RCS files with non-trunk default branches.
Line 
1
2/*
3 * sendsbr.c -- routines to help WhatNow/Send along
4 *
5 * $Id: sendsbr.c,v 1.1.1.1 1999-02-07 18:14:17 danw Exp $
6 */
7
8#include <h/mh.h>
9#include <h/signals.h>
10#include <setjmp.h>
11#include <signal.h>
12#include <fcntl.h>
13#include <h/mime.h>
14
15int debugsw = 0;                /* global */
16int forwsw  = 1;
17int inplace = 1;
18int pushsw  = 0;
19int splitsw = -1;
20int unique  = 0;
21int verbsw  = 0;
22
23char *altmsg   = NULL;          /*  .. */
24char *annotext = NULL;
25char *distfile = NULL;
26
27static int armed = 0;
28static jmp_buf env;
29
30/*
31 * external prototypes
32 */
33int sendsbr (char **, int, char *, struct stat *, int);
34void done (int);
35char *getusername (void);
36
37/*
38 * static prototypes
39 */
40static void alert (char *, int);
41static int tmp_fd (void);
42static void anno (int, struct stat *);
43static void annoaux (int);
44static int splitmsg (char **, int, char *, struct stat *, int);
45static int sendaux (char **, int, char *, struct stat *);
46
47
48/*
49 * Entry point into (back-end) routines to send message.
50 */
51
52int
53sendsbr (char **vec, int vecp, char *drft, struct stat *st, int rename_drft)
54{
55    int status;
56    char buffer[BUFSIZ], file[BUFSIZ];
57    struct stat sts;
58
59    armed++;
60    switch (setjmp (env)) {
61    case OK:
62        /*
63         * If given -push and -unique (which is undocumented), then
64         * rename the draft file.  I'm not quite sure why.
65         */
66        if (pushsw && unique) {
67            if (rename (drft, strncpy (file, m_scratch (drft, invo_name), sizeof(file)))
68                    == NOTOK)
69                adios (file, "unable to rename %s to", drft);
70            drft = file;
71        }
72
73        /*
74         * Check if we need to split the message into
75         * multiple messages of type "message/partial".
76         */
77        if (splitsw >= 0 && !distfile && stat (drft, &sts) != NOTOK
78                && sts.st_size >= CPERMSG) {
79            status = splitmsg (vec, vecp, drft, st, splitsw) ? NOTOK : OK;
80        } else {
81            status = sendaux (vec, vecp, drft, st) ? NOTOK : OK;
82        }
83
84        /* rename the original draft */
85        if (rename_drft && status == OK &&
86                rename (drft, strncpy (buffer, m_backup (drft), sizeof(buffer))) == NOTOK)
87            advise (buffer, "unable to rename %s to", drft);
88        break;
89
90    default:
91        status = DONE;
92        break;
93    }
94
95    armed = 0;
96    if (distfile)
97        unlink (distfile);
98
99    return status;
100}
101
102
103/*
104 * Split large message into several messages of
105 * type "message/partial" and send them.
106 */
107
108static int
109splitmsg (char **vec, int vecp, char *drft, struct stat *st, int delay)
110{
111    int compnum, nparts, partno, state, status;
112    long pos, start;
113    time_t clock;
114    char *cp, *dp, buffer[BUFSIZ], msgid[BUFSIZ];
115    char subject[BUFSIZ];
116    char name[NAMESZ], partnum[BUFSIZ];
117    FILE *in;
118
119    if ((in = fopen (drft, "r")) == NULL)
120        adios (drft, "unable to open for reading");
121
122    cp = dp = NULL;
123    start = 0L;
124
125    /*
126     * Scan through the message and examine the various header fields,
127     * as well as locate the beginning of the message body.
128     */
129    for (compnum = 1, state = FLD;;) {
130        switch (state = m_getfld (state, name, buffer, sizeof(buffer), in)) {
131            case FLD:
132            case FLDPLUS:
133            case FLDEOF:
134                compnum++;
135
136                /*
137                 * This header field is discarded.
138                 */
139                if (!strcasecmp (name, "Message-ID")) {
140                    while (state == FLDPLUS)
141                        state = m_getfld (state, name, buffer, sizeof(buffer), in);
142                } else if (uprf (name, XXX_FIELD_PRF)
143                        || !strcasecmp (name, VRSN_FIELD)
144                        || !strcasecmp (name, "Subject")
145                        || !strcasecmp (name, "Encrypted")) {
146                    /*
147                     * These header fields are copied to the enclosed
148                     * header of the first message in the collection
149                     * of message/partials.  For the "Subject" header
150                     * field, we also record it, so that a modified
151                     * version of it, can be copied to the header
152                     * of each messsage/partial in the collection.
153                     */
154                    if (!strcasecmp (name, "Subject")) {
155                        size_t sublen;
156
157                        strncpy (subject, buffer, BUFSIZ);
158                        sublen = strlen (subject);
159                        if (sublen > 0 && subject[sublen - 1] == '\n')
160                            subject[sublen - 1] = '\0';
161                    }
162
163                    dp = add (concat (name, ":", buffer, NULL), dp);
164                    while (state == FLDPLUS) {
165                        state = m_getfld (state, name, buffer, sizeof(buffer), in);
166                        dp = add (buffer, dp);
167                    }
168                } else {
169                    /*
170                     * These header fields are copied to the header of
171                     * each message/partial in the collection.
172                     */
173                    cp = add (concat (name, ":", buffer, NULL), cp);
174                    while (state == FLDPLUS) {
175                        state = m_getfld (state, name, buffer, sizeof(buffer), in);
176                        cp = add (buffer, cp);
177                    }
178                }
179
180                if (state != FLDEOF) {
181                    start = ftell (in) + 1;
182                    continue;
183                }
184                /* else fall... */
185
186           case BODY:
187           case BODYEOF:
188           case FILEEOF:
189                break;
190
191           case LENERR:
192           case FMTERR:
193                adios (NULL, "message format error in component #%d", compnum);
194
195           default:
196                adios (NULL, "getfld () returned %d", state);
197        }
198
199        break;
200    }
201    if (cp == NULL)
202        adios (NULL, "headers missing from draft");
203
204    nparts = 1;
205    pos = start;
206    while (fgets (buffer, sizeof(buffer) - 1, in)) {
207        long len;
208
209        if ((pos += (len = strlen (buffer))) > CPERMSG) {
210            nparts++;
211            pos = len;
212        }
213    }
214
215    /* Only one part, nothing to split */
216    if (nparts == 1) {
217        free (cp);
218        if (dp)
219            free (dp);
220
221        fclose (in);
222        return sendaux (vec, vecp, drft, st);
223    }
224
225    if (!pushsw) {
226        printf ("Sending as %d Partial Messages\n", nparts);
227        fflush (stdout);
228    }
229    status = OK;
230
231    vec[vecp++] = "-partno";
232    vec[vecp++] = partnum;
233    if (delay == 0)
234        vec[vecp++] = "-queued";
235
236    time (&clock);
237    snprintf (msgid, sizeof(msgid), "<%d.%ld@%s>",
238                (int) getpid(), (long) clock, LocalName());
239
240    fseek (in, start, SEEK_SET);
241    for (partno = 1; partno <= nparts; partno++) {
242        char tmpdrf[BUFSIZ];
243        FILE *out;
244
245        strncpy (tmpdrf, m_scratch (drft, invo_name), sizeof(tmpdrf));
246        if ((out = fopen (tmpdrf, "w")) == NULL)
247            adios (tmpdrf, "unable to open for writing");
248        chmod (tmpdrf, 0600);
249
250        /*
251         * Output the header fields
252         */
253        fputs (cp, out);
254        fprintf (out, "Subject: %s (part %d of %d)\n", subject, partno, nparts);
255        fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE);
256        fprintf (out, "%s: message/partial; id=\"%s\";\n", TYPE_FIELD, msgid);
257        fprintf (out, "\tnumber=%d; total=%d\n", partno, nparts);
258        fprintf (out, "%s: part %d of %d\n\n", DESCR_FIELD, partno, nparts);
259
260        /*
261         * If this is the first in the collection, output the
262         * header fields we are encapsulating at the beginning
263         * of the body of the first message.
264         */
265        if (partno == 1) {
266            if (dp)
267                fputs (dp, out);
268            fprintf (out, "Message-ID: %s\n", msgid);
269            fprintf (out, "\n");
270        }
271
272        pos = 0;
273        for (;;) {
274            long len;
275
276            if (!fgets (buffer, sizeof(buffer) - 1, in)) {
277                if (partno == nparts)
278                    break;
279                adios (NULL, "premature eof");
280            }
281           
282            if ((pos += (len = strlen (buffer))) > CPERMSG) {
283                fseek (in, -len, SEEK_CUR);
284                break;
285            }
286
287            fputs (buffer, out);
288        }
289
290        if (fflush (out))
291            adios (tmpdrf, "error writing to");
292
293        fclose (out);
294
295        if (!pushsw && verbsw) {
296            printf ("\n");
297            fflush (stdout);
298        }
299
300        /* Pause here, if a delay is specified */
301        if (delay > 0 && 1 < partno && partno <= nparts) {
302            if (!pushsw) {
303                printf ("pausing %d seconds before sending part %d...\n",
304                        delay, partno);
305                fflush (stdout);
306            }
307            sleep ((unsigned int) delay);
308        }
309
310        snprintf (partnum, sizeof(partnum), "%d", partno);
311        status = sendaux (vec, vecp, tmpdrf, st);
312        unlink (tmpdrf);
313        if (status != OK)
314            break;
315
316        /*
317         * This is so sendaux will only annotate
318         * the altmsg the first time it is called.
319         */
320        annotext = NULL;
321    }
322
323    free (cp);
324    if (dp)
325        free (dp);
326
327    fclose (in);        /* close the draft */
328    return status;
329}
330
331
332/*
333 * Annotate original message, and
334 * call `postproc' to send message.
335 */
336
337static int
338sendaux (char **vec, int vecp, char *drft, struct stat *st)
339{
340    pid_t child_id;
341    int i, status, fd, fd2;
342    char backup[BUFSIZ], buf[BUFSIZ];
343
344    fd = pushsw ? tmp_fd () : NOTOK;
345    fd2 = NOTOK;
346
347    vec[vecp++] = drft;
348    if (annotext) {
349        if ((fd2 = tmp_fd ()) != NOTOK) {
350            vec[vecp++] = "-idanno";
351            snprintf (buf, sizeof(buf), "%d", fd2);
352            vec[vecp++] = buf;
353        } else {
354            admonish (NULL, "unable to create file for annotation list");
355        }
356    }
357    if (distfile && distout (drft, distfile, backup) == NOTOK)
358        done (1);
359    vec[vecp] = NULL;
360
361    for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
362        sleep (5);
363
364    switch (child_id) {
365    case -1:
366        /* oops -- fork error */
367        adios ("fork", "unable to");
368        break;  /* NOT REACHED */
369
370    case 0:
371        /*
372         * child process -- send it
373         *
374         * If fd is ok, then we are pushing and fd points to temp
375         * file, so capture anything on stdout and stderr there.
376         */
377        if (fd != NOTOK) {
378            dup2 (fd, fileno (stdout));
379            dup2 (fd, fileno (stderr));
380            close (fd);
381        }
382        execvp (postproc, vec);
383        fprintf (stderr, "unable to exec ");
384        perror (postproc);
385        _exit (-1);
386        break;  /* NOT REACHED */
387
388    default:
389        /*
390         * parent process -- wait for it
391         */
392        if ((status = pidwait(child_id, NOTOK)) == OK) {
393            if (annotext && fd2 != NOTOK)
394                anno (fd2, st);
395        } else {
396            /*
397             * If postproc failed, and we have good fd (which means
398             * we pushed), then mail error message (and possibly the
399             * draft) back to the user.
400             */
401            if (fd != NOTOK) {
402                alert (drft, fd);
403                close (fd);
404            } else {
405                advise (NULL, "message not delivered to anyone");
406            }
407            if (annotext && fd2 != NOTOK)
408                close (fd2);
409            if (distfile) {
410                unlink (drft);
411                if (rename (backup, drft) == NOTOK)
412                    advise (drft, "unable to rename %s to", backup);
413            }
414        }
415        break;
416    }
417
418    return status;
419}
420
421
422/*
423 * Mail error notification (and possibly a copy of the
424 * message) back to the user, using the mailproc
425 */
426
427static void
428alert (char *file, int out)
429{
430    pid_t child_id;
431    int i, in;
432    char buf[BUFSIZ];
433
434    for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
435        sleep (5);
436
437    switch (child_id) {
438        case NOTOK:
439            /* oops -- fork error */
440            advise ("fork", "unable to");
441
442        case OK:
443            /* child process -- send it */
444            SIGNAL (SIGHUP, SIG_IGN);
445            SIGNAL (SIGINT, SIG_IGN);
446            SIGNAL (SIGQUIT, SIG_IGN);
447            SIGNAL (SIGTERM, SIG_IGN);
448            if (forwsw) {
449                if ((in = open (file, O_RDONLY)) == NOTOK) {
450                    admonish (file, "unable to re-open");
451                } else {
452                    lseek (out, (off_t) 0, SEEK_END);
453                    strncpy (buf, "\nMessage not delivered to anyone.\n", sizeof(buf));
454                    write (out, buf, strlen (buf));
455                    strncpy (buf, "\n------- Unsent Draft\n\n", sizeof(buf));
456                    write (out, buf, strlen (buf));
457                    cpydgst (in, out, file, "temporary file");
458                    close (in);
459                    strncpy (buf, "\n------- End of Unsent Draft\n", sizeof(buf));
460                    write (out, buf, strlen (buf));
461                    if (rename (file, strncpy (buf, m_backup (file), sizeof(buf))) == NOTOK)
462                        admonish (buf, "unable to rename %s to", file);
463                }
464            }
465            lseek (out, (off_t) 0, SEEK_SET);
466            dup2 (out, fileno (stdin));
467            close (out);
468            /* create subject for error notification */
469            snprintf (buf, sizeof(buf), "send failed on %s",
470                        forwsw ? "enclosed draft" : file);
471
472            execlp (mailproc, r1bindex (mailproc, '/'), getusername (),
473                    "-subject", buf, NULL);
474            fprintf (stderr, "unable to exec ");
475            perror (mailproc);
476            _exit (-1);
477
478        default:                /* no waiting... */
479            break;
480    }
481}
482
483
484static int
485tmp_fd (void)
486{
487    int fd;
488    char tmpfil[BUFSIZ];
489
490    strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
491    if ((fd = open (tmpfil, O_RDWR | O_CREAT | O_TRUNC, 0600)) == NOTOK)
492        return NOTOK;
493    if (debugsw)
494        advise (NULL, "temporary file %s selected", tmpfil);
495    else
496        if (unlink (tmpfil) == NOTOK)
497            advise (tmpfil, "unable to remove");
498
499    return fd;
500}
501
502
503static void
504anno (int fd, struct stat *st)
505{
506    pid_t child_id;
507    sigset_t set, oset;
508    static char *cwd = NULL;
509    struct stat st2;
510
511    if (altmsg &&
512            (stat (altmsg, &st2) == NOTOK
513                || st->st_mtime != st2.st_mtime
514                || st->st_dev != st2.st_dev
515                || st->st_ino != st2.st_ino)) {
516        if (debugsw)
517            admonish (NULL, "$mhaltmsg mismatch");
518        return;
519    }
520
521    child_id = debugsw ? NOTOK : fork ();
522    switch (child_id) {
523        case NOTOK:             /* oops */
524            if (!debugsw)
525                advise (NULL,
526                            "unable to fork, so doing annotations by hand...");
527            if (cwd == NULL)
528                cwd = getcpy (pwd ());
529
530        case OK:
531            /* block a few signals */
532            sigemptyset (&set);
533            sigaddset (&set, SIGHUP);
534            sigaddset (&set, SIGINT);
535            sigaddset (&set, SIGQUIT);
536            sigaddset (&set, SIGTERM);
537            SIGPROCMASK (SIG_BLOCK, &set, &oset);
538
539            annoaux (fd);
540            if (child_id == OK)
541                _exit (0);
542
543            /* reset the signal mask */
544            SIGPROCMASK (SIG_SETMASK, &oset, &set);
545
546            chdir (cwd);
547            break;
548
549        default:                /* no waiting... */
550            close (fd);
551            break;
552    }
553}
554
555
556static void
557annoaux (int fd)
558{
559    int fd2, fd3, msgnum;
560    char *cp, *folder, *maildir;
561    char buffer[BUFSIZ], **ap;
562    FILE *fp;
563    struct msgs *mp;
564
565    if ((folder = getenv ("mhfolder")) == NULL || *folder == 0) {
566        if (debugsw)
567            admonish (NULL, "$mhfolder not set");
568        return;
569    }
570    maildir = m_maildir (folder);
571    if (chdir (maildir) == NOTOK) {
572        if (debugsw)
573            admonish (maildir, "unable to change directory to");
574        return;
575    }
576    if (!(mp = folder_read (folder))) {
577        if (debugsw)
578            admonish (NULL, "unable to read folder %s");
579        return;
580    }
581
582    /* check for empty folder */
583    if (mp->nummsg == 0) {
584        if (debugsw)
585            admonish (NULL, "no messages in %s", folder);
586        goto oops;
587    }
588
589    if ((cp = getenv ("mhmessages")) == NULL || *cp == 0) {
590        if (debugsw)
591            admonish (NULL, "$mhmessages not set");
592        goto oops;
593    }
594    if (!debugsw                        /* MOBY HACK... */
595            && pushsw
596            && (fd3 = open ("/dev/null", O_RDWR)) != NOTOK
597            && (fd2 = dup (fileno (stderr))) != NOTOK) {
598        dup2 (fd3, fileno (stderr));
599        close (fd3);
600    }
601    else
602        fd2 = NOTOK;
603    for (ap = brkstring (cp = getcpy (cp), " ", NULL); *ap; ap++)
604        m_convert (mp, *ap);
605    free (cp);
606    if (fd2 != NOTOK)
607        dup2 (fd2, fileno (stderr));
608    if (mp->numsel == 0) {
609        if (debugsw)
610            admonish (NULL, "no messages to annotate");
611        goto oops;
612    }
613
614    lseek (fd, (off_t) 0, SEEK_SET);
615    if ((fp = fdopen (fd, "r")) == NULL) {
616        if (debugsw)
617            admonish (NULL, "unable to fdopen annotation list");
618        goto oops;
619    }
620    cp = NULL;
621    while (fgets (buffer, sizeof(buffer), fp) != NULL)
622        cp = add (buffer, cp);
623    fclose (fp);
624
625    if (debugsw)
626        advise (NULL, "annotate%s with %s: \"%s\"",
627                inplace ? " inplace" : "", annotext, cp);
628    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
629        if (is_selected(mp, msgnum)) {
630            if (debugsw)
631                advise (NULL, "annotate message %d", msgnum);
632            annotate (m_name (msgnum), annotext, cp, inplace, 1);
633        }
634    }
635
636    free (cp);
637
638oops:
639    folder_free (mp);   /* free folder/message structure */
640}
641
642
643void
644done (int status)
645{
646    if (armed)
647        longjmp (env, status ? status : NOTOK);
648
649    exit (status);
650}
Note: See TracBrowser for help on using the repository browser.