smfi.c revision 261363
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> 12SM_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 16static int smfi_header __P((SMFICTX *, int, int, char *, char *)); 17static 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 37static int 38smfi_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 89int 90smfi_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 114int 115smfi_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 140int 141smfi_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 169static int 170buf_crt_send __P((SMFICTX *, int cmd, char *, char **)); 171 172static int 173buf_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 234static int 235send2 __P((SMFICTX *, int cmd, char *, char *)); 236 237static int 238send2(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 295int 296smfi_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 320int 321smfi_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 357int 358smfi_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 381int 382smfi_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 410int 411smfi_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 440int 441smfi_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 483int 484smfi_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 524static int 525myisenhsc(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 563int 564smfi_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 635int 636#if SM_VA_STD 637smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) 638#else /* SM_VA_STD */ 639smfi_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 744int 745smfi_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 765void * 766smfi_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 787char * 788smfi_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 849int 850smfi_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 876int 877smfi_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} 890