1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 4961 4962 4963 4964 4965 4966 4967 4968 4969 4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 4991 4992 4993 4994 4995 4996 4997 4998 4999 5000 5001 5002 5003 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 5247 5248 5249 5250 5251 5252 5253 5254 5255 5256 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 5283 5284 5285 5286 5287 5288 5289 5290 5291 5292 5293 5294 5295 5296 5297 5298 5299 5300 5301 5302 5303 5304 5305 5306 5307 5308 5309 5310 5311 5312 5313 5314 5315 5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332 5333 5334 5335 5336 5337 5338 5339 5340 5341 5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366 5367 5368 5369 5370 5371 5372 5373 5374 5375 5376 5377 5378 5379 5380 5381 5382 5383 5384 5385 5386 5387 5388 5389 5390 5391 5392 5393 5394 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 5412 5413 5414 5415 5416 5417 5418 5419 5420 5421 5422 5423 5424 5425 5426 5427 5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 5447 5448 5449 5450 5451 5452 5453 5454 5455 5456 5457 5458 5459 5460 5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475 5476 5477 5478 5479 5480 5481 5482 5483 5484 5485 5486 5487 5488 5489 5490 5491 5492 5493 5494 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 5510 5511 5512 5513 5514 5515 5516 5517 5518 5519 5520 5521 5522 5523 5524 5525 5526 5527 5528 5529 5530 5531 5532 5533 5534 5535 5536 5537 5538 5539 5540 5541 5542 5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555 5556 5557 5558 5559 5560 5561 5562 5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573 5574 5575 5576 5577 5578 5579 5580 5581 5582 5583 5584 5585 5586 5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605 5606 5607 5608 5609 5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 5660 5661 5662 5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679 5680 5681 5682 5683 5684 5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697 5698 5699 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 5806 5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 5841 5842 5843 5844 5845 5846 5847 5848 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 6005 6006 6007 6008 6009 6010 6011 6012 6013 6014 6015 6016 6017 6018 6019 6020 6021 6022 6023 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291 6292 6293 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332 6333 6334 6335 6336 6337 6338 6339 6340 6341 6342 6343 6344 6345 6346 6347 6348 6349 6350 6351 6352 6353 6354 6355 6356 6357 6358 6359 6360 6361 6362 6363 6364 6365 6366 6367 6368 6369 6370 6371 6372 6373 6374 6375 6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388 6389 6390 6391 6392 6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405 6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439 6440 6441 6442 6443 6444 6445 6446 6447 6448 6449 6450 6451 6452 6453 6454 6455 6456 6457 6458 6459 6460 6461 6462 6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477 6478 6479 6480 6481 6482 6483 6484 6485 6486 6487 6488 6489 6490 6491 6492 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569
|
@c Copyright (C) 2005--2025 Sergey Poznyakoff
@c Permission is granted to copy, distribute and/or modify this document
@c under the terms of the GNU Free Documentation License, Version 1.3 or
@c any later version published by the Free Software Foundation; with no
@c Invariant Sections, with the Front and Back-Cover texts at your option.
This chapter describes library functions available in Mailfromd
version @value{VERSION}. For the simplicity of explanation, we use
the word @samp{boolean} to indicate variables of numeric type that are
used as boolean values. For such variables, the term @samp{False}
stands for the numeric 0, and @samp{True} for any non-zero value.
@menu
* Macro access::
* Character translation::
* String transformation::
* String manipulation::
* String formatting::
* Character Type::
* I/O functions::
* Filtering functions::
* Email processing functions::
* Envelope modification functions::
* Header modification functions::
* Body Modification Functions::
* Message modification queue::
* Mail header functions::
* Mail body functions::
* EOM Functions::
* Current Message Functions::
* Mailbox functions::
* Message functions::
* Quarantine functions::
* SMTP Callout functions::
* Compatibility Callout functions::
* Internet address manipulation functions::
* DNS functions::
* Geolocation functions::
* Database functions::
* Control database::
* System functions::
* Passwd functions::
* Sieve Interface::
* Interfaces to Third-Party Programs::
* Rate limiting functions::
* Greylisting functions::
* Special test functions::
* Mail Sending Functions::
* Blacklisting Functions::
* SPF Functions::
* DKIM::
* Sockmaps::
* NLS Functions::
* Syslog Interface::
* Debugging Functions::
* Informative Functions::
* Mfmod Interface::
@end menu
@node Macro access
@section Sendmail Macro Access Functions
@deftypefn {Built-in Function} string getmacro (string @var{macro})
Returns the value of Sendmail macro @var{macro}. If @var{macro} is
not defined, raises the @code{e_macroundef} exception.
Calling @code{getmacro(@var{name})} is completely equivalent to
referencing @code{$@{@var{name}@}}, except that it allows to construct
macro names programmatically, e.g.:
@example
if getmacro("auth_%var") = "foo"
@dots{}
fi
@end example
@end deftypefn
@deftypefn {Built-in Function} boolean macro_defined (string @var{name})
Return true if Sendmail macro @var{name} is defined.
@end deftypefn
Notice, that if your @acronym{MTA} supports macro name
negotiation@footnote{That is, if it supports Milter protocol 6 and
upper. Sendmail 8.14.0 and Postfix 2.6 and newer do. MeTA1 (via
@command{pmult}) does as well. @xref{MTA Configuration}, for more
details.}, you will have to export macro names used by these two
functions using @samp{#pragma miltermacros} construct. Consider this
example:
@example
func authcheck(string name)
do
string macname "auth_%name"
if macro_defined(macname)
if getmacro(macname)
@dots{}
fi
fi
done
#pragma miltermacros envfrom auth_authen
prog envfrom
do
authcheck("authen")
done
@end example
In this case, the parser cannot deduce that the @code{envfrom} handler
will attempt to reference the @samp{auth_authen} macro, therefore the
@samp{#pragma miltermacros} is used to help it.
@node Character translation
@section @code{tr}, @code{dc}, and @code{sq} functions
@deftypefn {Built-in Function} string tr (string @var{subj}, string @var{set1}, @
string @var{set2})
Translates characters in string @var{subj} and returns the resulting
string.
Translation rules are defined by two character sets: @var{set1} is a
set of characters which, when encountered in @var{subj}, must be replaced
with the corresponding characters from @var{set2}. E.g.:
@example
tr('text', 'tx', 'ni') @result{} 'nein'
@end example
The source set @var{set1} can contain @dfn{character classes}, sets
of characters enclosed in square brackets. A character class matches
the input character if that character is listed in the class. When a
match occurs, the character is replaced with the corresponding
character from @var{set2}:
@example
tr('abacus', '[abc]', '_') @result{} @samp{____us}
@end example
An exclamation sign at the beginning of a character class reverses
its meaning, i.e. the class matches any character @emph{not} listed in
it:
@example
tr('abacus', '[!abc]', '_') @result{} @samp{abac__}
@end example
A character set can contain @dfn{ranges}, specified as the first and
last characters from the range separated by a dash. A range
@samp{@var{x}-@var{y}} comprises all characters between @var{x} and
@var{y} inclusive. For example, @samp{[a-d]} is equivalent to
@samp{[abcd]}. Character sets must be ascending, i.e. @samp{[a-d]} is
correct, but @samp{[d-a]} is not. You may include @samp{-} in its
literal meaning by making it the first or last character between the
brackets: @samp{[0-9-]} matches any digit or dash.
Similarly, to include a closing bracket, make it the first character
in the class (after negation character, for excluding ranges),
e.g. @samp{[][!]} matches the three characters @samp{[}, @samp{]} and
@samp{!}, whereas @samp{[!][]} matches any character except @samp{[}
and @samp{]}.
@dfn{Named character classes} are special reserved names between
@samp{[:} and @samp{:]} delimiters:
@table @code
@item [:alnum:]
Matches any alphanumeric character. Equivalent to
@samp{[:alpha:][:digit:]}.
@item [:alpha:]
Matches any alphabetic character.
@item [:blank:]
Matches horizontal space or tab.
@item [:cntrl:]
Matches a @dfn{control character}, i.e. a character with ASCII code
less than 32.
@item [:digit:]
Matches a decimal digit (0 through 9).
@item [:graph:]
Matches any printable character except space (horizontal space and
tab).
@item [:lower:]
Matches any lowercase letter.
@item [:print:]
Matches any printable character including space.
@item [:punct:]
Matches any printable character which is not a space or an
alphanumeric character.
@item [:space:]
Matches @samp{white-space} characters: horizontal space (ASCII 32),
form-feed (ASCII 12, or @samp{\f}), newline (ASCII 10, or @samp{\n}),
carriage return (ASCII 13, or @samp{\r}), horizontal tab (ASCII 9, or
@samp{\t}), and vertical tab (ASCII 11, or @samp{\v}).
@item [:upper:]
Matches any upper case letter.
@item [:xdigit:]
Matches any hexagesimal digit: @samp{0} through @samp{9}, @samp{a}
through @samp{f} and @samp{A} through @samp{F}.
@end table
Named classes can appear in character classes in @var{set1} anywhere
a regular character is allowed. Examples:
@table @samp
@item [][:alpha:]-]
Mathes alphabet letters (both cases), digits, closing bracket and
dash.
@item [!][:alpha:]-]
A complement of the above: matches any character except the ones listed
above.
@item [[:xdigit:][:blank:]]
Matches any hexagesimal digit or horizontal whitespace characters.
@end table
The replacement set must not be empty. Its length must be equal to
or less than that of @var{set1} (character classes being counted as
one character). If @var{set1} contains more characters than
@var{set2}, the surplus ones will be translated to the last character
from @var{set2}:
@example
tr('lasted', 'alde', 'iL?') @result{} 'List??'
@end example
Both sets can contain character ranges, represented as
@samp{@var{c1}-@var{c2}}. Whenever a range appears in @var{set1}, a
range must appear in the corresponding position of @var{set2}:
@example
tr('gnu', 'a-z', 'A-Z') @result{} 'GNU'
@end example
Character ranges are not to be confused with ranges in character
classes: they are similiar, but quite distinct. Both match a single
character, but while ranges translate to a corresponding character
from the replacement range, ranges within character class translate to
a single character:
@example
tr('gnu', '[a-z]', 'A') @result{} 'AAA'
@end example
Character ranges in @var{set1} must always be in ascending order
(i.e. @samp{a-z} is allowed, whereas @samp{z-a} is not). Ranges in
@var{set2} can be both ascending and descending, e.g.:
@example
tr('8029', '0-9', '9-0') @result{} '1970'
@end example
To translate a dash, place it as the first or last character in
@var{set1}:
@example
tr('in-place', '-ilp', ' Irg') @result{} 'In grace'
@end example
The @code{tr} function will raise the @code{e_inval} exception if
@var{set2} is empty or @var{set1} contains a range without matching
range in @var{set2}. It will raise the @code{e_range} exception, if
a descending range appears in @var{set1} or number of characters in
a range from @var{set1} does not match that from the corresponding
range in @var{set2}.
@end deftypefn
@deftypefn {Built-in Function} string dc (string @var{subj}, string @var{set1})
Deletes from @var{subj} characters that appear in @var{set1}. The
syntax of @var{set1} is as described in @code{tr}, except that
character ranges are treated as if appearing within character class
(e.g. @samp{a-z} is the same as @samp{[a-z]}).
For example, @code{dc(subj, '0-9')} removes decimal digits from first
argument.
@end deftypefn
@deftypefn {Built-in Function} string sq (string @var{subj}, string @var{set1})
Squeezes repeats, i.e. replaces each sequence of a repeated character
that is listed in @var{set1}, with a single occurrence of that character.
The syntax of @var{set1} is as described in @code{tr}, except that
character ranges are treated as if appearing within character class
(e.g. @samp{a-z} is the same as @samp{[a-z]}).
For example, @code{sq(subj, '[[:space:]]')} replaces multiple
occurrences of whitespace characters with a single character.
@end deftypefn
@node String transformation
@section The @code{sed} function
The @code{sed} function allows you to transform a string by replacing
parts of it that match a regular expression with another string. This
function is somewhat similar to the @command{sed} command line utility
(hence its name) and bears similarities to analogous functions in
other programming languages (e.g. @code{sub} in @command{awk} or the
@code{s//} operator in @command{perl}).
@deftypefn {Built-in Function} string sed (string @var{subject}, @var{expr}, @dots{})
@cindex s-expression
@anchor{s-expression}
The @var{expr} argument is an @dfn{s-expressions} of the the form:
@example
s/@var{regexp}/@var{replacement}/[@var{flags}]
@end example
@noindent
where @var{regexp} is a @dfn{regular expression}, and @var{replacement} is a
replacement string for each part of the @var{subject} that matches
@var{regexp}. When @code{sed} is invoked, it attempts to match
@var{subject} against the @var{regexp}. If the match succeeds, the
portion of @var{subject} which was matched is replaced with
@var{replacement}. Depending on the value of @var{flags}
(@pxref{global replace}), this process may continue until the entire
@var{subject} has been scanned.
The resulting output serves as input for next argument, if such is
supplied. The process continues until all arguments have been
applied.
The function returns the output of the last s-expression.
@end deftypefn
Both @var{regexp} and @var{replacement} are described
in detail in @ref{The "s" Command, The "s" Command, The `s' Command,
sed, GNU sed}.
Supported @var{flags} are:
@table @samp
@anchor{global replace}
@cindex g, @option{transform} flag
@item g
Apply the replacement to @emph{all} matches to the @var{regexp}, not
just the first.
@cindex i, @option{transform} flag
@item i
Use case-insensitive matching. In the absence of this flag, the value
set by the recent @code{#pragma regex icase} is used (@pxref{pragma
regex, icase}).
@cindex x, @option{transform} flag
@item x
@var{regexp} is an @dfn{extended regular expression} (@pxref{Extended
regexps, Extended regular expressions, Extended regular expressions,
sed, GNU sed}). In the absence of this flag, the value set by the
recent @code{#pragma regex extended} (if any) is used (@pxref{pragma
regex, extended}).
@item @var{number}
Only replace the @var{number}th match of the @var{regexp}.
Note: the @acronym{POSIX} standard does not specify what should happen
when you mix the @samp{g} and @var{number} modifiers. @command{Mailfromd}
follows the GNU @command{sed} implementation in this regard, so
the interaction is defined to be: ignore matches before the
@var{number}th, and then match and replace all matches from the
@var{number}th on.
@end table
Any delimiter can be used in lieue of @samp{/}, the only requirement being
that it be used consistently throughout the expression. For example,
the following two expressions are equivalent:
@example
@group
s/one/two/
s,one,two,
@end group
@end example
Changing delimiters is often useful when the @var{regex} contains
slashes. For instance, it is more convenient to write @code{s,/,-,} than
@code{s/\//-/}.
Here is an example of @code{sed} usage:
@example
set email sed(input, 's/^<(.*)>$/\1/x')
@end example
@noindent
It removes angle quotes from the value of the @samp{input} variable
and assigns the result to @samp{email}.
To apply several s-expressions to the same input, you can either give
them as multiple arguments to the @code{sed} function:
@example
set email sed(input, 's/^<(.*)>$/\1/x', 's/(.+@@)(.+)/\1\L\2\E/x')
@end example
@noindent
or give them in a single argument separated with semicolons:
@example
set email sed(input, 's/^<(.*)>$/\1/x;s/(.+@@)(.+)/\1\L\2\E/x')
@end example
@noindent
Both examples above remove optional angle quotes and convert the
domain name part to lower case.
Regular expressions used in @code{sed} arguments are controlled by
the @code{#pragma regex}, as another expressions used throughout the
MFL source file. To avoid using the @samp{x} modifier in the above
example, one can write:
@example
#pragma regex +extended
set email sed(input, 's/^<(.*)>$/\1/', 's/(.+@@)(.+)/\1\L\2\E/')
@end example
@xref{regex}, for details about that @code{#pragma}.
So far all examples used constant s-expressions. However, this is
not a requirement. If necessary, the expression can be stored in a
variable or even constructed on the fly before passing it as argument
to @code{sed}. For example, assume that you wish to remove the domain
part from the value, but only if that part matches one of predefined
domains. Let a regular expression that matches these domains be
stored in the variable @code{domain_rx}. Then this can be done as
follows:
@example
set email sed(input, "s/(.+)(@@%domain_rx)/\1/")
@end example
If the constructed regular expression uses variables whose value
should be matched exactly, such variables must be quoted before being
used as part of the regexp. Mailfromd provides a convenience function
for this:
@deftypefn {Built-in Function} string qr (string @var{str}[; string @var{delim}])
Quote the string @var{str} as a regular expression. This function
selects the characters to be escaped using the currently selected
regular expression flavor (@pxref{regex}). At most two additional
characters that must be escaped can be supplied in the @var{delim}
optional parameter. For example, to quote the variable @samp{x}
for use in double-quoted s-expression:
@example
qr(x, '/"')
@end example
@end deftypefn
@node String manipulation
@section String Manipulation Functions
@deftypefn {Built-in Function} string escape (string @var{str}, @
[string @var{chars}])
Returns a copy of @var{str} with the characters from @var{chars}
escaped, i.e. prefixed with a backslash. If @var{chars} is not
specified, @samp{\"} is assumed.
@example
escape('"a\tstr"ing') @result{} '\"a\\tstr\"ing'
escape('new "value"', '\" ') @result{} 'new\ \"value\"'
@end example
@end deftypefn
@deftypefn {Built-in Function} string unescape (string @var{str})
Performs the reverse to @samp{escape}, i.e. removes any prefix
backslash characters.
@example
unescape('a \"quoted\" string') @result{} 'a "quoted" string'
@end example
@end deftypefn
@deftypefn {Built-in Function} string unescape (string @var{str}, @
[string @var{chars}])
@end deftypefn
@deftypefn {Built-in Function} string domainpart (string @var{str})
Returns the domain part of @var{str}, if it is a valid email address,
otherwise returns @var{str} itself.
@example
@group
domainpart("gray") @result{} "gray"
domainpart("gray@@gnu.org.ua") @result{} "gnu.org.ua"
@end group
@end example
@end deftypefn
@anchor{index-built-in}
@deftypefn {Built-in Function} number index (string @var{s}, string @var{t})
@deftypefnx {Built-in Function} number index (string @var{s}, string @var{t}, @
number @var{start})
Returns the index of the first occurrence of the string @var{t} in
the string @var{s}, or -1 if @var{t} is not present.
@example
index("string of rings", "ring") @result{} 2
@end example
Optional argument @var{start}, if supplied, indicates the position
in string where to start searching.
@example
index("string of rings", "ring", 3) @result{} 10
@end example
To find the last occurrence of a substring, use the function
@var{rindex} (@pxref{rindex}).
@end deftypefn
@anchor{interval}
@deftypefn {Built-in Function} number interval (string @var{str})
Converts @var{str}, which should be a valid time interval
specification (@pxref{time interval specification}), to seconds.
@end deftypefn
@deftypefn {Built-in Function} number length (string @var{str})
Returns the length of the string @var{str} in bytes.
@example
length("string") @result{} 6
@end example
@end deftypefn
@deftypefn {Built-in Function} string dequote (string @var{str})
Removes @samp{<} and @samp{>} surrounding @var{str}. If @var{str} is
not enclosed by angle brackets or these are unbalanced, the argument
is returned unchanged:
@example
dequote("<root@@gnu.org.ua>") @result{} "root@@gnu.org.ua"
dequote("root@@gnu.org.ua") @result{} "root@@gnu.org.ua"
dequote("there>") @result{} "there>"
@end example
@end deftypefn
@deftypefn {Built-in Function} string localpart (string @var{str})
Returns the local part of @var{str} if it is a valid email address,
otherwise returns @var{str} unchanged.
@example
@group
localpart("gray") @result{} "gray"
localpart("gray@@gnu.org.ua") @result{} "gray"
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} string replstr (string @var{s}, number @var{n})
Replicate a string, i.e. return a string, consisting of @var{s}
repeated @var{n} times:
@example
replstr("12", 3) @result{} "121212"
@end example
@end deftypefn
@deftypefn {Built-in Function} string revstr (string @var{s})
Returns the string composed of the characters from @var{s} in
reversed order:
@example
revstr("foobar") @result{} "raboof"
@end example
@end deftypefn
@anchor{rindex}
@deftypefn {Built-in Function} number rindex (string @var{s}, string @var{t})
@deftypefnx {Built-in Function} number rindex (string @var{s}, string @var{t}, @
number @var{start})
Returns the index of the last occurrence of the string @var{t} in
the string @var{s}, or -1 if @var{t} is not present.
@example
rindex("string of rings", "ring") @result{} 10
@end example
Optional argument @var{start}, if supplied, indicates the position
in string where to start searching. E.g.:
@example
rindex("string of rings", "ring", 10) @result{} 2
@end example
See also @ref{index-built-in, @code{index} built-in function, String manipulation}.
@end deftypefn
@anchor{substr}
@deftypefn {Built-in Function} string substr (string @var{str}, @
number @var{start})
@deftypefnx {Built-in Function} string substr (string @var{str}, @
number @var{start}, number @var{length})
Returns the at most @var{length}-character substring of @var{str}
starting at @var{start}. If @var{length} is omitted, the rest of
@var{str} is used.
If @var{length} is greater than the actual length of the string, the
@code{e_range} exception is signalled.
@example
substr("mailfrom", 4) @result{} "from"
substr("mailfrom", 4, 2) @result{} "fr"
@end example
@end deftypefn
@anchor{substring}
@deftypefn {Built-in Function} string substring (string @var{str}, @
number @var{start}, number @var{end})
Returns a substring of @var{str} between offsets @var{start} and
@var{end}, inclusive. Negative @var{end} means offset from the end of
the string. In other words, yo obtain a substring from @var{start} to the
end of the string, use @code{substring(@var{str}, @var{start}, -1)}:
@example
substring("mailfrom", 0, 3) @result{} "mail"
substring("mailfrom", 2, 5) @result{} "ilfr"
substring("mailfrom", 4, -1) @result{} "from"
substring("mailfrom", 4, length("mailfrom") - 1) @result{} "from"
substring("mailfrom", 4, -2) @result{} "fro"
@end example
This function signals @code{e_range} exception if either @var{start} or
@var{end} are outside the string length.
@end deftypefn
@anchor{tolower}
@deftypefn {Built-in Function} string tolower (string @var{str})
Returns a copy of the string @var{str}, with all the upper-case
characters translated to their corresponding lower-case counterparts.
Non-alphabetic characters are left unchanged.
@example
tolower("MAIL") @result{} "mail"
@end example
@end deftypefn
@anchor{toupper}
@deftypefn {Built-in Function} string toupper (string @var{str})
Returns a copy of the string @var{str}, with all the lower-case characters
translated to their corresponding upper-case counterparts.
Non-alphabetic characters are left unchanged.
@example
toupper("mail") @result{} "MAIL"
@end example
@end deftypefn
@deftypefn {Built-in Function} string ltrim (string @var{str}[, string @var{cset})
Returns a copy of the input string @var{str} with any leading
characters present in @var{cset} removed. If the latter is not given,
white space is removed (spaces, tabs, newlines, carriage returns, and
line feeds).
@example
ltrim(" a string") @result{} "a string"
ltrim("089", "0") @result{} "89"
@end example
Note the last example. It shows how @code{ltrim} can be used to
convert decimal numbers in string representation that begins with
@samp{0}. Normally such strings will be treated as representing octal
numbers. If they are indeed decimal, use @code{ltrim} to strip off
the leading zeros, e.g.:
@example
set dayofyear ltrim(strftime('%j', time()), "0")
@end example
@end deftypefn
@deftypefn {Built-in Function} string rtrim (string @var{str}[, string @var{cset})
Returns a copy of the input string @var{str} with any trailing
characters present in @var{cset} removed. If the latter is not given,
white space is removed (spaces, tabs, newlines, carriage returns, and
line feeds).
@end deftypefn
@deftypefn {Built-in Function} number vercmp (string @var{a}, string @var{b})
Compares two strings as @command{mailfromd} version numbers. The
result is negative if @var{b} precedes @var{a}, zero if they refer to
the same version, and positive if @var{b} follows @var{a}:
@example
vercmp("5.0", "5.1") @result{} 1
vercmp("4.4", "4.3") @result{} -1
vercmp("4.3.1", "4.3") @result{} -1
vercmp("8.0", "8.0") @result{} 0
@end example
@end deftypefn
@deftypefn {Library Function} string sa_format_score (number @var{code}, @
number @var{prec})
Format @var{code} as a floating-point number with @var{prec} decimal
digits:
@example
sa_format_score(5000, 3) @result{} "5.000"
@end example
@flindex sa.mfl
This function is convenient for formatting SpamAssassin scores for use
in message headers and textual reports. It is defined in module
@file{sa.mfl}.
@xref{sa, SpamAssassin}, for examples of its use.
@end deftypefn
@deftypefn {Library Function} string sa_format_report_header (string @var{text})
Format a SpamAssassin report text in order to include it in a RFC 822
header. This function selects the score listing from @var{text}, and
prefixes each line with @samp{* }. Its result looks like:
@example
* 0.2 NO_REAL_NAME From: does not include a real name
* 0.1 HTML_MESSAGE BODY: HTML included in message
@end example
@xref{sa, SpamAssassin}, for examples of its use.
@end deftypefn
@anchor{strip_domain_part}
@deftypefn {Library Function} string strip_domain_part (string @var{domain}, @
number @var{n})
Returns at most @var{n} last components of the domain name @var{domain}.
If @var{n} is 0 the function returns @var{domain}.
@flindex strip_domain_part.mfl
This function is defined in the module @file{strip_domain_part.mfl}
(@pxref{Modules}).
Examples:
@example
require strip_domain_part
strip_domain_part("puszcza.gnu.org.ua", 2) @result{} "org.ua"
strip_domain_part("puszcza.gnu.org.ua", 0) @result{} "puszcza.gnu.org.ua"
@end example
@end deftypefn
@deftypefn {Library Function} string verp_extract_user (string @var{email}, @
string @var{domain})
If @var{email} is a valid @acronym{VERP}-style email address for
@var{domain}, that corresponds to a valid local user name
(@pxref{validuser}), this function returns the local user name,
corresponding to that email. Otherwise, it returns empty string.
For example, assuming the local user @samp{gray} exists:
@example
verp_extract_user("gray=gnu.org.ua@@tuhs.org", 'gnu\..*')
@result{} "gray"
@end example
@end deftypefn
@node String formatting
@section String formatting
@deftypefn {Built-in Function} string sprintf (string @var{format}, @dots{})
The function @code{sprintf} formats its argument according to
@var{format} (see below) and returns the resulting string. It takes
varying number of parameters, the only mandatory one being
@var{format}.
@end deftypefn
@subheading Format string
The format string is a simplified version of the format argument to
@acronym{C} @code{printf}-family functions.
The format string is composed of zero or more @dfn{directives}: ordinary
characters (not @samp{%}), which are copied unchanged to the
output stream; and @dfn{conversion specifications}, each of
which results in fetching zero or more subsequent arguments.
Each conversion specification is introduced by the
character @samp{%}, and ends with a conversion specifier. In
between there may be (in this order) zero or more @dfn{flags},
an optional @dfn{minimum field width}, and an optional @dfn{precision}.
Notice, that in practice that means that you should use single quotes
with the @var{format} arguments, to protect conversion specifications from
being recognized as variable references (@pxref{singe-vs-double}).
No type conversion is done on arguments, so it is important that the
supplied arguments match their corresponding conversion specifiers.
By default, the arguments are used in the order given, where each
@samp{*} and each conversion specifier asks for the next argument. If
insufficiently many arguments are given, @code{sprintf} raises
@samp{e_range} exception. One can also specify explicitly which
argument is taken, at each place where an argument is required, by
writing @samp{%@var{m}$}, instead of @samp{%} and @samp{*@var{m}$}
instead of @samp{*}, where the decimal integer @var{m} denotes the
position in the argument list of the desired argument, indexed
starting from 1. Thus,
@example
sprintf('%*d', width, num);
@end example
@noindent
and
@example
sprintf('%2$*1$d', width, num);
@end example
@noindent
are equivalent. The second style allows repeated references to the
same argument.
@subheading Flag characters
The character @samp{%} is followed by zero or more of the following
@dfn{flags}:
@table @samp
@item #
The value should be converted to an @dfn{alternate form}. For
@samp{o} conversions, the first character of the output string is made
zero (by prefixing a @samp{0} if it was not zero already). For
@samp{x} and @samp{X} conversions, a non-zero result has the string
@samp{0x} (or @samp{0X} for @samp{X} conversions) prepended to it.
Other conversions are not affected by this flag.
@item 0
The value should be zero padded. For @samp{d}, @samp{i}, @samp{o}, @samp{u},
@samp{x}, and @samp{X} conversions, the converted value is padded on
the left with zeros rather than blanks. If the @samp{0} and @samp{-}
flags both appear, the @samp{0} flag is ignored. If a precision is given,
the @samp{0} flag is ignored.
Other conversions are not affected by this flag.
@item -
The converted value is to be left adjusted on the field boundary.
(The default is right justification.) The converted value is padded
on the right with blanks, rather than on the left with blanks or
zeros. A @samp{-} overrides a @samp{0} if both are given.
@item ' ' (a space)
A blank should be left before a positive number (or empty string)
produced by a signed conversion.
@item +
A sign (@samp{+} or @samp{-}) always be placed before a number
produced by a signed conversion. By default a sign is used only for
negative numbers. A @samp{+} overrides a space if both are used.
@end table
@subheading Field width
An optional decimal digit string (with nonzero first digit) specifying
a minimum field width. If the converted value has fewer characters
than the field width, it will be padded with spaces on the left (or
right, if the left-adjustment flag has been given). Instead of a
decimal digit string one may write @samp{*} or @samp{*@var{m}$} (for
some decimal integer @var{m}) to specify that the field width is given
in the next argument, or in the @var{m}-th argument, respectively,
which must be of numeric type. A negative field width is taken as a
@samp{-} flag followed by a positive field width. In no case does a
non-existent or small field width cause truncation of a field; if the
result of a conversion is wider than the field width, the field is
expanded to contain the conversion result.
@subheading Precision
An optional precision, in the form of a period (@samp{.}) followed by
an optional decimal digit string. Instead of a decimal digit string
one may write @samp{*} or @samp{*@var{m}$} (for some decimal integer
@var{m}) to specify that the precision is given in the next argument,
or in the @var{m}-th argument, respectively, which must be of numeric
type. If the precision is given as just @samp{.}, or the precision is
negative, the precision is taken to be zero. This gives the minimum number
of digits to appear for @samp{d}, @samp{i}, @samp{o}, @samp{u},
@samp{x}, and @samp{X} conversions, or the maximum number of
characters to be printed from a string for the @samp{s} conversion.
@subheading Conversion specifier
A character that specifies the type of conversion to be applied. The
conversion specifiers and their meanings are:
@table @asis
@item d
@itemx i
The numeric argument is converted to signed decimal notation. The
precision, if any, gives the minimum number of digits that must
appear; if the converted value requires fewer digits, it is padded on
the left with zeros. The default precision is @samp{1}. When @samp{0} is printed with an explicit precision @samp{0}, the output is empty.
@item o
@itemx u
@itemx x
@itemx X
The numeric argument is converted to unsigned octal (@samp{o}),
unsigned decimal (@samp{u}), or unsigned hexadecimal (@samp{x} and
@samp{X}) notation. The letters @samp{abcdef} are used for @samp{x}
conversions; the letters @samp{ABCDEF} are used for @samp{X}
conversions. The precision, if any, gives the minimum number of
digits that must appear; if the converted value requires fewer
digits, it is padded on the left with zeros. The default precision is
@samp{1}. When @samp{0} is printed with an explicit precision 0, the
output is empty.
@item s
The string argument is written to the output. If a precision is
specified, no more than the number specified of characters are
written.
@item %
A @samp{%} is written. No argument is converted. The complete
conversion specification is @samp{%%}.
@end table
@node Character Type
@section Character Type
@cindex ctype_mismatch, global variable
These functions check whether all characters of @var{str} fall into a
certain character class according to the @samp{C} (@samp{POSIX})
locale@footnote{Support for other locales is planned for future
versions.}. @samp{True} (1) is returned if they do, @samp{false} (0)
is returned otherwise. In the latter case, the global variable
@code{ctype_mismatch} is set to the index of the first character that
is outside of the character class (characters are indexed from 0).
@deftypefn {Built-in Function} boolean isalnum (string @var{str})
Checks for alphanumeric characters:
@example
isalnum("a123") @result{} 1
isalnum("a.123") @result{} 0 (ctype_mismatch = 1)
@end example
@end deftypefn
@deftypefn {Built-in Function} boolean isalpha (string @var{str})
Checks for an alphabetic character:
@example
isalnum("abc") @result{} 1
isalnum("a123") @result{} 0
@end example
@end deftypefn
@deftypefn {Built-in Function} boolean isascii (string @var{str})
Checks whether all characters in @var{str} are 7-bit ones, that fit into
the @acronym{ASCII} character set.
@example
isascii("abc") @result{} 1
isascii("ab\0200") @result{} 0
@end example
@end deftypefn
@deftypefn {Built-in Function} boolean isblank (string @var{str})
Checks if @var{str} contains only blank characters; that is, spaces or
tabs.
@end deftypefn
@deftypefn {Built-in Function} boolean iscntrl (string @var{str})
Checks for control characters.
@end deftypefn
@deftypefn {Built-in Function} boolean isdigit (string @var{str})
Checks for digits (0 through 9).
@end deftypefn
@deftypefn {Built-in Function} boolean isgraph (string @var{str})
Checks for any printable characters except spaces.
@end deftypefn
@deftypefn {Built-in Function} boolean islower (string @var{str})
Checks for lower-case characters.
@end deftypefn
@deftypefn {Built-in Function} boolean isprint (string @var{str})
Checks for printable characters including space.
@end deftypefn
@deftypefn {Built-in Function} boolean ispunct (string @var{str})
Checks for any printable characters which are not a spaces or
alphanumeric characters.
@end deftypefn
@deftypefn {Built-in Function} boolean isspace (string @var{str})
Checks for white-space characters, i.e.: space, form-feed (@samp{\f}),
newline (@samp{\n}), carriage return (@samp{\r}), horizontal tab
(@samp{\t}), and vertical tab (@samp{\v}).
@end deftypefn
@deftypefn {Built-in Function} boolean isupper (string @var{str})
Checks for uppercase letters.
@end deftypefn
@deftypefn {Built-in Function} boolean isxdigit (string @var{str})
Checks for hexadecimal digits, i.e. one of @samp{0}, @samp{1},
@samp{2}, @samp{3}, @samp{4}, @samp{5}, @samp{6}, @samp{7}, @samp{8},
@samp{9}, @samp{a}, @samp{b}, @samp{c}, @samp{d}, @samp{e}, @samp{f},
@samp{A}, @samp{B}, @samp{C}, @samp{D}, @samp{E}, @samp{F}.
@end deftypefn
@node I/O functions
@section I/O functions
@acronym{MFL} provides a set of functions for writing to disk files,
pipes or sockets and reading from them. The idea behind them is the
same as in most other programming languages: first you open the
resource with a call to @code{open} which returns a @dfn{descriptor}
i.e. an integer number uniquely identifying the resource. Then you
can write or read from it using this descriptor. Finally, when the
resource is no longer needed, you can close it with a call to
@code{close}.
The number of available resource descriptors is limited. The
default limit is @value{MAX_IOSTREAMS}. You can tailor it to your needs
using the @code{max-streams} runtime configuration statement.
@xref{conf-runtime, max-streams}, for a detailed description.
By default, all I/O operations are unbuffered. This can be changed
by setting the following global variables:
@anchor{io_buffering}
@deftypevr {Built-in variable} number io_buffering
Sets the default buffering type. Allowed values are (symbolic names
are defined in @file{status.mfl} module):
@table @code
@item 0
@itemx BUFFER_NONE
No buffering. This is the default.
@item 1
@itemx BUFFER_FULL
Full buffering. Size of the buffer is set by the
@code{io_buffer_size} global variable (see below).
@item 2
@itemx BUFFER_LINE
Line buffering. When reading, it is pretty much the same as
@code{BUFFER_FULL}. When writing, the data are accumulated in buffer
and actually sent to the underlying transport stream when the newline
character is seen.
The initial size of the buffer is set by the @code{io_buffer_size}
variable. It will grow as needed during the I/O.
@end table
@end deftypevr
@anchor{io_buffer_size}
@deftypevr {Built-in variable} number io_buffer_size
Set the buffer size if @code{io_buffering} is set to
@code{BUFFER_FULL} or @code{BUFFER_LINE}. By default, this variable
is set to the size of the system page.
@end deftypevr
@anchor{open}
@deftypefn {Built-in Function} number open (string @var{name})
The @var{name} argument specifies the name of a resource to open and
the access rights you need to have on it. The function returns a
descriptor of the opened stream, which can subsequently be used
as an argument to other @acronym{I/O} operations.
Buffering mode for the opened stream is defined by the
@code{io_buffering} and @code{io_buffer_size} global variables. It
can be changed using the @code{setbuf} function (@pxref{setbuf}).
First symbols of @var{name} determine the type of the resource to be
opened and the access mode:
@table @samp
@item >
The rest of @var{name} is a name of a file. Open the file for
read-write access. If the file exists, truncate it to zero length,
otherwise create the file.
@item >>
The rest of @var{name} is a name of a file. Open the file for
appending (writing at end of file). The file is created if it does
not exist.
@item |
Treat the rest of @var{name} as the command name and its arguments.
Run this command and open its standard input for writing. The standard
error is closed before launching the program. This can be altered by
using the following versions of this construct:
@table @asis
@item |2>null: @var{command}
Standard error is redirected to @file{/dev/null}.
@item |2>file:@var{name} @var{command}
Execute @var{command} with its standard error redirected to the file
@var{name}. If the file exists, it will be truncated.
@item |2>>file:@var{name} @var{command}
Standard error of the @var{command} is appended to the file
@var{name}. If file does not exist, it will be created.
The @samp{|2>null:} construct described above is a shortcut for
@example
|2>>file:/dev/null @var{command}
@end example
@item |2>syslog:@var{facility}[.@var{priority}] @var{command}
Standard error is redirected to the given syslog @var{facility} and,
optionally, @var{priority}. If the latter is omitted, @samp{LOG_ERR}
is assumed.
Valid values for @var{facility} are: @samp{user}, @samp{daemon},
@samp{auth}, @samp{authpriv}, @samp{mail}, and @samp{local0}
through @samp{local7}. Valid values for @var{priority} are:
@samp{emerg}, @samp{alert}, @samp{crit}, @samp{err}, @samp{warning},
@samp{notice}, @samp{info}, @samp{debug}. Both @var{facility} and
@var{priority} may be given in upper, lower or
mixed cases.
@end table
Notice, that no whitespace characters are allowed between @samp{|} and
@samp{2>}.
@item |<
Treat the rest of @var{name} as the command name and its arguments.
Run this command with its stdin closed and stdout open for reading.
The standard error is treated as described above (see @samp{|}).
@item |&
Treat the rest of @var{name} as the command name and its arguments.
Run this command and set up for two-way communication with it, i.e
writes to the descriptor returned by @code{open} will send data to the
program's standard input, reads from the descriptor will get data from
the program's standard output.
The standard error is treated as described above (see
@samp{|}). For example, the following redirects it to
syslog @samp{mail.debug}:
@example
|&2>syslog:mail.debug @var{command}
@end example
@item @@
Treat the rest of @var{name} as the @acronym{URL} of a socket to
connect to. Valid @acronym{URL} forms are described in @ref{milter
port specification}.
@end table
If none of these prefixes is used, @var{name} is treated as a name
of an existing file and @code{open} will attempt to open this file for
reading.
The @code{open} function will signal exception @code{e_failure} if it
is unable to open the resource or get the required access to it.
@end deftypefn
@deftypefn {Built-in Function} number spawn (string @var{cmd} [, @
number @var{in}, number @var{out}, number @var{err}])
Runs the supplied command @var{cmd}. The syntax of the @var{cmd} is
the same as for the @var{name} argument to @code{open} (see above),
which begins with @samp{|}, excepting that the @samp{|} sign is
optional. That is:
@example
spawn("/bin/cat")
@end example
@noindent
has exactly the same effect as
@example
open("|/bin/cat")
@end example
Optional arguments specify file stream descriptors to be used for the
program standard input, output and error streams, correspondingly.
If supplied, these should be the values returned by a previous call to
@code{open} or @code{tempfile}. The value @samp{-1} means no
redirection.
Buffering mode for the opened stream is defined by the
@code{io_buffering} and @code{io_buffer_size} global variables. It
can be changed using the @code{setbuf} function (@pxref{setbuf}).
The example below starts the @code{awk} program with a simple
expression as its argument and redirects the content of the
file @file{/etc/passwd} to its standard input. The returned
stream descriptor is bound to the command's standard output
(see the description of @samp{|<} prefix above). The standard
error is closed:
@example
number fd spawn("<awk -F: '@{print $1@}'", open("/etc/passwd"))
@end example
@end deftypefn
@deftypefn {Built-in Function} void close (number @var{rd})
The argument @var{rd} is a resource descriptor returned by a
previous call to @code{open}. The function @code{close} closes the
resource and deallocates any memory associated with it.
@code{close} will signal @code{e_range} exception if @var{rd} lies
outside of allowed range of resource descriptors. @xref{conf-runtime,
max-streams}.
@end deftypefn
Notice that you are not required to close resources opened by @code{open}.
Any unclosed resource will be closed automatically upon the
termination of the filtering program.
@deftypefn {Built-in Function} void shutdown (number @var{rd}, number @var{how})
This function causes all or part of a full-duplex connection to be
closed. The @var{rd} must be either a socket descriptor (returned by
@code{open(@@...)}) or a two-way pipe socket descriptor (returned by
@code{open(|&...)}), otherwise the call to @code{shutdown} is
completely equivalent to @code{close}.
The @code{how} argument identifies which part of the connection to
shut down:
@table @asis
@kwindex SHUT_RD
@item SHUT_RD
Read connection. All further receptions will be disallowed.
@kwindex SHUT_WR
@item SHUT_WR
Write connection. All further transmissions will be disallowed.
@kwindex SHUT_RDWR
@item SHUT_RDWR
Shut down both read and write parts.
@end table
@end deftypefn
@deftypefn {Built-in Function} number tempfile ([string @var{tmpdir}])
Creates a nameless temporary file and returns its descriptor.
Optional @var{tmpdir} supplies the directory where to create the file,
instead of the default @file{/tmp}.
@end deftypefn
@deftypefn {Built-in Function} void rewind (number @var{rd})
Rewinds the stream identified by @var{rd} to its beginning.
@end deftypefn
@deftypefn {Built-in Function} number copy (number @var{dst}, number @var{src})
Copies all data from the stream @var{src} to @var{dst}. Returns
number of bytes copied.
@end deftypefn
The following functions provide basic read/write capabilities.
@deftypefn {Built-in Function} void write (number @var{rd}, string @var{str} @
[, number @var{size}])
Writes the string @var{str} to the resource descriptor @var{rd}. If
the @var{size} argument is given, writes this number of bytes.
This function always attempts to write the requested amount of
data. It will signal @code{e_range} exception if @var{rd} lies
outside of allowed range of resource descriptors, @code{e_io}
exception if an @acronym{I/O} error occurs, and @code{e_eof} exception
if it wrote 0 bytes (e.g. because the underlying device is full).
@end deftypefn
@deftypefn {Built-in Function} void write_body (number @var{rd}, pointer @var{bp} @
, number @var{size})
Write the body segment of length @var{size} from pointer @var{bp} to
the stream @var{rd}. This function can be used only in @code{prog
body} (@pxref{body handler}). Its second and third arguments
correspond exactly to the parameters of the @code{body} handler, so
the following construct writes the message body to the resource
@code{fd}, which should have been open prior to invoking the
@code{body} handler:
@example
@group
prog body
do
write_body(fd, $1, $2)
done
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} string read (number @var{rd}, number @var{n})
Read and return at most @var{n} bytes from the resource descriptor @var{rd}.
If there are less than @var{n} bytes in the stream, the remaining
bytes will be returned. Use @code{length()} to obtain the actual size
of the returned data. If there are no bytes left, the @code{e_eof}
exception will be signalled.
The function may signal the following exceptions:
@table @asis
@item e_range
@var{rd} lies outside of allowed range of resource descriptors.
@item e_eof
End of file encountered.
@item e_io
An @acronym{I/O} error occurred.
@end table
@end deftypefn
@deftypefn {Built-in Function} string getdelim (number @var{rd}, string @var{delim})
Read and return the next string terminated by @var{delim} from the
resource descriptor @var{rd}.
The terminating @var{delim} string will be removed from the return
value.
When using this function, it is highly recommended to enable full
buffering for @var{fd}, either by setting @code{io_buffering} before
@code{open} (@pxref{io_buffering}) or by calling @code{setbuf}
after it (@pxref{setbuf}). @xref{getline}, for an example.
This function may signal the following exceptions:
@table @asis
@item e_range
@var{rd} lies outside of allowed range of resource descriptors.
@item e_eof
End of file encountered.
@item e_io
An @acronym{I/O} error occurred.
@end table
@end deftypefn
@anchor{getline}
@deftypefn {Built-in Function} string getline (number @var{rd})
Read and return the next @dfn{line} from the resource
descriptor @var{rd}. A line is any sequence of characters terminated
with the default @dfn{line delimiter}. The default delimiter is
a property of @var{rd}, i.e. different descriptors can have different
line delimiters. The default value is @samp{\n} (ASCII 10), and can
be changed using the @code{fd_set_delimiter} function (see below).
When using this function, it is highly recommended to enable full
buffering for @var{fd}, either by setting @code{io_buffering} before
@code{open} (@pxref{io_buffering}) or by calling @code{setbuf}
after it (@pxref{setbuf}), e.g.:
@example
@group
set fd open(input)
setbuf(fd, BUFFER_FULL)
set line getline(fd)
...
@end group
@end example
This function may signal the following exceptions:
@table @asis
@item e_range
@var{rd} lies outside of allowed range of resource descriptors.
@item e_eof
End of file encountered.
@item e_io
An @acronym{I/O} error occurred.
@end table
@end deftypefn
@deftypefn {Built-in Function} void fd_set_delimiter (number @var{fd}, @
string @var{delim})
Set new line delimiter for the descriptor @var{fd}, which must be in
opened state.
Default delimiter is a newline character (ASCII 10). The following
example shows how to change it to CRLF sequence:
@example
fd_set_delimiter(fd, "\r\n")
@end example
@end deftypefn
@deftypefn {Built-in Function} string fd_delimiter (number @var{fd})
Returns the line delimiter string for @var{fd}.
@end deftypefn
The following example shows how @command{mailfromd} @acronym{I/O} functions can
be used to automatically add @acronym{IP} addresses to an @acronym{RBL} zone:
@example
@group
set nsupdate_cmd
"/usr/bin/nsupdate -k /etc/bind/Kmail.+157+14657.private"
func block_address(string addr)
do
number fd
string domain
set fd open "|%nsupdate_cmd"
set domain reverse_ipstr(addr) . ".rbl.myzone.come"
write(fd, "prereq nxrrset %domain A\n"
"update add %domain 86400 A %addr\n\n"
done
@end group
@end example
@noindent
The function @code{reverse_ipstr} is defined in @ref{Internet address
manipulation functions}.
@anchor{setbuf}
@deftypefn {Built-in Function} void setbuf(number @var{fd}, @
[number @var{type}, number @var{size}])
Changes the buffering mode of @var{fd} according to the remaining two
arguments. The @var{type} specifies buffering type
(@pxref{io_buffering}), and @var{size} supplies the buffer size for
buffering types @code{BUFFER_FULL} and @code{BUFFER_LINE}. If
@var{size} is omitted, it defaults to @code{io_buffer_size}
(@pxref{io_buffer_size}). Omitted @var{type} defaults to
@code{io_buffering} (@pxref{io_buffering}).
@end deftypefn
@deftypefn {Built-in Function} number getbuftype(number @var{fd})
Returns the type of buffering currently in effect for the descriptor
@var{fd}. @xref{io_buffering}, for a list of possible return values.
If this function returns @code{BUFFER_FULL} or @code{BUFFER_LINE}, you
can use @code{getbufsize} to get the associated buffer size.
@end deftypefn
@deftypefn {Built-in Function} number getbufsize(number @var{fd})
Returns the buffer size for the descriptor @var{fd}.
@end deftypefn
@node Filtering functions
@section Filtering functions
This section describes functions that transform data using
Mailutils @dfn{filter pipes}. Filter pipe is a string defining data
flow between several @dfn{filters}. Each filter takes input,
transforms it according to certain rules and produces the transformed
data on its output. As in shell, multiple filters are connected
using pipe characters (@samp{|}). For example, the @code{crlf} filter
inserts a carriage return character before each newline character. A
filter doing that kind of transformation is defined as:
@example
"crlf"
@end example
Another filter, @code{base64}, converts its input to a BASE64 encoded
string. To transform each newline into carriage return + newline pair
and encode the resulting stream in BASE64, one would write:
@example
"crlf | base64"
@end example
Some filters take one or more @dfn{arguments}. These are specified as
a comma-delimited list in parentheses after the filter name. For
example, the @code{linelen} filter limits the length of each output
line to the given number of octets. The following filter pipe will
limit the length of base64 lines in the filter above to 62 octets:
@example
"crlf | base64 | linelen(62)"
@end example
Many filters operate in two modes: @dfn{encode} and @dfn{decode}. By
default all MFL functions apply filters in encode mode. The desired
mode can be stated explicitly in the filter string by using
@code{encode()} and @code{decode()} functions. They take a filter
pipe line as their argument. For example, the following will decode
the stream produced by the example filter above:
@example
"decode(base64 | crlf)"
@end example
@xref{Filters}, for a discussion of available filters and their arguments.
@deftypefn {Built-in Function} string filter_string (string @var{input}, string @var{filter_pipe})
Transforms the string @var{input} using filters in @var{filter_pipe}
and returns the result. Example:
@example
set input "test\ninput\n"
filter_string(input, "crlf|base64") @result{} "dGVzdA0KaW5wdXQNCg=="
@end example
@end deftypefn
@deftypefn {Built-in Function} void filter_fd (number @var{src_fd}, number @var{dst_fd}, string @var{filter_pipe})
Given two I/O descriptors, reads data from @var{src_fd}, transforms it
using @var{filter_pipe} and writes the result to descriptor
@var{dst_fd}.
Both descriptors must be obtained using functions described in
@ref{I/O functions}.
@end deftypefn
@node Filters
@subsection Filters and Filter Pipes
@include filter_pipe.texi
@node Email processing functions
@section Email processing functions
@deftypefn {Built-in Function} number email_map (string @var{email})
Parses @var{email} and returns a bitmap, consisting of zero or more of
the following flags:
@table @samp
@kwindex EMAIL_MULTIPLE
@item EMAIL_MULTIPLE
@var{email} has more than one email address.
@kwindex EMAIL_COMMENTS
@item EMAIL_COMMENTS
@var{email} has comment parts.
@kwindex EMAIL_PERSONAL
@item EMAIL_PERSONAL
@var{email} has personal part.
@kwindex EMAIL_LOCAL
@item EMAIL_LOCAL
@var{email} has local part.
@kwindex EMAIL_DOMAIN
@item EMAIL_DOMAIN
@var{email} has domain part.
@kwindex EMAIL_ROUTE
@item EMAIL_ROUTE
@var{email} has route part.
@end table
@flindex email.mfl
These constants are declared in the @file{email.mfl} module. The
function @code{email_map} returns 0 if its argument is not a valid
email address.
@end deftypefn
@deftypefn {Library Function} boolean email_valid (string @var{email})
Returns @samp{True} (1) if @var{email} is a valid email address,
consisting of local and domain parts only. E.g.:
@example
email_valid("gray@@gnu.org") @result{} 1
email_valid("gray") @result{} 0
email_valid('"Sergey Poznyakoff <gray@@gnu.org>') @result{} 0
@end example
This function is defined in @file{email.mfl} (@pxref{Modules}).
@end deftypefn
@node Envelope modification functions
@section Envelope Modification Functions
Envelope modification functions set sender and add or delete recipient
addresses from the message envelope. This allows @acronym{MFL} scripts to
redirect messages to another addresses.
@deftypefn {Built-in Function} void set_from (string @var{email} @
[, string @var{args}])
Sets envelope sender address to @var{email}, which must be a valid
email address. Optional @var{args} supply arguments to ESMTP @samp{MAIL
FROM} command.
@end deftypefn
@deftypefn {Built-in Function} void rcpt_add (string @var{address})
Add the e-mail @var{address} to the envelope.
@end deftypefn
@deftypefn {Built-in Function} void rcpt_delete (string @var{address})
Remove @var{address} from the envelope.
@end deftypefn
The following example code uses these functions to implement a
simple alias-like capability:
@example
prog envrcpt
do
string alias dbget(aliasdb, $1, "NULL", 1)
if alias != "NULL"
rcpt_delete($1)
rcpt_add(alias)
fi
done
@end example
@node Header modification functions
@section Header Modification Functions
@cindex header modification
There are two ways to modify message headers in a @acronym{MFL}
script. First is to use header actions, described in @ref{Actions},
and the second way is to use message modification functions. Compared
with the actions, the functions offer a series of advantages. For
example, using functions you can construct the name of the header to
operate upon (e.g. by concatenating several arguments), something
which is impossible when using actions. Moreover, apart from three
basic operations (add, modify and remove), as supported by header
actions, header functions allow to insert a new header into a
particular place.
@deftypefn {Built-in Function} void header_add (string @var{name}, @
string @var{value})
Adds a header @samp{@var{name}: @var{value}} to the message.
In contrast to the @code{add} action, this function allows to construct
the header name using arbitrary @acronym{MFL} expressions.
@end deftypefn
@deftypefn {Built-in Function} void header_add (string @var{name}, @
string @var{value}, number @var{idx})
This syntax is preserved for backward compatibility. It is equivalent
to @code{header_insert}, which see.
@end deftypefn
@deftypefn {Built-in Function} void header_insert (string @var{name}, @
string @var{value}, number @var{idx})
This function inserts a header @samp{@var{name}: @samp{value}} at
@var{idx}th header position in the internal list of headers maintained
by the MTA. That list contains headers added to the message either by
the filter or by the MTA itself, but not the headers included in the
message itself. Some of the headers in this list are conditional,
e.g. the ones added by the @samp{H?@var{cond}?} directive in
@file{sendmail.cf}. MTA evaluates them after all header modifications
have been done and removes those of headers for which they yield false.
This means that the position at which the header added by
@code{header_insert} will appear in the final message will differ from
@var{idx}.
@end deftypefn
@deftypefn {Built-in Function} void header_delete (string @var{name} @
[, number @var{index}])
Delete header @var{name} from the envelope. If @var{index} is given,
delete @var{index}th instance of the header @var{name}.
Notice the differences between this function and the @code{delete}
action:
@enumerate 1
@item It allows to construct the header name, whereas @code{delete}
requires it to be a literal string.
@item Optional @var{index} argument allows to select a particular header
instance to delete.
@end enumerate
@end deftypefn
@deftypefn {Built-in Function} void header_replace (string @var{name}, @
string @var{value} [, number @var{index}])
Replace the value of the header @var{name} with @var{value}. If
@var{index} is given, replace @var{index}th instance of header @var{name}.
Notice the differences between this function and the @code{replace}
action:
@enumerate 1
@item It allows to construct the header name, whereas @code{replace}
requires it to be a literal string.
@item Optional @var{index} argument allows to select a particular header
instance to replace.
@end enumerate
@end deftypefn
@deftypefn {Library Function} void header_rename (string @var{name}, @
string @var{newname}[, number @var{idx}])
@flindex header_rename.mfl
@*Defined in the module @file{header_rename.mfl}.
@*Available only in the @samp{eom} handler.
Renames the @var{idx}th instance of header @var{name} to
@var{newname}. If @var{idx} is not given, assumes 1.
If the specified header or the @var{idx} instance of it is not present
in the current message, the function silently returns. All other
errors cause run-time exception.
The position of the renamed header in the header list is not preserved.
The example below renames @samp{Subject} header to @samp{X-Old-Subject}:
@example
require 'header_rename'
prog eom
do
header_rename("Subject", "X-Old-Subject")
done
@end example
@end deftypefn
@deftypefn {Library Function} void header_prefix_all (string @var{name} @
[, string @var{prefix}])
@flindex header_rename.mfl
@*Defined in the module @file{header_rename.mfl}.
@*Available only in the @samp{eom} handler.
Renames all headers named @var{name} by prefixing them with
@var{prefix}. If @var{prefix} is not supplied, removes all such
headers.
All renamed headers will be placed in a continuous block in the header
list. The absolute position in the header list will change. Relative
ordering of renamed headers will be preserved.
@end deftypefn
@deftypefn {Library Function} void header_prefix_pattern (string @var{pattern}, string @var{prefix})
@flindex header_rename.mfl
@*Defined in the module @file{header_rename.mfl}.
@*Available only in the @samp{eom} handler.
Renames all headers with names matching @var{pattern} (in the sense of
@code{fnmatch}, @pxref{Special comparisons, fnmatches}) by prefixing
them with @var{prefix}.
All renamed headers will be placed in a continuous block in the header
list. The absolute position in the header list will change. Relative
ordering of renamed headers will be preserved.
If called with one argument, removes all headers matching @var{pattern}.
For example, to prefix all headers beginning with @samp{X-Spamd-} with
an additional @samp{X-}:
@example
require 'header_rename'
prog eom
do
header_prefix_pattern("X-Spamd-*", "X-")
done
@end example
@end deftypefn
@node Body Modification Functions
@section Body Modification Functions
Body modification is an experimental feature of @acronym{MFL}.
The version @value{VERSION} provides only one function for that purpose.
@deftypefn {Built-in Function} void replbody (string @var{text})
Replace the body of the message with @var{text}. Notice, that
@var{text} must not contain RFC 822 headers. See the previous section if
you want to manipulate message headers.
Example:
@example
replbody("Body of this message has been removed by the mail filter.")
@end example
No restrictions are imposed on the format of @var{text}.
@end deftypefn
@deftypefn {Built-in Function} void replbody_fd (number @var{fd})
Replaces the body of the message with the content of the stream
@var{fd}. Use this function if the body is very big, or if it is
returned by an external program.
Notice that this function starts reading from the current position in
@var{fd}. Use @code{rewind} if you wish to read from the beginning of
the stream.
The example below shows how to preprocess the body of the message
using external program @file{/usr/bin/mailproc}, which is supposed to
read the body from its standard input and write the processed text to
its standard output:
@example
number fd # @r{Temporary file descriptor}
prog data
do
# Open the temporary file
set fd tempfile()
done
prog body
do
# Write the body to it.
write_body(fd, $1, $2)
done
prog eom
do
# Use the resulting stream as the stdin to the @code{mailproc}
# command and read the new body from its standard output.
rewind(fd)
replbody_fd(spawn("</usr/bin/mailproc", fd))
done
@end example
@end deftypefn
@node Message modification queue
@section Message Modification Queue
@cindex message modification queue
Message modification functions described in the previous subsections
do not take effect immediately, in the moment they are called.
Instead they store the requested changes in the internal @dfn{message
modification queue}. These changes are applied at the end of
processing, before @samp{eom} stage finishes
(@pxref{milter-control-flow}).
One important consequence of this way of operation is that calling
any @acronym{MTA} action (@pxref{Actions}), causes all prior
modifications to the message to be ignored. That is because after
receiving the action command, @acronym{MTA} will not call filter
for that message any more. In particular, the @samp{eom} handler will
not be called, and the message modification queue will not be flushed.
While it is logical for such actions as @code{reject} or
@code{tempfail}, it may be quite confusing for @code{accept}.
Consider, for example, the following code:
@example
@group
prog envfrom
do
if $1 == ""
header_add("X-Filter", "foo")
accept
fi
done
@end group
@end example
Obviously, the intention was to add a @samp{X-Filter} header and
accept the message if it was sent from the null address. What happens
in reality, however, is a bit different: the message is accepted, but
no header is added to it. If you need to accept the message and
retain any modifications you have done to it, you need to use an
auxiliary variable, e.g.:
@example
@group
number accepted 0
prog envfrom
do
if $1 == ""
header_add("X-Filter", "foo")
set accepted 1
fi
done
@end group
@end example
Then, test this variable for non-zero value at the beginning of each
subsequent handler, e.g.:
@example
prog data
do
if accepted
continue
fi
...
done
@end example
To help you trace such problematic usages of @code{accept},
@command{mailfromd} emits the following warning:
@example
RUNTIME WARNING near /etc/mailfromd.mfl:36: `accept' causes previous
message modification commands to be ignored; call mmq_purge() prior
to `accept', to suppress this warning
@end example
If it is OK to lose all modifications, call @code{mmq_purge}, as
suggested in this message.
@deftypefn {Built-in Function} void mmq_purge ()
Remove all modification requests from the queue. This function undoes
the effect of any of the following functions, if they had been called
previously: @code{rcpt_add}, @code{rcpt_delete}, @code{header_add},
@code{header_insert}, @code{header_delete}, @code{header_replace},
@code{replbody}, @code{quarantine}.
@end deftypefn
@node Mail header functions
@section Mail Header Functions
@deftypefn {Built-in Function} string message_header_encode @
(string @var{text}, [string @var{enc}, string @var{charset}])
Encode @var{text} in accordance with @acronym{RFC} 2047. Optional
arguments:
@table @var
@item enc
Encoding to use. Valid values are @samp{quoted-printable}, or
@samp{Q} (the default) and @samp{base64}, or @samp{B}.
@item charset
Character set. By default @samp{UTF-8}.
@end table
If the function is unable to encode the string, it raises the
exception @code{e_failure}.
For example:
@example
@group
set string "Keld J@o{}rn Simonsen <keld@@dkuug.dk>"
message_header_encode(string, "ISO-8859-1")
@result{} "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@@dkuug.dk>"
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} string message_header_decode @
(string @var{text}, [string @var{charset}])
@var{text} must be a header value encoded in accordance with @acronym{RFC}
2047. The function returns the decoded string. If the decoding fails,
it raises @code{e_failure} exception. The optional argument
@var{charset} specifies the character set to use (default --
@samp{UTF-8}).
@example
@group
set string "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@@dkuug.dk>"
message_header_decode(string)
@result{} "Keld J@o{}rn Simonsen <keld@@dkuug.dk>"
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} string unfold (string @var{text})
If @var{text} is a ``folded'' multi-line @acronym{RFC} 2822 header value,
unfold it. If @var{text} is a single-line string, return its
unchanged copy.
For example, suppose that the message being processed contained the
following header:
@example
@group
List-Id: Sent bugreports to
<some-address@@some.net>
@end group
@end example
Then, applying @code{unfold} to its value@footnote{For example:
@example
prog header
do
echo unfold($2)
done
@end example
} will produce:
@example
Sent bugreports to <some-address@@some.net>
@end example
@end deftypefn
@node Mail body functions
@section Mail Body Functions
@deftypefn {Built-in Function} string body_string (pointer @var{text}, @
number @var{count})
Converts first @var{count} bytes from the memory location pointed to by
@var{text} into a regular string.
This function is intended to convert the @code{$1} argument passed to
a @code{body} handler to a regular @acronym{MFL} string. For more
information about its use, see @ref{body handler}.
@end deftypefn
@deftypefn {Built-in Function} bool body_has_nulls (pointer @var{text}, @
number @var{count})
Returns @samp{True} if first @var{count} bytes of the string pointed
to by @var{text} contain ASCII NUL characters.
Example:
@example
prog body
do
if body_has_nulls($1, $2)
reject
fi
done
@end example
@end deftypefn
@node EOM Functions
@section EOM Functions
The following function is available only in the @samp{eom} handler:
@deftypefn {Built-in Function} void progress ()
Notify the @acronym{MTA} that the filter is still
processing the message. This causes @acronym{MTA} to restart
its timeouts and allows additional amount of time for execution
of @samp{eom}.
Use this function if your @samp{eom} handler needs additional
time for processing the message (e.g. for scanning a very big
@acronym{MIME} message). You may call it several times, if the
need be, although such usage is not recommended.
@end deftypefn
@node Current Message Functions
@section Current Message Functions
@anchor{current_message}
@deftypefn {Built-in Function} number current_message ()
This function can be used in @code{eom} handlers only. It returns a
message descriptor referring to the current message. @xref{Message
functions}, for a description of functions for accessing messages.
@end deftypefn
The functions below access the headers from the current message. They
are available in the following handlers: @code{eoh}, @code{body}, @code{eom}.
@deftypefn {Built-in Function} number current_header_count ([string @var{name}])
Return number of headers in the current message. If @var{name}
is specified, return number of headers that have this name.
@example
current_header_count() @result{} 6
current_header_count("Subject") @result{} 1
@end example
@end deftypefn
@deftypefn {Built-in Function} string current_header_nth_name (number @var{n})
Return the name of the @var{n}th header. The index @var{n} is 1-based.
@end deftypefn
@deftypefn {Built-in Function} string current_header_nth_value (number @var{n})
Return the value of the @var{n}th header. The index @var{n} is
1-based.
@end deftypefn
@deftypefn {Built-in Function} string current_header (string name @
[, number @var{n}])
Return the value of the named header, e.g.:
@example
set s current_header("Subject")
@end example
Optional second argument specifies the header instance, if there are
more than 1 header of the same name, e.g.:
@example
set s current_header("Received", 2)
@end example
Header indices are 1-based.
@end deftypefn
All current_header function raise the @code{e_not_found} exception if the
requested header was not found.
@node Mailbox functions
@section Mailbox Functions
@cindex mailbox functions
A set of functions is provided for accessing mailboxes and messages
within them. In this subsection we describe the functions for
accessing mailboxes.
A mailbox is opened using @code{mailbox_open} function:
@deftypefn {Built-in Function} number mailbox_open (string @var{url} @
[, string @var{mode}, string @var{perms}])
Open a mailbox identified by @var{url}. Return a @dfn{mailbox
descriptor}: a unique numeric identifier that can subsequently be used
to access this mailbox.
The optional @var{mode} argument specifies the access mode for
the mailbox. Its valid values are:
@multitable @columnfractions 0.2 0.7
@headitem Value @tab Meaning
@item r @tab Open mailbox for reading. This is the default.
@item w @tab Open mailbox for writing. If the mailbox does not exist,
it is created.
@item rw @tab Open mailbox for reading and writing. If the mailbox
does not exist, it is created.
@item wr @tab Same as @samp{rw}.
@item w+ @tab Open mailbox for reading and writing. If the mailbox
does not exist, it is created.
@item a @tab Open mailbox for appending messages to it. If the
mailbox does not exist, an exception is signalled.
@item a+ @tab Open mailbox for appending messages to it. If the
mailbox does not exist, it is created.
@end multitable
The optional @var{perms} argument specifies the permissions to use in
case a new file (or files) is created. It is a comma-separated list
of:
@example
[go](+|=)[wr]+
@end example
The initial letter controls which users' access is to be set:
users in the file's group (@samp{g}) or other users not in the file's
group (@samp{o}). The following character controls whether the
permissions are added to the default ones (@samp{+}) or applied
instead of them (@samp{=}). The remaining letters specify the
permissions: @samp{r} for read access and @samp{w} for write access.
For example:
@example
g=rw,o+r
@end example
@end deftypefn
The number of mailbox descriptors available for simultaneous opening
is @value{MAX_MBOXES}. This value can be changed using the
@code{max-open-mailboxes} runtime configuration statement
(@pxref{conf-runtime, max-open-mailboxes}).
@deftypefn {Built-in Function} number mailbox_messages_count (number @var{nmbx})
Return the number of messages in mailbox. The argument @var{nmbx} is
a valid mailbox descriptor as returned by a previous call to
@code{mailbox_open}.
@end deftypefn
@deftypefn {Built-in Function} number mailbox_get_message (number @var{mbx}, @
number @var{n})
Retrieve @var{n}th message from the mailbox identified by descriptor
@var{mbx}. On success, the function returns a @dfn{message
descriptor}, an integer number that can subsequently be used to access
that message (@pxref{Message functions}). On error, an exception is
raised.
Messages in a mailbox are numbered starting from 1.
@end deftypefn
@deftypefn {Built-in Function} void mailbox_close (number @var{nmbx})
Close a mailbox previously opened by @code{mailbox_open}.
@end deftypefn
@deftypefn {Built-in Function} void mailbox_append_message (number @var{nmbx}, @
number @var{nmsg})
Append message @var{nmsg} to mailbox @var{nmbx}. The message
descriptor @var{nsmg} must be obtained from a previous call to
@code{mailbox_get_message} or @code{current_message}
(@pxref{current_message}).
@end deftypefn
@node Message functions
@section Message Functions
@cindex message functions
The functions described below retrieve information from RFC822
messages. The message to operate upon is identified by its
@dfn{descriptor}, an integer number returned by the previous call to
@code{mailbox_get_message} (@pxref{Mailbox functions,
mailbox_get_message}) or @code{current_message}
(@pxref{current_message}) function. The maximum number of message
descriptors is limited by @value{MAX_MSGS}. You can change this limit
using the @code{max-open-messages} runtime configuration statement
(@pxref{conf-runtime, max-open-messages}).
@deftypefn {Built-in Function} number message_size (number @var{nmsg})
Return the size of the message @var{nmsg}, in bytes. @emph{Notice},
that if @var{nmsg} refers to current message
(@pxref{current_message}), the returned value is less than the size
seen by the @acronym{MTA}, because @command{mailfromd} recodes
@acronym{CR-LF} sequences to @acronym{LF}, i.e. removes carriage
returns (@acronym{ASCII} 13) occurring before line feeds
(@acronym{ASCII} 10. To obtain actual message length as seen by the
@acronym{MTA}, add the number of lines in the message:
@example
set actual_length message_size(nmsg) + message_lines(nmsg)
@end example
@end deftypefn
@deftypefn {Built-in Function} boolean message_body_is_empty (number @var{nmsg})
Returns @code{true} if the body of message @var{nmsg} has zero size or
contains only whitespace characters. If the
@samp{Content-Transfer-Encoding} header is present, it is used to
decode body before processing.
@end deftypefn
@deftypefn {Built-in Function} void message_close (number @var{nmsg})
Close the message identified by descriptor @var{nmsg}.
@end deftypefn
@deftypefn {Built-in Function} number message_lines (number @var{nmsg})
Return total number of lines in message @var{nmsg}. The following
relation holds true:
@example
message_lines(@var{x}) = message_body_lines(@var{x})
+ message_header_lines(@var{x}) + 1
@end example
@end deftypefn
@deftypefn {Built-in Function} string message_read_line (number @var{nmsg})
Read and return next line from the message @var{nmsg}. If
there are no more lines to read, raise the @code{eof} exception.
Use @code{message_rewind} to rewind the message stream and read its
contents again.
@end deftypefn
@deftypefn {Built-in Function} void message_rewind (number @var{nmsg})
Rewind the stream associated with message referred to by descriptor @var{nmsg}.
@end deftypefn
@deftypefn {Built-in Function} number message_from_stream (number @var{fd}; @
string @var{filter_chain})
Converts contents of the stream identified by @var{fd} to a mail
message. Returns identifier of the created message.
Optional @var{filter_chain} supplies the name of a @dfn{Mailutils
filter chain}, through which the data will be passed before converting.
See @uref{http://mailutils.org/wiki/Filter_chain}, for a description
of filter chains.
@end deftypefn
@deftypefn {Built-in Function} void message_to_stream (number @var{fd},@
number @var{nmsg}; @
string @var{filter_chain})
Copies message @var{nsmg} to stream descriptor @var{fd}. The
descriptor must be obtained by a previous call to @code{open}.
Optional @var{filter_chain} supplies the name of a @dfn{Mailutils
filter chain}, through which the data will be passed before writing
them to @var{fd}. See @uref{http://mailutils.org/wiki/Filter_chain},
for a description of filter chains.
@end deftypefn
@menu
* Header functions::
* Message body functions::
* MIME functions::
* Message digest functions::
@end menu
@node Header functions
@subsection Header functions
@deftypefn {Built-in Function} number message_header_size (number @var{nmsg})
Return the size, in bytes of the headers of message @var{nmsg}. See the
note to the @code{message_size}, above.
@end deftypefn
@deftypefn {Built-in Function} number message_header_lines (number @var{nmsg})
Return number of lines occupied by headers in message @var{nmsg}.
@end deftypefn
@anchor{message_header_count}
@deftypefn {Built-in Function} number message_header_count (number @var{nmsg}, @
[string @var{name}])
Return number of headers in message @var{nmsg}.
If @var{name} is supplied, count only headers with that name.
@end deftypefn
@deftypefn {Built-in Function} string message_find_header (number @var{nmsg}, @
string @var{name} [, number @var{idx}])
Return value of header @var{name} from the message @var{nmsg}. If the
message contains several headers with the same name, optional
parameter @var{idx} may be used to select one of them. Headers are
numbered from @samp{1}.
If no matching header is not found, the @code{not_found} exception is
raised. If another error occurs, the @code{failure} exception is
raised.
The returned string is a verbatim copy of the message contents (except
for eventual @acronym{CR-LF} -> @acronym{LF} translation, see above).
You might need to apply the @code{unfold} function to it (@pxref{Mail
header functions, unfold}).
@end deftypefn
@deftypefn {Built-in Function} string message_nth_header_name (number @var{nmsg}, number @var{n})
Returns the name of the @var{n}th header in message @var{nmsg}. If
there is no such header, @code{e_range} exception is raised.
@end deftypefn
@deftypefn {Built-in Function} string message_nth_header_value (number @var{msg}, number @var{n})
Returns the value of the @var{n}th header in message @var{nmsg}. If
there is no such header, @code{e_range} exception is raised.
@end deftypefn
@deftypefn {Built-in Function} boolean message_has_header (number @var{nmsg},@
string @var{name} [, number @var{idx}])
Return @code{true} if message @var{nmsg} contains header with the
given @var{name}. If there are several headers with the same name,
optional parameter @var{idx} may be used to select one of them.
@end deftypefn
@node Message body functions
@subsection Message body functions
@deftypefn {Built-in Function} number message_body_size (number @var{nmsg})
Return the size, in bytes, of the body of message @var{nmsg}. See the
note to the @code{message_size}, above.
@end deftypefn
@deftypefn {Built-in Function} number message_body_lines (number @var{nmsg})
Return number of lines in the body of message referred to by
descriptor @var{nmsg}.
@end deftypefn
@deftypefn {Built-in Function} void message_body_rewind (number @var{nmsg})
Rewind the stream associated with the body of message referred to by
descriptor @var{nmsg}.
A call to @code{message_body_read_line} (see below) after calling this
function will return the first line from the message body.
@end deftypefn
@deftypefn {Built-in Function} string message_read_body_line (number @var{nmsg})
Read and return next line from the body of the message @var{nmsg}. If
there are no more lines to read, raise the @code{eof} exception.
Use @code{message_body_rewind} (see above) to rewind the body stream
and read its contents again.
@end deftypefn
@deftypefn {Built-in Function} void message_body_to_stream (number @var{fd},@
number @var{nmsg}; @
string @var{filter_pipe})
Copies the body of the message @var{nsmg} to stream descriptor
@var{fd}. The descriptor must be obtained by a previous call to
@code{open}.
Optional @var{filter_pipe} supplies a sequence of @dfn{Mailutils
filters}, through which the data will be passed before writing
them to @var{fd}. @xref{Filtering functions}, for a discussion of
filter pipe syntax.
@kwindex mimedecode
@cindex MIME, decoding
@cindex decode MIME
In addition to filters described in @xref{Filters}, two special
filters are provided for use with this function: @code{mimedecode} and
@code{charset}. The @code{mimedecode} filter instructs the function
to decode the message body by reverting the encoding specified by its
@code{Content-Transfer-Encoding} header. It is normally used as the
very first filter in chain. The @code{charset} filter recodes the
message body from it original character set to the character set
specified as its argument.
@xref{mimedecode}, for a detailed discussion of this feature.
@end deftypefn
@node MIME functions
@subsection MIME functions
@deftypefn {Built-in Function} boolean message_is_multipart (number @var{nmsg})
Return @code{true} if message @var{nmsg} is a multipart
(@acronym{MIME}) message.
@end deftypefn
@deftypefn {Built-in Function} number message_count_parts (number @var{nmsg})
Return number of parts in message @var{nmsg}, if it is a multipart
(@acronym{MIME}) message. If it is not, return @samp{1}.
Use @code{message_is_multipart} to check whether the message is a
multipart one.
@end deftypefn
@deftypefn {Built-in Function} number message_get_part (number nmsg, @
number @var{n})
Extract @var{n}th part from the multipart message @var{nmsg}.
Numeration of parts begins from @samp{1}. Return message descriptor
referring to the extracted part. Message parts are regarded as
messages, so any message functions can be applied to them.
@end deftypefn
@deftypefn {Built-in Function} string message_content_type (number @var{nmsg})
Returns content type for the message @var{nmsg}. The returned string
is composed of content type and subtype, delimited by slash.
If @var{nmsg} is not a multipart message, the function returns
@samp{text/plain}.
@end deftypefn
@anchor{MIME decoding}
@cindex MIME, decoding
@cindex decode MIME
Several functions are provided for decoding multi-part messages. Such
decoding is governed by @code{Content-Transfer-Encoding} and
@code{Content-Type} headers of the message. The
@code{Content-Transfer-Encoding} header defines the method used to
encode the message. The value of @code{Content-Type} header is used
to determine the character set the body is written in.
@anchor{mimedecode}
@kwindex mimedecode
Basic MIME decoding facilities are provided by the built-in function
@code{message_body_to_stream}, described in the previous subsection.
To instruct it to decode the content, pass it the @var{filter_chain}
argument beginning with the word @code{mimedecode}. The usual
sequence is:
@example
set fd open("> outfile")
message_body_to_stream(fd, msg, "mimedecode")
@end example
To ensure that the produced stream is represented in a specific
character set, use the @code{charset} special filter. Its argument is
the name of the character set to recode the text to:
@example
set fd open("> outfile")
message_body_to_stream(fd, msg, "mimedecode|charset(utf-8)")
@end example
The @code{charset} filter takes also an optional second argument -- a
@dfn{fallback} method, specifying what to do when an octet sequence is
encountered that cannot be represented in the requested character set.
Possible values for this argument are:
@table @samp
@item none
Stop further conversion and signal the @code{e_ilseq} exception.
@item copy-pass
Copy the offending character to the output verbatim.
@item copy-octal
Represent the offending character as a C octal sequence
(@samp{\@var{n}@var{n}@var{n}}, where @var{n} is an octal digit).
This is the default.
@end table
To decode a particular part of the message, first extract it using the
@code{message_get_part} function. Recall that message parts are
messages as well, and as such can be passed to
@code{message_body_to_stream}. For example, the following code
fragment extracts all top-level parts of a multi-part message to files
named @samp{part.@var{N}}:
@example
@group
if message_is_multipart(msg)
set n message_count_parts(msg)
loop for set i 1, while i <= n, set i i + 1
do
set fd open("> part.%i")
message_body_to_stream(fd, message_get_part(msg, i), "mimedecode")
close(fd)
done
fi
@end group
@end example
@flindex mime.mfl
The @file{mime.mfl} module provides additional functions for decoding
multi-part messages:
@deftypefn {Library Function} number message_body_decode (number @var{nmsg}; @
string @var{charset}, string @var{fallback})
Decodes the body of the message (or message part) @var{nmsg},
optionally converting it to the given @var{charset}. The
@var{fallback} argument specifies what to do if a byte sequence cannot
be converted to the specified character set. @xref{iconv fallback},
for a detailed discussion.
The function returns a descriptor of the I/O stream that contains the
decoded material. @xref{I/O functions} for a discussion of functions
available for reading from it.
@end deftypefn
@deftypefn {Library Function} number message_part_decode(number @var{nmsg}, number @var{part}; @
string @var{charset}, string @var{fallback})
Decodes the body of the given part of a MIME message @var{nmsg}. The
argument @var{part} is a 1-based index of the part in the message.
Optional arguments @var{charset} and @var{fallback} have the same
meaning as in @code{message_body_decode} (see above).
Returns a descriptor of the I/O stream that contains the decoded
material.
This function is equivalent to:
@example
message_body_decode(message_get_part(@var{nmsg}, @var{part}, @var{charset},
@var{fallback}))
@end example
@end deftypefn
@node Message digest functions
@subsection Message digest functions
@cindex message digest
@cindex digest, message
@dfn{Message digests} are specially formatted messages that
contain certain number of mail messages, encapsulated using
the method described in RFC 934. Such digests are often used
in mailing lists to reduce the frequency of sending mails.
Messages of this format are also produced by the @dfn{forward}
function in most @acronym{MUA}'s.
The usual way to handle a message digest in @acronym{MFL} is to
convert it first to a @acronym{MIME} message, and then to use
functions for accessing its parts (@pxref{MIME functions}).
@deftypefn {Built-in Function} number message_burst (number @var{nmsg} ; @
number @var{flags})
Converts the message identified by the descriptor @var{nmsg} to a
multi-part message. Returns a descriptor of the created message.
Optional argument @var{flags} controls the behavior of the bursting
agent. It is a bitwise @sc{or} of error action and bursting flags.
@kwindex BURST_ERR_FAIL
@kwindex BURST_ERR_IGNORE
@kwindex BURST_ERR_BODY
@dfn{Error action} defines what to do if a part of the digest is
not in RFC822 message format. If it is @samp{BURST_ERR_FAIL} (the
default), the function will raise the @samp{e_format} exception. If
@var{onerr} is @samp{BURST_ERR_IGNORE}, the improperly formatted part
will be ignored. Finally, the value @samp{BURST_ERR_BODY} instructs
@code{message_burst} to create a replacement part with empty headers
and the text of the offending part as its body.
@kwindex BURST_DECODE
@dfn{Bursting flags} control various aspects of the agent behavior.
Currently only one flag is defined, @samp{BURST_DECODE}, which
instructs the agent to decode any MIME parts (according to the
@samp{Content-Transfer-Encoding} header) it encounters while bursting
the message.
@end deftypefn
@cindex encapsulation boundaries, RFC 934
Parts of a message digest are separated by so-called
@dfn{encapsulation boundaries}, which are in essence lines
beginning with at least one dash followed by a non-whitespace
character. A dash followed by a whitespace serves as a
@dfn{byte-stuffing} character, a sort of escape for lines which begin
with a dash themselves. Unfortunately, there are mail agents which do
not follow byte-stuffing rules and pass lines beginning with dashes
unmodified into resulting digests. To help handle such cases a global
variable is provided which controls how much dashes should the line
begin with for it to be recognized as an encapsulation boundary.
@deftypevr {Built-in variable} number burst_eb_min_length
Minimal number of consecutive dashes an encapsulation boundary
must begin with.
The default is 2.
@end deftypevr
The following example shows a function which saves all parts of a
digest message to separate disk files. The argument @var{orig} is
a message descriptor. The resulting files are named by concatenating
the string supplied by the @var{stem} argument and the ordinal number
(1-based) of the message part.
@example
func burst_digest(number orig, string stem)
do
number msg message_burst(orig)
number nparts message_count_parts(msg)
loop for number i 1,
while i <= nparts,
set i i + 1
do
number part message_get_part(msg, i)
number out open(sprintf('>%s%02d', stem, i))
message_to_stream(out, part)
done
message_close(msg)
done
@end example
@node Quarantine functions
@section Quarantine Functions
@deftypefn {Built-in Function} void quarantine (string @var{text})
Place the message to the quarantine queue, using @var{text} as
explanatory reason.
@end deftypefn
@node SMTP Callout functions
@section SMTP Callout Functions
@deftypefn {Library Function} number callout_open (string @var{url})
Opens connection to the callout server listening at @var{url}.
Returns the descriptor of the connection.
@end deftypefn
@deftypefn {Library Function} void callout_close (number @var{fd})
Closes the connection. @var{fd} is the file descriptor returned by
the previous call to @code{callout_open}.
@end deftypefn
@deftypefn {Library Function} number callout_do (number @var{fd}, @
string @var{email} [, string @var{rest}])
Instructs the callout server identified by @var{fd} (a file descriptor
returned by a previous call to @code{callout_open}) to verify the
validity of the @var{email}. Optional @var{rest} argument supplies
additional parameters for the server. It is formatted as a sequence
of parameter settings delimited by whitespaces. Each setting is
a parameter name and value separated by a @samp{=} sign.
@xref{callout parameters}, for a discussion of available callout
parameters.
Possible return values:
@table @code
@item 0
Success. The @var{email} is found to be valid.
@item e_not_found
@var{email} does not exist.
@item e_temp_failure
The email validity cannot be determined right now, e.g. because remote
SMTP server returned temporary failure. The caller should retry
verification later.
@item e_failure
Some error occurred.
@end table
The function will throw the @code{e_callout_proto} exception if the
remote host doesn't speak the correct callout protocol.
Upon return, @code{callout_do} modifies the following variables:
@table @code
@item last_poll_host
Host name or IP address of the last polled SMTP server.
@item last_poll_greeting
Initial SMTP reply from the last polled host.
@item last_poll_helo
The reply to the @code{HELO} (@code{EHLO}) command, received from the
last polled host.
@item last_poll_sent
Last @acronym{SMTP} command sent to the polled host. If nothing was
sent, @code{last_poll_sent} contains the string @samp{nothing}.
@item last_poll_recv
Last @acronym{SMTP} reply received from the remote host. In case of
multi-line replies, only the first line is stored. If nothing was
received the variable contains the string @samp{nothing}.
@end table
@end deftypefn
The @dfn{default callout server} is defined by the @code{callout-url}
statement in the configuration file, or by the @code{callout}
statement in the @code{server milter} section (@pxref{configuring
default callout server}. The following functions operate on that server.
@deftypefn {Built-in Function} string default_callout_server_url ()
Returns URL of the default callout server.
@end deftypefn
@deftypefn {Library Function} number callout (string @var{email})
Verifies the validity of the @var{email} using the default callout
server.
@end deftypefn
@node Compatibility Callout functions
@section Compatibility Callout Functions
The following functions are wrappers over the callout functions
described in the previous section. They are provided for backward
compatibility.
@flindex poll.mfl
These functions are defined in the module @file{poll.mfl}, which you
must require prior to using any of them.
@deftypefn {Library Function} boolean _pollhost @
(string @var{ip}, string @var{email}, string @var{domain}, @
string @var{mailfrom})
Poll @acronym{SMTP} host @var{ip} for email address @var{email},
using @var{domain} as @code{EHLO} domain and @var{mailfrom} as
@code{MAIL FROM}. Returns 0 or 1 depending on the result of the test.
In contrast to the @code{strictpoll} function, this function does not use
cache database and does not fall back to polling @acronym{MX} servers if the
main poll tempfails. The function can throw one of the following
exceptions: @code{e_failure}, @code{e_temp_failure}.
@end deftypefn
@deftypefn {Library Function} boolean _pollmx @
(string @var{ip}, string @var{email}, string @var{domain}, @
string @var{mailfrom})
Poll @acronym{MX}s of the @var{domain} for email address @var{email}, using
@var{domain} as @code{EHLO} domain and @var{mailfrom} as @code{MAIL
FROM} address. Returns 0 or 1 depending on the result of the test.
In contrast to the @code{stdpoll} function, @code{_pollmx} does
not use cache database and does not fall back to polling the @var{ip}
if the poll fails. The function can throw one of the following
exceptions: @code{e_failure}, @code{e_temp_failure}.
@end deftypefn
@deftypefn {Library Function} boolean stdpoll @
(string @var{email}, string @var{domain}, string @var{mailfrom})
Performs standard poll for @var{email}, using @var{domain} as
@code{EHLO} domain and @var{mailfrom} as @code{MAIL FROM} address.
Returns 0 or 1 depending on the result of the test. Can raise one of
the following exceptions: @code{e_failure}, @code{e_temp_failure}.
In @code{on} statement context, it is synonymous to @code{poll}
without explicit @var{host}. @FIXME{more details and references.}
@end deftypefn
@deftypefn {Library Function} boolean strictpoll @
(string @var{host}, string @var{email}, @
string @var{domain}, string @var{mailfrom})
Performs strict poll for @var{email} on host @var{host}.
See the description of @code{stdpoll} for the detailed information.
In @code{on} context, it is synonymous to @code{poll host @var{host}}.
@end deftypefn
@cindex multiple sender addresses, using with polling commands.
@cindex trying several sender addresses
The @var{mailfrom} argument can be a comma-separated list of email
addresses, which can be useful for servers that are unusually picky about
sender addresses. It is advised, however, that this list always
contain the @samp{<>} address. For example:
@example
_pollhost($client_addr, $f, "domain", "postmaster@@my.net,<>")
@end example
See also @ref{conf-callout, mail-from-address}.
Before returning, all described functions set the following built-in
variables:
@float Table, poll-variables-table
@caption{Variables set by polling functions}
@multitable @columnfractions 0.30 0.70
@headitem Variable @tab Contains
@cindex last_poll_host, global variable, introduced
@item last_poll_host @tab Host name or @acronym{IP} address of the last polled
host.
@cindex last_poll_sent, global variable, introduced
@item last_poll_sent @tab Last @acronym{SMTP} command, sent to this
host. If nothing was sent, it contains literal string @samp{nothing}.
@cindex last_poll_recv, global variable, introduced
@item last_poll_recv @tab Last @acronym{SMTP} reply received from this
host. In case of multi-line replies, only the first line is stored. If
nothing was received the variable contains the string @samp{nothing}.
@cindex cache_used, global variable, introduced
@item cache_used @tab @code{1} if cached data were used instead of
polling, @code{0} otherwise. This variable is set by @code{stdpoll}
and @code{strictpoll}. If it equals @code{1}, none of the above
variables are modified. @xref{cache_used example}, for an example.
@end multitable
@end float
@node Internet address manipulation functions
@section Internet address manipulation functions
Following functions operate on @acronym{IP}v4 addresses in
numeric form.
@deftypefn {Built-in Function} number ntohl (number @var{n})
Converts the number @var{n}, from host to network byte order.
The argument @var{n} is treated as an unsigned 32-bit number.
@end deftypefn
@deftypefn {Built-in Function} number htonl (number @var{n})
Converts the number @var{n}, from network to host byte order.
The argument @var{n} is treated as an unsigned 32-bit number.
@end deftypefn
@deftypefn {Built-in Function} number ntohs (number @var{n})
The argument @var{n} is treated as an unsigned 16-bit number.
The function converts this number from network to host order.
@end deftypefn
@deftypefn {Built-in Function} number htons (number @var{n})
The argument @var{n} is treated as an unsigned 16-bit number.
The function converts this number from host to network order.
@end deftypefn
@deftypefn {Built-in Function} number inet_aton (string @var{s})
Converts the Internet host address @var{s} from the standard
numbers-and-dots notation into the equivalent integer in host
byte order.
@example
inet_aton("127.0.0.1") @result{} 2130706433
@end example
@emph{The numeric data type in @acronym{MFL} is signed, therefore
on machines with 32 bit integers, this conversion can result in a
negative number:}
@example
inet_aton("255.255.255.255") @result{} -1
@end example
@emph{However, this does not affect arithmetical operations on
@acronym{IP} addresses.}
@end deftypefn
@deftypefn {Built-in Function} string inet_ntoa (number @var{n})
Converts the Internet host address @var{n}, given in host byte order to
string in standard numbers-and-dots notation:
@example
inet_ntoa(2130706433) @result{} "127.0.0.1"
@end example
@end deftypefn
@deftypefn {Built-in Function} number len_to_netmask (number @var{n})
Convert number of masked bits @var{n} to @acronym{IP}v4 netmask:
@example
inet_ntoa(len_to_netmask(24)) @result{} 255.255.255.0
inet_ntoa(len_to_netmask(7)) @result{} 254.0.0.0
@end example
If @var{n} is greater than 32 the function raises @code{e_range}
exception.
@end deftypefn
@deftypefn {Built-in Function} number netmask_to_len (number @var{mask})
Convert @acronym{IP}v4 netmask @var{mask} into netmask length (number of bits
preserved by the mask):
@example
netmask_to_len(inet_aton("255.255.255.0")) @result{} 24
netmask_to_len(inet_aton("254.0.0.0")) @result{} 7
@end example
@end deftypefn
The following functions operate on string representation of IPv4 and
IPv6 addresses.
@deftypefn {Built-in Function} string reverse_ipstr(string ip)
Converts the IP address ip to reverse format suitable for use in DNS
labels. That is, if @var{ip} is an IPv4 address, the return value is
obtained by reversing the order of octets in the input:
@example
reverse_ipstr("192.0.2.10") @result{} "10.2.0.192"
@end example
If @var{IP} is an IPv6 address, the return string is formed as
a sequence of nibbles separated by dots, encoded in reverse order,
i.e. the low-order nibble is encoded first, followed by the next
low-order nibble and so on. Each nibble is represented by a
hexadecimal digit appending each byte from the IP represented in hex
from the last to first, delimited by dots, e.g.:
@example
reverse_ipstr("2001:db8:0:0:1::2")
@result{}
"2.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2"
@end example
@end deftypefn
@deftypefn {Built-in Function} number is_ipstr(string @var{s})
Returns 1 if @var{s} is a string representation of an IP address
(IPv4 or IPv6) and 0 otherwise.
@end deftypefn
@deftypefn {Built-in Function} number is_ip4str(string @var{s})
Returns 1 if @var{s} is a string representation of an IPv4 address,
and 0 otherwise.
@end deftypefn
@deftypefn {Built-in Function} number is_ip6str(string @var{s})
Returns 1 if @var{s} is a string representation of an IPv6 address,
and 0 otherwise.
@end deftypefn
The following functions operate on IP addresses or CIDRs represented
as strings:
@anchor{match_cidr}
@deftypefn {Built-in Function} boolean match_cidr (string @var{ip}, @
string @var{cidr})
It returns @code{true} if the @acronym{IP} address @var{ip} pertains to the
@acronym{IP} range @var{cidr}. The first argument, @var{ip}, is a string
representation of an @acronym{IP} address (IPv4 or IPv6). The second
argument, @var{cidr}, is a string representation of a @acronym{IP}
range in @acronym{CIDR} notation, i.e. @samp{@var{addr}/@var{N}},
where @var{addr} is IP address and @var{N} specifies address @dfn{prefix
length} -- the number of meaningful initial bits, counting from the
left side of the address.
The following example will reject the mail if the @acronym{IP} address of
the sending machine does not belong to the block @code{192.0.2.0/24}:
@example
@group
if not match_cidr($@{client_addr@}, "192.0.2.0/24")
reject
fi
@end group
@end example
The following example does the same for a IPv6 CIDR
@code{2001:DB8::/56}:
@example
@group
if not match_cidr($@{client_addr@}, "2001:DB8::/56")
reject
fi
@end group
@end example
Notice, that in previous versions of @command{mailfromd} this function
was implemented as MFL function and required the use of
@file{match_cidr.mfl} module (@pxref{8170-9000}. This is no longer
the case. The module itself is retained in the distribution for
compatibility reasons. If your code uses it, you will see the
following warning during compilation phase (split in two lines for
typesetting reasons):
@example
mailfromd: match_cidr.mfl:18.1-78: warning: This module is
deprecated. The match_cidr function is now built-in.
@end example
Just remove the @code{require 'match_cidr'} from your code to make
this warning disappear.
@end deftypefn
@node DNS functions
@section DNS Functions
MFL offers two sets of functions for querying the Domain Name
System. The @code{dns_query} function and associated
@code{dns_reply_} functions provide a generalized DNS API.
Other functions provide a simplified API.
@node dns_query
@subsection dns_query
@deftypefn {Built-in Function} number dns_query (number @var{type}, string @var{domain}; number @var{sort}, number @var{resolve})
This function looks up the domain name @var{name}. The @var{type}
argument specifies type of the query to perform. On success, the
function returns @dfn{DNS reply descriptor}, a non-negative integer
number identifying the reply. It can then be passed to any of the
@samp{dns_reply_} functions discussed below in order to retrieve the
information from it.
If no matching records were found, the function returns @samp{-1}.
On error, it throws a corresponding exception.
@flindex dns.mfl
The @var{type} argument is one of the following constants (defined in
the module @samp{dns}):
@table @code
@kwindex DNS_TYPE_A
@item DNS_TYPE_A
Query the @samp{A} record. The @var{domain} should be the hostname to
look up.
@kwindex DNS_TYPE_NS
@item DNS_TYPE_NS
Query the @samp{NS} records.
@kwindex DNS_TYPE_PTR
@item DNS_TYPE_PTR
Query the @samp{PTR} record. The @var{domain} address should be the IP
address in dotted-quad form.
@kwindex DNS_TYPE_MX
@item DNS_TYPE_MX
Query the @samp{MX} records.
@kwindex DNS_TYPE_TXT
@item DNS_TYPE_TXT
Query the @samp{TXT} records.
@end table
If the query returns multiple RR sets, the optional argument
@var{sort} controls whether they should be returned in the same order
as obtained from the DNS (0, the default), or should be sorted (1).
@flindex status.mfl
Optional argument @var{resolve} is consulted if @var{type} is
@code{DNS_TYPE_MX} or @code{DNS_TYPE_NS}. By default, queries for
these types return hostnames. The @var{resolve} argument controls
whether to return IP addresses instead. Its possible values (defined
in module @file{status.mfl} are:
@anchor{ip resolve modes}
@table @code
@kwindex RESOLVE_NONE
@item RESOLVE_NONE
Don't resolve hostnames to IP addresses. This is the default.
@kwindex RESOLVE_DFL
@item RESOLVE_DFL
Resolve hostnames to IP addresses according to the address family
of the SMTP session. That is, use @samp{A} records if the client connected
using the INET family (i.e. connected to the IPv4 address), and use
@samp{AAAA} records if the client connected to the IPv6 address.
@kwindex RESOLVE_IP4
@item RESOLVE_IP4
Resolve hostnames to IPv4 addresses (@samp{A} records).
@kwindex RESOLVE_IP6
@item RESOLVE_IP6
Resolve hostnames to IPv6 addresses (@samp{AAAA} records).
@end table
To extract actual data from the @code{dns_query} return value, use the
functions @code{dns_reply_count} and @code{dns_reply_string}. The
usual processing sequence is:
@example
require dns
# Send the query and save the reply descriptor
set n dns_query(DNS_TYPE_NS, domain_name)
if n >= 0
# If non-empty set is returned, iterate over each value in it:
loop for set i 0,
while i < dns_reply_count(n),
set i i + 1
do
# Get the actual data:
echo dns_reply_string(n, i)
done
# Release the memory associated with the reply.
dns_reply_release(n)
fi
@end example
@end deftypefn
@deftypefn {Built-in Function} void dns_reply_release (number @var{rd})
Release the memory associated with the reply @var{rd}.
If @var{rd} is -1, the function does nothing.
@end deftypefn
@deftypefn {Built-in Function} number dns_reply_count (number @var{rd})
Return the number of records in the reply @var{rd}. For convenience,
if @var{rd} is -1, the function returns 0. If @var{rd} is negative
(excepting -1), a @samp{e_failure} exception is thrown.
@end deftypefn
@deftypefn {Built-in Function} string dns_reply_string (number @var{rd}, number @var{n})
Returns @var{n}th record from the DNS reply @var{rd}.
@end deftypefn
@deftypefn {Built-in Function} number dns_reply_ip (number @var{rd}, number @var{n})
Returns @var{n}th record from the DNS reply @var{rd}, if the reply
contains IPv4 addresses.
@end deftypefn
@node Simplified DNS functions
@subsection Simplified DNS functions
These functions are implemented in two layers: @dfn{primitive}
built-in functions which raise exceptions if the lookup fails, and
library calls that are warranted to always return meaningful value
without throwing exceptions.
@flindex dns.mfl
The built-in layer is always available. The library calls become
available after requesting the @file{dns} module (@pxref{Modules}):
@example
require dns
@end example
@deftypefn {Library Function} string dns_getaddr (string @var{domain})
Returns a whitespace-separated list of @acronym{IP} addresses (@code{A}
records) for @var{domain}.
@end deftypefn
@deftypefn {Library Function} string dns_getname (string @var{ipstr})
Returns a whitespace-separated list of domain names (@code{PTR}
records) for the @acronym{IP}v4 address @var{ipstr}.
@end deftypefn
@deftypefn {Library Function} string getmx (string @var{domain} @
[, boolean @var{ip}])
Returns a whitespace-separated list of @samp{MX} names (if @var{ip} is not
given or if it is @code{0}) or @samp{MX} @acronym{IP} addresses (if
@code{@var{ip}!=0})) for @var{domain}. Within the returned
string, items are sorted in order of increasing @samp{MX} priority.
If @var{domain} has no @samp{MX} records, an empty string is returned.
If the @acronym{DNS} query fails, @code{getmx} raises an appropriate
exception.
Examples:
@example
getmx("mafra.cz") @result{} "smtp1.mafra.cz smtp2.mafra.cz relay.iol.cz"
getmx("idnes.cz") @result{} "smtp1.mafra.cz smtp2.mafra.cz relay.iol.cz"
getmx("gnu.org") @result{} "mx10.gnu.org mx20.gnu.org"
getmx("org.pl") @result{} ""
@end example
@emph{Notes}:
@enumerate 1
@item Number of items returned by @code{getmx(@var{domain})} can
differ from that obtained from @code{getmx(@var{domain}, 1)}, e.g.:
@example
@group
getmx("aol.com")
@result{} mailin-01.mx.aol.com mailin-02.mx.aol.com
mailin-03.mx.aol.com mailin-04.mx.aol.com
getmx("aol.com", 1)
@result{} 64.12.137.89 64.12.137.168 64.12.137.184
64.12.137.249 64.12.138.57 64.12.138.88
64.12.138.120 64.12.138.185 205.188.155.89
205.188.156.185 205.188.156.249 205.188.157.25
205.188.157.217 205.188.158.121 205.188.159.57
205.188.159.217
@end group
@end example
@item This function is a wrapper over @code{dns_query}.
If you intend to iterate over returned values, better use
@code{dns_query} directly, e.g. instead of doing
@example
string_list_iterate(getmx(domain), ` ', MX, `do_something(MX)')
@end example
@noindent
use
@example
@group
set n dns_query(DNS_TYPE_MX, domain)
if n >= 0
loop for set i 0,
while i < dns_reply_count(n),
set i i + 1
do
do_something(dns_reply_string(n, i))
done
dns_reply_release(n)
fi
@end group
@end example
@xref{dns_query}, for details about the @code{dns_query} function and
associated @code{dns_reply_*} calls.
@item This interface is semi-deprecated.
It will most probably be removed in future releases, when array
data types are implemented.
@end enumerate
@end deftypefn
@anchor{primitive_hasmx}
@deftypefn {Built-in Function} boolean primitive_hasmx (string @var{domain})
Returns @code{true} if the domain name given by its argument
has any @samp{MX} records.
If the @acronym{DNS} query fails, this function throws @code{failure} or
@code{temp_failure}.
@end deftypefn
@anchor{hasmx}
@deftypefn {Library Function} boolean hasmx (string @var{domain})
Returns @code{true} if the domain name given by its argument
has any @samp{MX} records.
Otherwise, if @var{domain} has no @samp{MX}s or if the @acronym{DNS} query fails,
@code{hasmx} returns @code{false}.
@end deftypefn
@deftypefn {Built-in Function} string primitive_hostname (string @var{ip})
The @var{ip} argument should be a string representing an @acronym{IP} address in
@dfn{dotted-quad} notation. The function returns the canonical name of
the host with this @acronym{IP} address obtained from @acronym{DNS} lookup. For example
@example
primitive_hostname ($@{client_addr@})
@end example
@noindent
returns the fully qualified domain name of the host represented by
Sendmail variable @samp{client_addr}.
If there is no @samp{PTR} record for @var{ip}, @code{primitive_hostname}
raises the exception @code{e_not_found}.
If @acronym{DNS} query fails, the function raises @code{failure} or
@code{temp_failure}, depending on the character of the failure.
@end deftypefn
@anchor{hostname function}
@deftypefn {Library Function} string hostname (string @var{ip})
The @var{ip} argument should be a string representing an @acronym{IP} address in
@dfn{dotted-quad} notation. The function returns the canonical name of
the host with this @acronym{IP} address obtained from @acronym{DNS} lookup.
If there is no @samp{PTR} record for @var{ip}, or if the lookup fails,
the function returns @var{ip} unchanged.
The previous @command{mailfromd} versions used the following
paradigm to check if an @acronym{IP} address resolves:
@example
if hostname(ip) != ip
...
@end example
@end deftypefn
@anchor{primitive_ismx}
@deftypefn {Built-in Function} boolean primitive_ismx (string @var{domain}, @
string @var{host})
The @var{domain} argument is any valid domain name, the @var{host}
is a host name or @acronym{IP} address.
The function returns @code{true} if @var{host} is one of the @samp{MX}
records for the @var{domain}.
If @var{domain} has no @samp{MX} records, @code{primitive_ismx} raises
exception @code{e_not_found}.
If @acronym{DNS} query fails, the function raises @code{failure} or
@code{temp_failure}, depending on the character of the failure.
@end deftypefn
@anchor{ismx}
@deftypefn {Library Function} boolean ismx (string @var{domain}, @
string @var{host})
The @var{domain} argument is any valid domain name, the @var{host}
is a host name or @acronym{IP} address.
The function returns @code{true} if @var{host} is one of the @samp{MX}
records for the @var{domain}. Otherwise it returns @code{false}.
If @var{domain} has no @samp{MX} records, or if the @acronym{DNS} query fails, the
function returns @code{false}.
@end deftypefn
@deftypefn {Built-in Function} string primitive_resolve (string @var{host}, @
[string @var{domain}, number @var{family}])
Reverse of @code{primitive_hostname}. The @code{primitive_resolve} function
returns the @acronym{IP} address for the host name specified by its @var{host}
argument. If the SMTP session uses IPv4 protocol, @samp{A} record
is queried. It it uses IPv6, @samp{AAAA} record is queried. A
particular record type can be requested via optional @var{family},
which can have one of the following values (defined in
@file{status.mfl}):
@table @code
@kwindex RESOLVE_DFL
@item RESOLVE_DFL
Look for @samp{A} or @samp{AAAA}, depending on the connection type.
This is the default.
@kwindex RESOLVE_IP4
@item RESOLVE_IP4
Resolve to IPv4 addresses (@samp{A} records).
@kwindex RESOLVE_IP6
@item RESOLVE_IP6
Resolve to IPv6 addresses (@samp{AAAA} records).
@end table
If @var{host} has no records of the requested type, the function raises the
exception @code{e_not_found}.
If @acronym{DNS} lookup fails, the function raises @code{failure} or
@code{temp_failure}, depending on the character of the failure.
Optional @var{domain} argument is deprecated. If a non-empty string
is given as @var{domain}, the function works as follows:
@enumerate 1
@item If @var{domain} is @samp{in-addr.arpa} (case-insensitive)
The @var{host} must be a string representation of an IPv4 address in
dotted-quad form. If it is not, a @code{e_inval} exception is thrown.
The octets in the IPv4 are reversed, a dot and @samp{domain} are
appended to it, and a @samp{PTR} record is queried for the resulting
name.
Thus, the call
@example
primitive_resolve("192.0.2.1", "in-addr.arpa")
@end example
@noindent
is equivalent to
@example
primitive_hostname("192.0.2.1")
@end example
@item @var{domain} is @samp{ip6.arpa} (case-insensitive)
The @var{host} must be a string representation of an IPv6. If it is
not, a @code{e_inval} exception is thrown. The octets of this IPv6
address are reversed, a dot and @samp{domain} are appended to it, and
a @samp{PTR} record is queried for the resulting name.
Thus, the call
@example
primitive_resolve("2001:DB8::1", "ip6.arpa")
@end example
@noindent
is equivalent to
@example
primitive_hostname("2001:DB8::1")
@end example
@item @var{host} is a string representation of an IPv4 address
The address is reversed as in (1), then a dot and @samp{domain} are
appended to it. Finally, the DNS is queried for an @samp{A} record of
the resulting name.
Thus,
@example
primitive_resolve("192.0.2.1", "rev.example.com")
@end example
@noindent
is equivalent to
@example
primitive_resolve("1.2.0.192.rev.example.com")
@end example
@item @var{host} is a string representation of an IPv6 address
The address is reversed as in (2), then a dot and @samp{domain} are
appended to it. Finally, the DNS is queried for an @samp{AAAA} record of
the resulting name.
@item None of the above.
Same as @code{primitive_hostname(@samp{@var{host}.@var{domain}},'',@var{resolve})}.
@end enumerate
@end deftypefn
@deftypefn {Library Function} string resolve (string @var{host}, @
[string @var{domain}, number @var{family}])
Reverse of @code{hostname}. The @code{resolve} function
returns @acronym{IP} address for the host name specified by @var{host}
argument. If the host name cannot be resolved, or a @acronym{DNS} failure
occurs, the function returns @samp{"0"}.
This function is entirely equivalent to @code{primitive_resolve}
(see above), except that it never raises exceptions.
@end deftypefn
@deftypefn {Built-in Function} string ptr_validate (string @var{ip})
Tests whether the DNS reverse-mapping for @var{ip} exists and
correctly points to a domain name within a particular domain.
First, it obtains all @samp{PTR} records for @var{ip}. Then, for each record
returned, a look up for @samp{A} (or @samp{AAAA}, if @var{ip} is an
IPv6 address) records is performed and IP addresses of each
record are compared against @var{ip}. The function returns true if a
matching @samp{A} (or @samp{AAAA}) record is found.
This function can raise the following exceptions:
@table @code
@item e_not_found
Unable to resolve IP address or hostname.
@item e_failure
Fatal error while resolving.
@item e_temp_failure
Temporary error in DNS resolution.
@item e_too_many
Too many CNAME records (@pxref{CNAME chains}).
@end table
@end deftypefn
@deftypefn {Built-in Function} boolean primitive_hasns (string @var{domain})
Returns @samp{True} if the domain @var{domain} has at least one
@samp{NS} record. Throws exception if DNS lookup fails.
@end deftypefn
@deftypefn {Library Function} boolean hasns (string @var{domain})
Returns @samp{True} if the domain @var{domain} has at least one
@samp{NS} record. Returns @samp{False} if there are no @samp{NS}
records or if the DNS lookup fails.
@end deftypefn
@deftypefn {Library Function} string getns (string @var{domain} ; @
boolean @var{resolve}, boolean @var{sort})
Returns a whitespace-separated list of all the @samp{NS} records for
the domain @var{domain}. Optional parameters @var{resolve} and
@var{sort} control the formatting. If @var{resolve} is 0 (the default), the
resulting string will contain IP addresses of the NS servers. If
@var{resolve} is not 0, hostnames will be returned instead. If
@var{sort} is 1, the returned items will be sorted.
If the @acronym{DNS} query fails, @code{getns} raises an appropriate
exception.
@emph{Notes}:
@enumerate 1
@item This function is a wrapper over @code{dns_query}.
If you intend to iterate over returned values, better use
@code{dns_query} directly, e.g. instead of doing
@example
string_list_iterate(getns(domain), ` ', NS, `do_something(NS)')
@end example
@noindent
use
@example
@group
set n dns_query(DNS_TYPE_NS, domain)
if n >= 0
loop for set i 0,
while i < dns_reply_count(n),
set i i + 1
do
do_something(dns_reply_string(n, i))
done
dns_reply_release(n)
fi
@end group
@end example
@xref{dns_query}, for details about the @code{dns_query} function and
associated @code{dns_reply_*} calls.
@item This interface is semi-deprecated.
It will most probably be removed in future releases, when array
data types are implemented.
@end enumerate
@end deftypefn
@node Geolocation functions
@section Geolocation functions
@cindex geolocation
@cindex GeoIP2
@flindex libmaxminddb
@kwindex WITH_GEOIP2
The @dfn{geolocation functions} allow you to identify the country where
the given IP address or host name is located. These functions are
available only if the @code{libmaxminddb} library is installed and
@command{mailfromd} is compiled with the @samp{GeoIP2} support.
The @code{libmaxminddb} library is distributed by @samp{MaxMind} under
the terms of the @cite{Apache License} Version 2.0. It is available
from @uref{https://dev.maxmind.com/geoip/geoip2/downloadable/#MaxMind_APIs}.
@deftypefn {Built-in Function} void geoip2_open (string @var{filename})
Opens the geolocation database file @var{filename}. The database must
be in GeoIP2 format.
If the database cannot be opened, @code{geoip2_open} throws the
@code{e_failure} exception.
If this function is not called, geolocation functions described below
will try to open the database file @samp{/usr/share/GeoIP/GeoLite2-City.mmdb}.
@end deftypefn
@deftypefn {Built-in Function} string geoip2_dbname (void)
Returns the name of the geolocation database currently in use.
@end deftypefn
The geolocation database for each IP address, which serves as a look
up key, stores a set of items describing this IP. This set is
organized as a map of key-value pairs. Each key is a string value.
A value can be a scalar, another map or array of values. Using
JSON notation, the result of a look up in the database might look as:
@example
@group
@{
"country":@{
"geoname_id":2921044,
"iso_code":"DE",
"names":@{
"en": "Germany",
"de": "Deutschland",
"fr":"Allemagne"
@},
@},
"continent":@{
"code":"EU",
"geoname_id":6255148,
"names":@{
"en":"Europe",
"de":"Europa",
"fr":"Europe"
@}
@},
"location":@{
"accuracy_radius":200,
"latitude":49.4478,
"longitude":11.0683,
"time_zone":"Europe/Berlin"
@},
"city":@{
"geoname_id":2861650,
"names":@{
"en":"Nuremberg",
"de":"N@"urnberg",
"fr":"Nuremberg"
@}
@},
"subdivisions":[@{
"geoname_id":2951839,
"iso_code":"BY",
"names":@{
"en":"Bavaria",
"de":"Bayern",
"fr":"Bavi@`ere"
@}
@}
@}
@end group
@end example
Each particular data item in such structure is identified by its
@dfn{search path}, which is a dot-delimited list of key names leading
to that value. For example, using the above map, the name of the city
in English can be retrieved using the key @code{city.names.en}.
@deftypefn {Built-in Function} string geoip2_get (string @var{ip}, string @var{path})
Looks up the IP address @var{ip} in the geolocation database. If
found, returns data item identified by the search path @var{path}.
The function can throw the following exceptions:
@table @asis
@item e_not_found
The @var{ip} was not found in the database.
@item e_range
The @var{path} does not exist the returned map.
@item e_failure
General error occurred. E.g. the database cannot be opened, @var{ip}
is not a valid IP address, etc.
@end table
@end deftypefn
@deftypefn {Built-in Function} string geoip2_get_json (string @var{ip} [; number @var{indent})
Looks up the @var{ip} in the database and returns entire data set
associated with it, formatted as a JSON object. If the optional
parameter @var{indent} is supplied and is greater than zero, it gives
the indentation for each nesting level in the JSON object.
@end deftypefn
@vrindex WITH_GEOIP2
Applications may test whether the GeoIP2 support is present and
enable the corresponding code blocks conditionally, by testing if
the @samp{WITH_GEOIP2} m4 macro is defined. For example, the
following code adds to the message the @samp{X-Originator-Country}
header, containing the 2 letter code of the country where the client
machine is located. If @command{mailfromd} is compiled without
the @samp{GeoIP2} support, it does nothing:
@example
m4_ifdef(`WITH_GEOIP2',`
try
do
header_add("X-Originator-Country", geoip2_get($client_addr,
'country.iso_code'))
done
catch e_not_found or e_range
do
pass
done
')
@end example
@node Database functions
@section Database Functions
The functions described below provide a user interface to
@acronym{DBM} databases.
Each @acronym{DBM} database is a separate disk file that keeps
@dfn{key/value pairs}. The interface allows to retrieve the value
corresponding to a given key. Both @samp{key} and @samp{value} are
null-terminated character strings. To lookup a key, it is important
to know whether its length includes the terminating null byte. By
default, it is assumed that it does not.
Another important database property is the @dfn{file mode} of the
database file. The default file mode is @samp{640}
(i.e.@: @samp{rw-r----}, in symbolic notation).
These and other properties can be configured using the @code{dbprop} pragma:
@kwindex dbprop,
@cindex dbprop, pragma
@cindex #pragma dbprop
@example
#pragma dbprop @var{pattern} @var{prop} [@var{prop}]
@end example
The @var{pattern} is the database name or shell-style globbing
pattern. Properties defined by that pragma apply to each database
whose name matches this pattern. If several @code{dbprop} pragmas
match the database name, the one that matches exactly is preferred.
The rest of arguments define properties for that database. The valid
values for @var{prop} are:
@enumerate 1
@item
The word @samp{null}, meaning that the terminating null byte is
included in the key length.
Setting @samp{null} property is necessary, for databases created with
@command{makemap -N hash} command.
@item
File mode for the disk file. It can be either an octal number, or
a symbolic mode specification in ls-like format. E.g., the following
two formats are equivalent:
@example
@group
640
rw-r----
@end group
@end example
@item
@dfn{DBM scheme}, which specifies the type of the DBM (@pxref{Database
Formats}) to use for this database. The scheme consists of DBM type
name followed by a colon and two slashes. E.g. @samp{gdbm://} means
a GDBM database. @xref{DBM scheme}, for a detailed discussion.
@end enumerate
For example, consider the following pragmas:
@example
#pragma dbprop /etc/mail/whitelist.db 640
@end example
It tells that the database file @file{whitelist.db} has
privileges @samp{640} and do not include null in the key length.
Similarly, the following pragma:
@example
#pragma dbprop `/etc/mail/*.db' null 600 bdb://
@end example
@noindent
declares that all database files in directory @file{/etc/mail} are
Berkeley DB files, have privileges @samp{640}, and include null terminator
in the key length. @emph{Notice}, the use of @command{m4} quoting
characters in the example below. Without them, the sequence @samp{/*}
would have been taken as the beginning of a comment.
Additionally, for compatibility with previous versions (up to 5.0),
the terminating null property can be requested via an optional
argument to the database functions (in description below, marked as
@var{null}).
@anchor{dbmap}
@deftypefn {Built-in Function} boolean dbmap (string @var{db}, @
string @var{key}, [boolean @var{null}])
Looks up @var{key} in the @acronym{DBM} file @var{db} and returns
@code{true} if it is found.
See above for the meaning of @var{null}.
@xref{whitelisting}, for an example of using this function.
@end deftypefn
@deftypefn {Built-in Function} string dbget (string @var{db}, @
string @var{key} [, string @var{default}, boolean @var{null}])
Looks up @var{key} in the database @var{db} and returns the value
associated with it. If the key is not found returns @var{default}, if
specified, or empty string otherwise.
See above for the meaning of @var{null}.
@end deftypefn
@deftypefn {Built-in Function} void dbput (string @var{db}, @
string @var{key}, string @var{value} [, @
boolean @var{null}, number @var{mode} ])
Inserts in the database a record with the given @var{key} and
@var{value}. If a record with the given @var{key} already exists, its
value is replaced with the supplied one.
See above for the meaning of @var{null}. Optional @var{mode} allows
to explicitly specify the file mode for this database. See also
@code{#pragma dbprop}, described above.
@end deftypefn
@deftypefn {Built-in Function} void dbinsert (string @var{db}, @
string @var{key}, string @var{value} [, @
boolean @var{replace}, boolean @var{null}, number @var{mode} ])
This is an improved variant of @code{dbput}, which provides a
better control on the actions to take if the @var{key} already exists in the
database. Namely, if @var{replace} is @samp{True}, the old value is
replaced with the new one. Otherwise, the @samp{e_exists} exception
is thrown.
@end deftypefn
@deftypefn {Built-in Function} void dbdel (string @var{db}, @
string @var{key} [, boolean @var{null}, number @var{mode}])
Delete from the database the record with the given @var{key}. If
there are no such record, return without signalling error.
If the optional @var{null} argument is given and is not zero, the
terminating null character will be included in @var{key} length.
Optional @var{mode} allows to explicitly specify the file mode for
this database. See also @code{#pragma dbprop}, described above.
@end deftypefn
@flindex safedb.mfl
The functions above have also the corresponding exception-safe
interfaces, which return cleanly if the @samp{e_dbfailure} exception
occurs. To use these interfaces, request the @file{safedb} module:
@example
require safedb
@end example
The exception-safe interfaces are:
@deftypefn {Library Function} number safedbmap (string @var{db}, @
string @var{key} [, number @var{default}, boolean @var{null}])
This is an exception-safe interface to @code{dbmap}. If a
database error occurs while attempting to retrieve the record,
@code{safedbmap} returns @var{default} or @samp{0}, if it is
not defined.
@end deftypefn
@deftypefn {Library Function} string safedbget (string @var{db}, @
string @var{key} [, string @var{default}, boolean @var{null}])
This is an exception-safe interface to @code{dbget}. If a
database error occurs while attempting to retrieve the record,
@code{safedbget} returns @var{default} or empty string, if it is
not defined.
@end deftypefn
@deftypefn {Library Function} void safedbput (string @var{db}, @
string @var{key}, string @var{value} [, boolean @var{null}])
This is an exception-safe interface to @code{dbput}. If a
database error occurs while attempting to retrieve the record,
the function returns without raising exception.
@end deftypefn
@deftypefn {Library Function} void safedbdel (string @var{db}, @
string @var{key} [, boolean @var{null}])
This is an exception-safe interface to @code{dbdel}. If a
database error occurs while attempting to delete the record,
the function returns without raising exception.
@end deftypefn
@anchor{safedb_verbose}
@vrindex safedb_verbose
The verbosity of @samp{safedb} interfaces in case of database error is
controlled by the value of @code{safedb_verbose} variable. If it is
@samp{0}, these functions return silently. This is the default
behavior. Otherwise, if @code{safedb_verbose} is not @samp{0}, these
functions log the detailed diagnostics about the database error and
return.
@anchor{dbm-seq}
The following functions provide a sequential access to the contents of
a @acronym{DBM} database:
@deftypefn {Built-in Function} number dbfirst (string @var{name})
Start sequential access to the database @var{name}. The return value
is an opaque identifier, which is used by the remaining sequential
access functions. This number is @samp{0} if the database is empty.
@end deftypefn
@deftypefn {Built-in Function} number dbnext (number @var{dn})
Select next record form the database. The argument @var{dn} is the
access identifier, returned by a previous call to @code{dbfirst} or
@code{dbnext}.
Returns new access identifier. This number is @samp{0} if all records
in the database have been visited.
@end deftypefn
The usual approach for iterating over all records in a database
@var{dbname} is:
@example
@group
loop for number dbn dbfirst(@var{dbname})
do
@dots{}
done while dbnext(dbn)
@end group
@end example
The following two functions can be used to access values of the
currently selected database record. Their argument, @var{dn}, is the
access identifier, returned by a previous call to @code{dbfirst} or
@code{dbnext}.
@deftypefn {Built-in Function} string dbkey (number @var{dn})
Return the key from the selected database record.
@end deftypefn
@deftypefn {Built-in Function} string dbvalue (number @var{dn})
Return the value from the selected database record.
@end deftypefn
@deftypefn {Built-in Function} void dbbreak (number @var{dn})
Stop sequential access to the database and deallocate all associated
resources. Use this function if you need to break from the sequential
access loop, as in the example below:
@example
@group
loop for number dbn dbfirst(@var{dbname})
do
if some_condition
dbbreak(dbn)
break
fi
done while dbnext(dbn)
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} number db_expire_interval (string @var{fmt})
The @var{fmt} argument is a database format identifier
(@pxref{Database Formats}). If it is valid, the function returns the
expiration interval for that format. @FIXME{How to obtain negative
expiration??} Otherwise, @code{db_expire_interval} raises the
@code{e_not_found} exception.
@end deftypefn
@deftypefn {Built-in Function} string db_name (string @var{fmtid})
The @var{fmtid} argument is a database format identifier
(@pxref{Database Formats}). The function returns the file name
for that format. If @var{fmtid} does not match any known format,
@code{db_name} raises the @code{e_not_found} exception.
@end deftypefn
@cindex getting cache status
@cindex cache, getting status
@deftypefn {Built-in Function} number db_get_active (string @var{fmtid})
Returns the flag indicating whether the cache database @var{fmtid}
is currently enabled. If @var{fmtid} does not match any known format,
@code{db_name} raises the @code{e_not_found} exception.
@end deftypefn
@cindex disabling cache
@cindex cache, disabling
@deftypefn {Built-in Function} void db_set_active (string @var{fmtid}, @
boolean @var{enable})
Enables the cache database @var{fmtid} if @var{enable} is @samp{True},
or disables it otherwise. For example, to disable @acronym{DNS}
caching, do:
@example
db_set_active("dns", 0)
@end example
@end deftypefn
@deftypefn {Built-in Function} boolean relayed (string @var{domain})
@anchor{relayed}
Returns @code{true} if the string @var{domain} is found in one of
relayed domain files (@pxref{conf-base, relayed-domain-file}). The
usual construct is:
@example
if relayed(hostname($@{client_addr@}))
@dots{}
@end example
@noindent
which yields @code{true} if the @acronym{IP} address from @command{Sendmail} variable
@samp{client_addr} is relayed by the local machine.
@end deftypefn
@node Control database
@section Control Database
@flindex cdb.mfl
@dfn{Control database} is a DBM file whose records define actions to
be taken for mails coming from particular IP or email addresses.
Functions and variables for using the control database are defined in
module @file{cdb.mfl}.
@deftypevr {cdb variable} string cdb_name
Name of the database file to use as control database. By default
it is @file{/etc/mail/mfctl.db}.
@end deftypevr
@deftypevr {cdb variable} number cdb_greylist_interval
Greylisting interval, for database records that prescribe greylisting
(see below). Defaults to 900 seconds.
@end deftypevr
@deftypefn {Library Function} void cdb_check (string @var{prefix}, string @var{addr})
Perform a look up in the database. If the value is found, take the
action it indicates.
The key to use for the look up depends on the value of @var{prefix}:
@table @asis
@item ip
The @var{addr} argument must be an IP address. The look up key is
@samp{ip:@var{addr}}.
@item email
The @var{addr} argument is an email address. The key is
@samp{email:@var{cemail}}, where @var{cemail} is @var{addr} in
@dfn{canonical form}, obtained by converting its domain part to lower
case.
@item domain
The @var{addr} argument is an email address. The key is formed as
@samp{domain:@var{dom}}, where @var{dom} is domain part of @var{addr}
converted to lower case.
@item subdomain
Initial key value is obtained as for @samp{domain}. If the key is
found, the requested action is performed. Otherwise, the shortest
hostname prefix (sequence of characters up to and including the first
dot) is stripped off the domain and the process is retried.
Thus, the action is determined by the longest subdomain of @var{addr},
@var{x}, for which the key @samp{domain:@var{x}} exists in the database.
@item mx
If @var{addr} is an email address, its domain part is selected,
otherwise it is used as is. The list of MX servers for this domain is
obtained. For each @var{host} from that list the key
@samp{mx:@var{host}} is looked up. First entry found is used.
@end table
The function @code{cdb_check} returns if the key was not found in the
database or if the value found was @samp{OK} (case-insensitive) or
empty. Otherwise, the looked up value determines the action
(@pxref{reply actions}), as described in the following table. The
action is returned and execution of the filter program stops.
@table @asis
@item CONTINUE
Continue to the next milter state. @xref{continue}.
@item ACCEPT
Accept the mail. @xref{accept}.
@item REJECT
Reject the mail. @xref{reject}.
@item TEMPFAIL
Return a temporary failure. @xref{tempfail}.
@item GREYLIST
Greylist the mail using the interval defined by the
@code{cdb_greylist_interval} variable. If called in @code{envrcpt}
handler, the action is taken immediately. Otherwise, if called
in @code{connect}, @code{helo} or @code{envfrom}, the action is
delayed until @code{envrcpt} is invoked. Otherwise, if called
in any other handler an error is reported.
@item @var{code} @var{xcode} @var{text}
@itemx @var{code} @var{text}
@itemx @var{text}
Here, @var{code} is SMTP response code, @var{xcode} is extended
STMP response code, and @var{text} is an arbitrary text. If
@var{code} is given, it must begin with @samp{4} or @samp{5}. Its
first digit defines the action to be taken: temporary failure (if
@samp{4}) or reject (if @samp{5}). If @var{xcode} is given, its first
digit must match that of @var{code}.
If only @var{text} is supplied, it is equivalent to
@example
reject(550, 5.1.0, @var{text})
@end example
@end table
@end deftypefn
@node System functions
@section System functions
@deftypefn {Built-in Function} boolean access (string @var{pathname}, @
number @var{mode})
Checks whether the calling process can access the file @var{pathname}.
If @var{pathname} is a symbolic link, it is dereferenced. The
function returns @samp{True} if the file can be accessed and
@samp{False} otherwise@footnote{@emph{Note}, that the return code is
inverted in respect to the system function @samp{access(2)}.}.
Symbolic values for @var{mode} are provided in module
@file{status}:
@table @asis
@kwindex F_OK
@item F_OK
Tests for the existence of the file.
@kwindex R_OK
@item R_OK
Tests whether the file exists and grants read permission.
@kwindex W_OK
@item W_OK
Tests whether the file exists and grants write permission.
@kwindex X_OK
@item X_OK
Tests whether the file exists and grants execute permission.
@end table
@end deftypefn
@deftypefn {Built-in Function} string getenv (string @var{name})
Searches the environment list for the variable @var{name} and returns
its value. If the variable is not defined, the function raises the
exception @samp{e_not_found}.
@end deftypefn
@deftypefn {Built-in Function} string gethostname ([bool @var{fqn}])
Return the host name of this machine.
If the optional @var{fqn} is given and is @samp{true}, the function
will attempt to return fully-qualified host name, by attempting to
resolve it using @acronym{DNS}.
@end deftypefn
@deftypefn {Built-in Function} string getdomainname ()
Return the domain name of this machine. Note, that it does not
necessarily coincide with the actual machine name in @acronym{DNS}.
Depending on the underlying @samp{libc} implementation, this call may
return empty string or the string @samp{(none)}. Do not rely on it to
get the real domain name of the box @command{mailfromd} runs on, use
@code{localdomain} (see below) instead.
@end deftypefn
@flindex localdomain.mfl
@deftypefn {Library Function} string localdomain ()
Return the local domain name of this machine.
This function first uses @code{getdomainname} to make a first guess.
If it does not return a meaningful value, @code{localdomain} calls
@code{gethostname(1)} to determine the fully qualified host name of
the machine, and returns its domain part.
To use this function, require the @file{localdomain} module
(@pxref{Modules}), e.g.: @code{require localdomain}.
@end deftypefn
@deftypefn {Built-in Function} number time ()
Return the time since the Epoch (00:00:00 UTC, January 1, 1970),
measured in seconds.
@end deftypefn
@anchor{strftime}
@deftypefn {Built-in Function} string strftime (string @var{fmt}, @
number @var{timestamp})
@deftypefnx {Built-in Function} string strftime (string @var{fmt}, @
number @var{timestamp}, boolean @var{gmt})
Formats the time @var{timestamp} (seconds since the Epoch) according
to the format specification @var{format}. Ordinary characters placed
in the format string are copied to the output without conversion.
Conversion specifiers are introduced by a @samp{%} character.
@xref{Time and Date Formats}, for a detailed description of the
conversion specifiers. We recommend using single quotes
around @var{fmt} to prevent @samp{%} specifiers from being interpreted
as @code{Mailfromd} variables (@xref{Literals}, for a
discussion of quoted literals and variable interpretation within
them).
The @var{timestamp} argument can be a return value of @code{time}
function (see above).
For example:
@example
@group
strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564)
@result{} 2006-11-25 19:59:24 EET
strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564, 1)
@result{} 2006-11-25 17:59:24 GMT
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} string uname (string @var{format})
This function returns system information formatted according to
the format specification @var{format}. Ordinary characters placed
in the format string are copied to the output without conversion.
Conversion specifiers are introduced by a @samp{%} character.
The following conversions are defined:
@table @asis
@item %s
Name of this system.
@item %n
Name of this node within the communications network to which this node
is attached. Note, that it does not necessarily coincide with the
actual machine name in @acronym{DNS}.
@item %r
Kernel release.
@item %v
Kernel version.
@item %m
Name of the hardware type on which the system is running.
@end table
For example:
@example
uname('%n runs %s, release %r on %m')
@result{} "Trurl runs Linux, release 2.6.26 on i686"
@end example
Notice the use of single quotes.
@end deftypefn
@deftypefn {Built-in Function} void unlink (string @var{name})
Unlinks (deletes) the file @var{name}. On error, throws the
@code{e_failure} exception.
@end deftypefn
@deftypefn {Built-in Function} number system (string @var{str})
The function @code{system} executes a command specified in @var{str}
by calling @command{/bin/sh -c string}, and returns -1 on error or
the return status of the command otherwise.
@end deftypefn
@deftypefn {Built-in Function} void sleep (number @var{secs}[, @var{usec}])
Sleep for @var{secs} seconds. If optional @var{usec} argument is
given, it specifies additional number of microseconds to wait for.
For example, to suspend execution of the filter for 1.5 seconds:
@example
sleep(1,500000)
@end example
This function is intended mostly for debugging and experimental purposes.
@end deftypefn
@deftypefn {Built-in Function} number umask (number @var{mask})
Set the umask to @var{mask} & 0777. Return the previous value of the
mask.
@end deftypefn
@node Passwd functions
@section System User Database
@deftypefn {Built-in Function} string getpwnam (string @var{name})
@deftypefnx {Built-in Function} string getpwuid (number @var{uid})
Look for the user @var{name} (@code{getpwnam}) or user ID @var{uid}
(@code{getpwuid}) in the system password database and return the
corresponding record, if found. If not found, raise the
@samp{e_not_found} exception.
The returned record consists of six fields, separated by colon sign:
@example
uname:passwd:uid:gid:gecos:dir:shell
@end example
@multitable @columnfractions 0.3 0.6
@headitem Field @tab Meaning
@item uname @tab user name
@item passwd @tab user password
@item uid @tab user ID
@item gid @tab group ID
@item gecos @tab real name
@item dir @tab home directory
@item shell @tab shell program
@end multitable
For example:
@example
getpwnam("gray")
@result{} "gray:x:1000:1000:Sergey Poznyakoff:/home/gray:/bin/bash"
@end example
@end deftypefn
Following two functions can be used to test for existence of a key in
the user database:
@deftypefn {Built-in Function} boolean mappwnam (string @var{name})
@deftypefnx {Built-in Function} boolean mappwuid (number @var{uid})
Return @samp{true} if @var{name} (or @var{uid}) is found in the system
user database.
@end deftypefn
@node Sieve Interface
@section Sieve Interface
@cindex Sieve
@samp{Sieve} is a powerful mail filtering language, defined in
@acronym{RFC} 3028. @command{Mailfromd} supports an extended form
of this language. For a description of the language and available
extensions, see @ref{Sieve Language, Sieve Language, Sieve Language,
mailutils, GNU Mailutils Manual}.
@deftypefn {Built-in Function} boolean sieve (number @var{msg}, @
string @var{script} @
[, number @var{flags}, string @var{file}, number @var{line}])
Compile the Sieve program @var{script} and execute it over the
message identified by the descriptor @var{nmsg}.
@flindex sieve.mfl
Optional @var{flags} modify the behavior of the function. It is a
bit-mask field, consisting of a bitwise @code{or} of one or more of
the following flags, defined in @file{sieve.mfl}:
@table @code
@kwindex MF_SIEVE_FILE
@item MF_SIEVE_FILE
The @var{script} argument specifies the name of a Sieve program file.
This is the default.
@kwindex MF_SIEVE_TEXT
@item MF_SIEVE_TEXT
The @var{script} argument is a string containing entire Sieve program.
Optional arguments @var{file} and @var{line} can be used to fix source
locations in Sieve diagnostic messages (see below).
@kwindex MF_SIEVE_LOG
@item MF_SIEVE_LOG
Log every executed @samp{Sieve} action.
@kwindex MF_SIEVE_DEBUG_TRACE
@item MF_SIEVE_DEBUG_TRACE
Trace execution of @samp{Sieve} tests.
@kwindex MF_SIEVE_DEBUG_INSTR
@item MF_SIEVE_DEBUG_INSTR
Log every instruction, executed in the compiled @samp{Sieve} code.
This produces huge amounts of output and is rarely useful, unless you
suspect some bug in @samp{Sieve} implementation and wish to trace it.
@end table
For example, @code{MF_SIEVE_LOG|MF_SIEVE_DEBUG_TRACE} enables logging
@samp{Sieve} actions and tests.
The @code{sieve} function returns @code{true} if the message was
accepted by the @var{script} program, and @code{false} otherwise.
Here, the word @dfn{accepted} means that some form of @samp{KEEP}
action (@pxref{Actions, keep, Actions, mailutils, GNU Mailutils
Manual}) was executed over the message.
While executing the Sieve script, Sieve environment (@cite{RFC 5183})
is initialized as follows:
@table @asis
@item domain
The domain name of the server Sieve is running on.
@item host
Host name of the server Sieve is running on.
@item location
The string @samp{MTA}.
@item name
The string @samp{GNU Mailutils}.
@item phase
The string @samp{pre}.
@item remote-host
Defined to the value of @samp{client_ptr} macro, if it was required.
@item remote-ip
Defined to the value of @samp{client_addr} macro, if it was required.
@item version
The version of GNU Mailutils.
@end table
@end deftypefn
The following example discards each message not accepted by the
@samp{Sieve} program @file{/etc/mail/filter.siv}:
@example
require 'sieve'
group eom
do
if not sieve(current_message(), "/etc/mail/filter.siv", MF_SIEVE_LOG)
discard
fi
done
@end example
The Sieve program can be embedded in the MFL filter, as shown in the
example below:
@example
require 'sieve'
prog eom
do
if not sieve(current_message(),
"require \"fileinto\";\n"
"fileinto \"/tmp/sieved.mbox\";",
MF_SIEVE_TEXT | MF_SIEVE_LOG)
discard
fi
done
@end example
In such cases, any Sieve diagnostics (error messages, traces, etc.)
will be marked with the locations relative to the line where the call
to @code{sieve} appears. For example, the above program produces the
following in the log:
@example
prog.mfl:7: FILEINTO; delivering into /tmp/sieved.mbox
@end example
Notice, that the line number correctly refers to the line where the
@code{fileinto} action appears in the source. However, there are
cases where the reported line number is incorrect. This happens,
for instance, if @var{script} is a string variable defined elsewhere.
To handle such cases, @code{sieve} accepts two optional parameters
which are used to compute the location in the Sieve program. The
@var{file} parameter specifies the file name where the definition of
the program appears, and the @var{line} parameter gives the number of
line in that file where the program begins. For example:
@example
require 'sieve'
const sieve_prog_line __line__ + 2
string sieve_prog <<EOT
require "fileinto";
fileinto "/tmp/sieved.mbox";
EOT
prog eom
do
if not sieve(current_message(),
sieve_prog, MF_SIEVE_TEXT | MF_SIEVE_LOG,
__file__, sieve_prog_line)
discard
fi
done
@end example
The actual Sieve program begins two lines below the
@code{sieve_prog_line} constant definition, which is reflected in its
initialization.
@node Interfaces to Third-Party Programs
@section Interfaces to Third-Party Programs
A set of functions is defined for interfacing with other filters via
@acronym{TCP}. Currently implemented are interfaces with
@command{SpamAssassin} @command{spamd} daemon and with
@command{ClamAV} anti-virus.
Both interfaces work much the same way: the remote filter is
connected and the message is passed to it. If the remote filter
confirms that the message matches its requirements, the function
returns @code{true}. Notice that in practice that means that such a
message @emph{should be rejected or deferred}.
The address of the remote filter is supplied as the second argument
in the form of a standard @acronym{URL}:
@example
@var{proto}://@var{path}[:@var{port}]
@end example
@noindent
The @var{proto} part specifies the @dfn{connection protocol}. It
should be @samp{tcp} for the @acronym{TCP} connection and @samp{file}
or @samp{socket} for the connection via @acronym{UNIX} socket. In the latter
case the @var{proto} part can be omitted. When using @acronym{TCP}
connection, the @var{path} part gives the remote host name or @acronym{IP}
address and the optional @var{port} specifies the port number or
service name to use. For example:
@example
# @r{connect to @samp{remote.filter.net} on port 3314}:
tcp://remote.filter.net:3314
# @r{the same, using symbolic service name (must be defined in
# @file{/etc/services}):}
tcp://remote.filter.net:spamd
# @r{Connect via a local @acronym{UNIX} socket (equivalent forms):}
/var/run/filter.sock
file:///var/run/filter.sock
socket:///var/run/filter.sock
@end example
The description of the interface functions follows.
@menu
* SpamAssassin::
* ClamAV::
@end menu
@node SpamAssassin
@subsection SpamAssassin
@anchor{sa}
@cindex SpamAssassin
@cindex spamd
@deftypefn {Built-in Function} boolean spamc (number @var{msg}, @
string @var{url}, number @var{prec}, number @var{command})
Send the message @var{msg}t to the SpamAssassin daemon (@code{spamd})
listening on the given @var{url}. The @var{command} argument
identifies what kind of processing is needed for the message. Allowed
values are:
@table @asis
@kwindex SA_SYMBOLS
@item SA_SYMBOLS
Process the message and return 1 or 0 depending on whether it is
diagnosed as spam or not. Store SpamAssassin keywords in the global
variable @code{sa_keywords} (see below).
@kwindex SA_REPORT
@item SA_REPORT
Process the message and return 1 or 0 depending on whether it is
diagnosed as spam or not. Store entire SpamAssassin report in the
global variable @code{sa_keywords}.
@kwindex SA_LEARN_SPAM
@item SA_LEARN_SPAM
Learn the supplied message as spam.
@kwindex SA_LEARN_HAM
@item SA_LEARN_HAM
Learn the supplied message as ham.
@kwindex SA_FORGET
@item SA_FORGET
Forget any prior classification of the message.
@end table
The second argument, @var{prec}, gives the precision, in decimal
digits, to be used when converting SpamAssassin diagnostic data and
storing them into @command{mailfromd} variables.
@anchor{sa-floating-point-conversion}
The floating point SpamAssassin data are converted to the integer
@command{mailfromd} variables using the following relation:
@example
@var{var} = int(@var{sa-var} * 10**@var{prec})
@end example
@noindent
where @var{sa-var} stands for the SpamAssassin value and @var{var}
stands for the corresponding @command{mailfromd} one. @code{int()}
means taking the integer part and @samp{**} denotes the exponentiation
operator.
The function returns additional information via the following
variables:
@table @code
@cindex sa_score, global variable
@item sa_score
The spam score, converted to integer as described above. To convert
it to a floating-point representation, use @code{sa_format_score}
function (@pxref{String manipulation, sa_format_score}). See also the
example below.
@cindex sa_threshold, global variable
@item sa_threshold
The threshold, converted to integer form.
@cindex sa_keywords, global variable
@item sa_keywords
If @var{command} is @samp{SA_SYMBOLS}, this variable contains a
string of comma-separated SpamAssassin keywords identifying this
message, e.g.:
@example
ADVANCE_FEE_1,AWL,BAYES_99
@end example
If @var{command} is @samp{SA_REPORT}, the value of this variable is
a @dfn{spam report} message. It is a multi-line textual message,
containing detailed description of spam scores in a tabular form.
It consists of the following parts:
@enumerate
@item A preamble.
@item Content preview.
The words @samp{Content preview}, followed by a colon and an excerpt
of the message body.
@item Content analysis details.
It has the following form:
@example
Content analysis details: (@var{score} points, @var{max} required)
@end example
@noindent
where @var{score} and @var{max} are spam score and threshold in
floating point.
@item Score table.
The score table is formatted in three columns:
@table @asis
@item pts
The score, as a floating point number with one decimal digit.
@item rule name
SpamAssassin rule name that contributed this score.
@item description
Textual description of the rule
@end table
The score table can be extracted from @code{sa_keywords} using
@code{sa_format_report_header} function (@pxref{String manipulation,
sa_format_report_header}), as illustrated in the example below.
@end enumerate
The value of this variable is undefined if @var{command} is
@samp{SA_LEARN_SPAM}, @samp{SA_LEARN_HAM} or @samp{SA_FORGET}.
@end table
The @code{spamc} function can signal the following exceptions:
@code{e_failure} if the connection fails, @code{e_url} if the supplied
@acronym{URL} is invalid and @code{e_range} if the supplied port number
is out of the range 1--65535.
An example of using this function:
@example
@group
prog eom
do
if spamc(current_message(), "tcp://192.168.10.1:3333", 3,
SA_SYMBOLS)
reject 550 5.7.0
"Spam detected, score %sa_score with threshold %sa_threshold"
fi
done
@end group
@end example
Here is a more advanced example:
@example
prog eom
do
set prec 3
if spamc(current_message(),
"tcp://192.168.10.1:3333", prec, SA_REPORT)
add "X-Spamd-Status" "SPAM"
else
add "X-Spamd-Status" "OK"
fi
add "X-Spamd-Score" sa_format_score(sa_score, prec)
add "X-Spamd-Threshold" sa_format_score(sa_threshold, prec)
add "X-Spamd-Keywords" sa_format_report_header(sa_keywords)
done
@end example
@end deftypefn
@deftypefn {Library Function} boolean sa (string @var{url}, @
number @var{prec}; number @var{command})
Additional interface to the @code{spamc} function, provided for
backward compatibility. It is equivalent to
@example
spamc(current_message(), @var{url}, @var{prec}, @var{command})
@end example
If @var{command} is not supplied, @samp{SA_SYMBOLS} is used.
@end deftypefn
@node ClamAV
@subsection ClamAV
@cindex ClamAV
@deftypefn {Built-in Function} boolean clamav (number @var{msg}, @
string @var{url})
@cindex clamav_virus_name, global variable
Pass the message @var{msg} to the ClamAV daemon at @var{url}. Return
@code{true} if it detects a virus in it. Return virus name in
@code{clamav_virus_name} global variable.
@cindex clamav_stream_max_length, global vaiable
@cindex @code{StreamMaxLength}, @file{clamd.conf} parameter.
@cindex clamd.conf
The message is sent to @command{clamd} in chunks. The size of each
chunk is defined by the variable @code{clamav_stream_max_length}. Its
value should not be greater than that of the @code{StreamMaxLength}
parameter in @file{clamd.conf}.
To stay on the safe side, @code{clamav_stream_max_length} defaults
to 4096 bytes. To improve performance, adjust it to the actual value
of @code{StreamMaxLength}.
The @code{clamav} function can signal the following exceptions:
@table @code
@item e_failure
Unable to connect to the server, or failed to send the message to it.
In particular, this exception will be raised if the value of
@code{clamav_stream_max_length} is greater than the
@code{StreamMaxLength} @code{clamd} configuration parameter. In this
case, the @code{clamd} server will report:
@example
WARNING: INSTREAM: Size limit reached, (requested: @var{n}, max: @var{m})
@end example
@noindent
where @var{n} and @var{m} are decimal numbers. Adjust your
@code{clamav_stream_max_length} value so that
@example
clamav_stream_max_length < m
@end example
@item e_url
The supplied @acronym{URL} is invalid.
@end table
An example usage:
@example
@group
prog eom
do
if clamav(current_message(), "tcp://192.168.10.1:6300")
reject 550 5.7.0 "Infected with %clamav_virus_name"
fi
done
@end group
@end example
@end deftypefn
@node Rate limiting functions
@section Rate limiting functions
@deftypefn {Built-in Function} number rate (string @var{key}, @
number @var{sample-interval}, @
[number @var{min-samples}, number @var{threshold}])
Returns the mail sending rate for @var{key} per
@var{sample-interval}. Optional @var{min-samples}, if supplied,
specifies the minimal number of mails needed to obtain the
statistics. The default is 2. Optional @var{threshold}
controls rate database updates. If the observed rate (per
@var{sample-interval} seconds) is higher than the @var{threshold}, the
hit counters for that key are not incremented and the database is not
updated. Although the @var{threshold} argument is
optional@footnote{It is made optional in order to provide backward
compatibility with the releases of mailfromd prior to 5.0.93.}, its
use is strongly encouraged. Normally, the value of @var{threshold} equals
the value compared with the return from @var{rate}, as in:
@example
@group
if rate("$f-$client_addr", rate_interval, 4, maxrate) > maxrate
tempfail 450 4.7.0 "Mail sending rate exceeded. Try again later"
fi
@end group
@end example
This function is a low-level interface. Instead of using it directly,
we advise to use the @code{rateok} function, described below.
@end deftypefn
@deftypefn {Library Function} boolean rateok (string @var{key}, @
number @var{sample-interval}, @
number @var{threshold},
[number @var{min-samples}])
@flindex rateok.mfl
To use this function, require the @file{rateok} module
(@pxref{Modules}), e.g.: @code{require rateok}.
The @code{rateok} function returns @samp{True} if the mail sending
rate for @var{key}, computed for the interval of @var{sample-interval}
seconds is less than the @var{threshold}. Optional @var{min-samples}
parameter supplies the minimal number of mails needed to obtain the
statistics. It defaults to 4.
@end deftypefn
@xref{Sending Rate}, for a detailed description of the @code{rateok} and
its use. The @code{interval} function (@pxref{interval}) is often
used in the second argument to @code{rateok} or @code{rate}.
@deftypefn {Built-in Function} boolean tbf_rate (string @var{key}, @
number @var{cost}, number @var{sample-interval}, number @var{burst-size})
This function implements a classical token bucket filter algorithm.
Tokens are added to the bucket identified by the @var{key} at constant
rate of 1 token per @var{sample-interval} microseconds, to a maximum
of @var{burst-size} tokens. If no bucket is found for the specified key, a
new bucket is created and initialized to contain @var{burst-size}
tokens. If the bucket contains @var{cost} or more tokens, @var{cost}
tokens are removed from it and @code{tbf_rate} returns @samp{True}.
Otherwise, the function returns @samp{False}.
For a detailed description of the Token Bucket Algorithm and its
use to limit mail rates, see @ref{TBF}.
@end deftypefn
@node Greylisting functions
@section Greylisting functions
@deftypefn {Built-in Function} boolean greylist @
(string @var{key}, number @var{interval})
@cindex greylist_seconds_left, global variable
Returns @samp{True} if the @var{key} is found in the greylist database
(controlled by @code{database greylist} configuration file statement,
@pxref{conf-database}). The argument @var{interval} gives the greylisting
interval in seconds. The function stores the number of seconds left to the end
of greylisting period in the global variable
@code{greylist_seconds_left}. @xref{Greylisting}, for a detailed
explanation.
The function @code{greylist} can signal @code{e_dbfailure} exception.
@end deftypefn
@deftypefn {Built-in Function} boolean is_greylisted (string @var{key}
Returns @samp{True} if the @var{key} is still greylisted. If
@samp{true} is returned, the function also stores the number of
seconds left to the end of greylisting period in the global variable
@code{greylist_seconds_left}.
This function is available only if Con Tassios implementation of
greylisting is used. @xref{greylisting types}, for a discussion of
available greylisting implementations. @xref{greylist}, for a
way to switch to Con Tassios implementation.
@end deftypefn
@node Special test functions
@section Special Test Functions
@deftypefn {Library Function} boolean portprobe (string @var{host}, [number @var{port}])
@deftypefnx {Library Function} boolean listens (string @var{host}, [number @var{port}])
@flindex portprobe.mfl
Returns @code{true} if the @acronym{IP} address or host name given by
@var{host} argument listens on the port number @var{port} (default
25).
This function is defined in the module @file{portprobe}.
@end deftypefn
@anchor{validuser}
@deftypefn {Built-in Function} boolean validuser (string @var{name})
Returns @code{true} if authenticity of the user @var{name} is
confirmed using mailutils authentication system. @xref{Local Account
Verification}, for more details.
@end deftypefn
@anchor{valid_domain}
@deftypefn {Library Function} boolean valid_domain (string @var{domain})
Returns @code{true} if the domain name @var{domain} has a
corresponding A record or if it has any @samp{MX} records, i.e. if it
is possible to send mail to it.
@flindex valid_domain.mfl
To use this function, require the @file{valid_domain} module
(@pxref{Modules}):
@example
require valid_domain
@end example
@end deftypefn
@anchor{heloarg_test}
@deftypefn {Library Function} number heloarg_test (string @var{arg}, @
string @var{remote_ip}, string @var{local_ip})
@flindex heloarg_test.mfl
Verify if an argument of @samp{HELO} (@samp{EHLO}) command is
valid. To use this function, require the @file{heloarg_test} module
(@pxref{Modules}).
Arguments:
@table @var
@item arg
@samp{HELO} (@samp{EHLO}) argument. Typically, the value of @code{$s}
Sendmail macro;
@item remote_ip
@acronym{IP} address of the remote client. Typically, the value of
@code{$client_addr} Sendmail macro;
@item local_ip
@acronym{IP} address of this @acronym{SMTP} server;
@end table
The function returns a number describing the result of the test, as
described in the following table.
@multitable @columnfractions 0.4 0.6
@headitem Code @tab Meaning
@item HELO_SUCCESS @tab @var{arg} successfully passes all tests.
@item HELO_MYIP @tab @var{arg} is our @acronym{IP} address.
@item HELO_IPNOMATCH @tab @var{arg} is an @acronym{IP}, but it does not match
the remote party @acronym{IP} address.
@item HELO_ARGNORESOLVE @tab @var{arg} is an @acronym{IP}, but it does not resolve.
@item HELO_ARGNOIP @tab @var{arg} is in square brackets, but it is not an @acronym{IP}
address.
@item HELO_ARGINVALID @tab @var{arg} is not an @acronym{IP} address and does not
resolve to one.
@item HELO_MYSERVERIP @tab @var{arg} resolves to our server @acronym{IP}.
@item HELO_IPMISMATCH @tab @var{arg} does not resolve to the remote
client @acronym{IP} address.
@end multitable
@end deftypefn
@node Mail Sending Functions
@section Mail Sending Functions
The mail sending functions are new interfaces, introduced in
version 3.1.
The underlying mechanism for sending mail, called @dfn{mailer}, is
specified by @option{--mailer} command line option. This global
setting can be overridden using the last optional argument to a
particular function. In any case, the mailer is specified in the
form of a @acronym{URL}.
@cindex @acronym{URL}, mailer
@cindex mailer @acronym{URL}
@anchor{mailer url}
@dfn{Mailer @acronym{URL}} begins with a protocol specification.
Two protocol specifications are currently supported: @samp{sendmail}
and @samp{smtp}. The former means to use a
@command{sendmail}-compatible program to send mails. Such a program
must be able to read mail from its standard input and must support the
following options:
@table @option
@item -oi
Do not treat @samp{.} as message terminator.
@item -f @var{addr}
Use @var{addr} as the address of the sender.
@item -t
Get recipient addresses from the message.
@end table
These conditions are met by most existing @acronym{MTA} programs, such
as @command{exim} or @command{postfix} (to say nothing of
@command{sendmail} itself).
Following the protocol specification is the @dfn{mailer location}, which
is separated from it with a colon. For the @samp{sendmail} protocol,
the mailer location sets the full file name of the Sendmail-compatible
@acronym{MTA} binary, for example:
@example
sendmail:/usr/sbin/sendmail
@end example
A special form of a sendmail @acronym{URL}, consisting of protocol
specification only (@samp{sendmail:}) is also allowed. It means
``use the sendmail binary from the @code{_PATH_SENDMAIL}
macro in your @file{/usr/include/paths.h} file''. This is the default
mailer.
The @samp{smtp} protocol means to use an @acronym{SMTP} server directly.
In this case the mailer location consists of two slashes,
followed by the @acronym{IP} address or host name of the @acronym{SMTP}
server, and, optionally, the port number. If the port number is
present, it is separated from the rest of @acronym{URL} by a colon.
For example:
@example
@group
smtp://remote.server.net
smtp://remote.server.net:24
@end group
@end example
@deftypefn {Built-in Function} void send_mail (string @var{msg} @
[, string @var{to}, string @var{from}, string @var{mailer}])
Sends message @var{msg} to the email address @var{to}. The value of
@var{msg} must be a valid @acronym{RFC} 2822 message, consisting of
headers and body. Optional argument @var{to} can contain several email
addresses. In this case the message will be sent to each recipient
specified in @var{to}. If it is not specified, recipient addresses
will be obtained from the message headers.
Other optional arguments are:
@table @var
@item from
Sets the sender address. By default @samp{<>} is used.
@item mailer
The @acronym{URL} of the mailer to use
@end table
Sample usage:
@example
@group
set message <<- EOT
Subject: Test message
To: Postmaster <postmaster@@gnu.org.ua>
From: Mailfromd <devnull@@gnu.org.ua>
X-Agent: %__package__ (%__version__)
Dear postmaster,
This is to notify you that our /etc/mailfromd.mfl
needs a revision.
--
Mailfromd filter administrator
EOT
send_mail(message, "postmaster@@gnu.org.ua")
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} void send_text (string @var{text}, @
string @var{headers} [, string @var{to}, @
string @var{from}, string @var{mailer}])
A more complex interface to mail sending functions.
Mandatory arguments:
@table @var
@item text
Text of the message to be sent.
@item headers
Headers for the message.
@end table
Optional arguments:
@table @var
@item to
Recipient email addresses.
@item from
Sender email address.
@item mailer
@acronym{URL} of the mailer to use.
@end table
The above example can be rewritten using @code{send_text} as follows:
@example
@group
set headers << -EOT
Subject: Test message
To: Postmaster <postmaster@@gnu.org.ua>
From: Mailfromd <devnull@@gnu.org.ua>
X-Agent: %__package__ (%__version__)
EOT
set text <<- EOT
Dear postmaster,
This is to notify you that our /etc/mailfromd.mfl
needs a revision.
--
Mailfromd filter administrator
EOT
send_text(text, headers, "postmaster@@gnu.org.ua")
@end group
@end example
@end deftypefn
@deftypefn {Built-in Function} void send_message (number @var{msg} @
[string @var{to}, string @var{from}, string @var{mailer}])
Send the message identified by descriptor @var{msg} (@pxref{Message
functions}).
Optional arguments are:
@table @var
@item to
Recipient email addresses.
@item from
Sender email address.
@item mailer
@acronym{URL} of the mailer to use.
@end table
@end deftypefn
@deftypefn {Built-in Function} void send_dsn (string @var{to}, @
string @var{sender}, string @var{rcpt}, string @var{text} @
[, string @var{headers}, string @var{from}, string @var{mailer}])
This is an experimental interface which will change in the future
versions. It sends a message disposition notification (@acronym{RFC}
2298, @acronym{RFC} 1894), of type @samp{deleted} to the email address
@var{to}. Arguments are:
@table @var
@item to
Recipient email address.
@item sender
Original sender email address.
@item rcpt
Original recipient email address.
@item text
Notification text.
@end table
Optional arguments:
@table @var
@item headers
Message headers
@item from
Sender address.
@item mailer
@acronym{URL} of the mailer to use.
@end table
@end deftypefn
@deftypefn {Built-in Function} void create_dsn (string @var{sender}, @
string @var{rcpt}, string @var{text} @
[, string @var{headers}, string @var{from}])
Creates DSN message and returns its descriptor. Arguments are:
@table @var
@item sender
Original sender email address.
@item rcpt
Original recipient email address.
@item text
Notification text.
@item headers
Message headers
@item from
Sender address.
@end table
@end deftypefn
@node Blacklisting Functions
@section Blacklisting Functions
The functions described in this subsection allow to check whether the
given @acronym{IP} address is listed in certain @dfn{black list} @acronym{DNS}
zone.
@anchor{match_dnsbl}
@deftypefn {Library Function} boolean match_dnsbl (string @var{address}, @
string @var{zone}, string @var{range})
This function looks up @var{address} in the @acronym{DNS}
blacklist zone @var{zone} and checks if the return falls into the
given @var{range} of @acronym{IP} addresses.
It is intended as a replacement for the Sendmail macros @samp{dnsbl} and
@samp{enhdnsbl}.
@flindex match_dnsbl.mfl
To use @code{match_dnsbl}, require the @file{match_dnsbl} module
(@pxref{Modules}).
Arguments:
@table @var
@item address
@acronym{IP} address of the @acronym{SMTP} server to be tested.
@item zone
@acronym{FQDN} of the @acronym{DNS}bl zone to test against.
@item range
The range of @acronym{IP} addresses in @acronym{CIDR} notation or
the word @samp{ANY}, which stands for @samp{127.0.0.0/8}.
@end table
The function returns @code{true} if dns lookup for @var{address} in
the zone @var{dnsbl} yields an @acronym{IP} that falls within the range,
specified by @var{cidr}. Otherwise, it returns @code{false}.
This function raises the following exceptions: @code{e_invip} if
@var{address} is invalid and @code{e_invcidr} if @var{cidr} is invalid.
@end deftypefn
@anchor{match_rhsbl}
@deftypefn {Library Function} boolean match_rhsbl (string @var{email}, @
string @var{zone}, string @var{range})
This function checks if the @acronym{IP} address, corresponding to the domain
part of @var{email} is listed in the @acronym{RHS DNS} blacklist zone
@var{zone}, and if so, whether its record falls into the given range of
@acronym{IP} addresses @var{range}.
It is intended as a replacement for the Sendmail macro @samp{rhsbl}
by Derek J.@: Balling.
@flindex match_rhsbl.mfl
To use this function, require the @file{match_rhsbl} module (@pxref{Modules}).
Arguments:
@table @var
@item email
E-mail address, whose domain name should be tested (usually, it is
@code{$f})
@item zone
Domain name of the @acronym{RHS DNS} blacklist zone.
@item range
The range of @acronym{IP} addresses in @acronym{CIDR} notation.
@end table
@end deftypefn
@node SPF Functions
@section SPF Functions
@cindex @acronym{SPF}, defined
@cindex Sender Policy Framework, defined
@dfn{Sender Policy Framework}, or @acronym{SPF} for short, is an
extension to @acronym{SMTP} protocol that allows to identify forged
identities supplied with the @code{MAIL FROM} and @code{HELO}
commands. The framework is explained in detail in @acronym{RFC} 4408
(@uref{http://tools.ietf.org/html/rfc4408}) and on the
@uref{http://www.openspf.org/, SPF Project Site}. The following
description is a short introduction only, and the users are encouraged
to refer to the original specification for the detailed description of
the framework.
The domain holder publishes an @dfn{SPF record} -- a special
@acronym{DNS} resource record that contains a set of rules declaring
which hosts are, and are not, authorized to use a domain name for
@code{HELO} and @code{MAIL FROM} identities. This resource record is
usually of type @code{TXT}.@footnote{Although @acronym{RFC} 4408
introduces a special @code{SPF} record type for this purpose, it is
not yet widely used. As of version @value{VERSION}, @acronym{MFL}
does not support @code{SPF} @acronym{DNS} records.}
@cindex @acronym{SPF}, checking host record
@cindex checking @acronym{SPF} host records
@cindex @code{check_host} function, introduced
The @acronym{MFL} script can verify if the identity matches the
published @acronym{SPF} record by calling @code{check_host} function
and analyzing its return code. The function can be called either in
@code{helo} or in @code{envfrom} handler. Its arguments are:
@table @var
@item ip
The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is @code{$client_addr}.
@item domain
The domain that provides the sought-after authorization information;
Normally it is the domain portion of the @code{MAIL FROM} or
@code{HELO} identity.
@item sender
The @code{MAIL FROM} identity.
@item helo_domain
The @code{HELO} identity.
@item my_domain
The @acronym{SMTP} domain served by the local server.
@end table
The function returns a numeric result code. For convenience, all
possible return values are defined as macros in module @file{spf.mfl}.
The table below describes each value along with the recommended
actions for it:
@table @code
@cindex None, SPF result code
@item None
A result of @code{None} means that no records were published by the domain
or that no checkable sender domain could be determined from the given
identity. The checking software cannot ascertain whether or not the
client host is authorized. Such a message can be subject to
further checks that will decide about its fate.
@cindex Neutral, SPF result code
@item Neutral
The domain owner has explicitly stated that he cannot or does not
want to assert whether or not the @acronym{IP} address is authorized. This
result must be treated exactly like @code{None}; the distinction
between them exists only for informational purposes
@cindex Pass, SPF result code
@item Pass
The client is authorized to send mail with the given identity. The
message can be subject to further policy checks with confidence in the
legitimate use of the identity or it can be accepted in the absence of
such checks.
@cindex Fail, SPF result code
@item Fail
The client is not authorized to use the domain in the given
identity. The proper action in this case can be to mark the message
with a header explicitly stating it is spam, or to reject it
outright.
If you choose to reject such mails, we suggest to use @code{reject
550 5.7.1}, as recommended by @acronym{RFC} 4408. The reject can return either
a default explanation string, or the one supplied by the domain that
published the SPF records, as in the example below:
@example
reject 550 5.7.1 "SPF check failed:\n%spf_explanation"
@end example
@noindent
(for the description of @code{spf_explanation}, @pxref{spf_explanation})
@cindex SoftFail, SPF result code
@item SoftFail
The domain believes the host is not authorized but is not willing to
make that strong of a statement. This result code should be treated
as somewhere in between a @code{Fail} and a @code{Neutral}. It is not
recommended to reject the message based solely on this result.
@item TempError
A transient error occurred while performing @acronym{SPF} check. The
proper action in this case is to accept or temporarily reject the
message. If you choose the latter, we suggest to use @acronym{SMTP}
reply code of @samp{451} and DSN code @samp{4.4.3}, for example:
@example
tempfail 451 4.4.3
"Transient error while performing SPF verification"
@end example
@item PermError
This result means that the domain's published records could not be
correctly interpreted. This signals an error condition that requires
manual intervention to be resolved, as opposed to the @code{TempError}
result.
@end table
The following example illustrates the use of @acronym{SPF}
verification in @code{envfrom} handler:
@example
require 'status'
require 'spf'
prog envfrom
do
switch check_host($client_addr, domainpart($f), $f, $s)
do
case Fail:
string text ""
if spf_explanation != ""
set text "%text\n%spf_explanation"
fi
reject 550 5.7.1 "SPF MAIL FROM check failed: %text"
case Pass:
accept
case TempError:
tempfail 451 4.4.3
"Transient error while performing SPF verification"
default:
on poll $f do
when success:
accept
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
tempfail 450 4.7.0 "Temporary failure during sender verification"
done
done
done
@end example
The @acronym{SPF} support is implemented in @acronym{MFL} in two
layers: a built-in layer that provides basic support, and a library
layer that provides a convenience wrapper over the library function.
@flindex spf.mfl
The library layer is implemented in the module @file{spf.mfl}
(@pxref{Modules}).
The rest of this node describes available @acronym{SPF} functions
and variables.
@deftypefn {Built-in Function} number spf_check_host (string @var{ip}, @
string @var{domain}, string @var{sender}, string @var{helo_domain}, @
string @var{my_domain})
This function is the basic implementation of the @code{check_host}
function, defined in @acronym{RFC} 4408, chapter 4. It fetches @acronym{SPF}
records, parses them, and evaluates them to determine whether a
particular host (@var{ip}) is or is not permitted to send mail from a
given email address (@var{sender}). The function returns an @dfn{SPF
result code}.
Arguments are:
@table @var
@item ip
The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is @code{$client_addr}.
@item domain
The domain that provides the sought-after authorization information;
Normally it is the domain portion of the @code{MAIL FROM} or
@code{HELO} identity.
@item sender
The @code{MAIL FROM} identity.
@item helo_domain
The @code{HELO} identity.
@item my_domain
The @acronym{SMTP} domain served by the local server.
@end table
@anchor{spf-globals}
Before returning the @code{spf_check_host} function stores
additional information in global variables:
@table @code
@item spf_explanation
@anchor{spf_explanation}
If the result code is @code{Fail}, this variable contains the
explanation string as returned by the publishing domain, prefixed with
the value of the global variable @code{spf_explanation_prefix}.
For example, if @code{spf_explanation_prefix} contains @samp{The
domain %@{o@} explains: }, and the publishing domain
@samp{example.com} returns the explanation string @samp{Please see
http://www.example.com/mailpolicy.html}, than the value of
@code{spf_explanation} will be:
@example
@group
The domain example.com explains:
Please see http://www.example.com/mailpolicy.html
@end group
@end example
(see @uref{http://tools.ietf.org/html/rfc4408, @acronym{RFC} 4408}, chapter 8,
for the description of @acronym{SPF} macro facility).
@item spf_mechanism
Name of the @acronym{SPF} mechanism that decided about the result
code of the @acronym{SPF} record. If one or more @samp{include} or
@samp{redirect} mechanisms were traversed before arriving at that
mechanism, their values are appended in the reverse order.
@end table
@end deftypefn
@deftypefn {Built-in Function} number spf_test_record (string @var{record}, @
string @var{ip}, string @var{domain}, string @var{sender}, @
string @var{helo_domain}, string @var{my_domain})
Evaluate @acronym{SPF} record @var{record} as if it were published
by @var{domain}. The rest of arguments are the same as for
@code{spf_check_host} above.
This function is designed primarily for testing and debugging
purposes. You would hardly need to use it.
The @code{spf_test_record} function sets the same global variables
as @code{spf_check_host}.
@end deftypefn
@deftypefn {Library Function} number check_host (string @var{ip}, @
string @var{domain}, string @var{sender}, string @var{helo})
This function implements the @code{check_host} function, defined in
@acronym{RFC} 4408, chapter 4. It fetches @acronym{SPF} records, parses them,
and evaluates them to determine whether a particular host (@var{ip})
is or is not permitted to send mail from a given email address
(@var{sender}). The function returns an @dfn{SPF result code}.
This function is a wrapper over the built-in @code{spf_check_host}.
The arguments are:
@table @var
@item ip
The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is the same as the value of @code{$client_addr}.
@item domain
The domain that provides the sought-after authorization information;
Normally it is the domain portion of the @code{MAIL FROM} or
@code{HELO} identity.
@item sender
The @code{MAIL FROM} identity.
@item helo
The @code{HELO} identity.
@end table
@end deftypefn
@deftypefn {Library Function} string spf_status_string (number @var{code})
Converts numeric @acronym{SPF} result @var{code} to its string
representation.
@end deftypefn
@deftypevr {Built-in variable} string spf_explanation
If @code{check_host} (or @code{spf_check_host} or
@code{spf_test_record}) returned @code{Fail}, this variable contains the
explanation string as returned by the publishing domain, prefixed with
the value of the global variable @code{spf_explanation_prefix}.
For example, if @code{spf_explanation_prefix} contains @samp{The
domain %@{o@} explains: }, and the publishing domain
@samp{example.com} returns the explanation string @samp{Please see
http://www.example.com/mailpolicy.html}, than the value of
@code{spf_explanation} will be:
@example
@group
The domain example.com explains:
Please see http://www.example.com/mailpolicy.html
@end group
@end example
@end deftypevr
@deftypevr {Built-in variable} string spf_mechanism
Set to the name of a @acronym{SPF} mechanism that decided about the
result code of the @acronym{SPF} record.
@end deftypevr
@deftypevr {Built-in variable} string spf_explanation_prefix
The prefix to be appended to the explanation string before storing
it in the @code{spf_explanation} variable. This string can contain
valid @acronym{SPF} macros (see
@uref{http://tools.ietf.org/html/rfc4408, @acronym{RFC} 4408}, chapter 8), for
example:
@example
set spf_explanation_prefix "%@{o@} explains: "
@end example
The default value is @samp{""} (an empty string).
@end deftypevr
@node DKIM
@section DKIM
@cindex @acronym{DKIM}, defined
@cindex DomainKeys Identified Mail
@acronym{DKIM} or @dfn{DomainKeys Identified Mail} is an email
authentication method that allows recipients to verify if an email was
authorized by the owner of the domain that email claims to originate
from. It does so by adding a digital signature which is verified
using a public key published as a DNS TXT record. For technical
details about @acronym{DKIM}, please refer to RFC 6376
(@uref{http://tools.ietf.org/html/rfc6376}).
MFL provides functions for DKIM signing and verification.
@deftypefn {Built-in Function} number dkim_verify (number @var{msg})
Verifies the message @var{msg} (a message descriptor, obtained from
a call to @code{current_message}, @code{mailbox_get_message},
@code{message_from_stream} or a similar function).
Return value (constants defined in the @samp{status} module):
@defvr {dkim_verify status} DKIM_VERIFY_OK
The message contains one or more @samp{DKIM-Signature} headers and
one of them verified successfully.
@end defvr
@defvr {dkim_verify status} DKIM_VERIFY_PERMFAIL
The message contains one or more @samp{DKIM-Signature} headers, all
of which failed to verify.
@end defvr
@defvr {dkim_verify status} DKIM_VERIFY_TEMPFAIL
The message was not signed using DKIM, or the DNS query to obtain the
public key failed, or an internal software error occurred during
verification.
@end defvr
The following two global variables are always set upon return from this
function: @code{dkim_explanation} and @code{dkim_explanation_code}.
These can be used to clarify the verification result to the end user.
The variable @code{dkim_signing_algorithm} is initialized with the
name of the algorithm used to sign the message.
Upon successful return, the variable @code{dkim_verified_signature} is
set to the value of the successfully verified DKIM signature.
@end deftypefn
@deftypevr {Built-in variable} string dkim_signing_algorithm
Name of the algorithm used to sign the message (either @samp{rsa-sha1}
or @samp{rsa-sha256}). If the algorithm was not specified (e.g. the
signature is malformed), this variable is assigned an empty value.
@end deftypevr
@deftypevr {Built-in variable} string dkim_explanation
An explanatory message clarifying the verification result.
@end deftypevr
@deftypevr {Built-in variable} number dkim_explanation_code
A numeric code corresponding to the @samp{dkim_explanation} string.
Its possible values are defined in @samp{status.mfl}:
@defvr {DKIM explanation code} DKIM_EXPL_OK
@samp{DKIM verification passed}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_OK
@samp{DKIM verification passed}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_NO_SIG
@samp{No DKIM signature}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_INTERNAL_ERROR
@samp{internal error}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_SIG_SYNTAX
@samp{signature syntax error}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_SIG_MISS
@samp{signature is missing required tag}
According to the DKIM specification, required tags are: @code{a=},
@code{b=}, @code{bh=}, @code{d=}, @code{h=}, @code{s=}, @code{v=}.
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_DOMAIN_MISMATCH
@samp{domain mismatch}
The domain part of the @code{i=} tag does not match and is not a
subdomain of the domain listed in the @code{d=} tag.
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_BAD_VERSION
@samp{incompatible version}
Incompatible DKIM version listed in the @code{v=} tag.
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_BAD_ALGORITHM
@samp{unsupported signing algorithm}
Either the @code{a=} tag of the DKIM signature contains an unsupported
algorithm (currently supported algorithms are: @samp{rsa-sha1} and
@samp{rsa-sha256}) or this algorithm, while being supported by
@command{mailfromd}, is not listed in the @code{h=} tag of the public
DKIM key.
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_BAD_QUERY
@samp{unsupported query method}
The @code{q=} tag of the public DKIM key contains something other than
@samp{dns/txt}.
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_FROM
@samp{From field not signed}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_EXPIRED
@samp{signature expired}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_DNS_UNAVAIL
@samp{public key unavailable}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_DNS_NOTFOUND
@samp{public key not found}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_KEY_SYNTAX
@samp{key syntax error}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_KEY_REVOKED
@samp{key revoked}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_BAD_BODY
@samp{body hash did not verify}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_BAD_BASE64
@samp{can't decode b= tag}
Base64 decoding of the @code{b=} tag failed.
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_BAD_SIG
@samp{signature did not verify}
@end defvr
@defvr {DKIM explanation code} DKIM_EXPL_BAD_KEY_TYPE
@samp{unsupported public key type}
The @code{k=} tag of the public DKIM signature contains a value, other
than @samp{rsa}.
@end defvr
@end deftypevr
@deftypevr {Built-in variable} string dkim_verified_signature
Upon successful return from the @code{dkim_verify} function, this
variable holds the value of the successfully verified DKIM header.
This value is unfolded and all whitespace is removed from it.
@end deftypevr
An example of using the @samp{dkim_verify} function:
@example
@group
require status
require dkim
prog eom
do
string result
switch dkim_verify(current_message())
do
case DKIM_VERIFY_OK:
set result "pass; verified for " .
dkim_verified_signature_tag('i')
case DKIM_VERIFY_PERMFAIL:
set result "fail (%dkim_explanation)"
case DKIM_VERIFY_TEMPFAIL:
set result "neutral"
done
header_add("X-Verification-Result", "dkim=%result")
done
@end group
@end example
@cindex dkim, module
The @samp{dkim} module defines convenience functions for manipulating
with DKIM signatures:
@deftypefn {Library Function} dkim_signature_tag (string @var{sig}, string @var{tag})
Extracts the value of the tag @var{tag} from the DKIM signature
@var{sig}. Signature must be normalized by performing the header
unwrapping and removing whitespace characters.
If the tag was not found, returns empty string, unless @var{tag} is
one of the tags listed in the table below. If any of these tags are
absent, the following values are returned instead:
@multitable @columnfractions 0.3 0.6
@headitem Tag @tab Default value
@item c @tab @samp{simple/simple}
@item q @tab @samp{dns/txt}
@item i @tab @samp{@@} + the value of the @samp{d} tag.
@end multitable
@end deftypefn
@deftypefn {Library Function} string dkim_verified_signature_tag (string @var{tag})
Returns the value of tag @var{tag} from the
@samp{dkim_verified_signature} variable.
@end deftypefn
@deftypefn {Built-in Function} void dkim_sign (string @var{d}, @
string @var{s}, string @var{keyfile}, [ string @var{ch}, @
string @var{cb}, string @var{headers}, string @var{algo} ])
This function is available only in the @code{eom} handler.
Signs the current message. @emph{Notice}, that no other modification
should be attempted on the message after calling this function. Doing
so would make the signature invalid.
Mandatory arguments:
@table @var
@item d
Name of the domain claiming responsibility for an introduction of a message
into the mail stream. It is also known as the signing domain
identifier (@acronym{SDID}).
@item s
The selector name. This value, along with @var{d} identifies the
location of the DKIM public key necessary for verifying the message.
The public key is stored in the DNS TXT record for
@example
@var{s}._domainkey.@var{d}
@end example
@item keyfile
Name of the disk file that keeps the private key for signing the
message. The file must be in PKCS#1 or PKCS#8 format (PEM formatted).
@end table
Optional arguments:
@table @var
@item ch
Canonicalization algorithm for message headers. Valid values are:
@samp{simple} and @samp{relaxed}. @samp{simple} is the default.
@item cb
Canonicalization algorithm for message body. Valid and default values
are the same as for @var{ch}.
@item headers
A colon-separated list of header field names that identify the
header fields that must be signed. Optional whitespace is allowed at
either side of each colon separator. Header names are
case-insensitive. This list must contain at least the @samp{From}
header.
It may contain names of headers that are not present in the message
being signed. This provides a way to explicitly assert the absence of
a header field. For example, if @var{headers} contained
@samp{X-Mailer} and that header is not present in the message being
signed, but is added by a third party later, the signature
verification will fail.
Similarly, listing a header field name once more than the actual
number of its occurrences in a message allows you to prevent any
further additions. For example, if there is a single @samp{Comments}
header field at the time of signing, putting @samp{Comments:Comments:}
in the @var{headers} parameter is sufficient to prevent any surplus
@samp{Comments} headers from being added later on.
Multiple instances of the same header name are allowed.
They mean that multiple occurrences of the corresponding header field
will be included in the header hash. When such multiple header
occurrences are referenced, they will be presented to the hashing
algorithm in the reverse order. E.g. if the @var{header} list
contained @samp{Received:Received}) and the current message contained
three @samp{Received} headers:
@example
Received: A
Received: B
Received: C
@end example
@noindent
then these headers will be signed in the following order:
@example
Received: C
Received: B
@end example
The default value for this parameter, split over multiple lines for
readability, is as follows:
@itemize @bullet
@item "From:From:"
@item "Reply-To:Reply-To:"
@item "Subject:Subject:"
@item "Date:Date:"
@item "To:"
@item "Cc:"
@item "Resent-Date:"
@item "Resent-From:"
@item "Resent-To:"
@item "Resent-Cc:"
@item "In-Reply-To:"
@item "References:"
@item "List-Id:"
@item "List-Help:"
@item "List-Unsubscribe:"
@item "List-Subscribe:"
@item "List-Post:"
@item "List-Owner:"
@item "List-Archive"
@end itemize
@item algo
Signing algorithm: either @samp{rsa-sha256} or @samp{rsa-sha1}.
Default is @samp{rsa-sha256}.
@end table
@end deftypefn
An example of using this function:
@example
@group
precious string domain "example.org"
precious string selector "s2048"
prog eom
do
dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem",
"relaxed", "relaxed", "from:to:subject")
done
@end group
@end example
@menu
* Setting up a DKIM record::
@end menu
@anchor{dkim_sign and sendmail}
@subheading Note on interaction of dkim_sign with Sendmail
When sending a signed message, it is critical that no other
modifications be applied to the message after it has been signed.
Unfortunately, it is not always the case when @command{mailfromd} is
used with @command{Sendmail}. Before sending the message over SMTP,
@command{Sendmail} reformats the headers that contain a list of email
addresses, by applying to them a procedure called in its parlance
@dfn{commaization}. The following headers are modified:
@code{Apparently-To}, @code{Bcc}, @code{Cc},
@code{Disposition-Notification-To}, @code{Errors-To}, @code{From},
@code{Reply-To}, @code{Resent-Bcc}, @code{Resent-Cc},
@code{Resent-From}, @code{Resent-Reply-To}, @code{Resent-Sender},
@code{Resent-To}, @code{Sender}, @code{To}. Thus, if your
@code{dkim_sign} includes any of these in the signature (which is the
default) and some of them happen to be formatted other way than the
one @command{Sendmail} prefers, the DKIM signature would not verify on
the recipient side. To prevent this from happening, @code{dkim_sign}
mimics the Sendmail behavior and reformats those headers before
signing the message. This should ensure that the message signed and
the message actually sent are the same. This default behavior is
controlled by the following global variable:
@deftypevr {Built-in variable} number dkim_sendmail_commaize
``Commaize'' the address headers (see the list above) of the message
the same way Sendmail does, and then sign the resulting message.
The default value is 1 (@code{true}). You can set it to 0
(@code{false}) if this behavior is not what you want (e.g. if you are
using @code{postfix} or some other MTA).
@end deftypevr
@anchor{MMQ and dkim_sign}
@subheading Note on interaction of dkim_sign with MMQ
The functions @code{header_add} and @code{header_insert}
(@pxref{Header modification functions}) as well as the @code{add}
action (@pxref{header manipulation}) cannot interact properly with
@code{dkim_sign} due to the shortcomings of the Milter API. If any of
these was called, @code{dkim_sign} will throw the @code{e_badmmq}
exception with the following diagnostics:
@example
MMQ incompatible with dkim_sign: @var{op} on @var{h}, value @var{v}
@end example
@noindent
where @var{op} is the operation code (@samp{ADD HEADER} or
@samp{INSERT HEADER}), @var{h} is the header name and @var{v} is its
value.
The following example shows one graceful way of handling such
exception:
@example
@group
prog eom
do
try
do
dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem")
done
catch e_badmmq
do
# Purge the message modification queue
mmq_purge()
# and retry
dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem")
done
done
@end group
@end example
@xref{Message modification queue}, for a discussion of the message
modification queue.
@node Setting up a DKIM record
@subsection Setting up a DKIM record
@cindex DKIM, setting up
Follow these steps to set up your own DKIM record:
@enumerate 1
@item Generate a key pair:
Use the @command{openssl genrsa} command. Run:
@example
openssl genrsa -out private.pem 2048
@end example
@noindent
The last argument is the size of the private key to generate in bits.
@item Extract the public key:
@example
openssl rsa -in private.pem -pubout -outform PEM -out public.pem
@end example
@item Set up a DKIM record in your domain:
A DKIM record is a TXT type DNS record that holds the public key part
for verifying messages. Its format is defined in RFC
4871@footnote{@uref{https://tools.ietf.org/html/rfc4871}}. The label
for this record is composed as follows:
@example
@var{s}._domainkey.@var{d}
@end example
@noindent
where @var{d} is your domain name, and @var{s} is the selector you
chose to use. You will use these two values as parameters to the
@code{dkim_sign} function in your @code{eom} handler. E.g. if your
domain in @samp{example.com} and selector is @samp{s2048}, then the
DKIM TXT record label is @samp{s2048._domainkey.example.com}.
The public key file generated in step 2 will have the following
contents:
@example
-----BEGIN PUBLIC KEY-----
@var{base64}
-----END PUBLIC KEY-----
@end example
@noindent
where @var{base64} is the key itself in base64 encoding. The minimal
DKIM TXT record will be:
@example
"v=DKIM1; p=@var{base64}"
@end example
The only mandatory @dfn{tag} is in fact @samp{p=}. The use of
@samp{v=} is recommended. More tags can be added as needed. In
particular, while testing the DKIM support, it is advisable to add the
@samp{t=y} tag.
@end enumerate
@node Sockmaps
@section Sockmap Functions
@cindex socket map
Socket map (@dfn{sockmap} for short) is a special type of
database used in Sendmail and MeTA1. It uses a simple server/client protocol
over @acronym{INET} or @acronym{UNIX} stream sockets. The server
listens on a socket for queries. The client connects to the
server and sends it a query, consisting of a @dfn{map name} and
a @dfn{key} separated by a single space. Both map name and key
are sequences of non-whitespace characters. The map name serves to
identify the type of the query. The server replies with a
response consisting of a @dfn{status indicator} and @dfn{result},
separated by a single space. The result part is optional.
For example, following is the query for key @samp{smith} in map @samp{aliases}:
@example
11:aliases news,
@end example
A possible reply is:
@example
18:OK root@@domain.net,
@end example
This reply means that the key @samp{news} was found in the map, and
the value corresponding to that key is @samp{root@@domain.net}.
The following reply means the key was not found:
@example
8:NOTFOUND,
@end example
For a detailed description of the sockmap protocol, see
@ref{Protocol,,, smap, Smap manual}.
@flindex sockmap.mfl
The @acronym{MFL} library provides two primitives for dealing with
sockmaps. Both primitives become available after requiring the
@file{sockmap} module.
@deftypefn {Library Function} string sockmap_lookup (number @var{fd}, @
string @var{map}, string @var{key})
This function look ups the @var{key} in the @var{map}. The @var{fd}
refers to the sockmap to use. It must be obtained as a result of a
previous call to @code{open} with the @acronym{URL} of the sockmap as
its first argument (@pxref{I/O functions, open}). For example:
@example
number fd open("@@ unix:///var/spool/meta1/smap/socket")
string ret sockmap_query(fd, "aliases", $rcpt_to)
if ret matches "OK (.+)"
set alias \1
fi
close(fd)
@end example
@end deftypefn
@deftypefn {Library Function} string sockmap_single_lookup (string @var{url}, @
string @var{map}, string @var{key})
This function connects to the sockmap identified by the @var{url},
queries for @var{key} in @var{map} and closes the connection. It is
useful when you need to perform only a single lookup on the sockmap.
@end deftypefn
@node NLS Functions
@section National Language Support Functions
@cindex @acronym{NLS}
@cindex National Language Support
The @dfn{National Language Support} functions allow you to write
your scripts in such a way, that any textual messages they display are
automatically translated to your native language, or, more precisely,
to the language required by your current locale.
@cindex internationalization
@cindex i18n
@cindex localization
@cindex l10n
This section assumes the reader is familiar with the concepts of program
@dfn{internationalization} and @dfn{localization}. If not, please
refer to @ref{Why, The Purpose of GNU @command{gettext}, The Purpose of GNU
@command{gettext}, gettext, GNU gettext manual}, before reading further.
@flindex nls.mfl
In general, internationalization of any @acronym{MFL} script
follows the same rules as described in the @cite{GNU gettext manual}.
First of all, you select the program @dfn{message domain}, i.e. the
identifier of a set of translatable messages your script contain.
This identifier is then used to select appropriate translation.
The message domain is set using @code{textdomain} function. For the
purposes of this section, let's suppose the domain name is
@samp{myfilter}. All @acronym{NLS} functions are provided in the
@file{nls} module, which you need to require prior to using
any of them.
To find translations of textual message to the current locale, the
underlying @command{gettext} mechanism will look for file
@file{@var{dirname}/@var{locale}/LC_MESSAGES/@var{domainname}.mo},
where @var{dirname} is the message catalog hierarchy name,
@var{locale} is the locale name, and @var{domainname} is the name of
the message domain. By default @var{dirname} is
@file{/usr/local/share/locale}, but you may change it using
@code{bindtextdomain} function. The right place for this initial
@acronym{NLS} setup is in the @samp{begin} block (@pxref{begin/end}).
To summarize all the above, the usual @acronym{NLS} setup will look like:
@example
require nls
begin
do
textdomain("myfilter")
bindtextdomain("myfilter", "/usr/share/locale");
done
@end example
For example, given the settings above, and supposing the environment
variable @env{LC_ALL} is set to @samp{pl}, translations will be looked
in file @file{/usr/share/locale/pl/LC_MESSAGES/myfilter.mo}.
Once this preparatory work is done, you can request each message to
be translated by using @code{gettext} function, or @code{_}
(underscore) macro. For example, the following statement will produce
translated textual description for @samp{450} response:
@example
tempfail 450 4.1.0 _("Try again later")
@end example
Of course it assumes that the appropriate @file{myfile.mo} file
already exists. If it does not, nothing bad happens: in this case the
macro @command{_} (as well as @code{gettext} function) will simply
return its argument unchanged, so that the remote party will get the
textual message in English.
The @samp{mo} files are binary files created from @samp{po} source
files using @command{msgfmt} utility, as described in @ref{Binaries,
Producing Binary MO Files, Producing Binary MO Files, gettext, GNU
gettext manual}. In turn, the format of @samp{po} files is described
in @ref{PO Files, The Format of PO Files, The Format of PO Files,
gettext, GNU gettext manual}.
@deftypefn {Built-in Function} string bindtextdomain (string @var{domain}, @
string @var{dirname})
This function sets the base directory of the hierarchy containing
message catalogs for a given message domain.
@var{domain} is a string identifying the textual domain. If
it is not empty, the base directory for message catalogs belonging to
domain @var{domain} is set to @var{dirname}. It is important that
@var{dirname} be an absolute pathname; otherwise it cannot be
guaranteed that the message catalogs will be found.
If @var{domain} is @samp{""}, @code{bindtextdomain} returns the
previously set base directory for domain @var{domain}.
@end deftypefn
The rest of this section describes the @acronym{NLS} functions
supplied in the @file{nls} module.
@deftypefn {Built-in Function} string dgettext (string @var{domain}, @
string @var{msgid})
@code{dgettext} attempts to translate the string @var{msgid} into the
currently active locale, according to the settings of the textual
domain @var{domain}. If there is no translation available,
@code{dgettext} returns @var{msgid} unchanged.
@end deftypefn
@deftypefn {Built-in Function} string dngettext @
(string @var{domain}, string @var{msgid}, string @var{msgid_plural}, @
number @var{n})
The @code{dngettext} functions attempts to translate a text string
into the language specified by the current locale, by looking up the
appropriate singular or plural form of the translation in a message
catalog, set for the textual domain @var{domain}.
@xref{Plural forms, Additional functions for plural forms,
Additional functions for plural forms, gettext, GNU gettext
utilities}, for a discussion of the plural form handling in
different languages.
@end deftypefn
@deftypefn {Library Function} string textdomain (string @var{domain})
The @code{textdomain} function sets the current message domain to
@var{domain}, if it is not empty. In any case the function returns
the current message domain. The current domain is @samp{mailfromd}
initially. For example, the following sequence of @code{textdomain}
invocations will yield:
@example
textdomain("") @result{} "mailfromd"
textdomain("myfilter") @result{} "myfilter"
textdomain("") @result{} "myfilter"
@end example
@end deftypefn
@deftypefn {Library Function} string gettext (string @var{msgid})
@code{gettext} attempts to translate the string @var{msgid} into the
currently active locale, according to the settings of the current textual
domain (set using @code{textdomain} function). If there is no
translation available, @code{gettext} returns @var{msgid} unchanged.
@end deftypefn
@deftypefn {Library Function} string ngettext @
(string @var{msgid}, string @var{msgid_plural}, number @var{n})
The @code{ngettext} functions attempts to translate a text string
into the language specified by the current locale, by looking up the
appropriate singular or plural form of the translation in a message
catalog, set for the current textual domain.
@xref{Plural forms, Additional functions for plural forms,
Additional functions for plural forms, gettext, GNU gettext
utilities}, for a discussion of the plural form handling in
different languages.
@end deftypefn
@node Syslog Interface
@section Syslog Interface
The basic means for outputting diagnostic messages is the
@samp{echo} instruction (@pxref{Echo}), which sends its arguments to
the currently established logging channel. In daemon mode, the latter
is normally connected to syslog, so any echoed messages are sent there
with the facility selected in mailfromd configuration and priority
@samp{info}.
If you want to send a message to another facility and/or priority,
use the @samp{syslog} function:
@deftypefn {Built-in Function} void syslog (number @var{priority}, string @var{text})
Sends @var{text} to syslog. The priority argument is formed by
ORing the facility and the level values (explained below). The
facility level is optional. If not supplied, the currently selected
logging facility is used.
@end deftypefn
@findex syslog.mfl
The facility specifies what type of program is logging the message,
and the level indicates its relative severity. The following symbolic
facility values are declared in the @file{syslog} module:
@samp{LOG_KERN}, @samp{LOG_USER}, @samp{LOG_MAIL}, @samp{LOG_DAEMON},
@samp{LOG_AUTH}, @samp{LOG_SYSLOG}, @samp{LOG_LPR}, @samp{LOG_NEWS},
@samp{LOG_UUCP}, @samp{LOG_CRON}, @samp{LOG_AUTHPRIV}, @samp{LOG_FTP}
and @samp{LOG_LOCAL0} through @samp{LOG_LOCAL7}
The declared severity levels are: @samp{LOG_EMERG}, @samp{LOG_ALERT},
@samp{LOG_CRIT}, @samp{LOG_ERR}, @samp{LOG_WARNING},
@samp{LOG_NOTICE}, @samp{LOG_INFO} and @samp{LOG_DEBUG}.
@node Debugging Functions
@section Debugging Functions
These functions are designed for debugging the MFL programs.
@deftypefn {Built-in Function} void debug (string @var{spec})
@kwindex mailutils_set_debug_level
Enable debugging. The value of @var{spec} sets the debugging level.
@xref{debugging level specification}, for a description of its format.
For compatibility with previous versions, this function is also
available under the name @samp{mailutils_set_debug_level}.
@end deftypefn
@deftypefn {Built-in Function} number debug_level ([string @var{srcname}])
This function returns the debugging level currently in effect for the source
module @var{srcname}, or the global debugging level, if called without
arguments.
For example, if the program was started with
@option{--debug='all.trace5;@/engine.trace8'} option, then:
@example
debug_level() @result{} 127
debug_level("engine") @result{} 1023
debug_level("db") @result{} 0
@end example
@end deftypefn
@deftypefn {Built-in Function} boolean callout_transcript ([boolean @var{value}])
Returns the current state of the callout SMTP transcript. The result
is 1 if the transcript is enabled and 0 otherwise. The transcript is
normally enabled either by the use of the @option{--transcript}
command line option (@pxref{SMTP transcript}) or via the
@samp{transcript} configuration statement (@pxref{conf-server,
transcript}).
The optional @var{value}, supplies the new state for SMTP transcript.
Thus, calling @samp{callout_transcript(0)} disables the transcript.
This function can be used in bracket-like fashion to enable transcript
for a certain part of MFL program, e.g.:
@example
number xstate callout_transcript(1)
on poll $f do
@dots{}
done
set xstate callout_transcript(0)
@end example
Note, that the use of this function (as well as the use of the
@option{--transcript} option) makes sense only if callouts are
performed by the @command{mailfromd} daemon itself. It will not
work if a dedicated callout server is used for that purpose
(@pxref{calloutd}).
@end deftypefn
@anchor{debug_spec}
@deftypefn {Built-in Function} string debug_spec ([string @var{catnames}, @
bool @var{showunset}])
Returns the current debugging level specification, as given by
@option{--debug} command line option or by the @code{debug} configuration
statement (@pxref{conf-debug}).
If the argument @var{srcnames} is specified, it is treated as a
semicolon-separated list of categories for which the debugging
specification is to be returned.
For example, if @command{mailfromd} was started with
@option{--debug=all.trace5;@/spf.trace1;@/engine.trace8;@/db.trace0}, then:
@example
debug_spec() @result{} "all.trace5,engine.trace8"
debug_spec("all;engine") @result{} "all.trace5,engine.trace8"
debug_spec("engine;db") @result{} "db.trace0;engine.trace8"
debug_spec("prog") @result{} ""
@end example
@noindent
When called without arguments, @code{debug_spec} returns only
those categories which have been set, as shown in the first example
above.
Optional @var{showunset} parameters controls whether to return unset
module specifications. To print all debugging specifications, whether
set or not, use
@example
debug_spec("", 1)
@end example
@end deftypefn
These three functions are intended to complement each other. The
calls to @code{debug} can be placed around some piece of code you wish
to debug, to enable specific debugging information for this code
fragment only. For example:
@example
@group
/* @r{Save debugging level for @file{dns.c} source} */
set dlev debug_spec("dns", 1)
/* @r{Set new debugging level} */
debug("dns.trace8")
.
.
.
/* @r{Restore previous level} */
debug(dlev)
@end group
@end example
@deftypefn {Built-in Function} void program_trace (string @var{module})
Enable tracing for a set of modules given in @var{module} argument.
@xref{--trace-program}, for a description of its format.
@end deftypefn
@deftypefn {Built-in Function} void cancel_program_trace (string @var{module})
Disable tracing for given modules.
@end deftypefn
This pair of functions is also designed to be used together in
a bracket-like fashion. They are useful for debugging
@command{mailfromd}, but are not advised to use otherwise, since
tracing slows down the execution considerably.
@deftypefn {Built-in Function} void stack_trace ()
Generate a stack trace in this point. @xref{tracing runtime errors},
for the detailed description of stack traces.
@end deftypefn
@deftypefn {Built-in Function} string stack_trace_format (string @var{fmt})
Format each frame in stack trace to a string according to @var{fmt},
and return the concatenated strings. The format string @var{fmt}
consists of regular characters, which are copied to the resulting
string verbatim, and @dfn{conversion specifiers}, prefixed with
percent sign, which expand to parts of stack trace. The following
conversion specifiers are defined:
@table @code
@item %d
Frame number, counting from the innermost (last) frame, 0-based.
@item %f
Name of the function.
@item %n
Line number in the source file.
@item %s
Name of the source file.
@item %p
Value of the PC register.
@item %%
Single percent sign.
@end table
A @samp{%} followed by any other letter is reproduced verbatim.
@end deftypefn
@deftypefn {Built-in Function} string stack_trace_top (string @var{fmt})
Format the top-level stack frame according to @var{fmt}. See above
for a discussion of format string.
@end deftypefn
The functions below are intended mainly for debugging MFL run-time
engine and for use in @command{mailfromd} testsuite. You will hardly
need to use them in your programs.
@deftypefn {Built-in Function} void _expand_dataseg (number @var{n})
Expands the run-time data segment by at least @var{n} words.
@end deftypefn
@deftypefn {Built-in Function} number _reg (number @var{r})
@flindex _register
Returns the value of the register @var{r} at the moment of the call.
Symbolic names for run-time registers are provided in the module
@file{_register}:
@multitable @columnfractions 0.3 0.7
@headitem Name @tab Register
@item REG_PC @tab Program counter
@item REG_TOS @tab Top of stack
@item REG_TOH @tab Top of heap
@item REG_BASE @tab Frame base
@item REG_REG @tab General-purpose accumulator
@item REG_MATCHSTR @tab Last matched string pointer
@end multitable
@end deftypefn
@deftypefn {Built-in Function} number _stack_free ()
Returns number of words available for use in stack. This is the same
as
@example
_reg(REG_TOS) - _reg(REG_TOH)
@end example
@end deftypefn
@deftypefn {Built-in Function} number _heap_reserve (number @var{n})
Use up next @var{n} words in the heap. Return the address of the
first word.
@end deftypefn
@deftypefn {Built-in Function} void _wd ([number @var{n}])
Enters a time-consuming loop and waits there for @var{n} seconds (by
default -- indefinitely). The intention is to facilitate attaching
to @command{mailfromd} with the debugger. Before entering the loop,
a diagnostic message is printed on the @samp{crit} facility, informing
about the PID of the process and suggesting the command to be used to
attach to it, e.g.:
@example
mailfromd: process 21831 is waiting for debug
mailfromd: to attach: gdb -ex 'set variable mu_wd::_count_down=0'
/usr/sbib/mailfromd 21831
@end example
@end deftypefn
@node Informative Functions
@section Informative Functions
These functions convert numeric identifiers of various MFL entities to
strings and vice-versa.
@deftypefn {Built-in Function} string milter_state_name (number @var{code})
Convert the numeric identifier of a @dfn{milter state} to textual form.
It is normally used to convert the @code{milter_state} variable
(@pxref{milter state}) to string, e.g.:
@example
milter_state_name(5) @result{} "connect"
@end example
If @var{code} does not refer to a valid milter state, the @code{e_inval}
exception is raised.
@end deftypefn
@deftypefn {Built-in Function} number milter_state_code (string @var{name})
Returns numeric code of the milter state @var{name}. If @var{name}
does not refer to a valid milter state, returns 0
(@code{milter_state_none} from the @file{milter.mfl} module).
@end deftypefn
@deftypefn {Built-in Function} string milter_action_name (number @var{code})
Convert the numeric identifier of a reply action (@pxref{reply actions})
to textual name.
If @var{code} does not refer to a valid reply action, the @code{e_inval}
exception is raised.
@example
milter_state_code("connect") @result{} 5
@end example
This function is useful in action hooks. @xref{action hook}, for details.
@end deftypefn
@deftypefn {Built-in Function} number milter_action_name (string @var{name})
Returns numeric code of the reply action identified by @var{name}. If
@var{name} does not refer to a valid action, returns -1.
@end deftypefn
@node Mfmod Interface
@section Mfmod Interface Functions
The calls described in this section provide interface for invoking
functions defined in a @dfn{dynamically loaded library} and retrieving
their return values. For a detailed description of this interface
and a discussion of its use, see @ref{mfmod}.
@anchor{dlopen}
@deftypefn {Built-in Function} number dlopen (@var{filename})
Loads the dynamically loaded library @var{filename} and returns a
numeric handle that can be used to call functions from that library.
Unless @var{filename} is an absolute pathname, it will be looked up in
@dfn{mfmod search path}, defined by the configuration variable
@code{runtime.mfmod-path} (@pxref{mfmod-path}).
Maximum number of dynamic libraries that can be loaded simultaneously
is limited by the configuration variable @code{runtime.max-mfmods}.
Once open, the library remains loaded until @command{mailfromd} exits.
There is no @code{dlclose} function, since it is not deemed necessary
(at the time of this writing, at least). Therefore, the common
practice is to call this function in a @code{begin} section
(@pxref{begin/end}) and assign its return value to a global or static
variable, which will then be used by further @code{dl*} calls in this
module.
@end deftypefn
@deftypefn {Built-in Function} any dlcall (number @var{dlh}, @
string @var{symbol}, string @var{types}, ...)
Calls a @dfn{mfmod function} @var{symbol} from the library identified
by handle @var{dlh} (a value returned by a previous call to
@code{dlopen}). The @var{types} parameter defines types of the
remaining arguments. It is a list of type letters, one for each argument:
@table @samp
@item s
String value.
@item n
@itemx d
Numeric value.
@item m
Message.
@end table
An example usage:
@example
set dlh dlopen("mfmod_test.so")
string x dlcall(dlh, "runtest", "sn", "input string", 3)
@end example
@noindent
This example calls the function @samp{runtest} from the
@file{mfmod_test.so} library with two arguments: a string @samp{input
string} and numeric value @samp{3} and assings the return value to the
variable @code{x}.
Type of the return value from @code{dlcall} is determined by the
value of @code{retval.type} upon return from the underlying library
function. @xref{Loadable Library}, for a detailed description.
For more details about using this function, @ref{Interface Module}.
@end deftypefn
|