]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/sendmail/libmilter/smfi.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / sendmail / libmilter / smfi.c
1 /*
2  *  Copyright (c) 1999-2007 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.83 2007/04/23 16:44:39 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                 l1 = strlen(arg1) + 1;
271                 SM_ASSERT(offset < len);
272                 SM_ASSERT(offset + l1 <= len);
273                 (void) memcpy(buf + offset, arg1, l1);
274                 offset += l1;
275                 SM_ASSERT(offset > l1);
276         }
277
278         r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
279         free(buf);
280         return r;
281 }
282
283 /*
284 **  SMFI_CHGFROM -- change enveloper sender ("from") address
285 **
286 **      Parameters:
287 **              ctx -- Opaque context structure
288 **              from -- new envelope sender address ("MAIL From")
289 **              args -- ESMTP arguments
290 **
291 **      Returns:
292 **              MI_SUCCESS/MI_FAILURE
293 */
294
295 int
296 smfi_chgfrom(ctx, from, args)
297         SMFICTX *ctx;
298         char *from;
299         char *args;
300 {
301         if (from == NULL || *from == '\0')
302                 return MI_FAILURE;
303         if (!mi_sendok(ctx, SMFIF_CHGFROM))
304                 return MI_FAILURE;
305         return send2(ctx, SMFIR_CHGFROM, from, args);
306 }
307
308 /*
309 **  SMFI_SETSYMLIST -- set list of macros that the MTA should send.
310 **
311 **      Parameters:
312 **              ctx -- Opaque context structure
313 **              where -- SMTP stage
314 **              macros -- list of macros
315 **
316 **      Returns:
317 **              MI_SUCCESS/MI_FAILURE
318 */
319
320 int
321 smfi_setsymlist(ctx, where, macros)
322         SMFICTX *ctx;
323         int where;
324         char *macros;
325 {
326         SM_ASSERT(ctx != NULL);
327
328         if (macros == NULL || *macros == '\0')
329                 return MI_FAILURE;
330         if (where < SMFIM_FIRST || where > SMFIM_LAST)
331                 return MI_FAILURE;
332         if (where < 0 || where >= MAX_MACROS_ENTRIES)
333                 return MI_FAILURE;
334
335         if (ctx->ctx_mac_list[where] != NULL)
336                 return MI_FAILURE;
337
338         ctx->ctx_mac_list[where] = strdup(macros);
339         if (ctx->ctx_mac_list[where] == NULL)
340                 return MI_FAILURE;
341
342         return MI_SUCCESS;
343 }
344
345 /*
346 **  SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
347 **
348 **      Parameters:
349 **              ctx -- Opaque context structure
350 **              rcpt -- recipient address
351 **              args -- ESMTP arguments
352 **
353 **      Returns:
354 **              MI_SUCCESS/MI_FAILURE
355 */
356
357 int
358 smfi_addrcpt_par(ctx, rcpt, args)
359         SMFICTX *ctx;
360         char *rcpt;
361         char *args;
362 {
363         if (rcpt == NULL || *rcpt == '\0')
364                 return MI_FAILURE;
365         if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR))
366                 return MI_FAILURE;
367         return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args);
368 }
369
370 /*
371 **  SMFI_ADDRCPT -- send an additional recipient to the MTA
372 **
373 **      Parameters:
374 **              ctx -- Opaque context structure
375 **              rcpt -- recipient address
376 **
377 **      Returns:
378 **              MI_SUCCESS/MI_FAILURE
379 */
380
381 int
382 smfi_addrcpt(ctx, rcpt)
383         SMFICTX *ctx;
384         char *rcpt;
385 {
386         size_t len;
387         struct timeval timeout;
388
389         if (rcpt == NULL || *rcpt == '\0')
390                 return MI_FAILURE;
391         if (!mi_sendok(ctx, SMFIF_ADDRCPT))
392                 return MI_FAILURE;
393         timeout.tv_sec = ctx->ctx_timeout;
394         timeout.tv_usec = 0;
395         len = strlen(rcpt) + 1;
396         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
397 }
398
399 /*
400 **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
401 **
402 **      Parameters:
403 **              ctx -- Opaque context structure
404 **              rcpt -- recipient address
405 **
406 **      Returns:
407 **              MI_SUCCESS/MI_FAILURE
408 */
409
410 int
411 smfi_delrcpt(ctx, rcpt)
412         SMFICTX *ctx;
413         char *rcpt;
414 {
415         size_t len;
416         struct timeval timeout;
417
418         if (rcpt == NULL || *rcpt == '\0')
419                 return MI_FAILURE;
420         if (!mi_sendok(ctx, SMFIF_DELRCPT))
421                 return MI_FAILURE;
422         timeout.tv_sec = ctx->ctx_timeout;
423         timeout.tv_usec = 0;
424         len = strlen(rcpt) + 1;
425         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
426 }
427
428 /*
429 **  SMFI_REPLACEBODY -- send a body chunk to the MTA
430 **
431 **      Parameters:
432 **              ctx -- Opaque context structure
433 **              bodyp -- body chunk
434 **              bodylen -- length of body chunk
435 **
436 **      Returns:
437 **              MI_SUCCESS/MI_FAILURE
438 */
439
440 int
441 smfi_replacebody(ctx, bodyp, bodylen)
442         SMFICTX *ctx;
443         unsigned char *bodyp;
444         int bodylen;
445 {
446         int len, off, r;
447         struct timeval timeout;
448
449         if (bodylen < 0 ||
450             (bodyp == NULL && bodylen > 0))
451                 return MI_FAILURE;
452         if (!mi_sendok(ctx, SMFIF_CHGBODY))
453                 return MI_FAILURE;
454         timeout.tv_sec = ctx->ctx_timeout;
455         timeout.tv_usec = 0;
456
457         /* split body chunk if necessary */
458         off = 0;
459         do
460         {
461                 len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
462                                                        bodylen;
463                 if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
464                                 (char *) (bodyp + off), len)) != MI_SUCCESS)
465                         return r;
466                 off += len;
467                 bodylen -= len;
468         } while (bodylen > 0);
469         return MI_SUCCESS;
470 }
471
472 /*
473 **  SMFI_QUARANTINE -- quarantine an envelope
474 **
475 **      Parameters:
476 **              ctx -- Opaque context structure
477 **              reason -- why?
478 **
479 **      Returns:
480 **              MI_SUCCESS/MI_FAILURE
481 */
482
483 int
484 smfi_quarantine(ctx, reason)
485         SMFICTX *ctx;
486         char *reason;
487 {
488         size_t len;
489         int r;
490         char *buf;
491         struct timeval timeout;
492
493         if (reason == NULL || *reason == '\0')
494                 return MI_FAILURE;
495         if (!mi_sendok(ctx, SMFIF_QUARANTINE))
496                 return MI_FAILURE;
497         timeout.tv_sec = ctx->ctx_timeout;
498         timeout.tv_usec = 0;
499         len = strlen(reason) + 1;
500         buf = malloc(len);
501         if (buf == NULL)
502                 return MI_FAILURE;
503         (void) memcpy(buf, reason, len);
504         r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
505         free(buf);
506         return r;
507 }
508
509 /*
510 **  MYISENHSC -- check whether a string contains an enhanced status code
511 **
512 **      Parameters:
513 **              s -- string with possible enhanced status code.
514 **              delim -- delim for enhanced status code.
515 **
516 **      Returns:
517 **              0  -- no enhanced status code.
518 **              >4 -- length of enhanced status code.
519 **
520 **      Side Effects:
521 **              none.
522 */
523
524 static int
525 myisenhsc(s, delim)
526         const char *s;
527         int delim;
528 {
529         int l, h;
530
531         if (s == NULL)
532                 return 0;
533         if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
534                 return 0;
535         h = 0;
536         l = 2;
537         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
538                 ++h;
539         if (h == 0 || s[l + h] != '.')
540                 return 0;
541         l += h + 1;
542         h = 0;
543         while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
544                 ++h;
545         if (h == 0 || s[l + h] != delim)
546                 return 0;
547         return l + h;
548 }
549
550 /*
551 **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
552 **
553 **      Parameters:
554 **              ctx -- Opaque context structure
555 **              rcode -- The three-digit (RFC 821) SMTP reply code.
556 **              xcode -- The extended (RFC 2034) reply code.
557 **              message -- The text part of the SMTP reply.
558 **
559 **      Returns:
560 **              MI_SUCCESS/MI_FAILURE
561 */
562
563 int
564 smfi_setreply(ctx, rcode, xcode, message)
565         SMFICTX *ctx;
566         char *rcode;
567         char *xcode;
568         char *message;
569 {
570         size_t len;
571         char *buf;
572
573         if (rcode == NULL || ctx == NULL)
574                 return MI_FAILURE;
575
576         /* ### <sp> \0 */
577         len = strlen(rcode) + 2;
578         if (len != 5)
579                 return MI_FAILURE;
580         if ((rcode[0] != '4' && rcode[0] != '5') ||
581             !isascii(rcode[1]) || !isdigit(rcode[1]) ||
582             !isascii(rcode[2]) || !isdigit(rcode[2]))
583                 return MI_FAILURE;
584         if (xcode != NULL)
585         {
586                 if (!myisenhsc(xcode, '\0'))
587                         return MI_FAILURE;
588                 len += strlen(xcode) + 1;
589         }
590         if (message != NULL)
591         {
592                 size_t ml;
593
594                 /* XXX check also for unprintable chars? */
595                 if (strpbrk(message, "\r\n") != NULL)
596                         return MI_FAILURE;
597                 ml = strlen(message);
598                 if (ml > MAXREPLYLEN)
599                         return MI_FAILURE;
600                 len += ml + 1;
601         }
602         buf = malloc(len);
603         if (buf == NULL)
604                 return MI_FAILURE;              /* oops */
605         (void) sm_strlcpy(buf, rcode, len);
606         (void) sm_strlcat(buf, " ", len);
607         if (xcode != NULL)
608                 (void) sm_strlcat(buf, xcode, len);
609         if (message != NULL)
610         {
611                 if (xcode != NULL)
612                         (void) sm_strlcat(buf, " ", len);
613                 (void) sm_strlcat(buf, message, len);
614         }
615         if (ctx->ctx_reply != NULL)
616                 free(ctx->ctx_reply);
617         ctx->ctx_reply = buf;
618         return MI_SUCCESS;
619 }
620
621 /*
622 **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
623 **
624 **      Parameters:
625 **              ctx -- Opaque context structure
626 **              rcode -- The three-digit (RFC 821) SMTP reply code.
627 **              xcode -- The extended (RFC 2034) reply code.
628 **              txt, ... -- The text part of the SMTP reply,
629 **                      MUST be terminated with NULL.
630 **
631 **      Returns:
632 **              MI_SUCCESS/MI_FAILURE
633 */
634
635 int
636 #if SM_VA_STD
637 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
638 #else /* SM_VA_STD */
639 smfi_setmlreply(ctx, rcode, xcode, va_alist)
640         SMFICTX *ctx;
641         const char *rcode;
642         const char *xcode;
643         va_dcl
644 #endif /* SM_VA_STD */
645 {
646         size_t len;
647         size_t rlen;
648         int args;
649         char *buf, *txt;
650         const char *xc;
651         char repl[16];
652         SM_VA_LOCAL_DECL
653
654         if (rcode == NULL || ctx == NULL)
655                 return MI_FAILURE;
656
657         /* ### <sp> */
658         len = strlen(rcode) + 1;
659         if (len != 4)
660                 return MI_FAILURE;
661         if ((rcode[0] != '4' && rcode[0] != '5') ||
662             !isascii(rcode[1]) || !isdigit(rcode[1]) ||
663             !isascii(rcode[2]) || !isdigit(rcode[2]))
664                 return MI_FAILURE;
665         if (xcode != NULL)
666         {
667                 if (!myisenhsc(xcode, '\0'))
668                         return MI_FAILURE;
669                 xc = xcode;
670         }
671         else
672         {
673                 if (rcode[0] == '4')
674                         xc = "4.0.0";
675                 else
676                         xc = "5.0.0";
677         }
678
679         /* add trailing space */
680         len += strlen(xc) + 1;
681         rlen = len;
682         args = 0;
683         SM_VA_START(ap, xcode);
684         while ((txt = SM_VA_ARG(ap, char *)) != NULL)
685         {
686                 size_t tl;
687
688                 tl = strlen(txt);
689                 if (tl > MAXREPLYLEN)
690                         break;
691
692                 /* this text, reply codes, \r\n */
693                 len += tl + 2 + rlen;
694                 if (++args > MAXREPLIES)
695                         break;
696
697                 /* XXX check also for unprintable chars? */
698                 if (strpbrk(txt, "\r\n") != NULL)
699                         break;
700         }
701         SM_VA_END(ap);
702         if (txt != NULL)
703                 return MI_FAILURE;
704
705         /* trailing '\0' */
706         ++len;
707         buf = malloc(len);
708         if (buf == NULL)
709                 return MI_FAILURE;              /* oops */
710         (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
711         (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
712                            xc, " ");
713         SM_VA_START(ap, xcode);
714         txt = SM_VA_ARG(ap, char *);
715         if (txt != NULL)
716         {
717                 (void) sm_strlcat2(buf, " ", txt, len);
718                 while ((txt = SM_VA_ARG(ap, char *)) != NULL)
719                 {
720                         if (--args <= 1)
721                                 repl[3] = ' ';
722                         (void) sm_strlcat2(buf, "\r\n", repl, len);
723                         (void) sm_strlcat(buf, txt, len);
724                 }
725         }
726         if (ctx->ctx_reply != NULL)
727                 free(ctx->ctx_reply);
728         ctx->ctx_reply = buf;
729         SM_VA_END(ap);
730         return MI_SUCCESS;
731 }
732
733 /*
734 **  SMFI_SETPRIV -- set private data
735 **
736 **      Parameters:
737 **              ctx -- Opaque context structure
738 **              privatedata -- pointer to private data
739 **
740 **      Returns:
741 **              MI_SUCCESS/MI_FAILURE
742 */
743
744 int
745 smfi_setpriv(ctx, privatedata)
746         SMFICTX *ctx;
747         void *privatedata;
748 {
749         if (ctx == NULL)
750                 return MI_FAILURE;
751         ctx->ctx_privdata = privatedata;
752         return MI_SUCCESS;
753 }
754
755 /*
756 **  SMFI_GETPRIV -- get private data
757 **
758 **      Parameters:
759 **              ctx -- Opaque context structure
760 **
761 **      Returns:
762 **              pointer to private data
763 */
764
765 void *
766 smfi_getpriv(ctx)
767         SMFICTX *ctx;
768 {
769         if (ctx == NULL)
770                 return NULL;
771         return ctx->ctx_privdata;
772 }
773
774 /*
775 **  SMFI_GETSYMVAL -- get the value of a macro
776 **
777 **      See explanation in mfapi.h about layout of the structures.
778 **
779 **      Parameters:
780 **              ctx -- Opaque context structure
781 **              symname -- name of macro
782 **
783 **      Returns:
784 **              value of macro (NULL in case of failure)
785 */
786
787 char *
788 smfi_getsymval(ctx, symname)
789         SMFICTX *ctx;
790         char *symname;
791 {
792         int i;
793         char **s;
794         char one[2];
795         char braces[4];
796
797         if (ctx == NULL || symname == NULL || *symname == '\0')
798                 return NULL;
799
800         if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
801         {
802                 one[0] = symname[1];
803                 one[1] = '\0';
804         }
805         else
806                 one[0] = '\0';
807         if (strlen(symname) == 1)
808         {
809                 braces[0] = '{';
810                 braces[1] = *symname;
811                 braces[2] = '}';
812                 braces[3] = '\0';
813         }
814         else
815                 braces[0] = '\0';
816
817         /* search backwards through the macro array */
818         for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
819         {
820                 if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
821                     ctx->ctx_mac_buf[i] == NULL)
822                         continue;
823                 while (s != NULL && *s != NULL)
824                 {
825                         if (strcmp(*s, symname) == 0)
826                                 return *++s;
827                         if (one[0] != '\0' && strcmp(*s, one) == 0)
828                                 return *++s;
829                         if (braces[0] != '\0' && strcmp(*s, braces) == 0)
830                                 return *++s;
831                         ++s;    /* skip over macro value */
832                         ++s;    /* points to next macro name */
833                 }
834         }
835         return NULL;
836 }
837
838 /*
839 **  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
840 **                   timeouts during long milter-side operations
841 **
842 **      Parameters:
843 **              ctx -- Opaque context structure
844 **
845 **      Return value:
846 **              MI_SUCCESS/MI_FAILURE
847 */
848
849 int
850 smfi_progress(ctx)
851         SMFICTX *ctx;
852 {
853         struct timeval timeout;
854
855         if (ctx == NULL)
856                 return MI_FAILURE;
857
858         timeout.tv_sec = ctx->ctx_timeout;
859         timeout.tv_usec = 0;
860
861         return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
862 }
863
864 /*
865 **  SMFI_VERSION -- return (runtime) version of libmilter
866 **
867 **      Parameters:
868 **              major -- (pointer to) major version
869 **              minor -- (pointer to) minor version
870 **              patchlevel -- (pointer to) patchlevel version
871 **
872 **      Return value:
873 **              MI_SUCCESS
874 */
875
876 int
877 smfi_version(major, minor, patchlevel)
878         unsigned int *major;
879         unsigned int *minor;
880         unsigned int *patchlevel;
881 {
882         if (major != NULL)
883                 *major = SM_LM_VRS_MAJOR(SMFI_VERSION);
884         if (minor != NULL)
885                 *minor = SM_LM_VRS_MINOR(SMFI_VERSION);
886         if (patchlevel != NULL)
887                 *patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION);
888         return MI_SUCCESS;
889 }