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