]> diplodocus.org Git - nmh/blob - uip/replsbr.c
Update manpages to use .TP for tagged paragraphs (part I).
[nmh] / uip / replsbr.c
1
2 /*
3 * replsbr.c -- routines to help repl 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/addrsbr.h>
12 #include <h/fmt_scan.h>
13 #include <h/utils.h>
14 #include <sys/file.h> /* L_SET */
15 #include <errno.h>
16
17 extern short ccto; /* from repl.c */
18 extern short cccc;
19 extern short ccme;
20 extern short querysw;
21
22 static int dftype=0;
23
24 static char *badaddrs = NULL;
25 static char *dfhost = NULL;
26
27 static struct mailname mq;
28 static int nodupcheck = 0; /* If set, no check for duplicates */
29
30 /*
31 * Buffer size for content part of header fields.
32 * We want this to be large enough so that we don't
33 * do a lot of extra FLDPLUS calls on m_getfld but
34 * small enough so that we don't snarf the entire
35 * message body when we're not going to use any of it.
36 */
37 #define SBUFSIZ 256
38
39 static char *addrcomps[] = {
40 "from",
41 "sender",
42 "reply-to",
43 "to",
44 "cc",
45 "bcc",
46 "resent-from",
47 "resent-sender",
48 "resent-reply-to",
49 "resent-to",
50 "resent-cc",
51 "resent-bcc",
52 NULL
53 };
54
55 /*
56 * static prototypes
57 */
58 static int insert (struct mailname *);
59 static void replfilter (FILE *, FILE *, char *, int);
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 register int state, i;
67 register struct comp *cptr;
68 char tmpbuf[SBUFSIZ];
69 struct format *fmt;
70 register char **ap;
71 int char_read = 0, format_len, mask;
72 char name[NAMESZ], *scanl;
73 unsigned char *cp;
74 static int dat[5]; /* aux. data for format routine */
75 m_getfld_state_t gstate = 0;
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 * getcpy(NULL) returns a malloc'd zero-length string, so it can safely
106 * be free()'d later.
107 */
108 if (!ccto) {
109 cptr = fmt_findcomp ("to");
110 if (cptr)
111 cptr->c_name = getcpy(NULL);
112 }
113 if (!cccc) {
114 cptr = fmt_findcomp("cc");
115 if (cptr)
116 cptr->c_name = getcpy(NULL);
117 }
118 /* set up the "fcc" pseudo-component */
119 if (fcc) {
120 cptr = fmt_findcomp ("fcc");
121 if (cptr)
122 cptr->c_text = getcpy (fcc);
123 }
124 if ((cp = getenv("USER"))) {
125 cptr = fmt_findcomp ("user");
126 if (cptr)
127 cptr->c_text = getcpy(cp);
128 }
129 if (!ccme)
130 ismymbox (NULL);
131
132 /*
133 * pick any interesting stuff out of msg "inb"
134 */
135 for (;;) {
136 int msg_count = sizeof tmpbuf;
137 state = m_getfld (&gstate, name, tmpbuf, &msg_count, inb);
138 switch (state) {
139 case FLD:
140 case FLDPLUS:
141 /*
142 * if we're interested in this component, save a pointer
143 * to the component text, then start using our next free
144 * buffer as the component temp buffer (buffer switching
145 * saves an extra copy of the component text).
146 */
147
148 i = fmt_addcomptext(name, tmpbuf);
149 if (i != -1) {
150 char_read += msg_count;
151 while (state == FLDPLUS) {
152 msg_count= sizeof tmpbuf;
153 state = m_getfld (&gstate, name, tmpbuf, &msg_count, inb);
154 fmt_appendcomp(i, name, tmpbuf);
155 char_read += msg_count;
156 }
157 }
158
159 while (state == FLDPLUS) {
160 msg_count= sizeof tmpbuf;
161 state = m_getfld (&gstate, name, tmpbuf, &msg_count, inb);
162 }
163 break;
164
165 case LENERR:
166 case FMTERR:
167 case BODY:
168 case FILEEOF:
169 goto finished;
170
171 default:
172 adios (NULL, "m_getfld() returned %d", state);
173 }
174 }
175 m_getfld_state_destroy (&gstate);
176
177 /*
178 * format and output the header lines.
179 */
180 finished:
181
182 /*
183 * if there's a "Subject" component, strip any "Re:"s off it
184 */
185 cptr = fmt_findcomp ("subject");
186 if (cptr && (cp = cptr->c_text)) {
187 register char *sp = cp;
188
189 for (;;) {
190 while (isspace(*cp))
191 cp++;
192 if(uprf(cp, "re:"))
193 cp += 3;
194 else
195 break;
196 sp = cp;
197 }
198 if (sp != cptr->c_text) {
199 cp = cptr->c_text;
200 cptr->c_text = getcpy (sp);
201 free (cp);
202 }
203 }
204 i = format_len + char_read + 256;
205 scanl = mh_xmalloc ((size_t) i + 2);
206 dat[0] = 0;
207 dat[1] = 0;
208 dat[2] = 0;
209 dat[3] = outputlinelen;
210 dat[4] = 0;
211 fmt_scan (fmt, scanl, i + 1, i, dat);
212 fputs (scanl, out);
213 if (badaddrs) {
214 fputs ("\nrepl: bad addresses:\n", out);
215 fputs ( badaddrs, out);
216 }
217
218 /*
219 * Check if we should filter the message
220 * or add mhn directives
221 */
222 if (filter) {
223 fflush(out);
224 if (ferror (out))
225 adios (drft, "error writing");
226
227 replfilter (inb, out, filter, fmtproc);
228 } else if (mime && mp) {
229 fprintf (out, "#forw [original message] +%s %s\n",
230 mp->foldpath, m_name (mp->lowsel));
231 }
232
233 fflush(out);
234 if (ferror (out))
235 adios (drft, "error writing");
236 fclose (out);
237
238 /* return dynamically allocated buffers */
239 free (scanl);
240 fmt_free(fmt, 1);
241 }
242
243 static char *buf; /* our current working buffer */
244 static char *bufend; /* end of working buffer */
245 static char *last_dst; /* buf ptr at end of last call */
246 static unsigned int bufsiz=0; /* current size of buf */
247
248 #define BUFINCR 512 /* how much to expand buf when if fills */
249
250 #define CPY(s) { cp = (s); while ((*dst++ = *cp++)) ; --dst; }
251
252 /*
253 * check if there's enough room in buf for str.
254 * add more mem if needed
255 */
256 #define CHECKMEM(str) \
257 if ((len = strlen (str)) >= bufend - dst) {\
258 int i = dst - buf;\
259 int n = last_dst - buf;\
260 bufsiz += ((dst + len - bufend) / BUFINCR + 1) * BUFINCR;\
261 buf = mh_xrealloc (buf, bufsiz);\
262 dst = buf + i;\
263 last_dst = buf + n;\
264 bufend = buf + bufsiz;\
265 }
266
267
268 /*
269 * fmt_scan will call this routine if the user includes the function
270 * "(formataddr {component})" in a format string. "orig" is the
271 * original contents of the string register. "str" is the address
272 * string to be formatted and concatenated onto orig. This routine
273 * returns a pointer to the concatenated address string.
274 *
275 * We try to not do a lot of malloc/copy/free's (which is why we
276 * don't call "getcpy") but still place no upper limit on the
277 * length of the result string.
278 */
279 char *
280 formataddr (char *orig, char *str)
281 {
282 register int len;
283 char baddr[BUFSIZ], error[BUFSIZ];
284 register int isgroup;
285 register char *dst;
286 register char *cp;
287 register char *sp;
288 register struct mailname *mp = NULL;
289
290 /* if we don't have a buffer yet, get one */
291 if (bufsiz == 0) {
292 buf = mh_xmalloc (BUFINCR);
293 last_dst = buf; /* XXX */
294 bufsiz = BUFINCR - 6; /* leave some slop */
295 bufend = buf + bufsiz;
296 }
297 /*
298 * If "orig" points to our buffer we can just pick up where we
299 * left off. Otherwise we have to copy orig into our buffer.
300 */
301 if (orig == buf)
302 dst = last_dst;
303 else if (!orig || !*orig) {
304 dst = buf;
305 *dst = '\0';
306 } else {
307 dst = last_dst; /* XXX */
308 CHECKMEM (orig);
309 CPY (orig);
310 }
311
312 /* concatenate all the new addresses onto 'buf' */
313 for (isgroup = 0; (cp = getname (str)); ) {
314 if ((mp = getm (cp, dfhost, dftype, AD_NAME, error)) == NULL) {
315 snprintf (baddr, sizeof(baddr), "\t%s -- %s\n", cp, error);
316 badaddrs = add (baddr, badaddrs);
317 continue;
318 }
319 if (isgroup && (mp->m_gname || !mp->m_ingrp)) {
320 *dst++ = ';';
321 isgroup = 0;
322 }
323 if (insert (mp)) {
324 /* if we get here we're going to add an address */
325 if (dst != buf) {
326 *dst++ = ',';
327 *dst++ = ' ';
328 }
329 if (mp->m_gname) {
330 CHECKMEM (mp->m_gname);
331 CPY (mp->m_gname);
332 isgroup++;
333 }
334 sp = adrformat (mp);
335 CHECKMEM (sp);
336 CPY (sp);
337 }
338 }
339
340 if (isgroup)
341 *dst++ = ';';
342
343 *dst = '\0';
344 last_dst = dst;
345 return (buf);
346 }
347
348
349 /*
350 * fmt_scan will call this routine if the user includes the function
351 * "(concataddr {component})" in a format string. This behaves exactly
352 * like formataddr, except that it does NOT suppress duplicate addresses
353 * between calls.
354 *
355 * As an implementation detail: I thought about splitting out formataddr()
356 * into the generic part and duplicate-suppressing part, but the call to
357 * insert() was buried deep within a couple of loops and I didn't see a
358 * way to do it easily. So instead we simply set a special flag to stop
359 * the duplicate check and call formataddr().
360 */
361 char *
362 concataddr(char *orig, char *str)
363 {
364 char *cp;
365
366 nodupcheck = 1;
367 cp = formataddr(orig, str);
368 nodupcheck = 0;
369 return cp;
370 }
371
372 static int
373 insert (struct mailname *np)
374 {
375 char buffer[BUFSIZ];
376 register struct mailname *mp;
377
378 if (nodupcheck)
379 return 1;
380
381 if (np->m_mbox == NULL)
382 return 0;
383
384 for (mp = &mq; mp->m_next; mp = mp->m_next) {
385 if (!mh_strcasecmp (np->m_host, mp->m_next->m_host)
386 && !mh_strcasecmp (np->m_mbox, mp->m_next->m_mbox))
387 return 0;
388 }
389 if (!ccme && ismymbox (np))
390 return 0;
391
392 if (querysw) {
393 snprintf (buffer, sizeof(buffer), "Reply to %s? ", adrformat (np));
394 if (!gans (buffer, anoyes))
395 return 0;
396 }
397 mp->m_next = np;
398
399 return 1;
400 }
401
402
403 /*
404 * Call the mhlproc
405 *
406 * This function expects that argument out has been fflushed by the caller.
407 */
408
409 static void
410 replfilter (FILE *in, FILE *out, char *filter, int fmtproc)
411 {
412 int pid;
413 char *mhl;
414 char *errstr;
415 char **arglist;
416 int argnum;
417
418 if (filter == NULL)
419 return;
420
421 if (access (filter, R_OK) == NOTOK)
422 adios (filter, "unable to read");
423
424 rewind (in);
425 lseek (fileno(in), (off_t) 0, SEEK_SET);
426
427 switch (pid = vfork()) {
428 case NOTOK:
429 adios ("fork", "unable to");
430
431 case OK:
432 dup2 (fileno (in), fileno (stdin));
433 dup2 (fileno (out), fileno (stdout));
434 closefds (3);
435
436 /*
437 * We're not allocating the memory for the extra arguments,
438 * because we never call arglist_free(). But if we ever change
439 * that be sure to use getcpy() for the extra arguments.
440 */
441 arglist = argsplit(mhlproc, &mhl, &argnum);
442 arglist[argnum++] = "-form";
443 arglist[argnum++] = filter;
444 arglist[argnum++] = "-noclear";
445
446 switch (fmtproc) {
447 case 1:
448 arglist[argnum++] = "-fmtproc";
449 arglist[argnum++] = formatproc;
450 break;
451 case 0:
452 arglist[argnum++] = "-nofmtproc";
453 break;
454 }
455
456 arglist[argnum++] = NULL;
457
458 execvp (mhl, arglist);
459 errstr = strerror(errno);
460 write(2, "unable to exec ", 15);
461 write(2, mhlproc, strlen(mhlproc));
462 write(2, ": ", 2);
463 write(2, errstr, strlen(errstr));
464 write(2, "\n", 1);
465 _exit (-1);
466
467 default:
468 if (pidXwait (pid, mhl))
469 done (1);
470 fseek (out, 0L, SEEK_END);
471 break;
472 }
473 }