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);