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