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