Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / uip / sendsbr.c
1 /* sendsbr.c - routines to help WhatNow/Send along */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: sendsbr.c,v 2.14 1993/08/25 17:28:05 jromine Exp $";
4 #endif  /* lint */
5
6 #include "../h/mh.h"
7 #include <setjmp.h>
8 #include <stdio.h>
9 #include <signal.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12
13 static          alert(), anno(), annoaux();
14 static int      tmp_fd();
15 static int      sendaux();
16 #ifdef  MIME
17 static int      sendaux2();
18 #endif
19
20 /* \f */
21
22 int     debugsw = 0;            /* global */
23 int     forwsw = 1;
24 int     inplace = 0;
25 int     mime = 0;
26 int     pushsw = 0;
27 int     splitsw = -1;
28 int     unique = 0;
29 int     verbsw = 0;
30
31 char   *altmsg = NULL;          /*  .. */
32 char   *annotext = NULL;
33 char   *distfile = NULL;
34
35
36 static int armed = 0;
37 static jmp_buf env;
38
39
40 char   *getusr ();
41 off_t   lseek ();
42 long    time ();
43
44 /* \f */
45
46 int     sendsbr (vec, vecp, drft, st)
47 char  **vec,
48        *drft;
49 int     vecp;
50 struct stat *st;
51 {
52     int     status;
53
54     armed++;
55     switch (setjmp (env)) {
56         case OK: 
57             status = sendaux (vec, vecp, drft, st) ? NOTOK : OK;
58             break;
59
60         default: 
61             status = DONE;
62             break;
63     }
64     armed = 0;
65     if (distfile)
66         (void) unlink (distfile);
67
68     return status;
69 }
70
71 /* \f */
72
73 #ifdef  MIME
74 #include "../h/mhn.h"
75
76 static int  sendaux (vec, vecp, drft, st)
77 register char **vec,
78                *drft;
79 int     vecp;
80 register struct stat *st;
81 {
82     int     compnum,
83             nparts,
84             partno,
85             state,
86             status;
87     long    clock,
88             pos,
89             start;
90     char   *cp,
91            *dp,
92             buffer[BUFSIZ],
93             msgid[BUFSIZ],
94             name[NAMESZ],
95             partnum[BUFSIZ];
96     struct stat sts;
97     FILE   *in;
98
99     if (splitsw < 0
100             || distfile
101             || stat (drft, &sts) == NOTOK
102             || sts.st_size < CPERMSG) {
103 one_shot: ;
104         splitsw = -1;
105         return sendaux2 (vec, vecp, drft, st);
106     }
107
108     if ((in = fopen (drft, "r")) == NULL)
109         adios (drft, "unable to open for reading");
110
111     cp = dp = NULL;
112     start = 0L;
113     for (compnum = 1, state = FLD;;) {
114         switch (state = m_getfld (state, name, buffer, sizeof buffer, in)) {
115             case FLD:
116             case FLDPLUS:
117             case FLDEOF:
118                 compnum++;
119
120                 if (uleq (name, VRSN_FIELD)
121                         || uleq (name, "Encrypted")
122                         || uleq (name, "Message-ID")) {
123                     while (state == FLDPLUS)
124                         state = m_getfld (state, name, buffer, sizeof buffer,
125                                           in);
126                 }
127                 else
128                     if (uprf (name, XXX_FIELD_PRF)) {
129                         dp = add (concat (name, ":", buffer, NULLCP), dp);
130                         while (state == FLDPLUS) {
131                             state = m_getfld (state, name, buffer,
132                                               sizeof buffer, in);
133                             dp = add (buffer, dp);
134                         }
135                     }
136                     else {
137                         cp = add (concat (name, ":", buffer, NULLCP), cp);
138                         while (state == FLDPLUS) {
139                             state = m_getfld (state, name, buffer,
140                                               sizeof buffer, in);
141                             cp = add (buffer, cp);
142                         }
143                     }
144                 if (state != FLDEOF) {
145                     start = ftell (in) + 1;
146                     continue;
147                 }
148                 /* else fall... */
149            case BODY:
150            case BODYEOF:
151            case FILEEOF:
152                 break;
153
154            case LENERR:
155            case FMTERR:
156                 adios (NULLCP, "message format error in component #%d",
157                        compnum);
158
159            default:
160                 adios (NULLCP, "getfld () returned %d", state);
161         }
162
163         break;
164     }
165     if (cp == NULL)
166         adios (NULLCP, "headers missing from draft");
167
168     nparts = 1, pos = start;
169     while (fgets (buffer, sizeof buffer - 1, in)) {
170         register long   len;
171
172         if ((pos += (len = strlen (buffer))) > CPERMSG)
173             nparts++, pos = len;
174     }
175     if (nparts == 1) {
176         free (cp);
177         if (dp)
178             free (dp);
179
180         (void) fclose (in);
181
182         goto one_shot;
183     }
184
185     if (!pushsw) {
186         printf ("Sending as %d Partial Messages\n", nparts);
187         (void) fflush (stdout);
188     }
189     status = OK;
190
191     vec[vecp++] = "-partno";
192     vec[vecp++] = partnum;
193     if (splitsw == 0)
194         vec[vecp++] = "-queued";
195
196     (void) time (&clock);
197     (void) sprintf (msgid, "<%d.%ld@%s>", getpid (), clock, LocalName ());
198
199     (void) fseek (in, start, 0);
200     for (partno = 1; partno <= nparts; partno++) {
201         char    tmpdrf[BUFSIZ];
202         FILE   *out;
203
204         (void) strcpy (tmpdrf, m_scratch (drft, invo_name));
205         if ((out = fopen (tmpdrf, "w")) == NULL)
206             adios (tmpdrf, "unable to open for writing");
207         (void) chmod (tmpdrf, 0600);
208
209         (void) fputs (cp, out);
210         fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE);
211         fprintf (out,
212                  "%s: message/partial; id=\"%s\"; number=%d; total=%d\n",
213                  TYPE_FIELD, msgid, partno, nparts);
214         fprintf (out, "%s: part %d of %d\n\n", DESCR_FIELD, partno,
215                  nparts);
216
217         if (partno == 1) {
218             if (dp)
219                 (void) fputs (dp, out);
220             fprintf (out, "Message-ID: %s\n", msgid);
221             fprintf (out, "\n");
222         }
223
224         pos = 0;
225         for (;;) {
226             register long    len;
227
228             if (!fgets (buffer, sizeof buffer - 1, in)) {
229                 if (partno == nparts)
230                     break;
231                 adios (NULLCP, "premature eof");
232             }
233             
234             if ((pos += (len = strlen (buffer))) > CPERMSG) {
235                 (void) fseek (in, -len, 1);
236                 break;
237             }
238
239             (void) fputs (buffer, out);
240         }
241
242         if (fflush (out))
243             adios (tmpdrf, "error writing to");
244
245         (void) fclose (out);
246
247         if (!pushsw && verbsw) {
248             printf ("\n");
249             (void) fflush (stdout);
250         }
251         if (splitsw > 0 && 1 < partno && partno < nparts) {
252             if (!pushsw) {
253                 printf ("pausing %d seconds before sending part %d...\n",
254                         splitsw, partno);
255                 (void) fflush (stdout);
256             }
257
258             sleep ((unsigned) splitsw);
259         }
260
261         (void) sprintf (partnum, "%d", partno);
262         status = sendaux2 (vec, vecp, tmpdrf, st);
263         (void) unlink (tmpdrf);
264         if (status != OK)
265             break;
266
267         annotext = NULL;
268     }
269
270     free (cp);
271     if (dp)
272         free (dp);
273
274     (void) fclose (in);
275
276     if (status == OK &&
277             rename (drft, strcpy (buffer, m_backup (drft))) == NOTOK)
278         advise (buffer, "unable to rename %s to", drft);
279
280     return status;
281 }
282 #endif
283
284 /* \f */
285
286 #ifndef MIME
287 static int  sendaux (vec, vecp, drft, st)
288 #else   /* MIME */
289 static int  sendaux2 (vec, vecp, drft, st)
290 #endif  /* MIME */
291 register char **vec,
292                *drft;
293 int     vecp;
294 register struct stat *st;
295 {
296     int     child_id,
297             i,
298             status,
299             fd,
300             fd2;
301     char    backup[BUFSIZ],
302             buf[BUFSIZ],
303             file[BUFSIZ];
304
305     fd = pushsw ? tmp_fd () : NOTOK;
306     fd2 = NOTOK;
307
308     if (pushsw && unique) {
309         if (rename (drft, strcpy (file, m_scratch (drft, invo_name)))
310                 == NOTOK)
311             adios (file, "unable to rename %s to", drft);
312         drft = file;
313     }
314     vec[vecp++] = drft;
315     if (annotext)
316         if ((fd2 = tmp_fd ()) != NOTOK) {
317             vec[vecp++] = "-idanno";
318             (void) sprintf (buf, "%d", fd2);
319             vec[vecp++] = buf;
320         }
321         else
322             admonish (NULLCP, "unable to create file for annotation list");
323     if (distfile && distout (drft, distfile, backup) == NOTOK)
324         done (1);
325     vec[vecp] = NULL;
326
327     for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
328         sleep (5);
329     switch (child_id) {
330         case NOTOK:             /* oops */
331             adios ("fork", "unable to");
332
333         case OK:                /* send it */
334             if (fd != NOTOK) {
335                 (void) dup2 (fd, fileno (stdout));
336                 (void) dup2 (fd, fileno (stderr));
337                 (void) close (fd);
338             }
339             execvp (postproc, vec);
340             fprintf (stderr, "unable to exec ");
341             perror (postproc);
342             _exit (-1);
343
344         default:                /* wait for it */
345             if ((status = pidwait (child_id, NOTOK)) == 0) {
346                 if (annotext && fd2 != NOTOK)
347                     anno (fd2, st);
348                 if (splitsw < 0
349                         && rename (drft, strcpy (buf, m_backup (drft)))
350                                 == NOTOK)
351                     advise (buf, "unable to rename %s to", drft);
352             }
353             else {
354                 if (fd != NOTOK) {
355                     alert (drft, fd);
356                     (void) close (fd);
357                 }
358                 else
359                     advise (NULLCP, "message not delivered to anyone");
360                 if (annotext && fd2 != NOTOK)
361                     (void) close (fd2);
362                 if (distfile) {
363                     (void) unlink (drft);
364                     if (rename (backup, drft) == NOTOK)
365                         advise (drft, "unable to rename %s to", backup);
366                 }
367             }
368             break;
369     }
370
371     return status;
372 }
373
374 /* \f */
375
376 static  alert (file, out)
377 register char   *file;
378 int     out;
379 {
380     int     child_id,
381             i,
382             in;
383     char    buf[BUFSIZ];
384
385     for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
386         sleep (5);
387     switch (child_id) {
388         case NOTOK:             /* oops */
389             advise ("fork", "unable to");
390
391         case OK:                /* send it */
392             (void) signal (SIGHUP, SIG_IGN);
393             (void) signal (SIGINT, SIG_IGN);
394             (void) signal (SIGQUIT, SIG_IGN);
395             (void) signal (SIGTERM, SIG_IGN);
396             if (forwsw)
397                 if ((in = open (file, 0)) == NOTOK)
398                     admonish (file, "unable to re-open");
399                 else {
400                     (void) lseek (out, (off_t)0, 2);
401                     (void) strcpy (buf, "\nMessage not delivered to anyone.\n");
402                     (void) write (out, buf, strlen (buf));
403                     (void) strcpy (buf, "\n------- Unsent Draft\n\n");
404                     (void) write (out, buf, strlen (buf));
405                     cpydgst (in, out, file, "temporary file");
406                     (void) close (in);
407                     (void) strcpy (buf, "\n------- End of Unsent Draft\n");
408                     (void) write (out, buf, strlen (buf));
409                     if (rename (file, strcpy (buf, m_backup (file))) == NOTOK)
410                         admonish (buf, "unable to rename %s to", file);
411                 }
412             (void) lseek (out, (off_t)0, 0);
413             (void) dup2 (out, fileno (stdin));
414             (void) close (out);
415             (void) sprintf (buf, "send failed on %s",
416                         forwsw ? "enclosed draft" : file);
417
418             execlp (mailproc, r1bindex (mailproc, '/'), getusr (),
419                     "-subject", buf, NULLCP);
420             fprintf (stderr, "unable to exec ");
421             perror (mailproc);
422             _exit (-1);
423
424         default:                /* no waiting... */
425             break;
426     }
427 }
428
429 /* \f */
430
431 static int  tmp_fd () {
432     int     fd;
433     char    tmpfil[BUFSIZ];
434
435     (void) strcpy (tmpfil, m_tmpfil (invo_name));
436     if ((fd = creat (tmpfil, 0600)) == NOTOK)
437         return NOTOK;
438     (void) close (fd);
439
440     if ((fd = open (tmpfil, 2)) == NOTOK)
441         return NOTOK;
442     if (debugsw)
443         advise (NULLCP, "temporary file %s selected", tmpfil);
444     else
445         if (unlink (tmpfil) == NOTOK)
446             advise (tmpfil, "unable to remove");
447
448     return fd;
449 }
450
451 /* \f */
452
453 static anno (fd, st)
454 int     fd;
455 register struct stat *st;
456 {
457     int     child_id;
458     TYPESIG (*hstat) (), (*istat) (), (*qstat) (), (*tstat) ();
459     static char *cwd = NULL;
460     struct stat st2;
461
462     if (altmsg &&
463             (stat (altmsg, &st2) == NOTOK
464                 || st -> st_mtime != st2.st_mtime
465                 || st -> st_dev != st2.st_dev
466                 || st -> st_ino != st2.st_ino)) {
467         if (debugsw)
468             admonish (NULLCP, "$mhaltmsg mismatch");
469         return;
470     }
471
472     child_id = debugsw ? NOTOK : fork ();
473     switch (child_id) {
474         case NOTOK:             /* oops */
475             if (!debugsw)
476                 advise (NULLCP,
477                             "unable to fork, so doing annotations by hand...");
478             if (cwd == NULL)
479                 cwd = getcpy (pwd ());
480
481         case OK: 
482             hstat = signal (SIGHUP, SIG_IGN);
483             istat = signal (SIGINT, SIG_IGN);
484             qstat = signal (SIGQUIT, SIG_IGN);
485             tstat = signal (SIGTERM, SIG_IGN);
486
487             annoaux (fd);
488             if (child_id == OK)
489                 _exit (0);
490
491             (void) signal (SIGHUP, hstat);
492             (void) signal (SIGINT, istat);
493             (void) signal (SIGQUIT, qstat);
494             (void) signal (SIGTERM, tstat);
495
496             (void) chdir (cwd);
497             break;
498
499         default:                /* no waiting... */
500             (void) close (fd);
501             break;
502     }
503 }
504
505 /* \f */
506
507 static  annoaux (fd)
508 int     fd;
509 {
510     int     fd2,
511             fd3,
512             msgnum;
513     char   *cp,
514            *folder,
515            *maildir,
516             buffer[BUFSIZ],
517           **ap;
518     FILE   *fp;
519     struct msgs *mp;
520
521     if ((folder = getenv ("mhfolder")) == NULL || *folder == 0) {
522         if (debugsw)
523             admonish (NULLCP, "$mhfolder not set");
524         return;
525     }
526     maildir = m_maildir (folder);
527     if (chdir (maildir) == NOTOK) {
528         if (debugsw)
529             admonish (maildir, "unable to change directory to");
530         return;
531     }
532     if (!(mp = m_gmsg (folder))) {
533         if (debugsw)
534             admonish (NULLCP, "unable to read folder %s");
535         return;
536     }
537     if (mp -> hghmsg == 0) {
538         if (debugsw)
539             admonish (NULLCP, "no messages in %s", folder);
540         goto oops;
541     }
542
543     if ((cp = getenv ("mhmessages")) == NULL || *cp == 0) {
544         if (debugsw)
545             admonish (NULLCP, "$mhmessages not set");
546         goto oops;
547     }
548     if (!debugsw                        /* MOBY HACK... */
549             && pushsw
550             && (fd3 = open ("/dev/null", 2)) != NOTOK
551             && (fd2 = dup (fileno (stderr))) != NOTOK) {
552         (void) dup2 (fd3, fileno (stderr));
553         (void) close (fd3);
554     }
555     else
556         fd2 = NOTOK;
557     for (ap = brkstring (cp = getcpy (cp), " ", NULLCP); *ap; ap++)
558         (void) m_convert (mp, *ap);
559     free (cp);
560     if (fd2 != NOTOK)
561         (void) dup2 (fd2, fileno (stderr));
562     if (mp -> numsel == 0) {
563         if (debugsw)
564             admonish (NULLCP, "no messages to annotate");
565         goto oops;
566     }
567
568     (void) lseek (fd, (off_t)0, 0);
569     if ((fp = fdopen (fd, "r")) == NULL) {
570         if (debugsw)
571             admonish (NULLCP, "unable to fdopen annotation list");
572         goto oops;
573     }
574     cp = NULL;
575     while (fgets (buffer, sizeof buffer, fp) != NULL)
576         cp = add (buffer, cp);
577     (void) fclose (fp);
578
579     if (debugsw)
580         advise (NULLCP, "annotate%s with %s: \"%s\"",
581                 inplace ? " inplace" : "", annotext, cp);
582     for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++)
583         if (mp -> msgstats[msgnum] & SELECTED) {
584             if (debugsw)
585                 advise (NULLCP, "annotate message %d", msgnum);
586             (void) annotate (m_name (msgnum), annotext, cp, inplace, 1);
587         }
588
589     free (cp);
590
591 oops: ;
592     m_fmsg (mp);
593 }
594
595 /* \f */
596
597 void done (status)
598 int     status;
599 {
600     if (armed)
601         longjmp (env, status ? status : NOTOK);
602
603     exit (status);
604 }