smfi.c revision 363466
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 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 294int 295smfi_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 319int 320smfi_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 356int 357smfi_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 380int 381smfi_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 409int 410smfi_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 439int 440smfi_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 482int 483smfi_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 523static int 524myisenhsc(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 562int 563smfi_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 634int 635#if SM_VA_STD 636smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...) 637#else /* SM_VA_STD */ 638smfi_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 743int 744smfi_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 764void * 765smfi_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 786char * 787smfi_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 848int 849smfi_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 875int 876smfi_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} 889