]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/libmilter/smfi.c
Merge sendmail 8.16.1 to HEAD: See contrib/sendmail/RELEASE_NOTES for details
[FreeBSD/FreeBSD.git] / contrib / sendmail / libmilter / smfi.c
1 /*
2  *  Copyright (c) 1999-2007 Proofpoint, 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.84 2013-11-22 20:51:36 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 **      Returns:
34 **              MI_SUCCESS/MI_FAILURE
35 */
36
37 static int
38 smfi_header(ctx, cmd, hdridx, headerf, headerv)
39         SMFICTX *ctx;
40         int cmd;
41         int hdridx;
42         char *headerf;
43         char *headerv;
44 {
45         size_t len, l1, l2, offset;
46         int r;
47         mi_int32 v;
48         char *buf;
49         struct timeval timeout;
50
51         if (headerf == NULL || *headerf == '\0' || headerv == NULL)
52                 return MI_FAILURE;
53         timeout.tv_sec = ctx->ctx_timeout;
54         timeout.tv_usec = 0;
55         l1 = strlen(headerf) + 1;
56         l2 = strlen(headerv) + 1;
57         len = l1 + l2;
58         if (hdridx >= 0)
59                 len += MILTER_LEN_BYTES;
60         buf = malloc(len);
61         if (buf == NULL)
62                 return MI_FAILURE;
63         offset = 0;
64         if (hdridx >= 0)
65         {
66                 v = htonl(hdridx);
67                 (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
68                 offset += MILTER_LEN_BYTES;
69         }
70         (void) memcpy(buf + offset, headerf, l1);
71         (void) memcpy(buf + offset + l1, headerv, l2);
72         r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
73         free(buf);
74         return r;
75 }
76
77 /*
78 **  SMFI_ADDHEADER -- send a new header to the MTA
79 **
80 **      Parameters:
81 **              ctx -- Opaque context structure
82 **              headerf -- Header field name
83 **              headerv -- Header field value
84 **
85 **      Returns:
86 **              MI_SUCCESS/MI_FAILURE
87 */
88
89 int
90 smfi_addheader(ctx, headerf, headerv)
91         SMFICTX *ctx;
92         char *headerf;
93         char *headerv;
94 {
95         if (!mi_sendok(ctx, SMFIF_ADDHDRS))
96                 return MI_FAILURE;
97
98         return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
99 }
100
101 /*
102 **  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
103 **
104 **      Parameters:
105 **              ctx -- Opaque context structure
106 **              hdridx -- index into header list where insertion should occur
107 **              headerf -- Header field name
108 **              headerv -- Header field value
109 **
110 **      Returns:
111 **              MI_SUCCESS/MI_FAILURE
112 */
113
114 int
115 smfi_insheader(ctx, hdridx, headerf, headerv)
116         SMFICTX *ctx;
117         int hdridx;
118         char *headerf;
119         char *headerv;
120 {
121         if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
122                 return MI_FAILURE;
123
124         return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
125 }
126
127 /*
128 **  SMFI_CHGHEADER -- send a changed header to the MTA
129 **
130 **      Parameters:
131 **              ctx -- Opaque context structure
132 **              headerf -- Header field name
133 **              hdridx -- Header index value
134 **              headerv -- Header field value
135 **
136 **      Returns:
137 **              MI_SUCCESS/MI_FAILURE
138 */
139
140 int
141 smfi_chgheader(ctx, headerf, hdridx, headerv)
142         SMFICTX *ctx;
143         char *headerf;
144         mi_int32 hdridx;
145         char *headerv;
146 {
147         if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
148                 return MI_FAILURE;
149         if (headerv == NULL)
150                 headerv = "";
151
152         return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
153 }
154
155 #if 0
156 /*
157 **  BUF_CRT_SEND -- construct buffer to send from arguments
158 **
159 **      Parameters:
160 **              ctx -- Opaque context structure
161 **              cmd -- command
162 **              arg0 -- first argument
163 **              argv -- list of arguments (NULL terminated)
164 **
165 **      Returns:
166 **              MI_SUCCESS/MI_FAILURE
167 */
168
169 static int
170 buf_crt_send __P((SMFICTX *, int cmd, char *, char **));
171
172 static int
173 buf_crt_send(ctx, cmd, arg0, argv)
174         SMFICTX *ctx;
175         int cmd;
176         char *arg0;
177         char **argv;
178 {
179         size_t len, l0, l1, offset;
180         int r;
181         char *buf, *arg, **argvl;
182         struct timeval timeout;
183
184         if (arg0 == NULL || *arg0 == '\0')
185                 return MI_FAILURE;
186         timeout.tv_sec = ctx->ctx_timeout;
187         timeout.tv_usec = 0;
188         l0 = strlen(arg0) + 1;
189         len = l0;
190         argvl = argv;
191         while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
192         {
193                 l1 = strlen(arg) + 1;
194                 len += l1;
195                 SM_ASSERT(len > l1);
196         }
197
198         buf = malloc(len);
199         if (buf == NULL)
200                 return MI_FAILURE;
201         (void) memcpy(buf, arg0, l0);
202         offset = l0;
203
204         argvl = argv;
205         while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
206         {
207                 l1 = strlen(arg) + 1;
208                 SM_ASSERT(offset < len);
209                 SM_ASSERT(offset + l1 <= len);
210                 (void) memcpy(buf + offset, arg, l1);
211                 offset += l1;
212                 SM_ASSERT(offset > l1);
213         }
214
215         r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
216         free(buf);
217         return r;
218 }
219 #endif /* 0 */
220
221 /*
222 **  SEND2 -- construct buffer to send from arguments
223 **
224 **      Parameters:
225 **              ctx -- Opaque context structure
226 **              cmd -- command
227 **              arg0 -- first argument
228 **              argv -- list of arguments (NULL terminated)
229 **
230 **      Returns:
231 **              MI_SUCCESS/MI_FAILURE
232 */
233
234 static int
235 send2 __P((SMFICTX *, int cmd, char *, char *));
236
237 static int
238 send2(ctx, cmd, arg0, arg1)
239         SMFICTX *ctx;
240         int cmd;
241         char *arg0;
242         char *arg1;
243 {
244         size_t len, l0, l1, offset;
245         int r;
246         char *buf;
247         struct timeval timeout;
248
249         if (arg0 == NULL || *arg0 == '\0')
250                 return MI_FAILURE;
251         timeout.tv_sec = ctx->ctx_timeout;
252         timeout.tv_usec = 0;
253         l0 = strlen(arg0) + 1;
254         len = l0;
255         if (arg1 != NULL)
256         {
257                 l1 = strlen(arg1) + 1;
258                 len += l1;
259                 SM_ASSERT(len > l1);
260         }
261
262         buf = malloc(len);
263         if (buf == NULL)
264                 return MI_FAILURE;
265         (void) memcpy(buf, arg0, l0);
266         offset = l0;
267
268         if (arg1 != NULL)
269         {
270                 SM_ASSERT(offset < len);
271                 SM_ASSERT(offset + l1 <= len);
272                 (void) memcpy(buf + offset, arg1, l1);
273                 offset += l1;
274                 SM_ASSERT(offset > l1);
275         }
276
277         r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
278         free(buf);
279         return r;
280 }
281
282 /*
283 **  SMFI_CHGFROM -- change enveloper sender ("from") address
284 **
285 **      Parameters:
286 **              ctx -- Opaque context structure
287 **              from -- new envelope sender address ("MAIL From")
288 **              args -- ESMTP arguments
289 **
290 **      Returns:
291 **              MI_SUCCESS/MI_FAILURE
292 */
293
294 int
295 smfi_chgfrom(ctx, from, args)
296         SMFICTX *ctx;
297         char *from;
298         char *args;
299 {
300         if (from == NULL || *from == '\0')
301                 return MI_FAILURE;
302         if (!mi_sendok(ctx, SMFIF_CHGFROM))
303                 return MI_FAILURE;
304         return send2(ctx, SMFIR_CHGFROM, from, args);
305 }
306
307 /*
308 **  SMFI_SETSYMLIST -- set list of macros that the MTA should send.
309 **
310 **      Parameters:
311 **              ctx -- Opaque context structure
312 **              where -- SMTP stage
313 **              macros -- list of macros
314 **
315 **      Returns:
316 **              MI_SUCCESS/MI_FAILURE
317 */
318
319 int
320 smfi_setsymlist(ctx, where, macros)
321         SMFICTX *ctx;
322         int where;
323         char *macros;
324 {
325         SM_ASSERT(ctx != NULL);
326
327         if (macros == NULL)
328                 return MI_FAILURE;
329         if (where < SMFIM_FIRST || where > SMFIM_LAST)
330                 return MI_FAILURE;
331         if (where < 0 || where >= MAX_MACROS_ENTRIES)
332                 return MI_FAILURE;
333
334         if (ctx->ctx_mac_list[where] != NULL)
335                 return MI_FAILURE;
336
337         ctx->ctx_mac_list[where] = strdup(macros);
338         if (ctx->ctx_mac_list[where] == NULL)
339                 return MI_FAILURE;
340
341         return MI_SUCCESS;
342 }
343
344 /*
345 **  SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
346 **
347 **      Parameters:
348 **              ctx -- Opaque context structure
349 **              rcpt -- recipient address
350 **              args -- ESMTP arguments
351 **
352 **      Returns:
353 **              MI_SUCCESS/MI_FAILURE
354 */
355
356 int
357 smfi_addrcpt_par(ctx, rcpt, args)
358         SMFICTX *ctx;
359         char *rcpt;
360         char *args;
361 {
362         if (rcpt == NULL || *rcpt == '\0')
363                 return MI_FAILURE;
364         if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR))
365                 return MI_FAILURE;
366         return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args);
367 }
368
369 /*
370 **  SMFI_ADDRCPT -- send an additional recipient to the MTA
371 **
372 **      Parameters:
373 **              ctx -- Opaque context structure
374 **              rcpt -- recipient address
375 **
376 **      Returns:
377 **              MI_SUCCESS/MI_FAILURE
378 */
379
380 int
381 smfi_addrcpt(ctx, rcpt)
382         SMFICTX *ctx;
383         char *rcpt;
384 {
385         size_t len;
386         struct timeval timeout;
387
388         if (rcpt == NULL || *rcpt == '\0')
389                 return MI_FAILURE;
390         if (!mi_sendok(ctx, SMFIF_ADDRCPT))
391                 return MI_FAILURE;
392         timeout.tv_sec = ctx->ctx_timeout;
393         timeout.tv_usec = 0;
394         len = strlen(rcpt) + 1;
395         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
396 }
397
398 /*
399 **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
400 **
401 **      Parameters:
402 **              ctx -- Opaque context structure
403 **              rcpt -- recipient address
404 **
405 **      Returns:
406 **              MI_SUCCESS/MI_FAILURE
407 */
408
409 int
410 smfi_delrcpt(ctx, rcpt)
411         SMFICTX *ctx;
412         char *rcpt;
413 {
414         size_t len;
415         struct timeval timeout;
416
417         if (rcpt == NULL || *rcpt == '\0')
418                 return MI_FAILURE;
419         if (!mi_sendok(ctx, SMFIF_DELRCPT))
420                 return MI_FAILURE;
421         timeout.tv_sec = ctx->ctx_timeout;
422         timeout.tv_usec = 0;
423         len = strlen(rcpt) + 1;
424         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
425 }
426
427 /*
428 **  SMFI_REPLACEBODY -- send a body chunk to the MTA
429 **
430 **      Parameters:
431 **              ctx -- Opaque context structure
432 **              bodyp -- body chunk
433 **              bodylen -- length of body chunk
434 **
435 **      Returns:
436 **              MI_SUCCESS/MI_FAILURE
437 */
438
439 int
440 smfi_replacebody(ctx, bodyp, bodylen)
441         SMFICTX *ctx;
442         unsigned char *bodyp;
443         int bodylen;
444 {
445         int len, off, r;
446         struct timeval timeout;
447
448         if (bodylen < 0 ||
449             (bodyp == NULL && bodylen > 0))
450                 return MI_FAILURE;
451         if (!mi_sendok(ctx, SMFIF_CHGBODY))
452                 return MI_FAILURE;
453         timeout.tv_sec = ctx->ctx_timeout;
454         timeout.tv_usec = 0;
455
456         /* split body chunk if necessary */
457         off = 0;
458         do
459         {
460                 len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
461                                                        bodylen;
462                 if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
463                                 (char *) (bodyp + off), len)) != MI_SUCCESS)
464                         return r;
465                 off += len;
466                 bodylen -= len;
467         } while (bodylen > 0);
468         return MI_SUCCESS;
469 }
470
471 /*
472 **  SMFI_QUARANTINE -- quarantine an envelope
473 **
474 **      Parameters:
475 **              ctx -- Opaque context structure
476 **              reason -- why?
477 **
478 **      Returns:
479 **              MI_SUCCESS/MI_FAILURE
480 */
481
482 int
483 smfi_quarantine(ctx, reason)
484         SMFICTX *ctx;
485         char *reason;
486 {
487         size_t len;
488         int r;
489         char *buf;
490         struct timeval timeout;
491
492         if (reason == NULL || *reason == '\0')
493                 return MI_FAILURE;
494         if (!mi_sendok(ctx, SMFIF_QUARANTINE))
495                 return MI_FAILURE;
496         timeout.tv_sec = ctx->ctx_timeout;
497         timeout.tv_usec = 0;
498         len = strlen(reason) + 1;
499         buf = malloc(len);
500         if (buf == NULL)
501                 return MI_FAILURE;
502         (void) memcpy(buf, reason, len);
503         r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
504         free(buf);
505         return r;
506 }
507
508 /*
509 **  MYISENHSC -- check whether a string contains an enhanced status code
510 **
511 **      Parameters:
512 **              s -- string with possible enhanced status code.
513 **              delim -- delim for enhanced status code.
514 **
515 **      Returns:
516 **              0  -- no enhanced status code.
517 **              >4 -- length of enhanced status code.
518 **
519 **      Side Effects:
520 **              none.
521 */
522
523 static int
524 myisenhsc(s, delim)
525         const char *s;
526         int delim;
527 {
528         int l, h;
529
530         if (s == NULL)
531                 return 0;
532         if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
533                 return 0;
534         h = 0;
535         l = 2;
536         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
537                 ++h;
538         if (h == 0 || s[l + h] != '.')
539                 return 0;
540         l += h + 1;
541         h = 0;
542         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
543                 ++h;
544         if (h == 0 || s[l + h] != delim)
545                 return 0;
546         return l + h;
547 }
548
549 /*
550 **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
551 **
552 **      Parameters:
553 **              ctx -- Opaque context structure
554 **              rcode -- The three-digit (RFC 821) SMTP reply code.
555 **              xcode -- The extended (RFC 2034) reply code.
556 **              message -- The text part of the SMTP reply.
557 **
558 **      Returns:
559 **              MI_SUCCESS/MI_FAILURE
560 */
561
562 int
563 smfi_setreply(ctx, rcode, xcode, message)
564         SMFICTX *ctx;
565         char *rcode;
566         char *xcode;
567         char *message;
568 {
569         size_t len;
570         char *buf;
571
572         if (rcode == NULL || ctx == NULL)
573                 return MI_FAILURE;
574
575         /* ### <sp> \0 */
576         len = strlen(rcode) + 2;
577         if (len != 5)
578                 return MI_FAILURE;
579         if ((rcode[0] != '4' && rcode[0] != '5') ||
580             !isascii(rcode[1]) || !isdigit(rcode[1]) ||
581             !isascii(rcode[2]) || !isdigit(rcode[2]))
582                 return MI_FAILURE;
583         if (xcode != NULL)
584         {
585                 if (!myisenhsc(xcode, '\0'))
586                         return MI_FAILURE;
587                 len += strlen(xcode) + 1;
588         }
589         if (message != NULL)
590         {
591                 size_t ml;
592
593                 /* XXX check also for unprintable chars? */
594                 if (strpbrk(message, "\r\n") != NULL)
595                         return MI_FAILURE;
596                 ml = strlen(message);
597                 if (ml > MAXREPLYLEN)
598                         return MI_FAILURE;
599                 len += ml + 1;
600         }
601         buf = malloc(len);
602         if (buf == NULL)
603                 return MI_FAILURE;              /* oops */
604         (void) sm_strlcpy(buf, rcode, len);
605         (void) sm_strlcat(buf, " ", len);
606         if (xcode != NULL)
607                 (void) sm_strlcat(buf, xcode, len);
608         if (message != NULL)
609         {
610                 if (xcode != NULL)
611                         (void) sm_strlcat(buf, " ", len);
612                 (void) sm_strlcat(buf, message, len);
613         }
614         if (ctx->ctx_reply != NULL)
615                 free(ctx->ctx_reply);
616         ctx->ctx_reply = buf;
617         return MI_SUCCESS;
618 }
619
620 /*
621 **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
622 **
623 **      Parameters:
624 **              ctx -- Opaque context structure
625 **              rcode -- The three-digit (RFC 821) SMTP reply code.
626 **              xcode -- The extended (RFC 2034) reply code.
627 **              txt, ... -- The text part of the SMTP reply,
628 **                      MUST be terminated with NULL.
629 **
630 **      Returns:
631 **              MI_SUCCESS/MI_FAILURE
632 */
633
634 int
635 #if SM_VA_STD
636 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
637 #else /* SM_VA_STD */
638 smfi_setmlreply(ctx, rcode, xcode, va_alist)
639         SMFICTX *ctx;
640         const char *rcode;
641         const char *xcode;
642         va_dcl
643 #endif /* SM_VA_STD */
644 {
645         size_t len;
646         size_t rlen;
647         int args;
648         char *buf, *txt;
649         const char *xc;
650         char repl[16];
651         SM_VA_LOCAL_DECL
652
653         if (rcode == NULL || ctx == NULL)
654                 return MI_FAILURE;
655
656         /* ### <sp> */
657         len = strlen(rcode) + 1;
658         if (len != 4)
659                 return MI_FAILURE;
660         if ((rcode[0] != '4' && rcode[0] != '5') ||
661             !isascii(rcode[1]) || !isdigit(rcode[1]) ||
662             !isascii(rcode[2]) || !isdigit(rcode[2]))
663                 return MI_FAILURE;
664         if (xcode != NULL)
665         {
666                 if (!myisenhsc(xcode, '\0'))
667                         return MI_FAILURE;
668                 xc = xcode;
669         }
670         else
671         {
672                 if (rcode[0] == '4')
673                         xc = "4.0.0";
674                 else
675                         xc = "5.0.0";
676         }
677
678         /* add trailing space */
679         len += strlen(xc) + 1;
680         rlen = len;
681         args = 0;
682         SM_VA_START(ap, xcode);
683         while ((txt = SM_VA_ARG(ap, char *)) != NULL)
684         {
685                 size_t tl;
686
687                 tl = strlen(txt);
688                 if (tl > MAXREPLYLEN)
689                         break;
690
691                 /* this text, reply codes, \r\n */
692                 len += tl + 2 + rlen;
693                 if (++args > MAXREPLIES)
694                         break;
695
696                 /* XXX check also for unprintable chars? */
697                 if (strpbrk(txt, "\r\n") != NULL)
698                         break;
699         }
700         SM_VA_END(ap);
701         if (txt != NULL)
702                 return MI_FAILURE;
703
704         /* trailing '\0' */
705         ++len;
706         buf = malloc(len);
707         if (buf == NULL)
708                 return MI_FAILURE;              /* oops */
709         (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
710         (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
711                            xc, " ");
712         SM_VA_START(ap, xcode);
713         txt = SM_VA_ARG(ap, char *);
714         if (txt != NULL)
715         {
716                 (void) sm_strlcat2(buf, " ", txt, len);
717                 while ((txt = SM_VA_ARG(ap, char *)) != NULL)
718                 {
719                         if (--args <= 1)
720                                 repl[3] = ' ';
721                         (void) sm_strlcat2(buf, "\r\n", repl, len);
722                         (void) sm_strlcat(buf, txt, len);
723                 }
724         }
725         if (ctx->ctx_reply != NULL)
726                 free(ctx->ctx_reply);
727         ctx->ctx_reply = buf;
728         SM_VA_END(ap);
729         return MI_SUCCESS;
730 }
731
732 /*
733 **  SMFI_SETPRIV -- set private data
734 **
735 **      Parameters:
736 **              ctx -- Opaque context structure
737 **              privatedata -- pointer to private data
738 **
739 **      Returns:
740 **              MI_SUCCESS/MI_FAILURE
741 */
742
743 int
744 smfi_setpriv(ctx, privatedata)
745         SMFICTX *ctx;
746         void *privatedata;
747 {
748         if (ctx == NULL)
749                 return MI_FAILURE;
750         ctx->ctx_privdata = privatedata;
751         return MI_SUCCESS;
752 }
753
754 /*
755 **  SMFI_GETPRIV -- get private data
756 **
757 **      Parameters:
758 **              ctx -- Opaque context structure
759 **
760 **      Returns:
761 **              pointer to private data
762 */
763
764 void *
765 smfi_getpriv(ctx)
766         SMFICTX *ctx;
767 {
768         if (ctx == NULL)
769                 return NULL;
770         return ctx->ctx_privdata;
771 }
772
773 /*
774 **  SMFI_GETSYMVAL -- get the value of a macro
775 **
776 **      See explanation in mfapi.h about layout of the structures.
777 **
778 **      Parameters:
779 **              ctx -- Opaque context structure
780 **              symname -- name of macro
781 **
782 **      Returns:
783 **              value of macro (NULL in case of failure)
784 */
785
786 char *
787 smfi_getsymval(ctx, symname)
788         SMFICTX *ctx;
789         char *symname;
790 {
791         int i;
792         char **s;
793         char one[2];
794         char braces[4];
795
796         if (ctx == NULL || symname == NULL || *symname == '\0')
797                 return NULL;
798
799         if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
800         {
801                 one[0] = symname[1];
802                 one[1] = '\0';
803         }
804         else
805                 one[0] = '\0';
806         if (strlen(symname) == 1)
807         {
808                 braces[0] = '{';
809                 braces[1] = *symname;
810                 braces[2] = '}';
811                 braces[3] = '\0';
812         }
813         else
814                 braces[0] = '\0';
815
816         /* search backwards through the macro array */
817         for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
818         {
819                 if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
820                     ctx->ctx_mac_buf[i] == NULL)
821                         continue;
822                 while (s != NULL && *s != NULL)
823                 {
824                         if (strcmp(*s, symname) == 0)
825                                 return *++s;
826                         if (one[0] != '\0' && strcmp(*s, one) == 0)
827                                 return *++s;
828                         if (braces[0] != '\0' && strcmp(*s, braces) == 0)
829                                 return *++s;
830                         ++s;    /* skip over macro value */
831                         ++s;    /* points to next macro name */
832                 }
833         }
834         return NULL;
835 }
836
837 /*
838 **  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
839 **                   timeouts during long milter-side operations
840 **
841 **      Parameters:
842 **              ctx -- Opaque context structure
843 **
844 **      Return value:
845 **              MI_SUCCESS/MI_FAILURE
846 */
847
848 int
849 smfi_progress(ctx)
850         SMFICTX *ctx;
851 {
852         struct timeval timeout;
853
854         if (ctx == NULL)
855                 return MI_FAILURE;
856
857         timeout.tv_sec = ctx->ctx_timeout;
858         timeout.tv_usec = 0;
859
860         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
861 }
862
863 /*
864 **  SMFI_VERSION -- return (runtime) version of libmilter
865 **
866 **      Parameters:
867 **              major -- (pointer to) major version
868 **              minor -- (pointer to) minor version
869 **              patchlevel -- (pointer to) patchlevel version
870 **
871 **      Return value:
872 **              MI_SUCCESS
873 */
874
875 int
876 smfi_version(major, minor, patchlevel)
877         unsigned int *major;
878         unsigned int *minor;
879         unsigned int *patchlevel;
880 {
881         if (major != NULL)
882                 *major = SM_LM_VRS_MAJOR(SMFI_VERSION);
883         if (minor != NULL)
884                 *minor = SM_LM_VRS_MINOR(SMFI_VERSION);
885         if (patchlevel != NULL)
886                 *patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION);
887         return MI_SUCCESS;
888 }