2 * Copyright (c) 1999-2007 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.
11 #pragma ident "%Z%%M% %I% %E% SMI"
14 SM_RCSID("@(#)$Id: smfi.c,v 8.83 2007/04/23 16:44:39 ca Exp $")
15 #include <sm/varargs.h>
16 #include "libmilter.h"
18 static int smfi_header
__P((SMFICTX
*, int, int, char *, char *));
19 static int myisenhsc
__P((const char *, int));
21 /* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
22 #define MAXREPLYLEN 980 /* max. length of a reply string */
23 #define MAXREPLIES 32 /* max. number of reply strings */
26 ** SMFI_HEADER -- send a header to the MTA
29 ** ctx -- Opaque context structure
30 ** cmd -- Header modification command
31 ** hdridx -- Header index
32 ** headerf -- Header field name
33 ** headerv -- Header field value
36 ** MI_SUCCESS/MI_FAILURE
40 smfi_header(ctx
, cmd
, hdridx
, headerf
, headerv
)
47 size_t len
, l1
, l2
, offset
;
51 struct timeval timeout
;
53 if (headerf
== NULL
|| *headerf
== '\0' || headerv
== NULL
)
55 timeout
.tv_sec
= ctx
->ctx_timeout
;
57 l1
= strlen(headerf
) + 1;
58 l2
= strlen(headerv
) + 1;
61 len
+= MILTER_LEN_BYTES
;
69 (void) memcpy(&(buf
[0]), (void *) &v
, MILTER_LEN_BYTES
);
70 offset
+= MILTER_LEN_BYTES
;
72 (void) memcpy(buf
+ offset
, headerf
, l1
);
73 (void) memcpy(buf
+ offset
+ l1
, headerv
, l2
);
74 r
= mi_wr_cmd(ctx
->ctx_sd
, &timeout
, cmd
, buf
, len
);
80 ** SMFI_ADDHEADER -- send a new header to the MTA
83 ** ctx -- Opaque context structure
84 ** headerf -- Header field name
85 ** headerv -- Header field value
88 ** MI_SUCCESS/MI_FAILURE
92 smfi_addheader(ctx
, headerf
, headerv
)
97 if (!mi_sendok(ctx
, SMFIF_ADDHDRS
))
100 return smfi_header(ctx
, SMFIR_ADDHEADER
, -1, headerf
, headerv
);
104 ** SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
107 ** ctx -- Opaque context structure
108 ** hdridx -- index into header list where insertion should occur
109 ** headerf -- Header field name
110 ** headerv -- Header field value
113 ** MI_SUCCESS/MI_FAILURE
117 smfi_insheader(ctx
, hdridx
, headerf
, headerv
)
123 if (!mi_sendok(ctx
, SMFIF_ADDHDRS
) || hdridx
< 0)
126 return smfi_header(ctx
, SMFIR_INSHEADER
, hdridx
, headerf
, headerv
);
130 ** SMFI_CHGHEADER -- send a changed header to the MTA
133 ** ctx -- Opaque context structure
134 ** headerf -- Header field name
135 ** hdridx -- Header index value
136 ** headerv -- Header field value
139 ** MI_SUCCESS/MI_FAILURE
143 smfi_chgheader(ctx
, headerf
, hdridx
, headerv
)
149 if (!mi_sendok(ctx
, SMFIF_CHGHDRS
) || hdridx
< 0)
154 return smfi_header(ctx
, SMFIR_CHGHEADER
, hdridx
, headerf
, headerv
);
159 ** BUF_CRT_SEND -- construct buffer to send from arguments
162 ** ctx -- Opaque context structure
164 ** arg0 -- first argument
165 ** argv -- list of arguments (NULL terminated)
168 ** MI_SUCCESS/MI_FAILURE
172 buf_crt_send
__P((SMFICTX
*, int cmd
, char *, char **));
175 buf_crt_send(ctx
, cmd
, arg0
, argv
)
181 size_t len
, l0
, l1
, offset
;
183 char *buf
, *arg
, **argvl
;
184 struct timeval timeout
;
186 if (arg0
== NULL
|| *arg0
== '\0')
188 timeout
.tv_sec
= ctx
->ctx_timeout
;
190 l0
= strlen(arg0
) + 1;
193 while (argvl
!= NULL
&& (arg
= *argv
) != NULL
&& *arg
!= '\0')
195 l1
= strlen(arg
) + 1;
203 (void) memcpy(buf
, arg0
, l0
);
207 while (argvl
!= NULL
&& (arg
= *argv
) != NULL
&& *arg
!= '\0')
209 l1
= strlen(arg
) + 1;
210 SM_ASSERT(offset
< len
);
211 SM_ASSERT(offset
+ l1
<= len
);
212 (void) memcpy(buf
+ offset
, arg
, l1
);
214 SM_ASSERT(offset
> l1
);
217 r
= mi_wr_cmd(ctx
->ctx_sd
, &timeout
, cmd
, buf
, len
);
224 ** SEND2 -- construct buffer to send from arguments
227 ** ctx -- Opaque context structure
229 ** arg0 -- first argument
230 ** argv -- list of arguments (NULL terminated)
233 ** MI_SUCCESS/MI_FAILURE
237 send2
__P((SMFICTX
*, int cmd
, char *, char *));
240 send2(ctx
, cmd
, arg0
, arg1
)
246 size_t len
, l0
, l1
, offset
;
249 struct timeval timeout
;
251 if (arg0
== NULL
|| *arg0
== '\0')
253 timeout
.tv_sec
= ctx
->ctx_timeout
;
255 l0
= strlen(arg0
) + 1;
259 l1
= strlen(arg1
) + 1;
267 (void) memcpy(buf
, arg0
, l0
);
272 l1
= strlen(arg1
) + 1;
273 SM_ASSERT(offset
< len
);
274 SM_ASSERT(offset
+ l1
<= len
);
275 (void) memcpy(buf
+ offset
, arg1
, l1
);
277 SM_ASSERT(offset
> l1
);
280 r
= mi_wr_cmd(ctx
->ctx_sd
, &timeout
, cmd
, buf
, len
);
286 ** SMFI_CHGFROM -- change enveloper sender ("from") address
289 ** ctx -- Opaque context structure
290 ** from -- new envelope sender address ("MAIL From")
291 ** args -- ESMTP arguments
294 ** MI_SUCCESS/MI_FAILURE
298 smfi_chgfrom(ctx
, from
, args
)
303 if (from
== NULL
|| *from
== '\0')
305 if (!mi_sendok(ctx
, SMFIF_CHGFROM
))
307 return send2(ctx
, SMFIR_CHGFROM
, from
, args
);
311 ** SMFI_SETSYMLIST -- set list of macros that the MTA should send.
314 ** ctx -- Opaque context structure
315 ** where -- SMTP stage
316 ** macros -- list of macros
319 ** MI_SUCCESS/MI_FAILURE
323 smfi_setsymlist(ctx
, where
, macros
)
328 SM_ASSERT(ctx
!= NULL
);
330 if (macros
== NULL
|| *macros
== '\0')
332 if (where
< SMFIM_FIRST
|| where
> SMFIM_LAST
)
334 if (where
< 0 || where
>= MAX_MACROS_ENTRIES
)
337 if (ctx
->ctx_mac_list
[where
] != NULL
)
340 ctx
->ctx_mac_list
[where
] = strdup(macros
);
341 if (ctx
->ctx_mac_list
[where
] == NULL
)
348 ** SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
351 ** ctx -- Opaque context structure
352 ** rcpt -- recipient address
353 ** args -- ESMTP arguments
356 ** MI_SUCCESS/MI_FAILURE
360 smfi_addrcpt_par(ctx
, rcpt
, args
)
365 if (rcpt
== NULL
|| *rcpt
== '\0')
367 if (!mi_sendok(ctx
, SMFIF_ADDRCPT_PAR
))
369 return send2(ctx
, SMFIR_ADDRCPT_PAR
, rcpt
, args
);
373 ** SMFI_ADDRCPT -- send an additional recipient to the MTA
376 ** ctx -- Opaque context structure
377 ** rcpt -- recipient address
380 ** MI_SUCCESS/MI_FAILURE
384 smfi_addrcpt(ctx
, rcpt
)
389 struct timeval timeout
;
391 if (rcpt
== NULL
|| *rcpt
== '\0')
393 if (!mi_sendok(ctx
, SMFIF_ADDRCPT
))
395 timeout
.tv_sec
= ctx
->ctx_timeout
;
397 len
= strlen(rcpt
) + 1;
398 return mi_wr_cmd(ctx
->ctx_sd
, &timeout
, SMFIR_ADDRCPT
, rcpt
, len
);
402 ** SMFI_DELRCPT -- send a recipient to be removed to the MTA
405 ** ctx -- Opaque context structure
406 ** rcpt -- recipient address
409 ** MI_SUCCESS/MI_FAILURE
413 smfi_delrcpt(ctx
, rcpt
)
418 struct timeval timeout
;
420 if (rcpt
== NULL
|| *rcpt
== '\0')
422 if (!mi_sendok(ctx
, SMFIF_DELRCPT
))
424 timeout
.tv_sec
= ctx
->ctx_timeout
;
426 len
= strlen(rcpt
) + 1;
427 return mi_wr_cmd(ctx
->ctx_sd
, &timeout
, SMFIR_DELRCPT
, rcpt
, len
);
431 ** SMFI_REPLACEBODY -- send a body chunk to the MTA
434 ** ctx -- Opaque context structure
435 ** bodyp -- body chunk
436 ** bodylen -- length of body chunk
439 ** MI_SUCCESS/MI_FAILURE
443 smfi_replacebody(ctx
, bodyp
, bodylen
)
445 unsigned char *bodyp
;
449 struct timeval timeout
;
452 (bodyp
== NULL
&& bodylen
> 0))
454 if (!mi_sendok(ctx
, SMFIF_CHGBODY
))
456 timeout
.tv_sec
= ctx
->ctx_timeout
;
459 /* split body chunk if necessary */
463 len
= (bodylen
>= MILTER_CHUNK_SIZE
) ? MILTER_CHUNK_SIZE
:
465 if ((r
= mi_wr_cmd(ctx
->ctx_sd
, &timeout
, SMFIR_REPLBODY
,
466 (char *) (bodyp
+ off
), len
)) != MI_SUCCESS
)
470 } while (bodylen
> 0);
475 ** SMFI_QUARANTINE -- quarantine an envelope
478 ** ctx -- Opaque context structure
482 ** MI_SUCCESS/MI_FAILURE
486 smfi_quarantine(ctx
, reason
)
493 struct timeval timeout
;
495 if (reason
== NULL
|| *reason
== '\0')
497 if (!mi_sendok(ctx
, SMFIF_QUARANTINE
))
499 timeout
.tv_sec
= ctx
->ctx_timeout
;
501 len
= strlen(reason
) + 1;
505 (void) memcpy(buf
, reason
, len
);
506 r
= mi_wr_cmd(ctx
->ctx_sd
, &timeout
, SMFIR_QUARANTINE
, buf
, len
);
512 ** MYISENHSC -- check whether a string contains an enhanced status code
515 ** s -- string with possible enhanced status code.
516 ** delim -- delim for enhanced status code.
519 ** 0 -- no enhanced status code.
520 ** >4 -- length of enhanced status code.
535 if (!((*s
== '2' || *s
== '4' || *s
== '5') && s
[1] == '.'))
539 while (h
< 3 && isascii(s
[l
+ h
]) && isdigit(s
[l
+ h
]))
541 if (h
== 0 || s
[l
+ h
] != '.')
545 while (h
< 3 && isascii(s
[l
+ h
]) && isdigit(s
[l
+ h
]))
547 if (h
== 0 || s
[l
+ h
] != delim
)
553 ** SMFI_SETREPLY -- set the reply code for the next reply to the MTA
556 ** ctx -- Opaque context structure
557 ** rcode -- The three-digit (RFC 821) SMTP reply code.
558 ** xcode -- The extended (RFC 2034) reply code.
559 ** message -- The text part of the SMTP reply.
562 ** MI_SUCCESS/MI_FAILURE
566 smfi_setreply(ctx
, rcode
, xcode
, message
)
575 if (rcode
== NULL
|| ctx
== NULL
)
579 len
= strlen(rcode
) + 2;
582 if ((rcode
[0] != '4' && rcode
[0] != '5') ||
583 !isascii(rcode
[1]) || !isdigit(rcode
[1]) ||
584 !isascii(rcode
[2]) || !isdigit(rcode
[2]))
588 if (!myisenhsc(xcode
, '\0'))
590 len
+= strlen(xcode
) + 1;
596 /* XXX check also for unprintable chars? */
597 if (strpbrk(message
, "\r\n") != NULL
)
599 ml
= strlen(message
);
600 if (ml
> MAXREPLYLEN
)
606 return MI_FAILURE
; /* oops */
607 (void) sm_strlcpy(buf
, rcode
, len
);
608 (void) sm_strlcat(buf
, " ", len
);
610 (void) sm_strlcat(buf
, xcode
, len
);
614 (void) sm_strlcat(buf
, " ", len
);
615 (void) sm_strlcat(buf
, message
, len
);
617 if (ctx
->ctx_reply
!= NULL
)
618 free(ctx
->ctx_reply
);
619 ctx
->ctx_reply
= buf
;
624 ** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
627 ** ctx -- Opaque context structure
628 ** rcode -- The three-digit (RFC 821) SMTP reply code.
629 ** xcode -- The extended (RFC 2034) reply code.
630 ** txt, ... -- The text part of the SMTP reply,
631 ** MUST be terminated with NULL.
634 ** MI_SUCCESS/MI_FAILURE
639 smfi_setmlreply(SMFICTX
*ctx
, const char *rcode
, const char *xcode
, ...)
640 #else /* SM_VA_STD */
641 smfi_setmlreply(ctx
, rcode
, xcode
, va_alist
)
646 #endif /* SM_VA_STD */
656 if (rcode
== NULL
|| ctx
== NULL
)
660 len
= strlen(rcode
) + 1;
663 if ((rcode
[0] != '4' && rcode
[0] != '5') ||
664 !isascii(rcode
[1]) || !isdigit(rcode
[1]) ||
665 !isascii(rcode
[2]) || !isdigit(rcode
[2]))
669 if (!myisenhsc(xcode
, '\0'))
681 /* add trailing space */
682 len
+= strlen(xc
) + 1;
685 SM_VA_START(ap
, xcode
);
686 while ((txt
= SM_VA_ARG(ap
, char *)) != NULL
)
691 if (tl
> MAXREPLYLEN
)
694 /* this text, reply codes, \r\n */
695 len
+= tl
+ 2 + rlen
;
696 if (++args
> MAXREPLIES
)
699 /* XXX check also for unprintable chars? */
700 if (strpbrk(txt
, "\r\n") != NULL
)
711 return MI_FAILURE
; /* oops */
712 (void) sm_strlcpyn(buf
, len
, 3, rcode
, args
== 1 ? " " : "-", xc
);
713 (void) sm_strlcpyn(repl
, sizeof repl
, 4, rcode
, args
== 1 ? " " : "-",
715 SM_VA_START(ap
, xcode
);
716 txt
= SM_VA_ARG(ap
, char *);
719 (void) sm_strlcat2(buf
, " ", txt
, len
);
720 while ((txt
= SM_VA_ARG(ap
, char *)) != NULL
)
724 (void) sm_strlcat2(buf
, "\r\n", repl
, len
);
725 (void) sm_strlcat(buf
, txt
, len
);
728 if (ctx
->ctx_reply
!= NULL
)
729 free(ctx
->ctx_reply
);
730 ctx
->ctx_reply
= buf
;
736 ** SMFI_SETPRIV -- set private data
739 ** ctx -- Opaque context structure
740 ** privatedata -- pointer to private data
743 ** MI_SUCCESS/MI_FAILURE
747 smfi_setpriv(ctx
, privatedata
)
753 ctx
->ctx_privdata
= privatedata
;
758 ** SMFI_GETPRIV -- get private data
761 ** ctx -- Opaque context structure
764 ** pointer to private data
773 return ctx
->ctx_privdata
;
777 ** SMFI_GETSYMVAL -- get the value of a macro
779 ** See explanation in mfapi.h about layout of the structures.
782 ** ctx -- Opaque context structure
783 ** symname -- name of macro
786 ** value of macro (NULL in case of failure)
790 smfi_getsymval(ctx
, symname
)
799 if (ctx
== NULL
|| symname
== NULL
|| *symname
== '\0')
802 if (strlen(symname
) == 3 && symname
[0] == '{' && symname
[2] == '}')
809 if (strlen(symname
) == 1)
812 braces
[1] = *symname
;
819 /* search backwards through the macro array */
820 for (i
= MAX_MACROS_ENTRIES
- 1 ; i
>= 0; --i
)
822 if ((s
= ctx
->ctx_mac_ptr
[i
]) == NULL
||
823 ctx
->ctx_mac_buf
[i
] == NULL
)
825 while (s
!= NULL
&& *s
!= NULL
)
827 if (strcmp(*s
, symname
) == 0)
829 if (one
[0] != '\0' && strcmp(*s
, one
) == 0)
831 if (braces
[0] != '\0' && strcmp(*s
, braces
) == 0)
833 ++s
; /* skip over macro value */
834 ++s
; /* points to next macro name */
841 ** SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
842 ** timeouts during long milter-side operations
845 ** ctx -- Opaque context structure
848 ** MI_SUCCESS/MI_FAILURE
855 struct timeval timeout
;
860 timeout
.tv_sec
= ctx
->ctx_timeout
;
863 return mi_wr_cmd(ctx
->ctx_sd
, &timeout
, SMFIR_PROGRESS
, NULL
, 0);
867 ** SMFI_VERSION -- return (runtime) version of libmilter
870 ** major -- (pointer to) major version
871 ** minor -- (pointer to) minor version
872 ** patchlevel -- (pointer to) patchlevel version
879 smfi_version(major
, minor
, patchlevel
)
882 unsigned int *patchlevel
;
885 *major
= SM_LM_VRS_MAJOR(SMFI_VERSION
);
887 *minor
= SM_LM_VRS_MINOR(SMFI_VERSION
);
888 if (patchlevel
!= NULL
)
889 *patchlevel
= SM_LM_VRS_PLVL(SMFI_VERSION
);