]> diplodocus.org Git - nmh/blob - uip/replsbr.c
sbr/dtime.c: Remove struct-assigning twscopy().
[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 <h/addrsbr.h>
10 #include <h/fmt_scan.h>
11 #include <h/utils.h>
12 #include <sys/file.h> /* L_SET */
13
14 extern short ccto; /* from repl.c */
15 extern short cccc;
16 extern short ccme;
17 extern short querysw;
18
19 static int dftype=0;
20
21 static char *badaddrs = NULL;
22 static char *dfhost = NULL;
23
24 static struct mailname mq;
25 static int nodupcheck = 0; /* If set, no check for duplicates */
26
27 /*
28 * Buffer size for content part of header fields.
29 * We want this to be large enough so that we don't
30 * do a lot of extra FLDPLUS calls on m_getfld but
31 * small enough so that we don't snarf the entire
32 * message body when we're not going to use any of it.
33 */
34 #define SBUFSIZ 256
35
36 static char *addrcomps[] = {
37 "from",
38 "sender",
39 "reply-to",
40 "to",
41 "cc",
42 "bcc",
43 "resent-from",
44 "resent-sender",
45 "resent-reply-to",
46 "resent-to",
47 "resent-cc",
48 "resent-bcc",
49 NULL
50 };
51
52 /*
53 * static prototypes
54 */
55 static int insert (struct mailname *);
56 static void replfilter (FILE *, FILE *, char *, int);
57 static char *replformataddr(char *, char *);
58 static char *replconcataddr(char *, char *);
59 static char *fix_addresses (char *);
60
61
62 void
63 replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
64 int mime, char *form, char *filter, char *fcc, int fmtproc)
65 {
66 int state, i;
67 struct comp *cptr;
68 char tmpbuf[SBUFSIZ];
69 struct format *fmt;
70 char **ap;
71 int char_read = 0, format_len, mask;
72 char name[NAMESZ], *cp;
73 charstring_t scanl;
74 static int dat[5]; /* aux. data for format routine */
75 m_getfld_state_t gstate = 0;
76 struct fmt_callbacks cb;
77
78 FILE *out;
79 NMH_UNUSED (msg);
80
81 mask = umask(~m_gmprot());
82 if ((out = fopen (drft, "w")) == NULL)
83 adios (drft, "unable to create");
84
85 umask(mask);
86
87 /* get new format string */
88 cp = new_fs (form, NULL, NULL);
89 format_len = strlen (cp);
90
91 /* compile format string */
92 fmt_compile (cp, &fmt, 1);
93
94 for (ap = addrcomps; *ap; ap++) {
95 cptr = fmt_findcomp (*ap);
96 if (cptr)
97 cptr->c_type |= CT_ADDR;
98 }
99
100 /*
101 * ignore any components killed by command line switches
102 *
103 * This prevents the component from being found via fmt_findcomp(),
104 * which makes sure no text gets added to it when the message is processed.
105 */
106 if (!ccto) {
107 cptr = fmt_findcomp ("to");
108 if (cptr)
109 cptr->c_name = mh_xstrdup("");
110 }
111 if (!cccc) {
112 cptr = fmt_findcomp("cc");
113 if (cptr)
114 cptr->c_name = mh_xstrdup("");
115 }
116 if (!ccme)
117 ismymbox (NULL);
118
119 /*
120 * pick any interesting stuff out of msg "inb"
121 */
122 for (;;) {
123 int msg_count = sizeof tmpbuf;
124 state = m_getfld (&gstate, name, tmpbuf, &msg_count, inb);
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_getfld (&gstate, name, tmpbuf, &msg_count, inb);
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_getfld (&gstate, name, tmpbuf, &msg_count, inb);
149 }
150 break;
151
152 case LENERR:
153 case FMTERR:
154 case BODY:
155 case FILEEOF:
156 goto finished;
157
158 default:
159 adios (NULL, "m_getfld() 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 mh_xfree(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 mh_xfree(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 memset(&cb, 0, sizeof(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 int 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 = 0; (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 = 0;
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++;
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 = 1;
378 cp = replformataddr(orig, str);
379 nodupcheck = 0;
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 (np->m_host ? np->m_host : "",
397 mp->m_next->m_host ? mp->m_next->m_host : "") &&
398 !strcasecmp (np->m_mbox ? np->m_mbox : "",
399 mp->m_next->m_mbox ? 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), (off_t) 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 break;
488 }
489 }
490
491
492 static
493 char *
494 fix_addresses (char *str) {
495 char *fixed_str = NULL;
496 int fixed_address = 0;
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 = 1;
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 }