Merge from vendor branch HOSTAPD:
[dragonfly.git] / contrib / sendmail-8.13.8 / libmilter / smfi.c
1 /*
2  *  Copyright (c) 1999-2005 Sendmail, Inc. and its suppliers.
3  *      All rights reserved.
4  *
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.
8  *
9  */
10
11 #include <sm/gen.h>
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"
15
16 static int smfi_header __P((SMFICTX *, int, int, char *, char *));
17 static int myisenhsc __P((const char *, int));
18
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 */
22
23 /*
24 **  SMFI_HEADER -- send a header to the MTA
25 **
26 **      Parameters:
27 **              ctx -- Opaque context structure
28 **              cmd -- Header modification command
29 **              hdridx -- Header index
30 **              headerf -- Header field name
31 **              headerv -- Header field value
32 **
33 **
34 **      Returns:
35 **              MI_SUCCESS/MI_FAILURE
36 */
37
38 static int
39 smfi_header(ctx, cmd, hdridx, headerf, headerv)
40         SMFICTX *ctx;
41         int cmd;
42         int hdridx;
43         char *headerf;
44         char *headerv;
45 {
46         size_t len, l1, l2, offset;
47         int r;
48         mi_int32 v;
49         char *buf;
50         struct timeval timeout;
51
52         if (headerf == NULL || *headerf == '\0' || headerv == NULL)
53                 return MI_FAILURE;
54         timeout.tv_sec = ctx->ctx_timeout;
55         timeout.tv_usec = 0;
56         l1 = strlen(headerf) + 1;
57         l2 = strlen(headerv) + 1;
58         len = l1 + l2;
59         if (hdridx >= 0)
60                 len += MILTER_LEN_BYTES;
61         buf = malloc(len);
62         if (buf == NULL)
63                 return MI_FAILURE;
64         offset = 0;
65         if (hdridx >= 0)
66         {
67                 v = htonl(hdridx);
68                 (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
69                 offset += MILTER_LEN_BYTES;
70         }
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);
74         free(buf);
75         return r;
76 }
77
78 /*
79 **  SMFI_ADDHEADER -- send a new header to the MTA
80 **
81 **      Parameters:
82 **              ctx -- Opaque context structure
83 **              headerf -- Header field name
84 **              headerv -- Header field value
85 **
86 **      Returns:
87 **              MI_SUCCESS/MI_FAILURE
88 */
89
90 int
91 smfi_addheader(ctx, headerf, headerv)
92         SMFICTX *ctx;
93         char *headerf;
94         char *headerv;
95 {
96         if (!mi_sendok(ctx, SMFIF_ADDHDRS))
97                 return MI_FAILURE;
98
99         return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
100 }
101
102 /*
103 **  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
104 **
105 **      Parameters:
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
110 **
111 **      Returns:
112 **              MI_SUCCESS/MI_FAILURE
113 */
114
115 int
116 smfi_insheader(ctx, hdridx, headerf, headerv)
117         SMFICTX *ctx;
118         int hdridx;
119         char *headerf;
120         char *headerv;
121 {
122         if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
123                 return MI_FAILURE;
124
125         return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
126 }
127
128 /*
129 **  SMFI_CHGHEADER -- send a changed header to the MTA
130 **
131 **      Parameters:
132 **              ctx -- Opaque context structure
133 **              headerf -- Header field name
134 **              hdridx -- Header index value
135 **              headerv -- Header field value
136 **
137 **      Returns:
138 **              MI_SUCCESS/MI_FAILURE
139 */
140
141 int
142 smfi_chgheader(ctx, headerf, hdridx, headerv)
143         SMFICTX *ctx;
144         char *headerf;
145         mi_int32 hdridx;
146         char *headerv;
147 {
148         if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
149                 return MI_FAILURE;
150         if (headerv == NULL)
151                 headerv = "";
152
153         return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
154 }
155
156 /*
157 **  SMFI_ADDRCPT -- send an additional recipient to the MTA
158 **
159 **      Parameters:
160 **              ctx -- Opaque context structure
161 **              rcpt -- recipient address
162 **
163 **      Returns:
164 **              MI_SUCCESS/MI_FAILURE
165 */
166
167 int
168 smfi_addrcpt(ctx, rcpt)
169         SMFICTX *ctx;
170         char *rcpt;
171 {
172         size_t len;
173         struct timeval timeout;
174
175         if (rcpt == NULL || *rcpt == '\0')
176                 return MI_FAILURE;
177         if (!mi_sendok(ctx, SMFIF_ADDRCPT))
178                 return MI_FAILURE;
179         timeout.tv_sec = ctx->ctx_timeout;
180         timeout.tv_usec = 0;
181         len = strlen(rcpt) + 1;
182         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
183 }
184
185 /*
186 **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
187 **
188 **      Parameters:
189 **              ctx -- Opaque context structure
190 **              rcpt -- recipient address
191 **
192 **      Returns:
193 **              MI_SUCCESS/MI_FAILURE
194 */
195
196 int
197 smfi_delrcpt(ctx, rcpt)
198         SMFICTX *ctx;
199         char *rcpt;
200 {
201         size_t len;
202         struct timeval timeout;
203
204         if (rcpt == NULL || *rcpt == '\0')
205                 return MI_FAILURE;
206         if (!mi_sendok(ctx, SMFIF_DELRCPT))
207                 return MI_FAILURE;
208         timeout.tv_sec = ctx->ctx_timeout;
209         timeout.tv_usec = 0;
210         len = strlen(rcpt) + 1;
211         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
212 }
213
214 /*
215 **  SMFI_REPLACEBODY -- send a body chunk to the MTA
216 **
217 **      Parameters:
218 **              ctx -- Opaque context structure
219 **              bodyp -- body chunk
220 **              bodylen -- length of body chunk
221 **
222 **      Returns:
223 **              MI_SUCCESS/MI_FAILURE
224 */
225
226 int
227 smfi_replacebody(ctx, bodyp, bodylen)
228         SMFICTX *ctx;
229         unsigned char *bodyp;
230         int bodylen;
231 {
232         int len, off, r;
233         struct timeval timeout;
234
235         if (bodylen < 0 ||
236             (bodyp == NULL && bodylen > 0))
237                 return MI_FAILURE;
238         if (!mi_sendok(ctx, SMFIF_CHGBODY))
239                 return MI_FAILURE;
240         timeout.tv_sec = ctx->ctx_timeout;
241         timeout.tv_usec = 0;
242
243         /* split body chunk if necessary */
244         off = 0;
245         do
246         {
247                 len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
248                                                        bodylen;
249                 if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
250                                 (char *) (bodyp + off), len)) != MI_SUCCESS)
251                         return r;
252                 off += len;
253                 bodylen -= len;
254         } while (bodylen > 0);
255         return MI_SUCCESS;
256 }
257
258 /*
259 **  SMFI_QUARANTINE -- quarantine an envelope
260 **
261 **      Parameters:
262 **              ctx -- Opaque context structure
263 **              reason -- why?
264 **
265 **      Returns:
266 **              MI_SUCCESS/MI_FAILURE
267 */
268
269 int
270 smfi_quarantine(ctx, reason)
271         SMFICTX *ctx;
272         char *reason;
273 {
274         size_t len;
275         int r;
276         char *buf;
277         struct timeval timeout;
278
279         if (reason == NULL || *reason == '\0')
280                 return MI_FAILURE;
281         if (!mi_sendok(ctx, SMFIF_QUARANTINE))
282                 return MI_FAILURE;
283         timeout.tv_sec = ctx->ctx_timeout;
284         timeout.tv_usec = 0;
285         len = strlen(reason) + 1;
286         buf = malloc(len);
287         if (buf == NULL)
288                 return MI_FAILURE;
289         (void) memcpy(buf, reason, len);
290         r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
291         free(buf);
292         return r;
293 }
294
295 /*
296 **  MYISENHSC -- check whether a string contains an enhanced status code
297 **
298 **      Parameters:
299 **              s -- string with possible enhanced status code.
300 **              delim -- delim for enhanced status code.
301 **
302 **      Returns:
303 **              0  -- no enhanced status code.
304 **              >4 -- length of enhanced status code.
305 **
306 **      Side Effects:
307 **              none.
308 */
309
310 static int
311 myisenhsc(s, delim)
312         const char *s;
313         int delim;
314 {
315         int l, h;
316
317         if (s == NULL)
318                 return 0;
319         if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
320                 return 0;
321         h = 0;
322         l = 2;
323         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
324                 ++h;
325         if (h == 0 || s[l + h] != '.')
326                 return 0;
327         l += h + 1;
328         h = 0;
329         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
330                 ++h;
331         if (h == 0 || s[l + h] != delim)
332                 return 0;
333         return l + h;
334 }
335
336 /*
337 **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
338 **
339 **      Parameters:
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.
344 **
345 **      Returns:
346 **              MI_SUCCESS/MI_FAILURE
347 */
348
349 int
350 smfi_setreply(ctx, rcode, xcode, message)
351         SMFICTX *ctx;
352         char *rcode;
353         char *xcode;
354         char *message;
355 {
356         size_t len;
357         char *buf;
358
359         if (rcode == NULL || ctx == NULL)
360                 return MI_FAILURE;
361
362         /* ### <sp> \0 */
363         len = strlen(rcode) + 2;
364         if (len != 5)
365                 return MI_FAILURE;
366         if ((rcode[0] != '4' && rcode[0] != '5') ||
367             !isascii(rcode[1]) || !isdigit(rcode[1]) ||
368             !isascii(rcode[2]) || !isdigit(rcode[2]))
369                 return MI_FAILURE;
370         if (xcode != NULL)
371         {
372                 if (!myisenhsc(xcode, '\0'))
373                         return MI_FAILURE;
374                 len += strlen(xcode) + 1;
375         }
376         if (message != NULL)
377         {
378                 size_t ml;
379
380                 /* XXX check also for unprintable chars? */
381                 if (strpbrk(message, "\r\n") != NULL)
382                         return MI_FAILURE;
383                 ml = strlen(message);
384                 if (ml > MAXREPLYLEN)
385                         return MI_FAILURE;
386                 len += ml + 1;
387         }
388         buf = malloc(len);
389         if (buf == NULL)
390                 return MI_FAILURE;              /* oops */
391         (void) sm_strlcpy(buf, rcode, len);
392         (void) sm_strlcat(buf, " ", len);
393         if (xcode != NULL)
394                 (void) sm_strlcat(buf, xcode, len);
395         if (message != NULL)
396         {
397                 if (xcode != NULL)
398                         (void) sm_strlcat(buf, " ", len);
399                 (void) sm_strlcat(buf, message, len);
400         }
401         if (ctx->ctx_reply != NULL)
402                 free(ctx->ctx_reply);
403         ctx->ctx_reply = buf;
404         return MI_SUCCESS;
405 }
406
407 /*
408 **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
409 **
410 **      Parameters:
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.
416 **
417 **      Returns:
418 **              MI_SUCCESS/MI_FAILURE
419 */
420
421 int
422 #if SM_VA_STD
423 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
424 #else /* SM_VA_STD */
425 smfi_setmlreply(ctx, rcode, xcode, va_alist)
426         SMFICTX *ctx;
427         const char *rcode;
428         const char *xcode;
429         va_dcl
430 #endif /* SM_VA_STD */
431 {
432         size_t len;
433         size_t rlen;
434         int args;
435         char *buf, *txt;
436         const char *xc;
437         char repl[16];
438         SM_VA_LOCAL_DECL
439
440         if (rcode == NULL || ctx == NULL)
441                 return MI_FAILURE;
442
443         /* ### <sp> */
444         len = strlen(rcode) + 1;
445         if (len != 4)
446                 return MI_FAILURE;
447         if ((rcode[0] != '4' && rcode[0] != '5') ||
448             !isascii(rcode[1]) || !isdigit(rcode[1]) ||
449             !isascii(rcode[2]) || !isdigit(rcode[2]))
450                 return MI_FAILURE;
451         if (xcode != NULL)
452         {
453                 if (!myisenhsc(xcode, '\0'))
454                         return MI_FAILURE;
455                 xc = xcode;
456         }
457         else
458         {
459                 if (rcode[0] == '4')
460                         xc = "4.0.0";
461                 else
462                         xc = "5.0.0";
463         }
464
465         /* add trailing space */
466         len += strlen(xc) + 1;
467         rlen = len;
468         args = 0;
469         SM_VA_START(ap, xcode);
470         while ((txt = SM_VA_ARG(ap, char *)) != NULL)
471         {
472                 size_t tl;
473
474                 tl = strlen(txt);
475                 if (tl > MAXREPLYLEN)
476                         break;
477
478                 /* this text, reply codes, \r\n */
479                 len += tl + 2 + rlen;
480                 if (++args > MAXREPLIES)
481                         break;
482
483                 /* XXX check also for unprintable chars? */
484                 if (strpbrk(txt, "\r\n") != NULL)
485                         break;
486         }
487         SM_VA_END(ap);
488         if (txt != NULL)
489                 return MI_FAILURE;
490
491         /* trailing '\0' */
492         ++len;
493         buf = malloc(len);
494         if (buf == 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 ? " " : "-",
498                            xc, " ");
499         SM_VA_START(ap, xcode);
500         txt = SM_VA_ARG(ap, char *);
501         if (txt != NULL)
502         {
503                 (void) sm_strlcat2(buf, " ", txt, len);
504                 while ((txt = SM_VA_ARG(ap, char *)) != NULL)
505                 {
506                         if (--args <= 1)
507                                 repl[3] = ' ';
508                         (void) sm_strlcat2(buf, "\r\n", repl, len);
509                         (void) sm_strlcat(buf, txt, len);
510                 }
511         }
512         if (ctx->ctx_reply != NULL)
513                 free(ctx->ctx_reply);
514         ctx->ctx_reply = buf;
515         SM_VA_END(ap);
516         return MI_SUCCESS;
517 }
518
519 /*
520 **  SMFI_SETPRIV -- set private data
521 **
522 **      Parameters:
523 **              ctx -- Opaque context structure
524 **              privatedata -- pointer to private data
525 **
526 **      Returns:
527 **              MI_SUCCESS/MI_FAILURE
528 */
529
530 int
531 smfi_setpriv(ctx, privatedata)
532         SMFICTX *ctx;
533         void *privatedata;
534 {
535         if (ctx == NULL)
536                 return MI_FAILURE;
537         ctx->ctx_privdata = privatedata;
538         return MI_SUCCESS;
539 }
540
541 /*
542 **  SMFI_GETPRIV -- get private data
543 **
544 **      Parameters:
545 **              ctx -- Opaque context structure
546 **
547 **      Returns:
548 **              pointer to private data
549 */
550
551 void *
552 smfi_getpriv(ctx)
553         SMFICTX *ctx;
554 {
555         if (ctx == NULL)
556                 return NULL;
557         return ctx->ctx_privdata;
558 }
559
560 /*
561 **  SMFI_GETSYMVAL -- get the value of a macro
562 **
563 **      See explanation in mfapi.h about layout of the structures.
564 **
565 **      Parameters:
566 **              ctx -- Opaque context structure
567 **              symname -- name of macro
568 **
569 **      Returns:
570 **              value of macro (NULL in case of failure)
571 */
572
573 char *
574 smfi_getsymval(ctx, symname)
575         SMFICTX *ctx;
576         char *symname;
577 {
578         int i;
579         char **s;
580         char one[2];
581         char braces[4];
582
583         if (ctx == NULL || symname == NULL || *symname == '\0')
584                 return NULL;
585
586         if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
587         {
588                 one[0] = symname[1];
589                 one[1] = '\0';
590         }
591         else
592                 one[0] = '\0';
593         if (strlen(symname) == 1)
594         {
595                 braces[0] = '{';
596                 braces[1] = *symname;
597                 braces[2] = '}';
598                 braces[3] = '\0';
599         }
600         else
601                 braces[0] = '\0';
602
603         /* search backwards through the macro array */
604         for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
605         {
606                 if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
607                     ctx->ctx_mac_buf[i] == NULL)
608                         continue;
609                 while (s != NULL && *s != NULL)
610                 {
611                         if (strcmp(*s, symname) == 0)
612                                 return *++s;
613                         if (one[0] != '\0' && strcmp(*s, one) == 0)
614                                 return *++s;
615                         if (braces[0] != '\0' && strcmp(*s, braces) == 0)
616                                 return *++s;
617                         ++s;    /* skip over macro value */
618                         ++s;    /* points to next macro name */
619                 }
620         }
621         return NULL;
622 }
623
624 /*
625 **  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
626 **                   timeouts during long milter-side operations
627 **
628 **      Parameters:
629 **              ctx -- Opaque context structure
630 **
631 **      Return value:
632 **              MI_SUCCESS/MI_FAILURE
633 */
634
635 int
636 smfi_progress(ctx)
637         SMFICTX *ctx;
638 {
639         struct timeval timeout;
640
641         if (ctx == NULL)
642                 return MI_FAILURE;
643
644         timeout.tv_sec = ctx->ctx_timeout;
645         timeout.tv_usec = 0;
646
647         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
648 }