2 * Copyright (c) 1999-2005 Sendmail, Inc. and its suppliers.
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
12 SM_RCSID("@(#)$Id: smfi.c,v 8.74 2005/03/30 00:44:07 ca Exp $")
13 #include <sm/varargs.h>
14 #include "libmilter.h"
16 static int smfi_header __P((SMFICTX *, int, int, char *, char *));
17 static int myisenhsc __P((const char *, int));
19 /* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
20 #define MAXREPLYLEN 980 /* max. length of a reply string */
21 #define MAXREPLIES 32 /* max. number of reply strings */
24 ** SMFI_HEADER -- send a header to the MTA
27 ** ctx -- Opaque context structure
28 ** cmd -- Header modification command
29 ** hdridx -- Header index
30 ** headerf -- Header field name
31 ** headerv -- Header field value
35 ** MI_SUCCESS/MI_FAILURE
39 smfi_header(ctx, cmd, hdridx, headerf, headerv)
46 size_t len, l1, l2, offset;
50 struct timeval timeout;
52 if (headerf == NULL || *headerf == '\0' || headerv == NULL)
54 timeout.tv_sec = ctx->ctx_timeout;
56 l1 = strlen(headerf) + 1;
57 l2 = strlen(headerv) + 1;
60 len += MILTER_LEN_BYTES;
68 (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
69 offset += MILTER_LEN_BYTES;
71 (void) memcpy(buf + offset, headerf, l1);
72 (void) memcpy(buf + offset + l1, headerv, l2);
73 r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
79 ** SMFI_ADDHEADER -- send a new header to the MTA
82 ** ctx -- Opaque context structure
83 ** headerf -- Header field name
84 ** headerv -- Header field value
87 ** MI_SUCCESS/MI_FAILURE
91 smfi_addheader(ctx, headerf, headerv)
96 if (!mi_sendok(ctx, SMFIF_ADDHDRS))
99 return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
103 ** SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
106 ** ctx -- Opaque context structure
107 ** hdridx -- index into header list where insertion should occur
108 ** headerf -- Header field name
109 ** headerv -- Header field value
112 ** MI_SUCCESS/MI_FAILURE
116 smfi_insheader(ctx, hdridx, headerf, headerv)
122 if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
125 return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
129 ** SMFI_CHGHEADER -- send a changed header to the MTA
132 ** ctx -- Opaque context structure
133 ** headerf -- Header field name
134 ** hdridx -- Header index value
135 ** headerv -- Header field value
138 ** MI_SUCCESS/MI_FAILURE
142 smfi_chgheader(ctx, headerf, hdridx, headerv)
148 if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
153 return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
157 ** SMFI_ADDRCPT -- send an additional recipient to the MTA
160 ** ctx -- Opaque context structure
161 ** rcpt -- recipient address
164 ** MI_SUCCESS/MI_FAILURE
168 smfi_addrcpt(ctx, rcpt)
173 struct timeval timeout;
175 if (rcpt == NULL || *rcpt == '\0')
177 if (!mi_sendok(ctx, SMFIF_ADDRCPT))
179 timeout.tv_sec = ctx->ctx_timeout;
181 len = strlen(rcpt) + 1;
182 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
186 ** SMFI_DELRCPT -- send a recipient to be removed to the MTA
189 ** ctx -- Opaque context structure
190 ** rcpt -- recipient address
193 ** MI_SUCCESS/MI_FAILURE
197 smfi_delrcpt(ctx, rcpt)
202 struct timeval timeout;
204 if (rcpt == NULL || *rcpt == '\0')
206 if (!mi_sendok(ctx, SMFIF_DELRCPT))
208 timeout.tv_sec = ctx->ctx_timeout;
210 len = strlen(rcpt) + 1;
211 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
215 ** SMFI_REPLACEBODY -- send a body chunk to the MTA
218 ** ctx -- Opaque context structure
219 ** bodyp -- body chunk
220 ** bodylen -- length of body chunk
223 ** MI_SUCCESS/MI_FAILURE
227 smfi_replacebody(ctx, bodyp, bodylen)
229 unsigned char *bodyp;
233 struct timeval timeout;
236 (bodyp == NULL && bodylen > 0))
238 if (!mi_sendok(ctx, SMFIF_CHGBODY))
240 timeout.tv_sec = ctx->ctx_timeout;
243 /* split body chunk if necessary */
247 len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
249 if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
250 (char *) (bodyp + off), len)) != MI_SUCCESS)
254 } while (bodylen > 0);
259 ** SMFI_QUARANTINE -- quarantine an envelope
262 ** ctx -- Opaque context structure
266 ** MI_SUCCESS/MI_FAILURE
270 smfi_quarantine(ctx, reason)
277 struct timeval timeout;
279 if (reason == NULL || *reason == '\0')
281 if (!mi_sendok(ctx, SMFIF_QUARANTINE))
283 timeout.tv_sec = ctx->ctx_timeout;
285 len = strlen(reason) + 1;
289 (void) memcpy(buf, reason, len);
290 r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
296 ** MYISENHSC -- check whether a string contains an enhanced status code
299 ** s -- string with possible enhanced status code.
300 ** delim -- delim for enhanced status code.
303 ** 0 -- no enhanced status code.
304 ** >4 -- length of enhanced status code.
319 if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
323 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
325 if (h == 0 || s[l + h] != '.')
329 while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
331 if (h == 0 || s[l + h] != delim)
337 ** SMFI_SETREPLY -- set the reply code for the next reply to the MTA
340 ** ctx -- Opaque context structure
341 ** rcode -- The three-digit (RFC 821) SMTP reply code.
342 ** xcode -- The extended (RFC 2034) reply code.
343 ** message -- The text part of the SMTP reply.
346 ** MI_SUCCESS/MI_FAILURE
350 smfi_setreply(ctx, rcode, xcode, message)
359 if (rcode == NULL || ctx == NULL)
363 len = strlen(rcode) + 2;
366 if ((rcode[0] != '4' && rcode[0] != '5') ||
367 !isascii(rcode[1]) || !isdigit(rcode[1]) ||
368 !isascii(rcode[2]) || !isdigit(rcode[2]))
372 if (!myisenhsc(xcode, '\0'))
374 len += strlen(xcode) + 1;
380 /* XXX check also for unprintable chars? */
381 if (strpbrk(message, "\r\n") != NULL)
383 ml = strlen(message);
384 if (ml > MAXREPLYLEN)
390 return MI_FAILURE; /* oops */
391 (void) sm_strlcpy(buf, rcode, len);
392 (void) sm_strlcat(buf, " ", len);
394 (void) sm_strlcat(buf, xcode, len);
398 (void) sm_strlcat(buf, " ", len);
399 (void) sm_strlcat(buf, message, len);
401 if (ctx->ctx_reply != NULL)
402 free(ctx->ctx_reply);
403 ctx->ctx_reply = buf;
408 ** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
411 ** ctx -- Opaque context structure
412 ** rcode -- The three-digit (RFC 821) SMTP reply code.
413 ** xcode -- The extended (RFC 2034) reply code.
414 ** txt, ... -- The text part of the SMTP reply,
415 ** MUST be terminated with NULL.
418 ** MI_SUCCESS/MI_FAILURE
423 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
424 #else /* SM_VA_STD */
425 smfi_setmlreply(ctx, rcode, xcode, va_alist)
430 #endif /* SM_VA_STD */
440 if (rcode == NULL || ctx == NULL)
444 len = strlen(rcode) + 1;
447 if ((rcode[0] != '4' && rcode[0] != '5') ||
448 !isascii(rcode[1]) || !isdigit(rcode[1]) ||
449 !isascii(rcode[2]) || !isdigit(rcode[2]))
453 if (!myisenhsc(xcode, '\0'))
465 /* add trailing space */
466 len += strlen(xc) + 1;
469 SM_VA_START(ap, xcode);
470 while ((txt = SM_VA_ARG(ap, char *)) != NULL)
475 if (tl > MAXREPLYLEN)
478 /* this text, reply codes, \r\n */
479 len += tl + 2 + rlen;
480 if (++args > MAXREPLIES)
483 /* XXX check also for unprintable chars? */
484 if (strpbrk(txt, "\r\n") != NULL)
495 return MI_FAILURE; /* oops */
496 (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
497 (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
499 SM_VA_START(ap, xcode);
500 txt = SM_VA_ARG(ap, char *);
503 (void) sm_strlcat2(buf, " ", txt, len);
504 while ((txt = SM_VA_ARG(ap, char *)) != NULL)
508 (void) sm_strlcat2(buf, "\r\n", repl, len);
509 (void) sm_strlcat(buf, txt, len);
512 if (ctx->ctx_reply != NULL)
513 free(ctx->ctx_reply);
514 ctx->ctx_reply = buf;
520 ** SMFI_SETPRIV -- set private data
523 ** ctx -- Opaque context structure
524 ** privatedata -- pointer to private data
527 ** MI_SUCCESS/MI_FAILURE
531 smfi_setpriv(ctx, privatedata)
537 ctx->ctx_privdata = privatedata;
542 ** SMFI_GETPRIV -- get private data
545 ** ctx -- Opaque context structure
548 ** pointer to private data
557 return ctx->ctx_privdata;
561 ** SMFI_GETSYMVAL -- get the value of a macro
563 ** See explanation in mfapi.h about layout of the structures.
566 ** ctx -- Opaque context structure
567 ** symname -- name of macro
570 ** value of macro (NULL in case of failure)
574 smfi_getsymval(ctx, symname)
583 if (ctx == NULL || symname == NULL || *symname == '\0')
586 if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
593 if (strlen(symname) == 1)
596 braces[1] = *symname;
603 /* search backwards through the macro array */
604 for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
606 if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
607 ctx->ctx_mac_buf[i] == NULL)
609 while (s != NULL && *s != NULL)
611 if (strcmp(*s, symname) == 0)
613 if (one[0] != '\0' && strcmp(*s, one) == 0)
615 if (braces[0] != '\0' && strcmp(*s, braces) == 0)
617 ++s; /* skip over macro value */
618 ++s; /* points to next macro name */
625 ** SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
626 ** timeouts during long milter-side operations
629 ** ctx -- Opaque context structure
632 ** MI_SUCCESS/MI_FAILURE
639 struct timeval timeout;
644 timeout.tv_sec = ctx->ctx_timeout;
647 return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);