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