]> diplodocus.org Git - nmh/blob - uip/sendsbr.c
Add new signal include file location for El Capitan. I am not sure
[nmh] / uip / sendsbr.c
1
2 /*
3 * sendsbr.c -- routines to help WhatNow/Send along
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 #include <h/mh.h>
11 #include <h/fmt_scan.h>
12 #include <h/fmt_compile.h>
13 #include <h/signals.h>
14 #include <setjmp.h>
15 #include <fcntl.h>
16 #include <h/mime.h>
17 #include <h/tws.h>
18 #include <h/utils.h>
19 #include <h/mts.h>
20
21 #ifdef HAVE_SYS_TIME_H
22 # include <sys/time.h>
23 #endif
24 #include <time.h>
25
26 #ifdef OAUTH_SUPPORT
27 #include <h/oauth.h>
28
29 static int setup_oauth_params(char *[], int *, int, const char **);
30 #endif /* OAUTH_SUPPORT */
31
32 int debugsw = 0; /* global */
33 int forwsw = 1;
34 int inplace = 1;
35 int pushsw = 0;
36 int splitsw = -1;
37 int unique = 0;
38 int verbsw = 0;
39
40 char *altmsg = NULL; /* .. */
41 char *annotext = NULL;
42 char *distfile = NULL;
43
44 static jmp_buf env;
45
46 /*
47 * static prototypes
48 */
49 static void alert (char *, int);
50 static int tmp_fd (void);
51 static void anno (int, struct stat *);
52 static void annoaux (int);
53 static int splitmsg (char **, int, char *, char *, struct stat *, int);
54 static int sendaux (char **, int, char *, char *, struct stat *);
55 static void handle_sendfrom(char **, int *, char *);
56 static int get_from_header_info(const char *, const char **, const char **, const char **);
57 static const char *get_message_header_info(FILE *, char *);
58 static void merge_profile_entry(const char *, const char *, char *[], int *);
59 static void armed_done (int) NORETURN;
60
61 /*
62 * Entry point into (back-end) routines to send message.
63 */
64
65 int
66 sendsbr (char **vec, int vecp, char *program, char *draft, struct stat *st,
67 int rename_drft)
68 {
69 int status, i;
70 pid_t child;
71 char buffer[BUFSIZ], file[BUFSIZ];
72 struct stat sts;
73 char **buildvec, *buildprogram;
74 char *volatile drft = draft;
75 /* nvecs is volatile to prevent warning from gcc about possible clobbering
76 by longjmp. */
77 volatile int nvecs = vecp;
78 int *nvecsp = (int *) &nvecs;
79
80 /*
81 * Run the mimebuildproc (which is by default mhbuild) on the message
82 * with the addition of the "-auto" flag
83 */
84
85 switch (child = fork()) {
86 case NOTOK:
87 adios("fork", "unable to");
88 break;
89
90 case OK:
91 buildvec = argsplit(buildmimeproc, &buildprogram, &i);
92 buildvec[i++] = "-auto";
93 if (distfile)
94 buildvec[i++] = "-dist";
95 buildvec[i++] = (char *) drft;
96 buildvec[i] = NULL;
97 execvp(buildprogram, buildvec);
98 fprintf(stderr, "unable to exec ");
99 perror(buildmimeproc);
100 _exit(-1);
101 break;
102
103 default:
104 if (pidXwait(child, buildmimeproc))
105 return NOTOK;
106 break;
107 }
108
109 done=armed_done;
110 switch (setjmp (env)) {
111 case OK:
112 /*
113 * If given -push and -unique (which is undocumented), then
114 * rename the draft file. I'm not quite sure why.
115 */
116 if (pushsw && unique) {
117 char *cp = m_mktemp2(drft, invo_name, NULL, NULL);
118 if (cp == NULL) {
119 adios(NULL, "unable to create temporary file in %s",
120 get_temp_dir());
121 }
122 if (rename (drft, strncpy(file, cp, sizeof(file))) == NOTOK)
123 adios (file, "unable to rename %s to", drft);
124 drft = file;
125 }
126
127 /*
128 * Rework the vec based on From: header in draft, as specified
129 * by sendfrom-address entries in profile.
130 */
131 if (context_find_prefix("sendfrom-")) {
132 handle_sendfrom(vec, nvecsp, draft);
133 }
134
135 /*
136 * Check if we need to split the message into
137 * multiple messages of type "message/partial".
138 */
139 if (splitsw >= 0 && !distfile && stat ((char *) drft, &sts) != NOTOK
140 && sts.st_size >= CPERMSG) {
141 status = splitmsg (vec, nvecs, program, drft,
142 st, splitsw) ? NOTOK : OK;
143 } else {
144 status = sendaux (vec, nvecs, program, drft, st) ? NOTOK : OK;
145 }
146
147 /* rename the original draft */
148 if (rename_drft && status == OK &&
149 rename (drft, strncpy (buffer, m_backup (drft),
150 sizeof(buffer))) == NOTOK)
151 advise (buffer, "unable to rename %s to", drft);
152 break;
153
154 default:
155 status = DONE;
156 break;
157 }
158
159 done=exit;
160 if (distfile)
161 (void) m_unlink (distfile);
162
163 return status;
164 }
165
166 /*
167 * Split large message into several messages of
168 * type "message/partial" and send them.
169 */
170
171 static int
172 splitmsg (char **vec, int vecp, char *program, char *drft,
173 struct stat *st, int delay)
174 {
175 int compnum, nparts, partno, state, status;
176 long pos, start;
177 time_t clock;
178 char *cp, *dp, buffer[BUFSIZ], msgid[BUFSIZ];
179 char subject[BUFSIZ];
180 char name[NAMESZ], partnum[BUFSIZ];
181 FILE *in;
182 m_getfld_state_t gstate = 0;
183
184 if ((in = fopen (drft, "r")) == NULL)
185 adios (drft, "unable to open for reading");
186
187 cp = dp = NULL;
188 start = 0L;
189
190 /*
191 * Scan through the message and examine the various header fields,
192 * as well as locate the beginning of the message body.
193 */
194 m_getfld_track_filepos (&gstate, in);
195 for (compnum = 1;;) {
196 int bufsz = sizeof buffer;
197 switch (state = m_getfld (&gstate, name, buffer, &bufsz, in)) {
198 case FLD:
199 case FLDPLUS:
200 compnum++;
201
202 /*
203 * This header field is discarded.
204 */
205 if (!strcasecmp (name, "Message-ID")) {
206 while (state == FLDPLUS) {
207 bufsz = sizeof buffer;
208 state = m_getfld (&gstate, name, buffer, &bufsz, in);
209 }
210 } else if (uprf (name, XXX_FIELD_PRF)
211 || !strcasecmp (name, VRSN_FIELD)
212 || !strcasecmp (name, "Subject")
213 || !strcasecmp (name, "Encrypted")) {
214 /*
215 * These header fields are copied to the enclosed
216 * header of the first message in the collection
217 * of message/partials. For the "Subject" header
218 * field, we also record it, so that a modified
219 * version of it, can be copied to the header
220 * of each message/partial in the collection.
221 */
222 if (!strcasecmp (name, "Subject")) {
223 size_t sublen;
224
225 strncpy (subject, buffer, BUFSIZ);
226 sublen = strlen (subject);
227 if (sublen > 0 && subject[sublen - 1] == '\n')
228 subject[sublen - 1] = '\0';
229 }
230
231 dp = add (concat (name, ":", buffer, NULL), dp);
232 while (state == FLDPLUS) {
233 bufsz = sizeof buffer;
234 state = m_getfld (&gstate, name, buffer, &bufsz, in);
235 dp = add (buffer, dp);
236 }
237 } else {
238 /*
239 * These header fields are copied to the header of
240 * each message/partial in the collection.
241 */
242 cp = add (concat (name, ":", buffer, NULL), cp);
243 while (state == FLDPLUS) {
244 bufsz = sizeof buffer;
245 state = m_getfld (&gstate, name, buffer, &bufsz, in);
246 cp = add (buffer, cp);
247 }
248 }
249
250 start = ftell (in) + 1;
251 continue;
252
253 case BODY:
254 case FILEEOF:
255 break;
256
257 case LENERR:
258 case FMTERR:
259 adios (NULL, "message format error in component #%d", compnum);
260
261 default:
262 adios (NULL, "getfld () returned %d", state);
263 }
264
265 break;
266 }
267 m_getfld_state_destroy (&gstate);
268 if (cp == NULL)
269 adios (NULL, "headers missing from draft");
270
271 nparts = 1;
272 pos = start;
273 while (fgets (buffer, sizeof(buffer) - 1, in)) {
274 long len;
275
276 if ((pos += (len = strlen (buffer))) > CPERMSG) {
277 nparts++;
278 pos = len;
279 }
280 }
281
282 /* Only one part, nothing to split */
283 if (nparts == 1) {
284 free (cp);
285 if (dp)
286 free (dp);
287
288 fclose (in);
289 return sendaux (vec, vecp, program, drft, st);
290 }
291
292 if (!pushsw) {
293 printf ("Sending as %d Partial Messages\n", nparts);
294 fflush (stdout);
295 }
296 status = OK;
297
298 vec[vecp++] = "-partno";
299 vec[vecp++] = partnum;
300 if (delay == 0)
301 vec[vecp++] = "-queued";
302
303 time (&clock);
304 snprintf (msgid, sizeof(msgid), "%s", message_id (clock, 0));
305
306 fseek (in, start, SEEK_SET);
307 for (partno = 1; partno <= nparts; partno++) {
308 char tmpdrf[BUFSIZ];
309 FILE *out;
310
311 char *cp = m_mktemp2(drft, invo_name, NULL, &out);
312 if (cp == NULL) {
313 adios(NULL, "unable to create temporary file in %s",
314 get_temp_dir());
315 }
316 strncpy(tmpdrf, cp, sizeof(tmpdrf));
317
318 /*
319 * Output the header fields
320 */
321 fputs (cp, out);
322 fprintf (out, "Subject: %s (part %d of %d)\n", subject, partno, nparts);
323 fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE);
324 fprintf (out, "%s: message/partial; id=\"%s\";\n", TYPE_FIELD, msgid);
325 fprintf (out, "\tnumber=%d; total=%d\n", partno, nparts);
326 fprintf (out, "%s: part %d of %d\n\n", DESCR_FIELD, partno, nparts);
327
328 /*
329 * If this is the first in the collection, output the
330 * header fields we are encapsulating at the beginning
331 * of the body of the first message.
332 */
333 if (partno == 1) {
334 if (dp)
335 fputs (dp, out);
336 fprintf (out, "Message-ID: %s\n", msgid);
337 fprintf (out, "\n");
338 }
339
340 pos = 0;
341 for (;;) {
342 long len;
343
344 if (!fgets (buffer, sizeof(buffer) - 1, in)) {
345 if (partno == nparts)
346 break;
347 adios (NULL, "premature eof");
348 }
349
350 if ((pos += (len = strlen (buffer))) > CPERMSG) {
351 fseek (in, -len, SEEK_CUR);
352 break;
353 }
354
355 fputs (buffer, out);
356 }
357
358 if (fflush (out))
359 adios (tmpdrf, "error writing to");
360
361 fclose (out);
362
363 if (!pushsw && verbsw) {
364 printf ("\n");
365 fflush (stdout);
366 }
367
368 /* Pause here, if a delay is specified */
369 if (delay > 0 && 1 < partno && partno <= nparts) {
370 if (!pushsw) {
371 printf ("pausing %d seconds before sending part %d...\n",
372 delay, partno);
373 fflush (stdout);
374 }
375 sleep ((unsigned int) delay);
376 }
377
378 snprintf (partnum, sizeof(partnum), "%d", partno);
379 status = sendaux (vec, vecp, program, tmpdrf, st);
380 (void) m_unlink (tmpdrf);
381 if (status != OK)
382 break;
383
384 /*
385 * This is so sendaux will only annotate
386 * the altmsg the first time it is called.
387 */
388 annotext = NULL;
389 }
390
391 free (cp);
392 if (dp)
393 free (dp);
394
395 fclose (in); /* close the draft */
396 return status;
397 }
398
399
400 /*
401 * Annotate original message, and
402 * call `postproc' (which is passed down in "program") to send message.
403 */
404
405 static int
406 sendaux (char **vec, int vecp, char *program, char *drft, struct stat *st)
407 {
408 pid_t child_id;
409 int i, status, fd, fd2;
410 char backup[BUFSIZ], buf[BUFSIZ];
411
412 fd = pushsw ? tmp_fd () : NOTOK;
413 fd2 = NOTOK;
414
415 if (annotext) {
416 if ((fd2 = tmp_fd ()) != NOTOK) {
417 vec[vecp++] = "-idanno";
418 snprintf (buf, sizeof(buf), "%d", fd2);
419 vec[vecp++] = buf;
420 } else {
421 admonish (NULL, "unable to create temporary file in %s "
422 "for annotation list", get_temp_dir());
423 }
424 }
425 vec[vecp++] = drft;
426 if (distfile && distout (drft, distfile, backup) == NOTOK)
427 done (1);
428 vec[vecp] = NULL;
429
430 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
431 sleep (5);
432
433 switch (child_id) {
434 case -1:
435 /* oops -- fork error */
436 adios ("fork", "unable to");
437 break; /* NOT REACHED */
438
439 case 0:
440 /*
441 * child process -- send it
442 *
443 * If fd is ok, then we are pushing and fd points to temp
444 * file, so capture anything on stdout and stderr there.
445 */
446 if (fd != NOTOK) {
447 dup2 (fd, fileno (stdout));
448 dup2 (fd, fileno (stderr));
449 close (fd);
450 }
451 execvp (program, vec);
452 fprintf (stderr, "unable to exec ");
453 perror (postproc);
454 _exit (-1);
455
456 default:
457 /*
458 * parent process -- wait for it
459 */
460 if ((status = pidwait(child_id, NOTOK)) == OK) {
461 if (annotext && fd2 != NOTOK)
462 anno (fd2, st);
463 } else {
464 /*
465 * If postproc failed, and we have good fd (which means
466 * we pushed), then mail error message (and possibly the
467 * draft) back to the user.
468 */
469 if (fd != NOTOK) {
470 alert (drft, fd);
471 close (fd);
472 } else {
473 advise (NULL, "message not delivered to anyone");
474 }
475 if (annotext && fd2 != NOTOK)
476 close (fd2);
477 if (distfile) {
478 (void) m_unlink (drft);
479 if (rename (backup, drft) == NOTOK)
480 advise (drft, "unable to rename %s to", backup);
481 }
482 }
483 break;
484 }
485
486 return status;
487 }
488
489
490 /*
491 * Mail error notification (and possibly a copy of the
492 * message) back to the user, using the mailproc
493 */
494
495 static void
496 alert (char *file, int out)
497 {
498 pid_t child_id;
499 int i, in, argp;
500 char buf[BUFSIZ];
501 char *program;
502 char **arglist;
503
504 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
505 sleep (5);
506
507 switch (child_id) {
508 case NOTOK:
509 /* oops -- fork error */
510 advise ("fork", "unable to");
511
512 case OK:
513 /* child process -- send it */
514 SIGNAL (SIGHUP, SIG_IGN);
515 SIGNAL (SIGINT, SIG_IGN);
516 SIGNAL (SIGQUIT, SIG_IGN);
517 SIGNAL (SIGTERM, SIG_IGN);
518 if (forwsw) {
519 if ((in = open (file, O_RDONLY)) == NOTOK) {
520 admonish (file, "unable to re-open");
521 } else {
522 lseek (out, (off_t) 0, SEEK_END);
523 strncpy (buf, "\nMessage not delivered to anyone.\n", sizeof(buf));
524 if (write (out, buf, strlen (buf)) < 0) {
525 advise (file, "write");
526 }
527 strncpy (buf, "\n------- Unsent Draft\n\n", sizeof(buf));
528 if (write (out, buf, strlen (buf)) < 0) {
529 advise (file, "write");
530 }
531 cpydgst (in, out, file, "temporary file");
532 close (in);
533 strncpy (buf, "\n------- End of Unsent Draft\n", sizeof(buf));
534 if (write (out, buf, strlen (buf)) < 0) {
535 advise (file, "write");
536 }
537 if (rename (file, strncpy (buf, m_backup (file), sizeof(buf))) == NOTOK)
538 admonish (buf, "unable to rename %s to", file);
539 }
540 }
541 lseek (out, (off_t) 0, SEEK_SET);
542 dup2 (out, fileno (stdin));
543 close (out);
544 /* create subject for error notification */
545 snprintf (buf, sizeof(buf), "send failed on %s",
546 forwsw ? "enclosed draft" : file);
547
548 arglist = argsplit(mailproc, &program, &argp);
549
550 arglist[argp++] = getusername();
551 arglist[argp++] = "-subject";
552 arglist[argp++] = buf;
553 arglist[argp] = NULL;
554
555 execvp (program, arglist);
556 fprintf (stderr, "unable to exec ");
557 perror (mailproc);
558 _exit (-1);
559
560 default: /* no waiting... */
561 break;
562 }
563 }
564
565
566 static int
567 tmp_fd (void)
568 {
569 int fd;
570 char *tfile;
571
572 if ((tfile = m_mktemp2(NULL, invo_name, &fd, NULL)) == NULL) return NOTOK;
573
574 if (debugsw)
575 advise (NULL, "temporary file %s selected", tfile);
576 else
577 if (m_unlink (tfile) == NOTOK)
578 advise (tfile, "unable to remove");
579
580 return fd;
581 }
582
583
584 static void
585 anno (int fd, struct stat *st)
586 {
587 pid_t child_id;
588 sigset_t set, oset;
589 static char *cwd = NULL;
590 struct stat st2;
591
592 if (altmsg &&
593 (stat (altmsg, &st2) == NOTOK
594 || st->st_mtime != st2.st_mtime
595 || st->st_dev != st2.st_dev
596 || st->st_ino != st2.st_ino)) {
597 if (debugsw)
598 admonish (NULL, "$mhaltmsg mismatch");
599 return;
600 }
601
602 child_id = debugsw ? NOTOK : fork ();
603 switch (child_id) {
604 case NOTOK: /* oops */
605 if (!debugsw)
606 advise (NULL,
607 "unable to fork, so doing annotations by hand...");
608 if (cwd == NULL)
609 cwd = getcpy (pwd ());
610
611 case OK:
612 /* block a few signals */
613 sigemptyset (&set);
614 sigaddset (&set, SIGHUP);
615 sigaddset (&set, SIGINT);
616 sigaddset (&set, SIGQUIT);
617 sigaddset (&set, SIGTERM);
618 sigprocmask (SIG_BLOCK, &set, &oset);
619
620 unregister_for_removal(0);
621
622 annoaux (fd);
623 if (child_id == OK)
624 _exit (0);
625
626 /* reset the signal mask */
627 sigprocmask (SIG_SETMASK, &oset, &set);
628
629 if (chdir (cwd) < 0) {
630 advise (cwd, "chdir");
631 }
632 break;
633
634 default: /* no waiting... */
635 close (fd);
636 break;
637 }
638 }
639
640
641 static void
642 annoaux (int fd)
643 {
644 int fd2, fd3, msgnum;
645 char *cp, *folder, *maildir;
646 char buffer[BUFSIZ], **ap;
647 FILE *fp;
648 struct msgs *mp;
649
650 if ((folder = getenv ("mhfolder")) == NULL || *folder == 0) {
651 if (debugsw)
652 admonish (NULL, "$mhfolder not set");
653 return;
654 }
655 maildir = m_maildir (folder);
656 if (chdir (maildir) == NOTOK) {
657 if (debugsw)
658 admonish (maildir, "unable to change directory to");
659 return;
660 }
661 if (!(mp = folder_read (folder, 0))) {
662 if (debugsw)
663 admonish (NULL, "unable to read folder %s", folder);
664 return;
665 }
666
667 /* check for empty folder */
668 if (mp->nummsg == 0) {
669 if (debugsw)
670 admonish (NULL, "no messages in %s", folder);
671 goto oops;
672 }
673
674 if ((cp = getenv ("mhmessages")) == NULL || *cp == 0) {
675 if (debugsw)
676 admonish (NULL, "$mhmessages not set");
677 goto oops;
678 }
679 if (!debugsw /* MOBY HACK... */
680 && pushsw
681 && (fd3 = open ("/dev/null", O_RDWR)) != NOTOK
682 && (fd2 = dup (fileno (stderr))) != NOTOK) {
683 dup2 (fd3, fileno (stderr));
684 close (fd3);
685 }
686 else
687 fd2 = NOTOK;
688 for (ap = brkstring (cp = getcpy (cp), " ", NULL); *ap; ap++)
689 m_convert (mp, *ap);
690 free (cp);
691 if (fd2 != NOTOK)
692 dup2 (fd2, fileno (stderr));
693 if (mp->numsel == 0) {
694 if (debugsw)
695 admonish (NULL, "no messages to annotate");
696 goto oops;
697 }
698
699 lseek (fd, (off_t) 0, SEEK_SET);
700 if ((fp = fdopen (fd, "r")) == NULL) {
701 if (debugsw)
702 admonish (NULL, "unable to fdopen annotation list");
703 goto oops;
704 }
705 cp = NULL;
706 while (fgets (buffer, sizeof(buffer), fp) != NULL)
707 cp = add (buffer, cp);
708 fclose (fp);
709
710 if (debugsw)
711 advise (NULL, "annotate%s with %s: \"%s\"",
712 inplace ? " inplace" : "", annotext, cp);
713 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
714 if (is_selected(mp, msgnum)) {
715 if (debugsw)
716 advise (NULL, "annotate message %d", msgnum);
717 annotate (m_name (msgnum), annotext, cp, inplace, 1, -2, 0);
718 }
719 }
720
721 free (cp);
722
723 oops:
724 folder_free (mp); /* free folder/message structure */
725 }
726
727
728 static
729 void
730 handle_sendfrom(char **vec, int *vecp, char *draft) {
731 const char *addr, *host;
732 const char *message;
733
734 /* Extract address and host from From: header line in draft. */
735 if (get_from_header_info(draft, &addr, &host, &message) != OK) {
736 adios(draft, message);
737 }
738
739 /* Merge in any address or host specific switches to post(1) from profile. */
740 merge_profile_entry(addr, host, vec, vecp);
741 free((void *) host);
742 free((void *) addr);
743
744 vec[*vecp] = NULL;
745
746 {
747 char **vp;
748
749 for (vp = vec; *vp; ++vp) {
750 if (strcmp(*vp, "xoauth2") == 0) {
751 #ifdef OAUTH_SUPPORT
752 int snoop = 0;
753
754 /* -snoop will be in vec if it was enabled. */
755 for (vp = vec; vp && *vp; ++vp) {
756 if (strcmp(*vp, "-snoop") == 0) {
757 snoop = 1;
758 break;
759 }
760 }
761
762 if (setup_oauth_params(vec, vecp, snoop, &message) != OK) {
763 adios(NULL, message);
764 }
765 break;
766 #else
767 adios(NULL, "sendfrom built without OAUTH_SUPPORT, "
768 "so -saslmech xoauth2 is not supported");
769 #endif /* OAUTH_SUPPORT */
770 }
771 }
772 }
773 }
774
775
776 #ifdef OAUTH_SUPPORT
777 /*
778 * For XOAUTH2, append access token, from mh_oauth_do_xoauth(), for the user to vec.
779 */
780 static
781 int
782 setup_oauth_params(char *vec[], int *vecp, int snoop, const char **message) {
783 const char *saslmech = NULL, *user = NULL, *auth_svc = NULL;
784 int i;
785
786 /* Make sure we have all the information we need. */
787 for (i = 1; i < *vecp; ++i) {
788 /* Don't support abbreviated switches, to avoid collisions in the
789 future if new ones are added. */
790 if (! strcmp(vec[i-1], "-saslmech")) {
791 saslmech = vec[i];
792 } else if (! strcmp(vec[i-1], "-user")) {
793 user = vec[i];
794 } else if (! strcmp(vec[i-1], "-authservice")) {
795 auth_svc = vec[i];
796 }
797 }
798
799 if (auth_svc == NULL) {
800 if (saslmech && ! strcasecmp(saslmech, "xoauth2")) {
801 *message = "must specify -authservice with -saslmech xoauth2";
802 return NOTOK;
803 }
804 } else {
805 if (user == NULL) {
806 *message = "must specify -user with -saslmech xoauth2";
807 return NOTOK;
808 }
809
810 vec[(*vecp)++] = getcpy("-authservice");
811 if (saslmech && ! strcasecmp(saslmech, "xoauth2")) {
812 vec[(*vecp)++] = mh_oauth_do_xoauth(user, auth_svc, snoop ? stderr : NULL);
813 } else {
814 vec[(*vecp)++] = getcpy(auth_svc);
815 }
816 }
817
818 return 0;
819 }
820 #endif /* OAUTH_SUPPORT */
821
822
823 /*
824 * Extract user and domain from From: header line in draft.
825 */
826 static
827 int
828 get_from_header_info(const char *filename, const char **addr, const char **host, const char **message) {
829 struct stat st;
830 FILE *in;
831
832 if (stat (filename, &st) == NOTOK) {
833 *message = "unable to stat draft file";
834 return NOTOK;
835 }
836
837 if ((in = fopen (filename, "r")) != NULL) {
838 /* There must be a non-blank Envelope-From or {Resent-}Sender or
839 {Resent-}From header. */
840 char *addrformat = "%(addr{Envelope-From})";
841 char *hostformat = "%(host{Envelope-From})";
842
843 if ((*addr = get_message_header_info (in, addrformat)) == NULL ||
844 strlen (*addr) == 0) {
845 addrformat = distfile == NULL ? "%(addr{Sender})" : "%(addr{Resent-Sender})";
846 hostformat = distfile == NULL ? "%(host{Sender})" : "%(host{Resent-Sender})";
847
848 if ((*addr = get_message_header_info (in, addrformat)) == NULL) {
849 addrformat = distfile == NULL ? "%(addr{From})" : "%(addr{Resent-From})";
850 hostformat = distfile == NULL ? "%(host{From})" : "%(host{Resent-From})";
851
852 if ((*addr = get_message_header_info (in, addrformat)) == NULL) {
853 *message = "unable to find sender address in";
854 fclose(in);
855 return NOTOK;
856 }
857 }
858 }
859
860 /* Use the hostformat that corresponds to the successful addrformat. */
861 if ((*host = get_message_header_info(in, hostformat)) == NULL) {
862 fclose(in);
863 *message = "unable to find sender host in";
864 fclose(in);
865 return NOTOK;
866 }
867 fclose(in);
868
869 return OK;
870 } else {
871 *message = "unable to open";
872 return NOTOK;
873 }
874 }
875
876
877 /*
878 * Get formatted information from header of a message.
879 * Adapted from process_single_file() in uip/fmttest.c.
880 */
881 static
882 const char *
883 get_message_header_info(FILE *in, char *format) {
884 int dat[5];
885 struct format *fmt;
886 struct stat st;
887 int parsing_header;
888 m_getfld_state_t gstate = 0;
889 charstring_t buffer = charstring_create(0);
890 char *retval;
891
892 dat[0] = dat[1] = dat[4] = 0;
893 dat[2] = fstat(fileno(in), &st) == 0 ? st.st_size : 0;
894 dat[3] = INT_MAX;
895
896 (void) fmt_compile(new_fs(NULL, NULL, format), &fmt, 1);
897 free_fs();
898
899 /*
900 * Read in the message and process the header.
901 */
902 rewind (in);
903 parsing_header = 1;
904 do {
905 char name[NAMESZ], rbuf[NMH_BUFSIZ];
906 int bufsz = sizeof rbuf;
907 int state = m_getfld(&gstate, name, rbuf, &bufsz, in);
908
909 switch (state) {
910 case FLD:
911 case FLDPLUS: {
912 int bucket = fmt_addcomptext(name, rbuf);
913
914 if (bucket != -1) {
915 while (state == FLDPLUS) {
916 bufsz = sizeof rbuf;
917 state = m_getfld(&gstate, name, rbuf, &bufsz, in);
918 fmt_appendcomp(bucket, name, rbuf);
919 }
920 }
921
922 while (state == FLDPLUS) {
923 bufsz = sizeof rbuf;
924 state = m_getfld(&gstate, name, rbuf, &bufsz, in);
925 }
926 break;
927 }
928 default:
929 parsing_header = 0;
930 }
931 } while (parsing_header);
932 m_getfld_state_destroy(&gstate);
933
934 fmt_scan(fmt, buffer, INT_MAX, dat, NULL);
935 fmt_free(fmt, 1);
936
937 /* Trim trailing newline, if any. */
938 retval = rtrim(charstring_buffer_copy((buffer)));
939 charstring_free(buffer);
940 if (strlen (retval) > 0) {
941 return retval;
942 } else {
943 free (retval);
944 return NULL;
945 }
946 }
947
948
949 /*
950 * Look in profile for entry corresponding to addr or host, and add its contents to vec.
951 *
952 * Could do some of this automatically, by looking for:
953 * 1) access-$(mbox{from}) in oauth-svc file using mh_oauth_cred_load(), which isn't
954 * static and doesn't have side effects; free the result with mh_oauth_cred_free())
955 * 2) machine $(mbox{from}) in creds
956 * If no -server passed in from profile or commandline, could use smtp.<svc>.com for gmail,
957 * but that might not generalize for other svcs.
958 */
959 static
960 void
961 merge_profile_entry(const char *addr, const char *host, char *vec[], int *vecp) {
962 char *addr_entry = concat("sendfrom-", addr, NULL);
963 char *profile_entry = context_find(addr_entry);
964
965 free(addr_entry);
966 if (profile_entry == NULL) {
967 /* No entry for the user. Look for one for the host. */
968 char *host_entry = concat("sendfrom-", host, NULL);
969
970 profile_entry = context_find(host_entry);
971 free(host_entry);
972 }
973
974 /* Use argsplit() to do the real work of splitting the args in the profile entry. */
975 if (profile_entry && strlen(profile_entry) > 0) {
976 int profile_vecp;
977 char *file;
978 char **profile_vec = argsplit(profile_entry, &file, &profile_vecp);
979 int i;
980
981 for (i = 0; i < profile_vecp; ++i) {
982 vec[(*vecp)++] = getcpy(profile_vec[i]);
983 }
984
985 arglist_free(file, profile_vec);
986 }
987 }
988
989
990 static void
991 armed_done (int status)
992 {
993 longjmp (env, status ? status : NOTOK);
994 }