]> diplodocus.org Git - nmh/blob - uip/replsbr.c
getpass.c: Move interface to own file.
[nmh] / uip / replsbr.c
1 /* replsbr.c -- routines to help repl along...
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "sbr/closefds.h"
10 #include "sbr/uprf.h"
11 #include "sbr/escape_addresses.h"
12 #include "sbr/pidstatus.h"
13 #include "sbr/arglist.h"
14 #include "sbr/error.h"
15 #include "h/addrsbr.h"
16 #include "h/fmt_scan.h"
17 #include "h/done.h"
18 #include "h/utils.h"
19 #include <sys/file.h>
20 #include "replsbr.h"
21
22 short ccto = -1;
23 short cccc = -1;
24 short ccme = -1;
25 short querysw = 0;
26
27 static int dftype=0;
28
29 static char *badaddrs = NULL;
30 static char *dfhost = NULL;
31
32 static struct mailname mq;
33 static bool nodupcheck; /* If set, no check for duplicates */
34
35 static char *addrcomps[] = {
36 "from",
37 "sender",
38 "reply-to",
39 "to",
40 "cc",
41 "bcc",
42 "resent-from",
43 "resent-sender",
44 "resent-reply-to",
45 "resent-to",
46 "resent-cc",
47 "resent-bcc",
48 NULL
49 };
50
51 /*
52 * static prototypes
53 */
54 static int insert (struct mailname *);
55 static void replfilter (FILE *, FILE *, char *, int);
56 static char *replformataddr(char *, char *);
57 static char *replconcataddr(char *, char *);
58 static char *fix_addresses (char *);
59
60
61 void
62 replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
63 int mime, char *form, char *filter, char *fcc, int fmtproc)
64 {
65 int state, i;
66 struct comp *cptr;
67 char tmpbuf[NMH_BUFSIZ];
68 struct format *fmt;
69 char **ap;
70 int char_read = 0, format_len, mask;
71 char name[NAMESZ], *cp;
72 charstring_t scanl;
73 static int dat[5]; /* aux. data for format routine */
74 m_getfld_state_t gstate;
75 struct fmt_callbacks cb;
76
77 FILE *out;
78 NMH_UNUSED (msg);
79
80 mask = umask(~m_gmprot());
81 if ((out = fopen (drft, "w")) == NULL)
82 adios (drft, "unable to create");
83
84 umask(mask);
85
86 /* get new format string */
87 cp = new_fs (form, NULL, NULL);
88 format_len = strlen (cp);
89
90 /* compile format string */
91 fmt_compile (cp, &fmt, 1);
92
93 for (ap = addrcomps; *ap; ap++) {
94 cptr = fmt_findcomp (*ap);
95 if (cptr)
96 cptr->c_type |= CT_ADDR;
97 }
98
99 /*
100 * ignore any components killed by command line switches
101 *
102 * This prevents the component from being found via fmt_findcomp(),
103 * which makes sure no text gets added to it when the message is processed.
104 */
105 if (!ccto) {
106 cptr = fmt_findcomp ("to");
107 if (cptr)
108 cptr->c_name = mh_xstrdup("");
109 }
110 if (!cccc) {
111 cptr = fmt_findcomp("cc");
112 if (cptr)
113 cptr->c_name = mh_xstrdup("");
114 }
115 if (!ccme)
116 ismymbox (NULL);
117
118 /*
119 * pick any interesting stuff out of msg "inb"
120 */
121 gstate = m_getfld_state_init(inb);
122 for (;;) {
123 int msg_count = sizeof tmpbuf;
124 state = m_getfld2(&gstate, name, tmpbuf, &msg_count);
125 switch (state) {
126 case FLD:
127 case FLDPLUS:
128 /*
129 * if we're interested in this component, save a pointer
130 * to the component text, then start using our next free
131 * buffer as the component temp buffer (buffer switching
132 * saves an extra copy of the component text).
133 */
134
135 i = fmt_addcomptext(name, tmpbuf);
136 if (i != -1) {
137 char_read += msg_count;
138 while (state == FLDPLUS) {
139 msg_count= sizeof tmpbuf;
140 state = m_getfld2(&gstate, name, tmpbuf, &msg_count);
141 fmt_appendcomp(i, name, tmpbuf);
142 char_read += msg_count;
143 }
144 }
145
146 while (state == FLDPLUS) {
147 msg_count= sizeof tmpbuf;
148 state = m_getfld2(&gstate, name, tmpbuf, &msg_count);
149 }
150 break;
151
152 case LENERR:
153 case FMTERR:
154 case BODY:
155 case FILEEOF:
156 goto finished;
157
158 default:
159 die("m_getfld2() returned %d", state);
160 }
161 }
162
163 /*
164 * format and output the header lines.
165 */
166 finished:
167 m_getfld_state_destroy (&gstate);
168
169 /* set up the "fcc" pseudo-component */
170 cptr = fmt_findcomp ("fcc");
171 if (cptr) {
172 free(cptr->c_text);
173 if (fcc)
174 cptr->c_text = mh_xstrdup(fcc);
175 else
176 cptr->c_text = NULL;
177 }
178 cptr = fmt_findcomp ("user");
179 if (cptr) {
180 free(cptr->c_text);
181 if ((cp = getenv("USER")))
182 cptr->c_text = mh_xstrdup(cp);
183 else
184 cptr = NULL;
185 }
186
187 /*
188 * if there's a "Subject" component, strip any "Re:"s off it
189 */
190 cptr = fmt_findcomp ("subject");
191 if (cptr && (cp = cptr->c_text)) {
192 char *sp = cp;
193
194 for (;;) {
195 while (isspace((unsigned char) *cp))
196 cp++;
197 if(uprf(cp, "re:"))
198 cp += 3;
199 else
200 break;
201 sp = cp;
202 }
203 if (sp != cptr->c_text) {
204 cp = cptr->c_text;
205 cptr->c_text = mh_xstrdup(sp);
206 free (cp);
207 }
208 }
209 i = format_len + char_read + 256;
210 scanl = charstring_create (i + 2);
211 dat[0] = 0;
212 dat[1] = 0;
213 dat[2] = 0;
214 dat[3] = outputlinelen;
215 dat[4] = 0;
216 ZERO(&cb);
217 cb.formataddr = replformataddr;
218 cb.concataddr = replconcataddr;
219 fmt_scan (fmt, scanl, i, dat, &cb);
220 fputs (charstring_buffer (scanl), out);
221 if (badaddrs) {
222 fputs ("\nrepl: bad addresses:\n", out);
223 fputs ( badaddrs, out);
224 }
225
226 /*
227 * Check if we should filter the message
228 * or add mhn directives
229 */
230 if (filter) {
231 fflush(out);
232 if (ferror (out))
233 adios (drft, "error writing");
234
235 replfilter (inb, out, filter, fmtproc);
236 } else if (mime && mp) {
237 fprintf (out, "#forw [original message] +%s %s\n",
238 mp->foldpath, m_name (mp->lowsel));
239 }
240
241 fflush(out);
242 if (ferror (out))
243 adios (drft, "error writing");
244 fclose (out);
245
246 /* return dynamically allocated buffers */
247 charstring_free (scanl);
248 fmt_free(fmt, 1);
249 }
250
251 static char *buf; /* our current working buffer */
252 static char *bufend; /* end of working buffer */
253 static char *last_dst; /* buf ptr at end of last call */
254 static unsigned int bufsiz=0; /* current size of buf */
255
256 #define BUFINCR 512 /* how much to expand buf when if fills */
257
258 #define CPY(s) { cp = (s); while ((*dst++ = *cp++)) ; --dst; }
259
260 /*
261 * check if there's enough room in buf for str.
262 * add more mem if needed
263 */
264 #define CHECKMEM(str) \
265 if ((len = strlen (str)) >= bufend - dst) {\
266 int i = dst - buf;\
267 int n = last_dst - buf;\
268 bufsiz += ((dst + len - bufend) / BUFINCR + 1) * BUFINCR;\
269 buf = mh_xrealloc (buf, bufsiz);\
270 dst = buf + i;\
271 last_dst = buf + n;\
272 bufend = buf + bufsiz;\
273 }
274
275
276 /*
277 * fmt_scan will call this routine if the user includes the function
278 * "(formataddr {component})" in a format string. "orig" is the
279 * original contents of the string register. "str" is the address
280 * string to be formatted and concatenated onto orig. This routine
281 * returns a pointer to the concatenated address string.
282 *
283 * We try to not do a lot of malloc/copy/free's (which is why we
284 * don't call "getcpy") but still place no upper limit on the
285 * length of the result string.
286 */
287 static char *
288 replformataddr (char *orig, char *str)
289 {
290 int len;
291 char baddr[BUFSIZ], error[BUFSIZ];
292 bool isgroup;
293 char *dst;
294 char *cp;
295 char *sp;
296 struct mailname *mp = NULL;
297 char *fixed_str = fix_addresses (str);
298
299 /* if we don't have a buffer yet, get one */
300 if (bufsiz == 0) {
301 buf = mh_xmalloc (BUFINCR);
302 last_dst = buf; /* XXX */
303 bufsiz = BUFINCR - 6; /* leave some slop */
304 bufend = buf + bufsiz;
305 }
306 /*
307 * If "orig" points to our buffer we can just pick up where we
308 * left off. Otherwise we have to copy orig into our buffer.
309 */
310 if (orig == buf)
311 dst = last_dst;
312 else if (!orig || !*orig) {
313 dst = buf;
314 *dst = '\0';
315 } else {
316 dst = last_dst; /* XXX */
317 CHECKMEM (orig);
318 CPY (orig);
319 }
320
321 /* concatenate all the new addresses onto 'buf' */
322 for (isgroup = false; (cp = getname (fixed_str)); ) {
323 if ((mp = getm (cp, dfhost, dftype, error, sizeof(error))) == NULL) {
324 snprintf (baddr, sizeof(baddr), "\t%s -- %s\n", cp, error);
325 badaddrs = add (baddr, badaddrs);
326 continue;
327 }
328 if (isgroup && (mp->m_gname || !mp->m_ingrp)) {
329 *dst++ = ';';
330 isgroup = false;
331 }
332 if (insert (mp)) {
333 /* if we get here we're going to add an address */
334 if (dst != buf) {
335 *dst++ = ',';
336 *dst++ = ' ';
337 }
338 if (mp->m_gname) {
339 CHECKMEM (mp->m_gname);
340 CPY (mp->m_gname);
341 isgroup = true;
342 }
343 sp = adrformat (mp);
344 CHECKMEM (sp);
345 CPY (sp);
346 }
347 }
348
349 free (fixed_str);
350
351 if (isgroup)
352 *dst++ = ';';
353
354 *dst = '\0';
355 last_dst = dst;
356 return buf;
357 }
358
359
360 /*
361 * fmt_scan will call this routine if the user includes the function
362 * "(concataddr {component})" in a format string. This behaves exactly
363 * like formataddr, except that it does NOT suppress duplicate addresses
364 * between calls.
365 *
366 * As an implementation detail: I thought about splitting out replformataddr()
367 * into the generic part and duplicate-suppressing part, but the call to
368 * insert() was buried deep within a couple of loops and I didn't see a
369 * way to do it easily. So instead we simply set a special flag to stop
370 * the duplicate check and call replformataddr().
371 */
372 static char *
373 replconcataddr(char *orig, char *str)
374 {
375 char *cp;
376
377 nodupcheck = true;
378 cp = replformataddr(orig, str);
379 nodupcheck = false;
380 return cp;
381 }
382
383 static int
384 insert (struct mailname *np)
385 {
386 char buffer[BUFSIZ];
387 struct mailname *mp;
388
389 if (nodupcheck)
390 return 1;
391
392 if (np->m_mbox == NULL)
393 return 0;
394
395 for (mp = &mq; mp->m_next; mp = mp->m_next) {
396 if (!strcasecmp (FENDNULL(np->m_host),
397 FENDNULL(mp->m_next->m_host)) &&
398 !strcasecmp (FENDNULL(np->m_mbox),
399 FENDNULL(mp->m_next->m_mbox)))
400 return 0;
401 }
402 if (!ccme && ismymbox (np))
403 return 0;
404
405 if (querysw) {
406 snprintf (buffer, sizeof(buffer), "Reply to %s? ", adrformat (np));
407 if (!read_switch (buffer, anoyes))
408 return 0;
409 }
410 mp->m_next = np;
411
412 return 1;
413 }
414
415
416 /*
417 * Call the mhlproc
418 *
419 * This function expects that argument out has been fflushed by the caller.
420 */
421
422 static void
423 replfilter (FILE *in, FILE *out, char *filter, int fmtproc)
424 {
425 int pid;
426 char *mhl;
427 char *errstr;
428 char **arglist;
429 int argnum;
430
431 if (filter == NULL)
432 return;
433
434 if (access (filter, R_OK) == NOTOK)
435 adios (filter, "unable to read");
436
437 rewind (in);
438 lseek(fileno(in), 0, SEEK_SET);
439
440 arglist = argsplit(mhlproc, &mhl, &argnum);
441
442 switch (pid = fork()) {
443 case NOTOK:
444 adios ("fork", "unable to");
445
446 case OK:
447 dup2 (fileno (in), fileno (stdin));
448 dup2 (fileno (out), fileno (stdout));
449 closefds (3);
450
451 /*
452 * We're not allocating the memory for the extra arguments,
453 * because we never call arglist_free(). But if we ever change
454 * that be sure to use getcpy() for the extra arguments.
455 */
456 arglist[argnum++] = "-form";
457 arglist[argnum++] = filter;
458 arglist[argnum++] = "-noclear";
459
460 switch (fmtproc) {
461 case 1:
462 arglist[argnum++] = "-fmtproc";
463 arglist[argnum++] = formatproc;
464 break;
465 case 0:
466 arglist[argnum++] = "-nofmtproc";
467 break;
468 }
469
470 arglist[argnum++] = NULL;
471
472 execvp (mhl, arglist);
473 errstr = strerror(errno);
474 if (write(2, "unable to exec ", 15) < 0 ||
475 write(2, mhlproc, strlen(mhlproc)) < 0 ||
476 write(2, ": ", 2) < 0 ||
477 write(2, errstr, strlen(errstr)) < 0 ||
478 write(2, "\n", 1) < 0) {
479 advise ("stderr", "write");
480 }
481 _exit(1);
482
483 default:
484 if (pidXwait (pid, mhl))
485 done (1);
486 fseek (out, 0L, SEEK_END);
487 arglist_free(mhl, arglist);
488 break;
489 }
490 }
491
492
493 static char *
494 fix_addresses (char *str)
495 {
496 char *fixed_str = NULL;
497 bool fixed_address = false;
498
499 if (str) {
500 /*
501 * Attempt to parse each of the addresses in str. If any fail
502 * and can be fixed with escape_local_part(), do that. This
503 * is extra ugly because getm()'s state can only be reset by
504 * call getname(), and getname() needs to be called repeatedly
505 * until it returns NULL to reset its state.
506 */
507 struct adr_node {
508 char *adr;
509 int escape_local_part;
510 int fixed;
511 struct adr_node *next;
512 } *adrs = NULL;
513 struct adr_node *np = adrs;
514 char *cp;
515
516 /*
517 * First, put each of the addresses in a linked list. Note
518 * invalid addresses that might be fixed by escaping the
519 * local part.
520 */
521 while ((cp = getname (str))) {
522 struct adr_node *adr_nodep;
523 char error[BUFSIZ];
524 struct mailname *mp;
525
526 NEW(adr_nodep);
527 adr_nodep->adr = mh_xstrdup (cp);
528 adr_nodep->escape_local_part = 0;
529 adr_nodep->fixed = 0;
530 adr_nodep->next = NULL;
531
532 /* With AD_NAME, errors are not reported to user. */
533 if ((mp = getm (cp, dfhost, dftype, error,
534 sizeof(error))) == NULL) {
535 const char *no_at_sign = "no at-sign after local-part";
536
537 adr_nodep->escape_local_part =
538 has_prefix(error, no_at_sign);
539 } else {
540 mnfree (mp);
541 }
542
543 if (np) {
544 np = np->next = adr_nodep;
545 } else {
546 np = adrs = adr_nodep;
547 }
548 }
549
550 /*
551 * Walk the list and try to fix broken addresses.
552 */
553 for (np = adrs; np; np = np->next) {
554 char *display_name = mh_xstrdup (np->adr);
555 size_t len = strlen (display_name);
556
557 if (np->escape_local_part) {
558 char *local_part_end = strrchr (display_name, '<');
559 char *angle_addr = mh_xstrdup (local_part_end);
560 struct mailname *mp;
561 char *new_adr, *adr;
562
563 *local_part_end = '\0';
564 /* Trim any trailing whitespace. */
565 while (local_part_end > display_name &&
566 isspace ((unsigned char) *--local_part_end)) {
567 *local_part_end = '\0';
568 }
569 escape_local_part (display_name, len);
570 new_adr = concat (display_name, " ", angle_addr, NULL);
571 adr = getname (new_adr);
572 if (adr != NULL &&
573 (mp = getm (adr, dfhost, dftype, NULL, 0)) != NULL) {
574 fixed_address = true;
575 mnfree (mp);
576 }
577 free (angle_addr);
578 free (new_adr);
579 free (np->adr);
580 np->adr = mh_xstrdup (adr);
581
582 /* Need to flush getname() */
583 while ((cp = getname (""))) continue;
584 } /* else the np->adr is OK, so use it as-is. */
585
586 free (display_name);
587 }
588
589 /*
590 * If any addresses were repaired, build new address string,
591 * replacing broken addresses.
592 */
593 for (np = adrs; np; ) {
594 struct adr_node *next = np->next;
595
596 if (fixed_address) {
597 if (fixed_str) {
598 char *new_str = concat (fixed_str, ", ", np->adr, NULL);
599
600 free (fixed_str);
601 fixed_str = new_str;
602 } else {
603 fixed_str = mh_xstrdup (np->adr);
604 }
605 }
606
607 free (np->adr);
608 free (np);
609 np = next;
610 }
611 }
612
613 if (fixed_address) {
614 return fixed_str;
615 }
616 free (fixed_str);
617 return str ? mh_xstrdup (str) : NULL;
618 }