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