Home | History | Annotate | Download | only in m4
      1 divert(-1)
      2 #
      3 # Copyright (c) 1998-2009 Sendmail, Inc. and its suppliers.
      4 #	All rights reserved.
      5 # Copyright (c) 1983, 1995 Eric P. Allman.  All rights reserved.
      6 # Copyright (c) 1988, 1993
      7 #	The Regents of the University of California.  All rights reserved.
      8 #
      9 # Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     10 # Use is subject to license terms.
     11 #
     12 # By using this file, you agree to the terms and conditions set
     13 # forth in the LICENSE file which can be found at the top level of
     14 # the sendmail distribution.
     15 #
     16 #
     17 divert(0)
     18 
     19 VERSIONID(`$Id: proto.m4,v 8.741 2009/12/11 00:04:53 ca Exp $')
     20 
     21 # level CF_LEVEL config file format
     22 V`'CF_LEVEL/ifdef(`VENDOR_NAME', `VENDOR_NAME', `Sun')
     23 divert(-1)
     24 
     25 dnl if MAILER(`local') not defined: do it ourself; be nice
     26 dnl maybe we should issue a warning?
     27 ifdef(`_MAILER_local_',`', `MAILER(local)')
     28 
     29 # do some sanity checking
     30 ifdef(`__OSTYPE__',,
     31 	`errprint(`*** ERROR: No system type defined (use OSTYPE macro)
     32 ')')
     33 
     34 # pick our default mailers
     35 ifdef(`confSMTP_MAILER',, `define(`confSMTP_MAILER', `esmtp')')
     36 ifdef(`confLOCAL_MAILER',, `define(`confLOCAL_MAILER', `local')')
     37 ifdef(`confRELAY_MAILER',,
     38 	`define(`confRELAY_MAILER',
     39 		`ifdef(`_MAILER_smtp_', `relay',
     40 			`ifdef(`_MAILER_uucp', `uucp-new', `unknown')')')')
     41 ifdef(`confUUCP_MAILER',, `define(`confUUCP_MAILER', `uucp-old')')
     42 define(`_SMTP_', `confSMTP_MAILER')dnl		for readability only
     43 define(`_LOCAL_', `confLOCAL_MAILER')dnl	for readability only
     44 define(`_RELAY_', `confRELAY_MAILER')dnl	for readability only
     45 define(`_UUCP_', `confUUCP_MAILER')dnl		for readability only
     46 
     47 # back compatibility with old config files
     48 ifdef(`confDEF_GROUP_ID',
     49 `errprint(`*** confDEF_GROUP_ID is obsolete.
     50     Use confDEF_USER_ID with a colon in the value instead.
     51 ')')
     52 ifdef(`confREAD_TIMEOUT',
     53 `errprint(`*** confREAD_TIMEOUT is obsolete.
     54     Use individual confTO_<timeout> parameters instead.
     55 ')')
     56 ifdef(`confMESSAGE_TIMEOUT',
     57 	`define(`_ARG_', index(confMESSAGE_TIMEOUT, /))
     58 	 ifelse(_ARG_, -1,
     59 		`define(`confTO_QUEUERETURN', confMESSAGE_TIMEOUT)',
     60 		`define(`confTO_QUEUERETURN',
     61 			substr(confMESSAGE_TIMEOUT, 0, _ARG_))
     62 		 define(`confTO_QUEUEWARN',
     63 			substr(confMESSAGE_TIMEOUT, eval(_ARG_+1)))')')
     64 ifdef(`confMIN_FREE_BLOCKS', `ifelse(index(confMIN_FREE_BLOCKS, /), -1,,
     65 `errprint(`*** compound confMIN_FREE_BLOCKS is obsolete.
     66     Use confMAX_MESSAGE_SIZE for the second part of the value.
     67 ')')')
     68 
     69 
     70 # Sanity check on ldap_routing feature
     71 # If the user doesn't specify a new map, they better have given as a
     72 # default LDAP specification which has the LDAP base (and most likely the host)
     73 ifdef(`confLDAP_DEFAULT_SPEC',, `ifdef(`_LDAP_ROUTING_WARN_', `errprint(`
     74 WARNING: Using default FEATURE(ldap_routing) map definition(s)
     75 without setting confLDAP_DEFAULT_SPEC option.
     76 ')')')dnl
     77 
     78 # clean option definitions below....
     79 define(`_OPTION', `ifdef(`$2', `O $1`'ifelse(defn(`$2'), `',, `=$2')', `#O $1`'ifelse(`$3', `',,`=$3')')')dnl
     80 
     81 dnl required to "rename" the check_* rulesets...
     82 define(`_U_',ifdef(`_DELAY_CHECKS_',`',`_'))
     83 dnl default relaying denied message
     84 ifdef(`confRELAY_MSG', `', `define(`confRELAY_MSG',
     85 ifdef(`_USE_AUTH_', `"550 Relaying denied. Proper authentication required."', `"550 Relaying denied"'))')
     86 ifdef(`confRCPTREJ_MSG', `', `define(`confRCPTREJ_MSG', `"550 Mailbox disabled for this recipient"')')
     87 define(`_CODE553', `553')
     88 divert(0)dnl
     89 
     90 # override file safeties - setting this option compromises system security,
     91 # addressing the actual file configuration problem is preferred
     92 # need to set this before any file actions are encountered in the cf file
     93 _OPTION(DontBlameSendmail, `confDONT_BLAME_SENDMAIL', `safe')
     94 
     95 # default LDAP map specification
     96 # need to set this now before any LDAP maps are defined
     97 _OPTION(LDAPDefaultSpec, `confLDAP_DEFAULT_SPEC', `-h localhost')
     98 
     99 ##################
    100 #   local info   #
    101 ##################
    102 
    103 # my LDAP cluster
    104 # need to set this before any LDAP lookups are done (including classes)
    105 ifdef(`confLDAP_CLUSTER', `D{sendmailMTACluster}`'confLDAP_CLUSTER', `#D{sendmailMTACluster}$m')
    106 
    107 Cwlocalhost
    108 ifdef(`USE_CW_FILE',
    109 `# file containing names of hosts for which we receive email
    110 Fw`'confCW_FILE',
    111 	`dnl')
    112 
    113 # my official domain name
    114 # ... `define' this only if sendmail cannot automatically determine your domain
    115 ifdef(`confDOMAIN_NAME', `Dj`'confDOMAIN_NAME', `#Dj$w.Foo.COM')
    116 
    117 # host/domain names ending with a token in class P are canonical
    118 CP.
    119 
    120 ifdef(`UUCP_RELAY',
    121 `# UUCP relay host
    122 DY`'UUCP_RELAY
    123 CPUUCP
    124 
    125 ')dnl
    126 ifdef(`BITNET_RELAY',
    127 `#  BITNET relay host
    128 DB`'BITNET_RELAY
    129 CPBITNET
    130 
    131 ')dnl
    132 ifdef(`DECNET_RELAY',
    133 `define(`_USE_DECNET_SYNTAX_', 1)dnl
    134 # DECnet relay host
    135 DC`'DECNET_RELAY
    136 CPDECNET
    137 
    138 ')dnl
    139 ifdef(`FAX_RELAY',
    140 `# FAX relay host
    141 DF`'FAX_RELAY
    142 CPFAX
    143 
    144 ')dnl
    145 # "Smart" relay host (may be null)
    146 DS`'ifdef(`SMART_HOST', `SMART_HOST')
    147 
    148 ifdef(`LUSER_RELAY', `dnl
    149 # place to which unknown users should be forwarded
    150 Kuser user -m -a<>
    151 DL`'LUSER_RELAY',
    152 `dnl')
    153 
    154 # operators that cannot be in local usernames (i.e., network indicators)
    155 CO @ % ifdef(`_NO_UUCP_', `', `!')
    156 
    157 # a class with just dot (for identifying canonical names)
    158 C..
    159 
    160 # a class with just a left bracket (for identifying domain literals)
    161 C[[
    162 
    163 ifdef(`_ACCESS_TABLE_', `dnl
    164 # access_db acceptance class
    165 C{Accept}OK RELAY
    166 ifdef(`_DELAY_COMPAT_8_10_',`dnl
    167 ifdef(`_BLACKLIST_RCPT_',`dnl
    168 # possible access_db RHS for spam friends/haters
    169 C{SpamTag}SPAMFRIEND SPAMHATER')')',
    170 `dnl')
    171 
    172 dnl mark for "domain is ok" (resolved or accepted anyway)
    173 define(`_RES_OK_', `OKR')dnl
    174 ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',`dnl',`dnl
    175 # Resolve map (to check if a host exists in check_mail)
    176 Kresolve host -a<_RES_OK_> -T<TEMP>')
    177 C{ResOk}_RES_OK_
    178 
    179 ifdef(`_NEED_MACRO_MAP_', `dnl
    180 ifdef(`_MACRO_MAP_', `', `# macro storage map
    181 define(`_MACRO_MAP_', `1')dnl
    182 Kmacro macro')', `dnl')
    183 
    184 ifdef(`confCR_FILE', `dnl
    185 # Hosts for which relaying is permitted ($=R)
    186 FR`'confCR_FILE',
    187 `dnl')
    188 
    189 define(`TLS_SRV_TAG', `"TLS_Srv"')dnl
    190 define(`TLS_CLT_TAG', `"TLS_Clt"')dnl
    191 define(`TLS_RCPT_TAG', `"TLS_Rcpt"')dnl
    192 define(`TLS_TRY_TAG', `"Try_TLS"')dnl
    193 define(`SRV_FEAT_TAG', `"Srv_Features"')dnl
    194 dnl this may be useful in other contexts too
    195 ifdef(`_ARITH_MAP_', `', `# arithmetic map
    196 define(`_ARITH_MAP_', `1')dnl
    197 Karith arith')
    198 ifdef(`_ACCESS_TABLE_', `dnl
    199 ifdef(`_MACRO_MAP_', `', `# macro storage map
    200 define(`_MACRO_MAP_', `1')dnl
    201 Kmacro macro')
    202 # possible values for TLS_connection in access map
    203 C{Tls}VERIFY ENCR', `dnl')
    204 ifdef(`_CERT_REGEX_ISSUER_', `dnl
    205 # extract relevant part from cert issuer
    206 KCERTIssuer regex _CERT_REGEX_ISSUER_', `dnl')
    207 ifdef(`_CERT_REGEX_SUBJECT_', `dnl
    208 # extract relevant part from cert subject
    209 KCERTSubject regex _CERT_REGEX_SUBJECT_', `dnl')
    210 
    211 ifdef(`LOCAL_RELAY', `dnl
    212 # who I send unqualified names to if `FEATURE(stickyhost)' is used
    213 # (null means deliver locally)
    214 DR`'LOCAL_RELAY')
    215 
    216 ifdef(`MAIL_HUB', `dnl
    217 # who gets all local email traffic
    218 # ($R has precedence for unqualified names if `FEATURE(stickyhost)' is used)
    219 DH`'MAIL_HUB')
    220 
    221 # dequoting map
    222 Kdequote dequote`'ifdef(`confDEQUOTE_OPTS', ` confDEQUOTE_OPTS', `')
    223 
    224 divert(0)dnl	# end of nullclient diversion
    225 # class E: names that should be exposed as from this host, even if we masquerade
    226 # class L: names that should be delivered locally, even if we have a relay
    227 # class M: domains that should be converted to $M
    228 # class N: domains that should not be converted to $M
    229 #CL root
    230 undivert(5)dnl
    231 ifdef(`_VIRTHOSTS_', `CR$={VirtHost}', `dnl')
    232 
    233 ifdef(`MASQUERADE_NAME', `dnl
    234 # who I masquerade as (null for no masquerading) (see also $=M)
    235 DM`'MASQUERADE_NAME')
    236 
    237 # my name for error messages
    238 ifdef(`confMAILER_NAME', `Dn`'confMAILER_NAME', `#DnMAILER-DAEMON')
    239 
    240 undivert(6)dnl LOCAL_CONFIG
    241 include(_CF_DIR_`m4/version.m4')
    242 
    243 ###############
    244 #   Options   #
    245 ###############
    246 ifdef(`confAUTO_REBUILD',
    247 `errprint(WARNING: `confAUTO_REBUILD' is no longer valid.
    248 	There was a potential for a denial of service attack if this is set.
    249 )')dnl
    250 
    251 # strip message body to 7 bits on input?
    252 _OPTION(SevenBitInput, `confSEVEN_BIT_INPUT', `False')
    253 
    254 # 8-bit data handling
    255 _OPTION(EightBitMode, `confEIGHT_BIT_HANDLING', `pass8')
    256 
    257 # wait for alias file rebuild (default units: minutes)
    258 _OPTION(AliasWait, `confALIAS_WAIT', `5m')
    259 
    260 # location of alias file
    261 _OPTION(AliasFile, `ALIAS_FILE', `MAIL_SETTINGS_DIR`'aliases')
    262 
    263 # minimum number of free blocks on filesystem
    264 _OPTION(MinFreeBlocks, `confMIN_FREE_BLOCKS', `100')
    265 
    266 # maximum message size
    267 _OPTION(MaxMessageSize, `confMAX_MESSAGE_SIZE', `0')
    268 
    269 # substitution for space (blank) characters
    270 _OPTION(BlankSub, `confBLANK_SUB', `_')
    271 
    272 # avoid connecting to "expensive" mailers on initial submission?
    273 _OPTION(HoldExpensive, `confCON_EXPENSIVE', `False')
    274 
    275 # checkpoint queue runs after every N successful deliveries
    276 _OPTION(CheckpointInterval, `confCHECKPOINT_INTERVAL', `10')
    277 
    278 # default delivery mode
    279 _OPTION(DeliveryMode, `confDELIVERY_MODE', `background')
    280 
    281 # error message header/file
    282 _OPTION(ErrorHeader, `confERROR_MESSAGE', `MAIL_SETTINGS_DIR`'error-header')
    283 
    284 # error mode
    285 _OPTION(ErrorMode, `confERROR_MODE', `print')
    286 
    287 # save Unix-style "From_" lines at top of header?
    288 _OPTION(SaveFromLine, `confSAVE_FROM_LINES', `False')
    289 
    290 # queue file mode (qf files)
    291 _OPTION(QueueFileMode, `confQUEUE_FILE_MODE', `0600')
    292 
    293 # temporary file mode
    294 _OPTION(TempFileMode, `confTEMP_FILE_MODE', `0600')
    295 
    296 # match recipients against GECOS field?
    297 _OPTION(MatchGECOS, `confMATCH_GECOS', `False')
    298 
    299 # maximum hop count
    300 _OPTION(MaxHopCount, `confMAX_HOP', `25')
    301 
    302 # location of help file
    303 O HelpFile=ifdef(`HELP_FILE', HELP_FILE, `MAIL_SETTINGS_DIR`'helpfile')
    304 
    305 # ignore dots as terminators in incoming messages?
    306 _OPTION(IgnoreDots, `confIGNORE_DOTS', `False')
    307 
    308 # name resolver options
    309 _OPTION(ResolverOptions, `confBIND_OPTS', `+AAONLY')
    310 
    311 # deliver MIME-encapsulated error messages?
    312 _OPTION(SendMimeErrors, `confMIME_FORMAT_ERRORS', `True')
    313 
    314 # Forward file search path
    315 _OPTION(ForwardPath, `confFORWARD_PATH', `/var/forward/$u:$z/.forward.$w:$z/.forward')
    316 
    317 # open connection cache size
    318 _OPTION(ConnectionCacheSize, `confMCI_CACHE_SIZE', `2')
    319 
    320 # open connection cache timeout
    321 _OPTION(ConnectionCacheTimeout, `confMCI_CACHE_TIMEOUT', `5m')
    322 
    323 # persistent host status directory
    324 _OPTION(HostStatusDirectory, `confHOST_STATUS_DIRECTORY', `.hoststat')
    325 
    326 # single thread deliveries (requires HostStatusDirectory)?
    327 _OPTION(SingleThreadDelivery, `confSINGLE_THREAD_DELIVERY', `False')
    328 
    329 # use Errors-To: header?
    330 _OPTION(UseErrorsTo, `confUSE_ERRORS_TO', `False')
    331 
    332 # log level
    333 _OPTION(LogLevel, `confLOG_LEVEL', `10')
    334 
    335 # send to me too, even in an alias expansion?
    336 _OPTION(MeToo, `confME_TOO', `True')
    337 
    338 # verify RHS in newaliases?
    339 _OPTION(CheckAliases, `confCHECK_ALIASES', `False')
    340 
    341 # default messages to old style headers if no special punctuation?
    342 _OPTION(OldStyleHeaders, `confOLD_STYLE_HEADERS', `False')
    343 
    344 # SMTP daemon options
    345 ifelse(defn(`confDAEMON_OPTIONS'), `', `dnl',
    346 `errprint(WARNING: `confDAEMON_OPTIONS' is no longer valid.
    347 	Use `DAEMON_OPTIONS()'; see cf/README.
    348 )'dnl
    349 `DAEMON_OPTIONS(`confDAEMON_OPTIONS')')
    350 ifelse(defn(`_DPO_'), `',
    351 `ifdef(`_NETINET6_', `O DaemonPortOptions=Name=MTA-v4, Family=inet
    352 O DaemonPortOptions=Name=MTA-v6, Family=inet6',`O DaemonPortOptions=Name=MTA')', `_DPO_')
    353 ifdef(`_NO_MSA_', `dnl', `O DaemonPortOptions=Port=587, Name=MSA, M=E')
    354 
    355 # SMTP client options
    356 ifelse(defn(`confCLIENT_OPTIONS'), `', `dnl',
    357 `errprint(WARNING: `confCLIENT_OPTIONS' is no longer valid.  See cf/README for more information.
    358 )'dnl
    359 `CLIENT_OPTIONS(`confCLIENT_OPTIONS')')
    360 ifelse(defn(`_CPO_'), `',
    361 `#O ClientPortOptions=Family=inet, Address=0.0.0.0', `_CPO_')
    362 
    363 # Modifiers to `define' {daemon_flags} for direct submissions
    364 _OPTION(DirectSubmissionModifiers, `confDIRECT_SUBMISSION_MODIFIERS', `')
    365 
    366 # Use as mail submission program? See sendmail/SECURITY
    367 _OPTION(UseMSP, `confUSE_MSP', `')
    368 
    369 # privacy flags
    370 _OPTION(PrivacyOptions, `confPRIVACY_FLAGS', `authwarnings')
    371 
    372 # who (if anyone) should get extra copies of error messages
    373 _OPTION(PostmasterCopy, `confCOPY_ERRORS_TO', `Postmaster')
    374 
    375 # slope of queue-only function
    376 _OPTION(QueueFactor, `confQUEUE_FACTOR', `600000')
    377 
    378 # limit on number of concurrent queue runners
    379 _OPTION(MaxQueueChildren, `confMAX_QUEUE_CHILDREN', `')
    380 
    381 # maximum number of queue-runners per queue-grouping with multiple queues
    382 _OPTION(MaxRunnersPerQueue, `confMAX_RUNNERS_PER_QUEUE', `1')
    383 
    384 # priority of queue runners (nice(3))
    385 _OPTION(NiceQueueRun, `confNICE_QUEUE_RUN', `')
    386 
    387 # shall we sort the queue by hostname first?
    388 _OPTION(QueueSortOrder, `confQUEUE_SORT_ORDER', `priority')
    389 
    390 # minimum time in queue before retry
    391 _OPTION(MinQueueAge, `confMIN_QUEUE_AGE', `30m')
    392 
    393 # how many jobs can you process in the queue?
    394 _OPTION(MaxQueueRunSize, `confMAX_QUEUE_RUN_SIZE', `0')
    395 
    396 # perform initial split of envelope without checking MX records
    397 _OPTION(FastSplit, `confFAST_SPLIT', `1')
    398 
    399 # queue directory
    400 O QueueDirectory=ifdef(`QUEUE_DIR', QUEUE_DIR, `/var/spool/mqueue')
    401 
    402 # key for shared memory; 0 to turn off, -1 to auto-select
    403 _OPTION(SharedMemoryKey, `confSHARED_MEMORY_KEY', `0')
    404 
    405 # file to store auto-selected key for shared memory (SharedMemoryKey = -1)
    406 _OPTION(SharedMemoryKeyFile, `confSHARED_MEMORY_KEY_FILE', `')
    407 
    408 # timeouts (many of these)
    409 _OPTION(Timeout.initial, `confTO_INITIAL', `5m')
    410 _OPTION(Timeout.connect, `confTO_CONNECT', `5m')
    411 _OPTION(Timeout.aconnect, `confTO_ACONNECT', `0s')
    412 _OPTION(Timeout.iconnect, `confTO_ICONNECT', `5m')
    413 _OPTION(Timeout.helo, `confTO_HELO', `5m')
    414 _OPTION(Timeout.mail, `confTO_MAIL', `10m')
    415 _OPTION(Timeout.rcpt, `confTO_RCPT', `1h')
    416 _OPTION(Timeout.datainit, `confTO_DATAINIT', `5m')
    417 _OPTION(Timeout.datablock, `confTO_DATABLOCK', `1h')
    418 _OPTION(Timeout.datafinal, `confTO_DATAFINAL', `1h')
    419 _OPTION(Timeout.rset, `confTO_RSET', `5m')
    420 _OPTION(Timeout.quit, `confTO_QUIT', `2m')
    421 _OPTION(Timeout.misc, `confTO_MISC', `2m')
    422 _OPTION(Timeout.command, `confTO_COMMAND', `1h')
    423 _OPTION(Timeout.ident, `confTO_IDENT', `5s')
    424 _OPTION(Timeout.fileopen, `confTO_FILEOPEN', `60s')
    425 _OPTION(Timeout.control, `confTO_CONTROL', `2m')
    426 _OPTION(Timeout.queuereturn, `confTO_QUEUERETURN', `5d')
    427 _OPTION(Timeout.queuereturn.normal, `confTO_QUEUERETURN_NORMAL', `5d')
    428 _OPTION(Timeout.queuereturn.urgent, `confTO_QUEUERETURN_URGENT', `2d')
    429 _OPTION(Timeout.queuereturn.non-urgent, `confTO_QUEUERETURN_NONURGENT', `7d')
    430 _OPTION(Timeout.queuereturn.dsn, `confTO_QUEUERETURN_DSN', `5d')
    431 _OPTION(Timeout.queuewarn, `confTO_QUEUEWARN', `4h')
    432 _OPTION(Timeout.queuewarn.normal, `confTO_QUEUEWARN_NORMAL', `4h')
    433 _OPTION(Timeout.queuewarn.urgent, `confTO_QUEUEWARN_URGENT', `1h')
    434 _OPTION(Timeout.queuewarn.non-urgent, `confTO_QUEUEWARN_NONURGENT', `12h')
    435 _OPTION(Timeout.queuewarn.dsn, `confTO_QUEUEWARN_DSN', `4h')
    436 _OPTION(Timeout.hoststatus, `confTO_HOSTSTATUS', `30m')
    437 _OPTION(Timeout.resolver.retrans, `confTO_RESOLVER_RETRANS', `5s')
    438 _OPTION(Timeout.resolver.retrans.first, `confTO_RESOLVER_RETRANS_FIRST', `5s')
    439 _OPTION(Timeout.resolver.retrans.normal, `confTO_RESOLVER_RETRANS_NORMAL', `5s')
    440 _OPTION(Timeout.resolver.retry, `confTO_RESOLVER_RETRY', `4')
    441 _OPTION(Timeout.resolver.retry.first, `confTO_RESOLVER_RETRY_FIRST', `4')
    442 _OPTION(Timeout.resolver.retry.normal, `confTO_RESOLVER_RETRY_NORMAL', `4')
    443 _OPTION(Timeout.lhlo, `confTO_LHLO', `2m')
    444 _OPTION(Timeout.auth, `confTO_AUTH', `10m')
    445 _OPTION(Timeout.starttls, `confTO_STARTTLS', `1h')
    446 
    447 # time for DeliverBy; extension disabled if less than 0
    448 _OPTION(DeliverByMin, `confDELIVER_BY_MIN', `0')
    449 
    450 # should we not prune routes in route-addr syntax addresses?
    451 _OPTION(DontPruneRoutes, `confDONT_PRUNE_ROUTES', `False')
    452 
    453 # queue up everything before forking?
    454 _OPTION(SuperSafe, `confSAFE_QUEUE', `True')
    455 
    456 # status file
    457 _OPTION(StatusFile, `STATUS_FILE')
    458 
    459 # time zone handling:
    460 #  if undefined, use system default
    461 #  if defined but null, use TZ envariable passed in
    462 #  if defined and non-null, use that info
    463 ifelse(confTIME_ZONE, `USE_SYSTEM', `#O TimeZoneSpec=',
    464 	confTIME_ZONE, `USE_TZ', `O TimeZoneSpec=',
    465 	`O TimeZoneSpec=confTIME_ZONE')
    466 
    467 # default UID (can be username or userid:groupid)
    468 _OPTION(DefaultUser, `confDEF_USER_ID', `mailnull')
    469 
    470 # list of locations of user database file (null means no lookup)
    471 _OPTION(UserDatabaseSpec, `confUSERDB_SPEC', `MAIL_SETTINGS_DIR`'userdb')
    472 
    473 # fallback MX host
    474 _OPTION(FallbackMXhost, `confFALLBACK_MX', `fall.back.host.net')
    475 
    476 # fallback smart host
    477 _OPTION(FallbackSmartHost, `confFALLBACK_SMARTHOST', `fall.back.host.net')
    478 
    479 # if we are the best MX host for a site, try it directly instead of config err
    480 _OPTION(TryNullMXList, `confTRY_NULL_MX_LIST', `False')
    481 
    482 # load average at which we just queue messages
    483 _OPTION(QueueLA, `confQUEUE_LA', `8')
    484 
    485 # load average at which we refuse connections
    486 _OPTION(RefuseLA, `confREFUSE_LA', `12')
    487 
    488 # log interval when refusing connections for this long
    489 _OPTION(RejectLogInterval, `confREJECT_LOG_INTERVAL', `3h')
    490 
    491 # load average at which we delay connections; 0 means no limit
    492 _OPTION(DelayLA, `confDELAY_LA', `0')
    493 
    494 # maximum number of children we allow at one time
    495 _OPTION(MaxDaemonChildren, `confMAX_DAEMON_CHILDREN', `0')
    496 
    497 # maximum number of new connections per second
    498 _OPTION(ConnectionRateThrottle, `confCONNECTION_RATE_THROTTLE', `0')
    499 
    500 # Width of the window 
    501 _OPTION(ConnectionRateWindowSize, `confCONNECTION_RATE_WINDOW_SIZE', `60s')
    502 
    503 # work recipient factor
    504 _OPTION(RecipientFactor, `confWORK_RECIPIENT_FACTOR', `30000')
    505 
    506 # deliver each queued job in a separate process?
    507 _OPTION(ForkEachJob, `confSEPARATE_PROC', `False')
    508 
    509 # work class factor
    510 _OPTION(ClassFactor, `confWORK_CLASS_FACTOR', `1800')
    511 
    512 # work time factor
    513 _OPTION(RetryFactor, `confWORK_TIME_FACTOR', `90000')
    514 
    515 # default character set
    516 _OPTION(DefaultCharSet, `confDEF_CHAR_SET', `unknown-8bit')
    517 
    518 # service switch file (name hardwired on Solaris, Ultrix, OSF/1, others)
    519 _OPTION(ServiceSwitchFile, `confSERVICE_SWITCH_FILE', `MAIL_SETTINGS_DIR`'service.switch')
    520 
    521 # hosts file (normally /etc/hosts)
    522 _OPTION(HostsFile, `confHOSTS_FILE', `/etc/hosts')
    523 
    524 # dialup line delay on connection failure
    525 _OPTION(DialDelay, `confDIAL_DELAY', `0s')
    526 
    527 # action to take if there are no recipients in the message
    528 _OPTION(NoRecipientAction, `confNO_RCPT_ACTION', `none')
    529 
    530 # chrooted environment for writing to files
    531 _OPTION(SafeFileEnvironment, `confSAFE_FILE_ENV', `')
    532 
    533 # are colons OK in addresses?
    534 _OPTION(ColonOkInAddr, `confCOLON_OK_IN_ADDR', `True')
    535 
    536 # shall I avoid expanding CNAMEs (violates protocols)?
    537 _OPTION(DontExpandCnames, `confDONT_EXPAND_CNAMES', `False')
    538 
    539 # SMTP initial login message (old $e macro)
    540 _OPTION(SmtpGreetingMessage, `confSMTP_LOGIN_MSG', `$j Sendmail $v ready at $b')
    541 
    542 # UNIX initial From header format (old $l macro)
    543 _OPTION(UnixFromLine, `confFROM_LINE', `From $g $d')
    544 
    545 # From: lines that have embedded newlines are unwrapped onto one line
    546 _OPTION(SingleLineFromHeader, `confSINGLE_LINE_FROM_HEADER', `False')
    547 
    548 # Allow HELO SMTP command that does not `include' a host name
    549 _OPTION(AllowBogusHELO, `confALLOW_BOGUS_HELO', `False')
    550 
    551 # Characters to be quoted in a full name phrase (@,;:\()[] are automatic)
    552 _OPTION(MustQuoteChars, `confMUST_QUOTE_CHARS', `.')
    553 
    554 # delimiter (operator) characters (old $o macro)
    555 _OPTION(OperatorChars, `confOPERATORS', `.:@[]')
    556 
    557 # shall I avoid calling initgroups(3) because of high NIS costs?
    558 _OPTION(DontInitGroups, `confDONT_INIT_GROUPS', `False')
    559 
    560 # are group-writable `:include:' and .forward files (un)trustworthy?
    561 # True (the default) means they are not trustworthy.
    562 _OPTION(UnsafeGroupWrites, `confUNSAFE_GROUP_WRITES', `True')
    563 ifdef(`confUNSAFE_GROUP_WRITES',
    564 `errprint(`WARNING: confUNSAFE_GROUP_WRITES is deprecated; use confDONT_BLAME_SENDMAIL.
    565 ')')
    566 
    567 # where do errors that occur when sending errors get sent?
    568 _OPTION(DoubleBounceAddress, `confDOUBLE_BOUNCE_ADDRESS', `postmaster')
    569 
    570 # issue temporary errors (4xy) instead of permanent errors (5xy)?
    571 _OPTION(SoftBounce, `confSOFT_BOUNCE', `False')
    572 
    573 # where to save bounces if all else fails
    574 _OPTION(DeadLetterDrop, `confDEAD_LETTER_DROP', `/var/tmp/dead.letter')
    575 
    576 # what user id do we assume for the majority of the processing?
    577 _OPTION(RunAsUser, `confRUN_AS_USER', `sendmail')
    578 
    579 # maximum number of recipients per SMTP envelope
    580 _OPTION(MaxRecipientsPerMessage, `confMAX_RCPTS_PER_MESSAGE', `0')
    581 
    582 # limit the rate recipients per SMTP envelope are accepted
    583 # once the threshold number of recipients have been rejected
    584 _OPTION(BadRcptThrottle, `confBAD_RCPT_THROTTLE', `0')
    585 
    586 
    587 # shall we get local names from our installed interfaces?
    588 _OPTION(DontProbeInterfaces, `confDONT_PROBE_INTERFACES', `False')
    589 
    590 # Return-Receipt-To: header implies DSN request
    591 _OPTION(RrtImpliesDsn, `confRRT_IMPLIES_DSN', `False')
    592 
    593 # override connection address (for testing)
    594 _OPTION(ConnectOnlyTo, `confCONNECT_ONLY_TO', `0.0.0.0')
    595 
    596 # Trusted user for file ownership and starting the daemon
    597 _OPTION(TrustedUser, `confTRUSTED_USER', `root')
    598 
    599 # Control socket for daemon management
    600 _OPTION(ControlSocketName, `confCONTROL_SOCKET_NAME', `/var/spool/mqueue/.control')
    601 
    602 # Maximum MIME header length to protect MUAs
    603 _OPTION(MaxMimeHeaderLength, `confMAX_MIME_HEADER_LENGTH', `2048/1024')
    604 
    605 # Maximum length of the sum of all headers
    606 _OPTION(MaxHeadersLength, `confMAX_HEADERS_LENGTH', `32768')
    607 
    608 # Maximum depth of alias recursion
    609 _OPTION(MaxAliasRecursion, `confMAX_ALIAS_RECURSION', `10')
    610 
    611 # location of pid file
    612 _OPTION(PidFile, `confPID_FILE', `/var/run/sendmail.pid')
    613 
    614 # Prefix string for the process title shown on 'ps' listings
    615 _OPTION(ProcessTitlePrefix, `confPROCESS_TITLE_PREFIX', `prefix')
    616 
    617 # Data file (df) memory-buffer file maximum size
    618 _OPTION(DataFileBufferSize, `confDF_BUFFER_SIZE', `4096')
    619 
    620 # Transcript file (xf) memory-buffer file maximum size
    621 _OPTION(XscriptFileBufferSize, `confXF_BUFFER_SIZE', `4096')
    622 
    623 # lookup type to find information about local mailboxes
    624 _OPTION(MailboxDatabase, `confMAILBOX_DATABASE', `pw')
    625 
    626 # override compile time flag REQUIRES_DIR_FSYNC
    627 _OPTION(RequiresDirfsync, `confREQUIRES_DIR_FSYNC', `true')
    628 
    629 # list of authentication mechanisms
    630 _OPTION(AuthMechanisms, `confAUTH_MECHANISMS', `EXTERNAL GSSAPI KERBEROS_V4 DIGEST-MD5 CRAM-MD5')
    631 
    632 # Authentication realm
    633 _OPTION(AuthRealm, `confAUTH_REALM', `')
    634 
    635 # default authentication information for outgoing connections
    636 _OPTION(DefaultAuthInfo, `confDEF_AUTH_INFO', `MAIL_SETTINGS_DIR`'default-auth-info')
    637 
    638 # SMTP AUTH flags
    639 _OPTION(AuthOptions, `confAUTH_OPTIONS', `')
    640 
    641 # SMTP AUTH maximum encryption strength
    642 _OPTION(AuthMaxBits, `confAUTH_MAX_BITS', `')
    643 
    644 # SMTP STARTTLS server options
    645 _OPTION(TLSSrvOptions, `confTLS_SRV_OPTIONS', `')
    646 
    647 
    648 # Input mail filters
    649 _OPTION(InputMailFilters, `confINPUT_MAIL_FILTERS', `')
    650 
    651 ifelse(len(X`'_MAIL_FILTERS_DEF), `1', `dnl', `dnl
    652 # Milter options
    653 _OPTION(Milter.LogLevel, `confMILTER_LOG_LEVEL', `')
    654 _OPTION(Milter.macros.connect, `confMILTER_MACROS_CONNECT', `')
    655 _OPTION(Milter.macros.helo, `confMILTER_MACROS_HELO', `')
    656 _OPTION(Milter.macros.envfrom, `confMILTER_MACROS_ENVFROM', `')
    657 _OPTION(Milter.macros.envrcpt, `confMILTER_MACROS_ENVRCPT', `')
    658 _OPTION(Milter.macros.eom, `confMILTER_MACROS_EOM', `')
    659 _OPTION(Milter.macros.eoh, `confMILTER_MACROS_EOH', `')
    660 _OPTION(Milter.macros.data, `confMILTER_MACROS_DATA', `')')
    661 
    662 # CA directory
    663 _OPTION(CACertPath, `confCACERT_PATH', `')
    664 # CA file
    665 _OPTION(CACertFile, `confCACERT', `')
    666 # Server Cert
    667 _OPTION(ServerCertFile, `confSERVER_CERT', `')
    668 # Server private key
    669 _OPTION(ServerKeyFile, `confSERVER_KEY', `')
    670 # Client Cert
    671 _OPTION(ClientCertFile, `confCLIENT_CERT', `')
    672 # Client private key
    673 _OPTION(ClientKeyFile, `confCLIENT_KEY', `')
    674 # File containing certificate revocation lists 
    675 _OPTION(CRLFile, `confCRL', `')
    676 # DHParameters (only required if DSA/DH is used)
    677 _OPTION(DHParameters, `confDH_PARAMETERS', `')
    678 # Random data source (required for systems without /dev/urandom under OpenSSL)
    679 _OPTION(RandFile, `confRAND_FILE', `')
    680 
    681 # Maximum number of "useless" commands before slowing down
    682 _OPTION(MaxNOOPCommands, `confMAX_NOOP_COMMANDS', `20')
    683 
    684 # Name to use for EHLO (defaults to $j)
    685 _OPTION(HeloName, `confHELO_NAME')
    686 
    687 ############################
    688 `# QUEUE GROUP DEFINITIONS  #'
    689 ############################
    690 _QUEUE_GROUP_
    691 
    692 ###########################
    693 #   Message precedences   #
    694 ###########################
    695 
    696 Pfirst-class=0
    697 Pspecial-delivery=100
    698 Plist=-30
    699 Pbulk=-60
    700 Pjunk=-100
    701 
    702 #####################
    703 #   Trusted users   #
    704 #####################
    705 
    706 # this is equivalent to setting class "t"
    707 ifdef(`_USE_CT_FILE_', `', `#')Ft`'ifdef(`confCT_FILE', confCT_FILE, `MAIL_SETTINGS_DIR`'trusted-users')
    708 Troot
    709 Tdaemon
    710 ifdef(`_NO_UUCP_', `dnl', `Tuucp')
    711 ifdef(`confTRUSTED_USERS', `T`'confTRUSTED_USERS', `dnl')
    712 
    713 #########################
    714 #   Format of headers   #
    715 #########################
    716 
    717 ifdef(`confFROM_HEADER',, `define(`confFROM_HEADER', `$?x$x <$g>$|$g$.')')dnl
    718 ifdef(`confMESSAGEID_HEADER',, `define(`confMESSAGEID_HEADER', `<$t.$i@$j>')')dnl
    719 H?P?Return-Path: <$g>
    720 HReceived: confRECEIVED_HEADER
    721 H?D?Resent-Date: $a
    722 H?D?Date: $a
    723 H?F?Resent-From: confFROM_HEADER
    724 H?F?From: confFROM_HEADER
    725 H?x?Full-Name: $x
    726 # HPosted-Date: $a
    727 # H?l?Received-Date: $b
    728 H?M?Resent-Message-Id: confMESSAGEID_HEADER
    729 H?M?Message-Id: confMESSAGEID_HEADER
    730 
    731 #
    733 ######################################################################
    734 ######################################################################
    735 #####
    736 #####			REWRITING RULES
    737 #####
    738 ######################################################################
    739 ######################################################################
    740 
    741 ############################################
    742 ###  Ruleset 3 -- Name Canonicalization  ###
    743 ############################################
    744 Scanonify=3
    745 
    746 # handle null input (translate to <@> special case)
    747 R$@			$@ <@>
    748 
    749 # strip group: syntax (not inside angle brackets!) and trailing semicolon
    750 R$*			$: $1 <@>			mark addresses
    751 R$* < $* > $* <@>	$: $1 < $2 > $3			unmark <addr>
    752 R@ $* <@>		$: @ $1				unmark @host:...
    753 R$* [ IPv6 : $+ ] <@>	$: $1 [ IPv6 : $2 ]		unmark IPv6 addr
    754 R$* :: $* <@>		$: $1 :: $2			unmark node::addr
    755 R:`include': $* <@>	$: :`include': $1			unmark :`include':...
    756 R$* : $* [ $* ]		$: $1 : $2 [ $3 ] <@>		remark if leading colon
    757 R$* : $* <@>		$: $2				strip colon if marked
    758 R$* <@>			$: $1				unmark
    759 R$* ;			   $1				strip trailing semi
    760 R$* < $+ :; > $*	$@ $2 :; <@>			catch <list:;>
    761 R$* < $* ; >		   $1 < $2 >			bogus bracketed semi
    762 
    763 # null input now results from list:; syntax
    764 R$@			$@ :; <@>
    765 
    766 # strip angle brackets -- note RFC733 heuristic to get innermost item
    767 R$*			$: < $1 >			housekeeping <>
    768 R$+ < $* >		   < $2 >			strip excess on left
    769 R< $* > $+		   < $1 >			strip excess on right
    770 R<>			$@ < @ >			MAIL FROM:<> case
    771 R< $+ >			$: $1				remove housekeeping <>
    772 
    773 ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
    774 # make sure <@a,@b,@c:user@d> syntax is easy to parse -- undone later
    775 R@ $+ , $+		@ $1 : $2			change all "," to ":"
    776 
    777 # localize and dispose of route-based addresses
    778 dnl XXX: IPv6 colon conflict
    779 ifdef(`NO_NETINET6', `dnl',
    780 `R@ [$+] : $+		$@ $>Canonify2 < @ [$1] > : $2	handle <route-addr>')
    781 R@ $+ : $+		$@ $>Canonify2 < @$1 > : $2	handle <route-addr>
    782 dnl',`dnl
    783 # strip route address <@a,@b,@c:user@d> -> <user@d>
    784 R@ $+ , $+		$2
    785 ifdef(`NO_NETINET6', `dnl',
    786 `R@ [ $* ] : $+		$2')
    787 R@ $+ : $+		$2
    788 dnl')
    789 
    790 # find focus for list syntax
    791 R $+ : $* ; @ $+	$@ $>Canonify2 $1 : $2 ; < @ $3 >	list syntax
    792 R $+ : $* ;		$@ $1 : $2;			list syntax
    793 
    794 # find focus for @ syntax addresses
    795 R$+ @ $+		$: $1 < @ $2 >			focus on domain
    796 R$+ < $+ @ $+ >		$1 $2 < @ $3 >			move gaze right
    797 R$+ < @ $+ >		$@ $>Canonify2 $1 < @ $2 >	already canonical
    798 
    799 dnl This is flagged as an error in S0; no need to silently fix it here.
    800 dnl # do some sanity checking
    801 dnl R$* < @ $~[ $* : $* > $*	$1 < @ $2 $3 > $4	nix colons in addrs
    802 
    803 ifdef(`_NO_UUCP_', `dnl',
    804 `# convert old-style addresses to a domain-based address
    805 R$- ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	resolve uucp names
    806 R$+ . $- ! $+		$@ $>Canonify2 $3 < @ $1 . $2 >		domain uucps
    807 R$+ ! $+		$@ $>Canonify2 $2 < @ $1 .UUCP >	uucp subdomains
    808 ')
    809 ifdef(`_USE_DECNET_SYNTAX_',
    810 `# convert node::user addresses into a domain-based address
    811 R$- :: $+		$@ $>Canonify2 $2 < @ $1 .DECNET >	resolve DECnet names
    812 R$- . $- :: $+		$@ $>Canonify2 $3 < @ $1.$2 .DECNET >	numeric DECnet addr
    813 ',
    814 	`dnl')
    815 # if we have % signs, take the rightmost one
    816 R$* % $*		$1 @ $2				First make them all @s.
    817 R$* @ $* @ $*		$1 % $2 @ $3			Undo all but the last.
    818 R$* @ $*		$@ $>Canonify2 $1 < @ $2 >	Insert < > and finish
    819 
    820 # else we must be a local name
    821 R$*			$@ $>Canonify2 $1
    822 
    823 
    824 ################################################
    825 ###  Ruleset 96 -- bottom half of ruleset 3  ###
    826 ################################################
    827 
    828 SCanonify2=96
    829 
    830 # handle special cases for local names
    831 R$* < @ localhost > $*		$: $1 < @ $j . > $2		no domain at all
    832 R$* < @ localhost . $m > $*	$: $1 < @ $j . > $2		local domain
    833 ifdef(`_NO_UUCP_', `dnl',
    834 `R$* < @ localhost . UUCP > $*	$: $1 < @ $j . > $2		.UUCP domain')
    835 
    836 # check for IPv4/IPv6 domain literal
    837 R$* < @ [ $+ ] > $*		$: $1 < @@ [ $2 ] > $3		mark [addr]
    838 R$* < @@ $=w > $*		$: $1 < @ $j . > $3		self-literal
    839 R$* < @@ $+ > $*		$@ $1 < @ $2 > $3		canon IP addr
    840 
    841 ifdef(`_DOMAIN_TABLE_', `dnl
    842 # look up domains in the domain table
    843 R$* < @ $+ > $* 		$: $1 < @ $(domaintable $2 $) > $3', `dnl')
    844 
    845 undivert(2)dnl LOCAL_RULE_3
    846 
    847 ifdef(`_BITDOMAIN_TABLE_', `dnl
    848 # handle BITNET mapping
    849 R$* < @ $+ .BITNET > $*		$: $1 < @ $(bitdomain $2 $: $2.BITNET $) > $3', `dnl')
    850 
    851 ifdef(`_UUDOMAIN_TABLE_', `dnl
    852 # handle UUCP mapping
    853 R$* < @ $+ .UUCP > $*		$: $1 < @ $(uudomain $2 $: $2.UUCP $) > $3', `dnl')
    854 
    855 ifdef(`_NO_UUCP_', `dnl',
    856 `ifdef(`UUCP_RELAY',
    857 `# pass UUCP addresses straight through
    858 R$* < @ $+ . UUCP > $*		$@ $1 < @ $2 . UUCP . > $3',
    859 `# if really UUCP, handle it immediately
    860 ifdef(`_CLASS_U_',
    861 `R$* < @ $=U . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
    862 ifdef(`_CLASS_V_',
    863 `R$* < @ $=V . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
    864 ifdef(`_CLASS_W_',
    865 `R$* < @ $=W . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
    866 ifdef(`_CLASS_X_',
    867 `R$* < @ $=X . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
    868 ifdef(`_CLASS_Y_',
    869 `R$* < @ $=Y . UUCP > $*	$@ $1 < @ $2 . UUCP . > $3', `dnl')
    870 
    871 ifdef(`_NO_CANONIFY_', `dnl', `dnl
    872 # try UUCP traffic as a local address
    873 R$* < @ $+ . UUCP > $*		$: $1 < @ $[ $2 $] . UUCP . > $3
    874 R$* < @ $+ . . UUCP . > $*	$@ $1 < @ $2 . > $3')
    875 ')')
    876 # hostnames ending in class P are always canonical
    877 R$* < @ $* $=P > $*		$: $1 < @ $2 $3 . > $4
    878 dnl apply the next rule only for hostnames not in class P
    879 dnl this even works for phrases in class P since . is in class P
    880 dnl which daemon flags are set?
    881 R$* < @ $* $~P > $*		$: $&{daemon_flags} $| $1 < @ $2 $3 > $4
    882 dnl the other rules in this section only apply if the hostname
    883 dnl does not end in class P hence no further checks are done here
    884 dnl if this ever changes make sure the lookups are "protected" again!
    885 ifdef(`_NO_CANONIFY_', `dnl
    886 dnl do not canonify unless:
    887 dnl domain ends in class {Canonify} (this does not work if the intersection
    888 dnl	with class P is non-empty)
    889 dnl or {daemon_flags} has c set
    890 # pass to name server to make hostname canonical if in class {Canonify}
    891 R$* $| $* < @ $* $={Canonify} > $*	$: $2 < @ $[ $3 $4 $] > $5
    892 # pass to name server to make hostname canonical if requested
    893 R$* c $* $| $* < @ $* > $*	$: $3 < @ $[ $4 $] > $5
    894 dnl trailing dot? -> do not apply _CANONIFY_HOSTS_
    895 R$* $| $* < @ $+ . > $*		$: $2 < @ $3 . > $4
    896 # add a trailing dot to qualified hostnames so other rules will work
    897 R$* $| $* < @ $+.$+ > $*	$: $2 < @ $3.$4 . > $5
    898 ifdef(`_CANONIFY_HOSTS_', `dnl
    899 dnl this should only apply to unqualified hostnames
    900 dnl but if a valid character inside an unqualified hostname is an OperatorChar
    901 dnl then $- does not work.
    902 # lookup unqualified hostnames
    903 R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4', `dnl')', `dnl
    904 dnl _NO_CANONIFY_ is not set: canonify unless:
    905 dnl {daemon_flags} contains CC (do not canonify)
    906 dnl but add a trailing dot to qualified hostnames so other rules will work
    907 dnl should we do this for every hostname: even unqualified?
    908 R$* CC $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
    909 R$* CC $* $| $*			$: $3
    910 ifdef(`_FFR_NOCANONIFY_HEADERS', `dnl
    911 # do not canonify header addresses
    912 R$* $| $* < @ $* $~P > $*	$: $&{addr_type} $| $2 < @ $3 $4 > $5
    913 R$* h $* $| $* < @ $+.$+ > $*	$: $3 < @ $4.$5 . > $6
    914 R$* h $* $| $*			$: $3', `dnl')
    915 # pass to name server to make hostname canonical
    916 R$* $| $* < @ $* > $*		$: $2 < @ $[ $3 $] > $4')
    917 dnl remove {daemon_flags} for other cases
    918 R$* $| $*			$: $2
    919 
    920 # local host aliases and pseudo-domains are always canonical
    921 R$* < @ $=w > $*		$: $1 < @ $2 . > $3
    922 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
    923 `R$* < @ $* $=M > $*		$: $1 < @ $2 $3 . > $4',
    924 `R$* < @ $=M > $*		$: $1 < @ $2 . > $3')
    925 ifdef(`_VIRTUSER_TABLE_', `dnl
    926 dnl virtual hosts are also canonical
    927 ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
    928 `R$* < @ $* $={VirtHost} > $* 	$: $1 < @ $2 $3 . > $4',
    929 `R$* < @ $={VirtHost} > $* 	$: $1 < @ $2 . > $3')',
    930 `dnl')
    931 ifdef(`_GENERICS_TABLE_', `dnl
    932 dnl hosts for genericstable are also canonical
    933 ifdef(`_GENERICS_ENTIRE_DOMAIN_',
    934 `R$* < @ $* $=G > $* 	$: $1 < @ $2 $3 . > $4',
    935 `R$* < @ $=G > $* 	$: $1 < @ $2 . > $3')',
    936 `dnl')
    937 dnl remove superfluous dots (maybe repeatedly) which may have been added
    938 dnl by one of the rules before
    939 R$* < @ $* . . > $*		$1 < @ $2 . > $3
    940 
    941 
    942 ##################################################
    943 ###  Ruleset 4 -- Final Output Post-rewriting  ###
    944 ##################################################
    945 Sfinal=4
    946 
    947 R$+ :; <@>		$@ $1 :				handle <list:;>
    948 R$* <@>			$@				handle <> and list:;
    949 
    950 # strip trailing dot off possibly canonical name
    951 R$* < @ $+ . > $*	$1 < @ $2 > $3
    952 
    953 # eliminate internal code
    954 R$* < @ *LOCAL* > $*	$1 < @ $j > $2
    955 
    956 # externalize local domain info
    957 R$* < $+ > $*		$1 $2 $3			defocus
    958 R@ $+ : @ $+ : $+	@ $1 , @ $2 : $3		<route-addr> canonical
    959 R@ $*			$@ @ $1				... and exit
    960 
    961 ifdef(`_NO_UUCP_', `dnl',
    962 `# UUCP must always be presented in old form
    963 R$+ @ $- . UUCP		$2!$1				u (a] h.UUCP => h!u')
    964 
    965 ifdef(`_USE_DECNET_SYNTAX_',
    966 `# put DECnet back in :: form
    967 R$+ @ $+ . DECNET	$2 :: $1			u (a] h.DECNET => h::u',
    968 	`dnl')
    969 # delete duplicate local names
    970 R$+ % $=w @ $=w		$1 @ $2				u%host@host => u@host
    971 
    972 
    973 
    974 ##############################################################
    975 ###   Ruleset 97 -- recanonicalize and call ruleset zero   ###
    976 ###		   (used for recursive calls)		   ###
    977 ##############################################################
    978 
    979 SRecurse=97
    980 R$*			$: $>canonify $1
    981 R$*			$@ $>parse $1
    982 
    983 
    984 ######################################
    985 ###   Ruleset 0 -- Parse Address   ###
    986 ######################################
    987 
    988 Sparse=0
    989 
    990 R$*			$: $>Parse0 $1		initial parsing
    991 R<@>			$#_LOCAL_ $: <@>		special case error msgs
    992 R$*			$: $>ParseLocal $1	handle local hacks
    993 R$*			$: $>Parse1 $1		final parsing
    994 
    995 #
    996 #  Parse0 -- do initial syntax checking and eliminate local addresses.
    997 #	This should either return with the (possibly modified) input
    998 #	or return with a #error mailer.  It should not return with a
    999 #	#mailer other than the #error mailer.
   1000 #
   1001 
   1002 SParse0
   1003 R<@>			$@ <@>			special case error msgs
   1004 R$* : $* ; <@>		$#error $@ 5.1.3 $: "_CODE553 List:; syntax illegal for recipient addresses"
   1005 R@ <@ $* >		< @ $1 >		catch "@@host" bogosity
   1006 R<@ $+>			$#error $@ 5.1.3 $: "_CODE553 User address required"
   1007 R$+ <@>			$#error $@ 5.1.3 $: "_CODE553 Hostname required"
   1008 R$*			$: <> $1
   1009 dnl allow tricks like [host1]:[host2]
   1010 R<> $* < @ [ $* ] : $+ > $*	$1 < @ [ $2 ] : $3 > $4
   1011 R<> $* < @ [ $* ] , $+ > $*	$1 < @ [ $2 ] , $3 > $4
   1012 dnl but no a@[b]c
   1013 R<> $* < @ [ $* ] $+ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid address"
   1014 R<> $* < @ [ $+ ] > $*		$1 < @ [ $2 ] > $3
   1015 R<> $* <$* : $* > $*	$#error $@ 5.1.3 $: "_CODE553 Colon illegal in host name part"
   1016 R<> $*			$1
   1017 R$* < @ . $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
   1018 R$* < @ $* .. $* > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid host name"
   1019 dnl no a@b@
   1020 R$* < @ $* @ > $*	$#error $@ 5.1.2 $: "_CODE553 Invalid route address"
   1021 dnl no a@b@c
   1022 R$* @ $* < @ $* > $*	$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
   1023 dnl comma only allowed before @; this check is not complete
   1024 R$* , $~O $*		$#error $@ 5.1.3 $: "_CODE553 Invalid route address"
   1025 
   1026 ifdef(`_STRICT_RFC821_', `# more RFC 821 checks
   1027 R$* . < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not end with a dot"
   1028 R. $* < @ $* > $*	$#error $@ 5.1.2 $: "_CODE553 Local part must not begin with a dot"
   1029 dnl', `dnl')
   1030 
   1031 # now delete the local info -- note $=O to find characters that cause forwarding
   1032 R$* < @ > $*		$@ $>Parse0 $>canonify $1	user@ => user
   1033 R< @ $=w . > : $*	$@ $>Parse0 $>canonify $2	@here:... -> ...
   1034 R$- < @ $=w . >		$: $(dequote $1 $) < @ $2 . >	dequote "foo"@here
   1035 R< @ $+ >		$#error $@ 5.1.3 $: "_CODE553 User address required"
   1036 R$* $=O $* < @ $=w . >	$@ $>Parse0 $>canonify $1 $2 $3	...@here -> ...
   1037 R$- 			$: $(dequote $1 $) < @ *LOCAL* >	dequote "foo"
   1038 R< @ *LOCAL* >		$#error $@ 5.1.3 $: "_CODE553 User address required"
   1039 R$* $=O $* < @ *LOCAL* >
   1040 			$@ $>Parse0 $>canonify $1 $2 $3	...@*LOCAL* -> ...
   1041 R$* < @ *LOCAL* >	$: $1
   1042 
   1043 #
   1044 #  Parse1 -- the bottom half of ruleset 0.
   1045 #
   1046 
   1047 SParse1
   1048 ifdef(`_LDAP_ROUTING_', `dnl
   1049 # handle LDAP routing for hosts in $={LDAPRoute}
   1050 R$+ < @ $={LDAPRoute} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $2> <>
   1051 R$+ < @ $={LDAPRouteEquiv} . >	$: $>LDAPExpand <$1 < @ $2 . >> <$1 @ $M> <>',
   1052 `dnl')
   1053 
   1054 ifdef(`_MAILER_smtp_',
   1055 `# handle numeric address spec
   1056 dnl there is no check whether this is really an IP number
   1057 R$* < @ [ $+ ] > $*	$: $>ParseLocal $1 < @ [ $2 ] > $3	numeric internet spec
   1058 R$* < @ [ $+ ] > $*	$: $1 < @ [ $2 ] : $S > $3	Add smart host to path
   1059 R$* < @ [ $+ ] : > $*		$#_SMTP_ $@ [$2] $: $1 < @ [$2] > $3	no smarthost: send
   1060 R$* < @ [ $+ ] : $- : $*> $*	$#$3 $@ $4 $: $1 < @ [$2] > $5	smarthost with mailer
   1061 R$* < @ [ $+ ] : $+ > $*	$#_SMTP_ $@ $3 $: $1 < @ [$2] > $4	smarthost without mailer',
   1062 	`dnl')
   1063 
   1064 ifdef(`_VIRTUSER_TABLE_', `dnl
   1065 # handle virtual users
   1066 ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
   1067 dnl this is not a documented option
   1068 dnl it stops looping in virtusertable mapping if input and output
   1069 dnl are identical, i.e., if address A is mapped to A.
   1070 dnl it does not deal with multi-level recursion
   1071 # handle full domains in RHS of virtusertable
   1072 R$+ < @ $+ >			$: $(macro {RecipientAddress} $) $1 < @ $2 >
   1073 R$+ < @ $+ > 			$: <?> $1 < @ $2 > $| $>final $1 < @ $2 >
   1074 R<?> $+ $| $+			$: $1 $(macro {RecipientAddress} $@ $2 $)
   1075 R<?> $+ $| $*			$: $1',
   1076 `dnl')
   1077 R$+			$: <!> $1		Mark for lookup
   1078 dnl input: <!> local<@domain>
   1079 ifdef(`_VIRTUSER_ENTIRE_DOMAIN_',
   1080 `R<!> $+ < @ $* $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $3 $@ $1 $: @ $) > $1 < @ $2 $3 . >',
   1081 `R<!> $+ < @ $={VirtHost} . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >')
   1082 dnl input: <result-of-lookup | @> local<@domain> | <!> local<@domain>
   1083 R<!> $+ < @ $=w . > 	$: < $(virtuser $1 @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
   1084 dnl if <@> local<@domain>: no match but try lookup
   1085 dnl user+detail: try user++@domain if detail not empty
   1086 R<@> $+ + $+ < @ $* . >
   1087 			$: < $(virtuser $1 + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
   1088 dnl user+detail: try user+*@domain
   1089 R<@> $+ + $* < @ $* . >
   1090 			$: < $(virtuser $1 + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
   1091 dnl user+detail: try user@domain
   1092 R<@> $+ + $* < @ $* . >
   1093 			$: < $(virtuser $1 @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
   1094 dnl try default entry: @domain
   1095 dnl ++@domain
   1096 R<@> $+ + $+ < @ $+ . >	$: < $(virtuser + + @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
   1097 dnl +*@domain
   1098 R<@> $+ + $* < @ $+ . >	$: < $(virtuser + * @ $3 $@ $1 $@ $2 $@ +$2 $: @ $) > $1 + $2 < @ $3 . >
   1099 dnl @domain if +detail exists
   1100 dnl if no match, change marker to prevent a second @domain lookup
   1101 R<@> $+ + $* < @ $+ . >	$: < $(virtuser @ $3 $@ $1 $@ $2 $@ +$2 $: ! $) > $1 + $2 < @ $3 . >
   1102 dnl without +detail
   1103 R<@> $+ < @ $+ . >	$: < $(virtuser @ $2 $@ $1 $: @ $) > $1 < @ $2 . >
   1104 dnl no match
   1105 R<@> $+			$: $1
   1106 dnl remove mark
   1107 R<!> $+			$: $1
   1108 R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
   1109 R< error : $- $+ > $* 	$#error $@ $(dequote $1 $) $: $2
   1110 ifdef(`_VIRTUSER_STOP_ONE_LEVEL_RECURSION_',`dnl
   1111 # check virtuser input address against output address, if same, skip recursion
   1112 R< $+ > $+ < @ $+ >				$: < $1 > $2 < @ $3 > $| $1
   1113 # it is the same: stop now
   1114 R< $+ > $+ < @ $+ > $| $&{RecipientAddress}	$: $>ParseLocal $>Parse0 $>canonify $1
   1115 R< $+ > $+ < @ $+ > $| $* 			$: < $1 > $2 < @ $3 >
   1116 dnl', `dnl')
   1117 dnl this is not a documented option
   1118 dnl it performs no looping at all for virtusertable
   1119 ifdef(`_NO_VIRTUSER_RECURSION_',
   1120 `R< $+ > $+ < @ $+ >	$: $>ParseLocal $>Parse0 $>canonify $1',
   1121 `R< $+ > $+ < @ $+ >	$: $>Recurse $1')
   1122 dnl', `dnl')
   1123 
   1124 # short circuit local delivery so forwarded email works
   1125 ifdef(`_MAILER_usenet_', `dnl
   1126 R$+ . USENET < @ $=w . >	$#usenet $@ usenet $: $1	handle usenet specially', `dnl')
   1127 
   1128 
   1129 ifdef(`_STICKY_LOCAL_DOMAIN_',
   1130 `R$+ < @ $=w . >		$: < $H > $1 < @ $2 . >		first try hub
   1131 R< $+ > $+ < $+ >	$>MailerToTriple < $1 > $2 < $3 >	yep ....
   1132 dnl $H empty (but @$=w.)
   1133 R< > $+ + $* < $+ >	$#_LOCAL_ $: $1 + $2		plussed name?
   1134 R< > $+ < $+ >		$#_LOCAL_ $: @ $1			nope, local address',
   1135 `R$=L < @ $=w . >	$#_LOCAL_ $: @ $1			special local names
   1136 R$+ < @ $=w . >		$#_LOCAL_ $: $1			regular local name')
   1137 
   1138 ifdef(`_MAILER_TABLE_', `dnl
   1139 # not local -- try mailer table lookup
   1140 R$* <@ $+ > $*		$: < $2 > $1 < @ $2 > $3	extract host name
   1141 R< $+ . > $*		$: < $1 > $2			strip trailing dot
   1142 R< $+ > $*		$: < $(mailertable $1 $) > $2	lookup
   1143 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
   1144 R< $~[ : $* > $* 	$>MailerToTriple < $1 : $2 > $3		check -- resolved?
   1145 R< $+ > $*		$: $>Mailertable <$1> $2		try domain',
   1146 `dnl')
   1147 undivert(4)dnl UUCP rules from `MAILER(uucp)'
   1148 
   1149 ifdef(`_NO_UUCP_', `dnl',
   1150 `# resolve remotely connected UUCP links (if any)
   1151 ifdef(`_CLASS_V_',
   1152 `R$* < @ $=V . UUCP . > $*		$: $>MailerToTriple < $V > $1 <@$2.UUCP.> $3',
   1153 	`dnl')
   1154 ifdef(`_CLASS_W_',
   1155 `R$* < @ $=W . UUCP . > $*		$: $>MailerToTriple < $W > $1 <@$2.UUCP.> $3',
   1156 	`dnl')
   1157 ifdef(`_CLASS_X_',
   1158 `R$* < @ $=X . UUCP . > $*		$: $>MailerToTriple < $X > $1 <@$2.UUCP.> $3',
   1159 	`dnl')')
   1160 
   1161 # resolve fake top level domains by forwarding to other hosts
   1162 ifdef(`BITNET_RELAY',
   1163 `R$*<@$+.BITNET.>$*	$: $>MailerToTriple < $B > $1 <@$2.BITNET.> $3	user (a] host.BITNET',
   1164 	`dnl')
   1165 ifdef(`DECNET_RELAY',
   1166 `R$*<@$+.DECNET.>$*	$: $>MailerToTriple < $C > $1 <@$2.DECNET.> $3	user (a] host.DECNET',
   1167 	`dnl')
   1168 ifdef(`_MAILER_pop_',
   1169 `R$+ < @ POP. >		$#pop $: $1			user@POP',
   1170 	`dnl')
   1171 ifdef(`_MAILER_fax_',
   1172 `R$+ < @ $+ .FAX. >	$#fax $@ $2 $: $1		user (a] host.FAX',
   1173 `ifdef(`FAX_RELAY',
   1174 `R$*<@$+.FAX.>$*		$: $>MailerToTriple < $F > $1 <@$2.FAX.> $3	user (a] host.FAX',
   1175 	`dnl')')
   1176 
   1177 ifdef(`UUCP_RELAY',
   1178 `# forward non-local UUCP traffic to our UUCP relay
   1179 R$*<@$*.UUCP.>$*		$: $>MailerToTriple < $Y > $1 <@$2.UUCP.> $3	uucp mail',
   1180 `ifdef(`_MAILER_uucp_',
   1181 `# forward other UUCP traffic straight to UUCP
   1182 R$* < @ $+ .UUCP. > $*		$#_UUCP_ $@ $2 $: $1 < @ $2 .UUCP. > $3	user (a] host.UUCP',
   1183 	`dnl')')
   1184 ifdef(`_MAILER_usenet_', `
   1185 # addresses sent to net.group.USENET will get forwarded to a newsgroup
   1186 R$+ . USENET		$#usenet $@ usenet $: $1',
   1187 	`dnl')
   1188 
   1189 ifdef(`_LOCAL_RULES_',
   1190 `# figure out what should stay in our local mail system
   1191 undivert(1)', `dnl')
   1192 
   1193 # pass names that still have a host to a smarthost (if defined)
   1194 R$* < @ $* > $*		$: $>MailerToTriple < $S > $1 < @ $2 > $3	glue on smarthost name
   1195 
   1196 # deal with other remote names
   1197 ifdef(`_MAILER_smtp_',
   1198 `R$* < @$* > $*		$#_SMTP_ $@ $2 $: $1 < @ $2 > $3	user (a] host.domain',
   1199 `R$* < @$* > $*		$#error $@ 5.1.2 $: "_CODE553 Unrecognized host name " $2')
   1200 
   1201 # handle locally delivered names
   1202 R$=L			$#_LOCAL_ $: @ $1		special local names
   1203 R$+			$#_LOCAL_ $: $1			regular local names
   1204 
   1205 ###########################################################################
   1206 ###   Ruleset 5 -- special rewriting after aliases have been expanded   ###
   1207 ###########################################################################
   1208 
   1209 SLocal_localaddr
   1210 Slocaladdr=5
   1211 R$+			$: $1 $| $>"Local_localaddr" $1
   1212 R$+ $| $#ok		$@ $1			no change
   1213 R$+ $| $#$*		$#$2
   1214 R$+ $| $*		$: $1
   1215 
   1216 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
   1217 # Preserve rcpt_host in {Host}
   1218 R$+			$: $1 $| $&h $| $&{Host}	check h and {Host}
   1219 R$+ $| $|		$: $(macro {Host} $@ $) $1	no h or {Host}
   1220 R$+ $| $| $+		$: $1			h not set, {Host} set
   1221 R$+ $| +$* $| $*	$: $1			h is +detail, {Host} set
   1222 R$+ $| $* @ $+ $| $*	$: $(macro {Host} $@ @$3 $) $1	set {Host} to host in h
   1223 R$+ $| $+ $| $*		$: $(macro {Host} $@ @$2 $) $1	set {Host} to h
   1224 ')dnl
   1225 
   1226 ifdef(`_FFR_5_', `dnl
   1227 # Preserve host in a macro
   1228 R$+			$: $(macro {LocalAddrHost} $) $1
   1229 R$+ @ $+		$: $(macro {LocalAddrHost} $@ @ $2 $) $1')
   1230 
   1231 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `', `dnl
   1232 # deal with plussed users so aliases work nicely
   1233 R$+ + *			$#_LOCAL_ $@ $&h $: $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
   1234 R$+ + $*		$#_LOCAL_ $@ + $2 $: $1 + *`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')
   1235 ')
   1236 # prepend an empty "forward host" on the front
   1237 R$+			$: <> $1
   1238 
   1239 ifdef(`LUSER_RELAY', `dnl
   1240 # send unrecognized local users to a relay host
   1241 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
   1242 R< > $+ + $*		$: < ? $L > <+ $2> $(user $1 $)	look up user+
   1243 R< > $+			$: < ? $L > < > $(user $1 $)	look up user
   1244 R< ? $* > < $* > $+ <>	$: < > $3 $2			found; strip $L
   1245 R< ? $* > < $* > $+	$: < $1 > $3 $2			not found', `
   1246 R< > $+ 		$: < $L > $(user $1 $)		look up user
   1247 R< $* > $+ <>		$: < > $2			found; strip $L')
   1248 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
   1249 R< $+ > $+		$: < $1 > $2 $&{Host}')
   1250 dnl')
   1251 
   1252 ifdef(`MAIL_HUB', `dnl
   1253 R< > $+			$: < $H > $1			try hub', `dnl')
   1254 ifdef(`LOCAL_RELAY', `dnl
   1255 R< > $+			$: < $R > $1			try relay', `dnl')
   1256 ifdef(`_PRESERVE_LOCAL_PLUS_DETAIL_', `dnl
   1257 R< > $+			$@ $1', `dnl
   1258 R< > $+			$: < > < $1 <> $&h >		nope, restore +detail
   1259 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
   1260 R< > < $+ @ $+ <> + $* >	$: < > < $1 + $3 @ $2 >	check whether +detail')
   1261 R< > < $+ <> + $* >	$: < > < $1 + $2 >		check whether +detail
   1262 R< > < $+ <> $* >	$: < > < $1 >			else discard
   1263 R< > < $+ + $* > $*	   < > < $1 > + $2 $3		find the user part
   1264 R< > < $+ > + $*	$#_LOCAL_ $@ $2 $: @ $1`'ifdef(`_FFR_5_', ` $&{LocalAddrHost}')		strip the extra +
   1265 R< > < $+ >		$@ $1				no +detail
   1266 R$+			$: $1 <> $&h			add +detail back in
   1267 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
   1268 R$+ @ $+ <> + $*	$: $1 + $3 @ $2			check whether +detail')
   1269 R$+ <> + $*		$: $1 + $2			check whether +detail
   1270 R$+ <> $*		$: $1				else discard')
   1271 R< local : $* > $*	$: $>MailerToTriple < local : $1 > $2	no host extension
   1272 R< error : $* > $*	$: $>MailerToTriple < error : $1 > $2	no host extension
   1273 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
   1274 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
   1275 R< $~[ : $+ > $+ @ $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $4 >')
   1276 R< $~[ : $+ > $+	$: $>MailerToTriple < $1 : $2 > $3 < @ $2 >
   1277 ifdef(`_PRESERVE_LUSER_HOST_', `dnl
   1278 R< $+ > $+ @ $+		$@ $>MailerToTriple < $1 > $2 < @ $3 >')
   1279 R< $+ > $+		$@ $>MailerToTriple < $1 > $2 < @ $1 >
   1280 
   1281 ifdef(`_MAILER_TABLE_', `dnl
   1282 ifdef(`_LDAP_ROUTING_', `dnl
   1283 ###################################################################
   1284 ###  Ruleset LDAPMailertable -- mailertable lookup for LDAP     ###
   1285 dnl input: <Domain> FullAddress
   1286 ###################################################################
   1287 
   1288 SLDAPMailertable
   1289 R< $+ > $*		$: < $(mailertable $1 $) > $2		lookup
   1290 R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		check resolved?
   1291 R< $+ > $*		$: < $1 > $>Mailertable <$1> $2		try domain
   1292 R< $+ > $#$*		$#$2					found
   1293 R< $+ > $*		$#_RELAY_ $@ $1 $: $2			not found, direct relay',
   1294 `dnl')
   1295 
   1296 ###################################################################
   1297 ###  Ruleset 90 -- try domain part of mailertable entry 	###
   1298 dnl input: LeftPartOfDomain <RightPartOfDomain> FullAddress
   1299 ###################################################################
   1300 
   1301 SMailertable=90
   1302 dnl shift and check
   1303 dnl %2 is not documented in cf/README
   1304 R$* <$- . $+ > $*	$: $1$2 < $(mailertable .$3 $@ $1$2 $@ $2 $) > $4
   1305 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
   1306 R$* <$~[ : $* > $*	$>MailerToTriple < $2 : $3 > $4		check -- resolved?
   1307 R$* < . $+ > $* 	$@ $>Mailertable $1 . <$2> $3		no -- strip & try again
   1308 dnl is $2 always empty?
   1309 R$* < $* > $*		$: < $(mailertable . $@ $1$2 $) > $3	try "."
   1310 R< $~[ : $* > $*	$>MailerToTriple < $1 : $2 > $3		"." found?
   1311 dnl return full address
   1312 R< $* > $*		$@ $2				no mailertable match',
   1313 `dnl')
   1314 
   1315 ###################################################################
   1316 ###  Ruleset 95 -- canonify mailer:[user@]host syntax to triple	###
   1317 dnl input: in general: <[mailer:]host> lp<@domain>rest
   1318 dnl	<> address				-> address
   1319 dnl	<error:d.s.n:text>			-> error
   1320 dnl	<error:keyword:text>			-> error
   1321 dnl	<error:text>				-> error
   1322 dnl	<mailer:user@host> lp<@domain>rest	-> mailer host user
   1323 dnl	<mailer:host> address			-> mailer host address
   1324 dnl	<localdomain> address			-> address
   1325 dnl	<host> address				-> relay host address
   1326 ###################################################################
   1327 
   1328 SMailerToTriple=95
   1329 R< > $*				$@ $1			strip off null relay
   1330 R< error : $-.$-.$- : $+ > $* 	$#error $@ $1.$2.$3 $: $4
   1331 R< error : $- : $+ > $*		$#error $@ $(dequote $1 $) $: $2
   1332 R< error : $+ > $*		$#error $: $1
   1333 R< local : $* > $*		$>CanonLocal < $1 > $2
   1334 dnl it is $~[ instead of $- to avoid matches on IPv6 addresses
   1335 R< $~[ : $+ @ $+ > $*<$*>$*	$# $1 $@ $3 $: $2<@$3>	use literal user
   1336 R< $~[ : $+ > $*		$# $1 $@ $2 $: $3	try qualified mailer
   1337 R< $=w > $*			$@ $2			delete local host
   1338 R< $+ > $*			$#_RELAY_ $@ $1 $: $2	use unqualified mailer
   1339 
   1340 ###################################################################
   1341 ###  Ruleset CanonLocal -- canonify local: syntax		###
   1342 dnl input: <user> address
   1343 dnl <x> <@host> : rest			-> Recurse rest
   1344 dnl <x> p1 $=O p2 <@host>		-> Recurse p1 $=O p2
   1345 dnl <> user <@host> rest		-> local user@host user
   1346 dnl <> user				-> local user user
   1347 dnl <user@host> lp <@domain> rest	-> <user> lp <@host> [cont]
   1348 dnl <user> lp <@host> rest		-> local lp@host user
   1349 dnl <user> lp				-> local lp user
   1350 ###################################################################
   1351 
   1352 SCanonLocal
   1353 # strip local host from routed addresses
   1354 R< $* > < @ $+ > : $+		$@ $>Recurse $3
   1355 R< $* > $+ $=O $+ < @ $+ >	$@ $>Recurse $2 $3 $4
   1356 
   1357 # strip trailing dot from any host name that may appear
   1358 R< $* > $* < @ $* . >		$: < $1 > $2 < @ $3 >
   1359 
   1360 # handle local: syntax -- use old user, either with or without host
   1361 R< > $* < @ $* > $*		$#_LOCAL_ $@ $1@$2 $: $1
   1362 R< > $+				$#_LOCAL_ $@ $1    $: $1
   1363 
   1364 # handle local:user@host syntax -- ignore host part
   1365 R< $+ @ $+ > $* < @ $* >	$: < $1 > $3 < @ $4 >
   1366 
   1367 # handle local:user syntax
   1368 R< $+ > $* <@ $* > $*		$#_LOCAL_ $@ $2@$3 $: $1
   1369 R< $+ > $* 			$#_LOCAL_ $@ $2    $: $1
   1370 
   1371 ###################################################################
   1372 ###  Ruleset 93 -- convert header names to masqueraded form	###
   1373 ###################################################################
   1374 
   1375 SMasqHdr=93
   1376 
   1377 ifdef(`_GENERICS_TABLE_', `dnl
   1378 # handle generics database
   1379 ifdef(`_GENERICS_ENTIRE_DOMAIN_',
   1380 dnl if generics should be applied add a @ as mark
   1381 `R$+ < @ $* $=G . >	$: < $1@$2$3 > $1 < @ $2$3 . > @	mark',
   1382 `R$+ < @ $=G . >	$: < $1@$2 > $1 < @ $2 . > @	mark')
   1383 R$+ < @ *LOCAL* >	$: < $1@$j > $1 < @ *LOCAL* > @	mark
   1384 dnl workspace: either user<@domain> or <user@domain> user <@domain> @
   1385 dnl ignore the first case for now
   1386 dnl if it has the mark lookup full address
   1387 dnl broken: %1 is full address not just detail
   1388 R< $+ > $+ < $* > @	$: < $(generics $1 $: @ $1 $) > $2 < $3 >
   1389 dnl workspace: ... or <match|@user@domain> user <@domain>
   1390 dnl no match, try user+detail@domain
   1391 R<@$+ + $* @ $+> $+ < @ $+ >
   1392 		$: < $(generics $1+*@$3 $@ $2 $:@$1 + $2@$3 $) >  $4 < @ $5 >
   1393 R<@$+ + $* @ $+> $+ < @ $+ >
   1394 		$: < $(generics $1@$3 $: $) > $4 < @ $5 >
   1395 dnl no match, remove mark
   1396 R<@$+ > $+ < @ $+ >	$: < > $2 < @ $3 >
   1397 dnl no match, try @domain for exceptions
   1398 R< > $+ < @ $+ . >	$: < $(generics @$2 $@ $1 $: $) > $1 < @ $2 . >
   1399 dnl workspace: ... or <match> user <@domain>
   1400 dnl no match, try local part
   1401 R< > $+ < @ $+ > 	$: < $(generics $1 $: $) > $1 < @ $2 >
   1402 R< > $+ + $* < @ $+ > 	$: < $(generics $1+* $@ $2 $: $) > $1 + $2 < @ $3 >
   1403 R< > $+ + $* < @ $+ > 	$: < $(generics $1 $: $) > $1 + $2 < @ $3 >
   1404 R< $* @ $* > $* < $* >	$@ $>canonify $1 @ $2		found qualified
   1405 R< $+ > $* < $* >	$: $>canonify $1 @ *LOCAL*	found unqualified
   1406 R< > $*			$: $1				not found',
   1407 `dnl')
   1408 
   1409 # do not masquerade anything in class N
   1410 R$* < @ $* $=N . >	$@ $1 < @ $2 $3 . >
   1411 
   1412 ifdef(`MASQUERADE_NAME', `dnl
   1413 # special case the users that should be exposed
   1414 R$=E < @ *LOCAL* >	$@ $1 < @ $j . >		leave exposed
   1415 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
   1416 `R$=E < @ $* $=M . >	$@ $1 < @ $2 $3 . >',
   1417 `R$=E < @ $=M . >	$@ $1 < @ $2 . >')
   1418 ifdef(`_LIMITED_MASQUERADE_', `dnl',
   1419 `R$=E < @ $=w . >	$@ $1 < @ $2 . >')
   1420 
   1421 # handle domain-specific masquerading
   1422 ifdef(`_MASQUERADE_ENTIRE_DOMAIN_',
   1423 `R$* < @ $* $=M . > $*	$: $1 < @ $2 $3 . @ $M > $4	convert masqueraded doms',
   1424 `R$* < @ $=M . > $*	$: $1 < @ $2 . @ $M > $3	convert masqueraded doms')
   1425 ifdef(`_LIMITED_MASQUERADE_', `dnl',
   1426 `R$* < @ $=w . > $*	$: $1 < @ $2 . @ $M > $3')
   1427 R$* < @ *LOCAL* > $*	$: $1 < @ $j . @ $M > $2
   1428 R$* < @ $+ @ > $*	$: $1 < @ $2 > $3		$M is null
   1429 R$* < @ $+ @ $+ > $*	$: $1 < @ $3 . > $4		$M is not null
   1430 dnl', `dnl no masquerading
   1431 dnl just fix *LOCAL* leftovers
   1432 R$* < @ *LOCAL* >	$@ $1 < @ $j . >')
   1433 
   1434 ###################################################################
   1435 ###  Ruleset 94 -- convert envelope names to masqueraded form	###
   1436 ###################################################################
   1437 
   1438 SMasqEnv=94
   1439 ifdef(`_MASQUERADE_ENVELOPE_',
   1440 `R$+			$@ $>MasqHdr $1',
   1441 `R$* < @ *LOCAL* > $*	$: $1 < @ $j . > $2')
   1442 
   1443 ###################################################################
   1444 ###  Ruleset 98 -- local part of ruleset zero (can be null)	###
   1445 ###################################################################
   1446 
   1447 SParseLocal=98
   1448 undivert(3)dnl LOCAL_RULE_0
   1449 
   1450 ifdef(`_LDAP_ROUTING_', `dnl
   1451 ######################################################################
   1452 ###  LDAPExpand: Expand address using LDAP routing
   1453 ###
   1454 ###	Parameters:
   1455 ###		<$1> -- parsed address (user < @ domain . >) (pass through)
   1456 ###		<$2> -- RFC822 address (user @ domain) (used for lookup)
   1457 ###		<$3> -- +detail information
   1458 ###
   1459 ###	Returns:
   1460 ###		Mailer triplet ($#mailer $@ host $: address)
   1461 ###		Parsed address (user < @ domain . >)
   1462 ######################################################################
   1463 
   1464 # SMTP operation modes
   1465 C{SMTPOpModes} s d D
   1466 
   1467 SLDAPExpand
   1468 # do the LDAP lookups
   1469 R<$+><$+><$*>	$: <$(ldapmra $2 $: $)> <$(ldapmh $2 $: $)> <$1> <$2> <$3>
   1470 
   1471 # look for temporary failures and...
   1472 R<$* <TMPF>> <$*> <$+> <$+> <$*>	$: $&{opMode} $| TMPF <$&{addr_type}> $| $3
   1473 R<$*> <$* <TMPF>> <$+> <$+> <$*>	$: $&{opMode} $| TMPF <$&{addr_type}> $| $3
   1474 ifelse(_LDAP_ROUTE_MAPTEMP_, `_TEMPFAIL_', `dnl
   1475 # ... temp fail RCPT SMTP commands
   1476 R$={SMTPOpModes} $| TMPF <e r> $| $+	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."')
   1477 # ... return original address for MTA to queue up
   1478 R$* $| TMPF <$*> $| $+			$@ $3
   1479 
   1480 # if mailRoutingAddress and local or non-existant mailHost,
   1481 # return the new mailRoutingAddress
   1482 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
   1483 R<$+@$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $6 @ $2
   1484 R<$+@$+> <> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1 $5 @ $2')
   1485 R<$+> <$=w> <$+> <$+> <$*>	$@ $>Parse0 $>canonify $1
   1486 R<$+> <> <$+> <$+> <$*>		$@ $>Parse0 $>canonify $1
   1487 
   1488 
   1489 # if mailRoutingAddress and non-local mailHost,
   1490 # relay to mailHost with new mailRoutingAddress
   1491 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
   1492 ifdef(`_MAILER_TABLE_', `dnl
   1493 # check mailertable for host, relay from there
   1494 R<$+@$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$3> $>canonify $1 $6 @ $2',
   1495 `R<$+@$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $3 $: $>canonify $1 $6 @ $2')')
   1496 ifdef(`_MAILER_TABLE_', `dnl
   1497 # check mailertable for host, relay from there
   1498 R<$+> <$+> <$+> <$+> <$*>	$>LDAPMailertable <$2> $>canonify $1',
   1499 `R<$+> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $2 $: $>canonify $1')
   1500 
   1501 # if no mailRoutingAddress and local mailHost,
   1502 # return original address
   1503 R<> <$=w> <$+> <$+> <$*>	$@ $2
   1504 
   1505 
   1506 # if no mailRoutingAddress and non-local mailHost,
   1507 # relay to mailHost with original address
   1508 ifdef(`_MAILER_TABLE_', `dnl
   1509 # check mailertable for host, relay from there
   1510 R<> <$+> <$+> <$+> <$*>		$>LDAPMailertable <$1> $2',
   1511 `R<> <$+> <$+> <$+> <$*>	$#_RELAY_ $@ $1 $: $2')
   1512 
   1513 ifdef(`_LDAP_ROUTE_DETAIL_',
   1514 `# if no mailRoutingAddress and no mailHost,
   1515 # try without +detail
   1516 R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <$2 @ $4> <+$3>')dnl
   1517 
   1518 ifdef(`_LDAP_ROUTE_NODOMAIN_', `
   1519 # pretend we did the @domain lookup
   1520 R<> <> <$+> <$+ @ $+> <$*>	$: <> <> <$1> <@ $3> <$4>', `
   1521 # if still no mailRoutingAddress and no mailHost,
   1522 # try @domain
   1523 ifelse(_LDAP_ROUTE_DETAIL_, `_PRESERVE_', `dnl
   1524 R<> <> <$+> <$+ + $* @ $+> <>	$@ $>LDAPExpand <$1> <@ $4> <+$3>')
   1525 R<> <> <$+> <$+ @ $+> <$*>	$@ $>LDAPExpand <$1> <@ $3> <$4>')
   1526 
   1527 # if no mailRoutingAddress and no mailHost and this was a domain attempt,
   1528 ifelse(_LDAP_ROUTING_, `_MUST_EXIST_', `dnl
   1529 # user does not exist
   1530 R<> <> <$+> <@ $+> <$*>		$: <?> < $&{addr_type} > < $1 >
   1531 # only give error for envelope recipient
   1532 R<?> <e r> <$+>			$#error $@ nouser $: "550 User unknown"
   1533 ifdef(`_LDAP_SENDER_MUST_EXIST_', `dnl
   1534 # and the sender too
   1535 R<?> <e s> <$+>			$#error $@ nouser $: "550 User unknown"')
   1536 R<?> <$*> <$+>			$@ $2',
   1537 `dnl
   1538 # return the original address
   1539 R<> <> <$+> <@ $+> <$*>		$@ $1')',
   1540 `dnl')
   1541 
   1542 ifelse(substr(confDELIVERY_MODE,0,1), `d', `errprint(`WARNING: Antispam rules not available in deferred delivery mode.
   1543 ')')
   1544 ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
   1545 ######################################################################
   1546 ###  D: LookUpDomain -- search for domain in access database
   1547 ###
   1548 ###	Parameters:
   1549 ###		<$1> -- key (domain name)
   1550 ###		<$2> -- default (what to return if not found in db)
   1551 dnl			must not be empty
   1552 ###		<$3> -- mark (must be <(!|+) single-token>)
   1553 ###			! does lookup only with tag
   1554 ###			+ does lookup with and without tag
   1555 ###		<$4> -- passthru (additional data passed unchanged through)
   1556 dnl returns:		<default> <passthru>
   1557 dnl 			<result> <passthru>
   1558 ######################################################################
   1559 
   1560 SD
   1561 dnl workspace <key> <default> <passthru> <mark>
   1562 dnl lookup with tag (in front, no delimiter here)
   1563 dnl    2    3  4    5
   1564 R<$*> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
   1565 dnl workspace <result-of-lookup|?> <key> <default> <passthru> <mark>
   1566 dnl lookup without tag?
   1567 dnl   1    2      3    4
   1568 R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
   1569 ifdef(`_LOOKUPDOTDOMAIN_', `dnl omit first component: lookup .rest
   1570 dnl XXX apply this also to IP addresses?
   1571 dnl currently it works the wrong way round for [1.2.3.4]
   1572 dnl   1  2    3    4  5    6
   1573 R<?> <$+.$+> <$+> <$- $-> <$*>	$: < $(access $5`'_TAG_DELIM_`'.$2 $: ? $) > <$1.$2> <$3> <$4 $5> <$6>
   1574 dnl   1  2    3      4    5
   1575 R<?> <$+.$+> <$+> <+ $-> <$*>	$: < $(access .$2 $: ? $) > <$1.$2> <$3> <+ $4> <$5>', `dnl')
   1576 ifdef(`_ACCESS_SKIP_', `dnl
   1577 dnl found SKIP: return <default> and <passthru>
   1578 dnl      1    2    3  4    5
   1579 R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
   1580 dnl not found: IPv4 net (no check is done whether it is an IP number!)
   1581 dnl    1  2     3    4  5    6
   1582 R<?> <[$+.$-]> <$+> <$- $-> <$*>	$@ $>D <[$1]> <$3> <$4 $5> <$6>
   1583 ifdef(`NO_NETINET6', `dnl',
   1584 `dnl not found: IPv6 net
   1585 dnl (could be merged with previous rule if we have a class containing .:)
   1586 dnl    1   2     3    4  5    6
   1587 R<?> <[$+::$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>
   1588 R<?> <[$+:$-]> <$+> <$- $-> <$*>	$: $>D <[$1]> <$3> <$4 $5> <$6>')
   1589 dnl not found, but subdomain: try again
   1590 dnl   1  2    3    4  5    6
   1591 R<?> <$+.$+> <$+> <$- $-> <$*>	$@ $>D <$2> <$3> <$4 $5> <$6>
   1592 ifdef(`_FFR_LOOKUPTAG_', `dnl lookup Tag:
   1593 dnl   1    2      3    4
   1594 R<?> <$+> <$+> <! $-> <$*>	$: < $(access $3`'_TAG_DELIM_ $: ? $) > <$1> <$2> <! $3> <$4>', `dnl')
   1595 dnl not found, no subdomain: return <default> and <passthru>
   1596 dnl   1    2    3  4    5
   1597 R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
   1598 ifdef(`_ATMPF_', `dnl tempfail?
   1599 dnl            2    3    4  5    6
   1600 R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
   1601 dnl return <result of lookup> and <passthru>
   1602 dnl    2    3    4  5    6
   1603 R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
   1604 
   1605 ######################################################################
   1606 ###  A: LookUpAddress -- search for host address in access database
   1607 ###
   1608 ###	Parameters:
   1609 ###		<$1> -- key (dot quadded host address)
   1610 ###		<$2> -- default (what to return if not found in db)
   1611 dnl			must not be empty
   1612 ###		<$3> -- mark (must be <(!|+) single-token>)
   1613 ###			! does lookup only with tag
   1614 ###			+ does lookup with and without tag
   1615 ###		<$4> -- passthru (additional data passed through)
   1616 dnl	returns:	<default> <passthru>
   1617 dnl			<result> <passthru>
   1618 ######################################################################
   1619 
   1620 SA
   1621 dnl lookup with tag
   1622 dnl    2    3  4    5
   1623 R<$+> <$+> <$- $-> <$*>		$: < $(access $4`'_TAG_DELIM_`'$1 $: ? $) > <$1> <$2> <$3 $4> <$5>
   1624 dnl lookup without tag
   1625 dnl   1    2      3    4
   1626 R<?> <$+> <$+> <+ $-> <$*>	$: < $(access $1 $: ? $) > <$1> <$2> <+ $3> <$4>
   1627 dnl workspace <result-of-lookup|?> <key> <default> <mark> <passthru>
   1628 ifdef(`_ACCESS_SKIP_', `dnl
   1629 dnl found SKIP: return <default> and <passthru>
   1630 dnl      1    2    3  4    5
   1631 R<SKIP> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>', `dnl')
   1632 ifdef(`NO_NETINET6', `dnl',
   1633 `dnl no match; IPv6: remove last part
   1634 dnl   1   2    3    4  5    6
   1635 R<?> <$+::$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
   1636 R<?> <$+:$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>')
   1637 dnl no match; IPv4: remove last part
   1638 dnl   1  2    3    4  5    6
   1639 R<?> <$+.$-> <$+> <$- $-> <$*>		$@ $>A <$1> <$3> <$4 $5> <$6>
   1640 dnl no match: return default
   1641 dnl   1    2    3  4    5
   1642 R<?> <$+> <$+> <$- $-> <$*>	$@ <$2> <$5>
   1643 ifdef(`_ATMPF_', `dnl tempfail?
   1644 dnl            2    3    4  5    6
   1645 R<$* _ATMPF_> <$+> <$+> <$- $-> <$*>	$@ <_ATMPF_> <$6>', `dnl')
   1646 dnl match: return result
   1647 dnl    2    3    4  5    6
   1648 R<$*> <$+> <$+> <$- $-> <$*>	$@ <$1> <$6>
   1649 dnl endif _ACCESS_TABLE_
   1650 divert(0)
   1651 ######################################################################
   1652 ###  CanonAddr --	Convert an address into a standard form for
   1653 ###			relay checking.  Route address syntax is
   1654 ###			crudely converted into a %-hack address.
   1655 ###
   1656 ###	Parameters:
   1657 ###		$1 -- full recipient address
   1658 ###
   1659 ###	Returns:
   1660 ###		parsed address, not in source route form
   1661 dnl		user%host%host<@domain>
   1662 dnl		host!user<@domain>
   1663 ######################################################################
   1664 
   1665 SCanonAddr
   1666 R$*			$: $>Parse0 $>canonify $1	make domain canonical
   1667 ifdef(`_USE_DEPRECATED_ROUTE_ADDR_',`dnl
   1668 R< @ $+ > : $* @ $*	< @ $1 > : $2 % $3	change @ to % in src route
   1669 R$* < @ $+ > : $* : $*	$3 $1 < @ $2 > : $4	change to % hack.
   1670 R$* < @ $+ > : $*	$3 $1 < @ $2 >
   1671 dnl')
   1672 
   1673 ######################################################################
   1674 ###  ParseRecipient --	Strip off hosts in $=R as well as possibly
   1675 ###			$* $=m or the access database.
   1676 ###			Check user portion for host separators.
   1677 ###
   1678 ###	Parameters:
   1679 ###		$1 -- full recipient address
   1680 ###
   1681 ###	Returns:
   1682 ###		parsed, non-local-relaying address
   1683 ######################################################################
   1684 
   1685 SParseRecipient
   1686 dnl mark and canonify address
   1687 R$*				$: <?> $>CanonAddr $1
   1688 dnl workspace: <?> localpart<@domain[.]>
   1689 R<?> $* < @ $* . >		<?> $1 < @ $2 >			strip trailing dots
   1690 dnl workspace: <?> localpart<@domain>
   1691 R<?> $- < @ $* >		$: <?> $(dequote $1 $) < @ $2 >	dequote local part
   1692 
   1693 # if no $=O character, no host in the user portion, we are done
   1694 R<?> $* $=O $* < @ $* >		$: <NO> $1 $2 $3 < @ $4>
   1695 dnl no $=O in localpart: return
   1696 R<?> $*				$@ $1
   1697 
   1698 dnl workspace: <NO> localpart<@domain>, where localpart contains $=O
   1699 dnl mark everything which has an "authorized" domain with <RELAY>
   1700 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
   1701 # if we relay, check username portion for user%host so host can be checked also
   1702 R<NO> $* < @ $* $=m >		$: <RELAY> $1 < @ $2 $3 >', `dnl')
   1703 dnl workspace: <(NO|RELAY)> localpart<@domain>, where localpart contains $=O
   1704 dnl if mark is <NO> then change it to <RELAY> if domain is "authorized"
   1705 
   1706 dnl what if access map returns something else than RELAY?
   1707 dnl we are only interested in RELAY entries...
   1708 dnl other To: entries: blacklist recipient; generic entries?
   1709 dnl if it is an error we probably do not want to relay anyway
   1710 ifdef(`_RELAY_HOSTS_ONLY_',
   1711 `R<NO> $* < @ $=R >		$: <RELAY> $1 < @ $2 >
   1712 ifdef(`_ACCESS_TABLE_', `dnl
   1713 R<NO> $* < @ $+ >		$: <$(access To:$2 $: NO $)> $1 < @ $2 >
   1714 R<NO> $* < @ $+ >		$: <$(access $2 $: NO $)> $1 < @ $2 >',`dnl')',
   1715 `R<NO> $* < @ $* $=R >		$: <RELAY> $1 < @ $2 $3 >
   1716 ifdef(`_ACCESS_TABLE_', `dnl
   1717 R<NO> $* < @ $+ >		$: $>D <$2> <NO> <+ To> <$1 < @ $2 >>
   1718 R<$+> <$+>			$: <$1> $2',`dnl')')
   1719 
   1720 
   1721 ifdef(`_RELAY_MX_SERVED_', `dnl
   1722 dnl do "we" ($=w) act as backup MX server for the destination domain?
   1723 R<NO> $* < @ $+ >		$: <MX> < : $(mxserved $2 $) : > < $1 < @$2 > >
   1724 R<MX> < : $* <TEMP> : > $*	$#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
   1725 dnl yes: mark it as <RELAY>
   1726 R<MX> < $* : $=w. : $* > < $+ >	$: <RELAY> $4
   1727 dnl no: put old <NO> mark back
   1728 R<MX> < : $* : > < $+ >		$: <NO> $2', `dnl')
   1729 
   1730 dnl do we relay to this recipient domain?
   1731 R<RELAY> $* < @ $* >		$@ $>ParseRecipient $1
   1732 dnl something else
   1733 R<$+> $*			$@ $2
   1734 
   1735 
   1736 ######################################################################
   1737 ###  check_relay -- check hostname/address on SMTP startup
   1738 ######################################################################
   1739 
   1740 ifdef(`_CONTROL_IMMEDIATE_',`dnl
   1741 Scheck_relay
   1742 ifdef(`_RATE_CONTROL_IMMEDIATE_',`dnl
   1743 dnl workspace: ignored...
   1744 R$*		$: $>"RateControl" dummy', `dnl')
   1745 ifdef(`_CONN_CONTROL_IMMEDIATE_',`dnl
   1746 dnl workspace: ignored...
   1747 R$*		$: $>"ConnControl" dummy', `dnl')
   1748 dnl')
   1749 
   1750 SLocal_check_relay
   1751 Scheck`'_U_`'relay
   1752 ifdef(`_USE_CLIENT_PTR_',`dnl
   1753 R$* $| $*		$: $&{client_ptr} $| $2', `dnl')
   1754 R$*			$: $1 $| $>"Local_check_relay" $1
   1755 R$* $| $* $| $#$*	$#$3
   1756 R$* $| $* $| $*		$@ $>"Basic_check_relay" $1 $| $2
   1757 
   1758 SBasic_check_relay
   1759 # check for deferred delivery mode
   1760 R$*			$: < $&{deliveryMode} > $1
   1761 R< d > $*		$@ deferred
   1762 R< $* > $*		$: $2
   1763 
   1764 ifdef(`_ACCESS_TABLE_', `dnl
   1765 dnl workspace: {client_name} $| {client_addr}
   1766 R$+ $| $+		$: $>D < $1 > <?> <+ Connect> < $2 >
   1767 dnl workspace: <result-of-lookup> <{client_addr}>
   1768 dnl OR $| $+ if client_name is empty
   1769 R   $| $+		$: $>A < $1 > <?> <+ Connect> <>	empty client_name
   1770 dnl workspace: <result-of-lookup> <{client_addr}>
   1771 R<?> <$+>		$: $>A < $1 > <?> <+ Connect> <>	no: another lookup
   1772 dnl workspace: <result-of-lookup> (<>|<{client_addr}>)
   1773 R<?> <$*>		$: OK				found nothing
   1774 dnl workspace: <result-of-lookup> (<>|<{client_addr}>) | OK
   1775 R<$={Accept}> <$*>	$@ $1				return value of lookup
   1776 R<REJECT> <$*>		$#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
   1777 R<DISCARD> <$*>		$#discard $: discard
   1778 R<QUARANTINE:$+> <$*>	$#error $@ quarantine $: $1
   1779 dnl error tag
   1780 R<ERROR:$-.$-.$-:$+> <$*>	$#error $@ $1.$2.$3 $: $4
   1781 R<ERROR:$+> <$*>		$#error $: $1
   1782 ifdef(`_ATMPF_', `R<$* _ATMPF_> <$*>		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   1783 dnl generic error from access map
   1784 R<$+> <$*>		$#error $: $1', `dnl')
   1785 
   1786 ifdef(`_RBL_',`dnl
   1787 # DNS based IP address spam list
   1788 dnl workspace: ignored...
   1789 R$*			$: $&{client_addr}
   1790 R$-.$-.$-.$-		$: <?> $(host $4.$3.$2.$1._RBL_. $: OK $)
   1791 R<?>OK			$: OKSOFAR
   1792 R<?>$+			$#error $@ 5.7.1 $: "550 Rejected: " $&{client_addr} " listed at _RBL_"',
   1793 `dnl')
   1794 ifdef(`_RATE_CONTROL_',`dnl
   1795 ifdef(`_RATE_CONTROL_IMMEDIATE_',`', `dnl
   1796 dnl workspace: ignored...
   1797 R$*		$: $>"RateControl" dummy')', `dnl')
   1798 ifdef(`_CONN_CONTROL_',`dnl
   1799 ifdef(`_CONN_CONTROL_IMMEDIATE_',`',`dnl
   1800 dnl workspace: ignored...
   1801 R$*		$: $>"ConnControl" dummy')', `dnl')
   1802 undivert(8)
   1803 ifdef(`_REQUIRE_RDNS_', `dnl
   1804 R$*			$: $&{client_addr} $| $&{client_resolve}
   1805 R$=R $*			$@ RELAY		We relay for these
   1806 R$* $| OK		$@ OK			Resolves.
   1807 R$* $| FAIL		$#error $@ 5.7.1 $: 550 Fix reverse DNS for $1
   1808 R$* $| TEMP		$#error $@ 4.1.8 $: 451 Client IP address $1 does not resolve
   1809 R$* $| FORGED		$#error $@ 4.1.8 $: 451 Possibly forged hostname for $1
   1810 ', `dnl')
   1811 
   1812 ######################################################################
   1813 ###  check_mail -- check SMTP ``MAIL FROM:'' command argument
   1814 ######################################################################
   1815 
   1816 SLocal_check_mail
   1817 Scheck`'_U_`'mail
   1818 R$*			$: $1 $| $>"Local_check_mail" $1
   1819 R$* $| $#$*		$#$2
   1820 R$* $| $*		$@ $>"Basic_check_mail" $1
   1821 
   1822 SBasic_check_mail
   1823 # check for deferred delivery mode
   1824 R$*			$: < $&{deliveryMode} > $1
   1825 R< d > $*		$@ deferred
   1826 R< $* > $*		$: $2
   1827 
   1828 # authenticated?
   1829 dnl done first: we can require authentication for every mail transaction
   1830 dnl workspace: address as given by MAIL FROM: (sender)
   1831 R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
   1832 R$* $| $#$+		$#$2
   1833 dnl undo damage: remove result of tls_client call
   1834 R$* $| $*		$: $1
   1835 
   1836 dnl workspace: address as given by MAIL FROM:
   1837 R<>			$@ <OK>			we MUST accept <> (RFC 1123)
   1838 ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
   1839 dnl do some additional checks
   1840 dnl no user@host
   1841 dnl no user@localhost (if nonlocal sender)
   1842 dnl this is a pretty simple canonification, it will not catch every case
   1843 dnl just make sure the address has <> around it (which is required by
   1844 dnl the RFC anyway, maybe we should complain if they are missing...)
   1845 dnl dirty trick: if it is user@host, just add a dot: user@host. this will
   1846 dnl not be modified by host lookups.
   1847 R$+			$: <?> $1
   1848 R<?><$+>		$: <@> <$1>
   1849 R<?>$+			$: <@> <$1>
   1850 dnl workspace: <@> <address>
   1851 dnl prepend daemon_flags
   1852 R$*			$: $&{daemon_flags} $| $1
   1853 dnl workspace: ${daemon_flags} $| <@> <address>
   1854 dnl do not allow these at all or only from local systems?
   1855 R$* f $* $| <@> < $* @ $- >	$: < ? $&{client_name} > < $3 @ $4 >
   1856 dnl accept unqualified sender: change mark to avoid test
   1857 R$* u $* $| <@> < $* >	$: <?> < $3 >
   1858 dnl workspace: ${daemon_flags} $| <@> <address>
   1859 dnl        or:                    <? ${client_name} > <address>
   1860 dnl        or:                    <?> <address>
   1861 dnl remove daemon_flags
   1862 R$* $| $*		$: $2
   1863 # handle case of @localhost on address
   1864 R<@> < $* @ localhost >	$: < ? $&{client_name} > < $1 @ localhost >
   1865 R<@> < $* @ [127.0.0.1] >
   1866 			$: < ? $&{client_name} > < $1 @ [127.0.0.1] >
   1867 R<@> < $* @ localhost.$m >
   1868 			$: < ? $&{client_name} > < $1 @ localhost.$m >
   1869 ifdef(`_NO_UUCP_', `dnl',
   1870 `R<@> < $* @ localhost.UUCP >
   1871 			$: < ? $&{client_name} > < $1 @ localhost.UUCP >')
   1872 dnl workspace: < ? $&{client_name} > <user@localhost|host>
   1873 dnl	or:    <@> <address>
   1874 dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
   1875 R<@> $*			$: $1			no localhost as domain
   1876 dnl workspace: < ? $&{client_name} > <user@localhost|host>
   1877 dnl	or:    <address>
   1878 dnl	or:    <?> <address>	(thanks to u in ${daemon_flags})
   1879 R<? $=w> $*		$: $2			local client: ok
   1880 R<? $+> <$+>		$#error $@ 5.5.4 $: "_CODE553 Real domain name required for sender address"
   1881 dnl remove <?> (happens only if ${client_name} == "" or u in ${daemon_flags})
   1882 R<?> $*			$: $1')
   1883 dnl workspace: address (or <address>)
   1884 R$*			$: <?> $>CanonAddr $1		canonify sender address and mark it
   1885 dnl workspace: <?> CanonicalAddress (i.e. address in canonical form localpart<@host>)
   1886 dnl there is nothing behind the <@host> so no trailing $* needed
   1887 R<?> $* < @ $+ . >	<?> $1 < @ $2 >			strip trailing dots
   1888 # handle non-DNS hostnames (*.bitnet, *.decnet, *.uucp, etc)
   1889 R<?> $* < @ $* $=P >	$: <_RES_OK_> $1 < @ $2 $3 >
   1890 dnl workspace <mark> CanonicalAddress	where mark is ? or OK
   1891 dnl A sender address with my local host name ($j) is safe
   1892 R<?> $* < @ $j >	$: <_RES_OK_> $1 < @ $j >
   1893 ifdef(`_ACCEPT_UNRESOLVABLE_DOMAINS_',
   1894 `R<?> $* < @ $+ >	$: <_RES_OK_> $1 < @ $2 >		... unresolvable OK',
   1895 `R<?> $* < @ $+ >	$: <? $(resolve $2 $: $2 <PERM> $) > $1 < @ $2 >
   1896 R<? $* <$->> $* < @ $+ >
   1897 			$: <$2> $3 < @ $4 >')
   1898 dnl workspace <mark> CanonicalAddress	where mark is ?, _RES_OK_, PERM, TEMP
   1899 dnl mark is ? iff the address is user (wo @domain)
   1900 
   1901 ifdef(`_ACCESS_TABLE_', `dnl
   1902 # check sender address: user@address, user@, address
   1903 dnl should we remove +ext from user?
   1904 dnl workspace: <mark> CanonicalAddress where mark is: ?, _RES_OK_, PERM, TEMP
   1905 R<$+> $+ < @ $* >	$: @<$1> <$2 < @ $3 >> $| <F:$2@$3> <U:$2@> <D:$3>
   1906 R<$+> $+		$: @<$1> <$2> $| <U:$2@>
   1907 dnl workspace: @<mark> <CanonicalAddress> $| <@type:address> ....
   1908 dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
   1909 dnl will only return user<@domain when "reversing" the args
   1910 R@ <$+> <$*> $| <$+>	$: <@> <$1> <$2> $| $>SearchList <+ From> $| <$3> <>
   1911 dnl workspace: <@><mark> <CanonicalAddress> $| <result>
   1912 R<@> <$+> <$*> $| <$*>	$: <$3> <$1> <$2>		reverse result
   1913 dnl workspace: <result> <mark> <CanonicalAddress>
   1914 # retransform for further use
   1915 dnl required form:
   1916 dnl <ResultOfLookup|mark> CanonicalAddress
   1917 R<?> <$+> <$*>		$: <$1> $2	no match
   1918 R<$+> <$+> <$*>		$: <$1> $3	relevant result, keep it', `dnl')
   1919 dnl workspace <ResultOfLookup|mark> CanonicalAddress
   1920 dnl mark is ? iff the address is user (wo @domain)
   1921 
   1922 ifdef(`_ACCEPT_UNQUALIFIED_SENDERS_',`dnl',`dnl
   1923 # handle case of no @domain on address
   1924 dnl prepend daemon_flags
   1925 R<?> $*			$: $&{daemon_flags} $| <?> $1
   1926 dnl accept unqualified sender: change mark to avoid test
   1927 R$* u $* $| <?> $*	$: <_RES_OK_> $3
   1928 dnl remove daemon_flags
   1929 R$* $| $*		$: $2
   1930 R<?> $*			$: < ? $&{client_addr} > $1
   1931 R<?> $*			$@ <_RES_OK_>			...local unqualed ok
   1932 R<? $+> $*		$#error $@ 5.5.4 $: "_CODE553 Domain name required for sender address " $&f
   1933 							...remote is not')
   1934 # check results
   1935 R<?> $*			$: @ $1		mark address: nothing known about it
   1936 R<$={ResOk}> $*		$: @ $2		domain ok
   1937 R<TEMP> $*		$#error $@ 4.1.8 $: "451 Domain of sender address " $&f " does not resolve"
   1938 R<PERM> $*		$#error $@ 5.1.8 $: "_CODE553 Domain of sender address " $&f " does not exist"
   1939 ifdef(`_ACCESS_TABLE_', `dnl
   1940 R<$={Accept}> $*	$# $1		accept from access map
   1941 R<DISCARD> $*		$#discard $: discard
   1942 R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1
   1943 R<REJECT> $*		$#error ifdef(`confREJECT_MSG', `$: confREJECT_MSG', `$@ 5.7.1 $: "550 Access denied"')
   1944 dnl error tag
   1945 R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
   1946 R<ERROR:$+> $*		$#error $: $1
   1947 ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   1948 dnl generic error from access map
   1949 R<$+> $*		$#error $: $1		error from access db',
   1950 `dnl')
   1951 dnl workspace: @ CanonicalAddress (i.e. address in canonical form localpart<@host>)
   1952 
   1953 ifdef(`_BADMX_CHK_', `dnl
   1954 R@ $*<@$+>$*		$: $1<@$2>$3 $| $>BadMX $2
   1955 R$* $| $#$*		$#$2
   1956 
   1957 SBadMX
   1958 # Look up MX records and ferret away a copy of the original address.
   1959 # input: domain part of address to check
   1960 R$+				$:<MX><$1><:$(mxlist $1$):><:>
   1961 # workspace: <MX><domain><: mxlist-result $><:>
   1962 R<MX><$+><:$*<TEMP>:><$*>	$#error $@ 4.1.2 $: "450 MX lookup failure for "$1
   1963 # workspace: <MX> <original destination> <unchecked mxlist> <checked mxlist>
   1964 # Recursively run badmx check on each mx.
   1965 R<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><: $4 $(badmx $2 $):>
   1966 # See if any of them fail.
   1967 R<MX><$*><$*><$*<BADMX>:$*>	$#error $@ 5.1.2 $:"550 Illegal MX record for host "$1
   1968 # Reverse the mxlists so we can use the same argument order again.
   1969 R<MX><$*><$*><$*>		$:<MX><$1><$3><$2>
   1970 R<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><:$4 $(dnsA $2 $) :>
   1971 
   1972 # Reverse the lists so we can use the same argument order again.
   1973 R<MX><$*><$*><$*>		$:<MX><$1><$3><$2>
   1974 R<MX><$*><:$+:$*><:$*>		<MX><$1><:$3><:$4 $(BadMXIP $2 $) :>
   1975 
   1976 R<MX><$*><$*><$*<BADMXIP>:$*>	$#error $@ 5.1.2 $:"550 Invalid MX record for host "$1',
   1977 `dnl')
   1978 
   1979 
   1980 ######################################################################
   1981 ###  check_rcpt -- check SMTP ``RCPT TO:'' command argument
   1982 ######################################################################
   1983 
   1984 SLocal_check_rcpt
   1985 Scheck`'_U_`'rcpt
   1986 R$*			$: $1 $| $>"Local_check_rcpt" $1
   1987 R$* $| $#$*		$#$2
   1988 R$* $| $*		$@ $>"Basic_check_rcpt" $1
   1989 
   1990 SBasic_check_rcpt
   1991 # empty address?
   1992 R<>			$#error $@ nouser $: "553 User address required"
   1993 R$@			$#error $@ nouser $: "553 User address required"
   1994 # check for deferred delivery mode
   1995 R$*			$: < $&{deliveryMode} > $1
   1996 R< d > $*		$@ deferred
   1997 R< $* > $*		$: $2
   1998 
   1999 ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
   2000 dnl this code checks for user@host where host is not a FQHN.
   2001 dnl it is not activated.
   2002 dnl notice: code to check for a recipient without a domain name is
   2003 dnl available down below; look for the same macro.
   2004 dnl this check is done here because the name might be qualified by the
   2005 dnl canonicalization.
   2006 # require fully qualified domain part?
   2007 dnl very simple canonification: make sure the address is in < >
   2008 R$+			$: <?> $1
   2009 R<?> <$+>		$: <@> <$1>
   2010 R<?> $+			$: <@> <$1>
   2011 R<@> < postmaster >	$: postmaster
   2012 R<@> < $* @ $+ . $+ >	$: < $1 @ $2 . $3 >
   2013 dnl prepend daemon_flags
   2014 R<@> $*			$: $&{daemon_flags} $| <@> $1
   2015 dnl workspace: ${daemon_flags} $| <@> <address>
   2016 dnl _r_equire qual.rcpt: ok
   2017 R$* r $* $| <@> < $+ @ $+ >	$: < $3 @ $4 >
   2018 dnl do not allow these at all or only from local systems?
   2019 R$* r $* $| <@> < $* >	$: < ? $&{client_name} > < $3 >
   2020 R<?> < $* >		$: <$1>
   2021 R<? $=w> < $* >		$: <$1>
   2022 R<? $+> <$+>		$#error $@ 5.5.4 $: "553 Fully qualified domain name required"
   2023 dnl remove daemon_flags for other cases
   2024 R$* $| <@> $*		$: $2', `dnl')
   2025 
   2026 dnl ##################################################################
   2027 dnl call subroutines for recipient and relay
   2028 dnl possible returns from subroutines:
   2029 dnl $#TEMP	temporary failure
   2030 dnl $#error	permanent failure (or temporary if from access map)
   2031 dnl $#other	stop processing
   2032 dnl RELAY	RELAYing allowed
   2033 dnl other	otherwise
   2034 ######################################################################
   2035 R$*			$: $1 $| @ $>"Rcpt_ok" $1
   2036 dnl temporary failure? remove mark @ and remember
   2037 R$* $| @ $#TEMP $+	$: $1 $| T $2
   2038 dnl error or ok (stop)
   2039 R$* $| @ $#$*		$#$2
   2040 ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
   2041 R$* $| @ RELAY		$@ RELAY
   2042 dnl something else: call check sender (relay)
   2043 R$* $| @ $*		$: O $| $>"Relay_ok" $1
   2044 dnl temporary failure: call check sender (relay)
   2045 R$* $| T $+		$: T $2 $| $>"Relay_ok" $1
   2046 dnl temporary failure? return that
   2047 R$* $| $#TEMP $+	$#error $2
   2048 dnl error or ok (stop)
   2049 R$* $| $#$*		$#$2
   2050 R$* $| RELAY		$@ RELAY
   2051 dnl something else: return previous temp failure
   2052 R T $+ $| $*		$#error $1
   2053 # anything else is bogus
   2054 R$*			$#error $@ 5.7.1 $: confRELAY_MSG
   2055 divert(0)
   2056 
   2057 ######################################################################
   2058 ### Rcpt_ok: is the recipient ok?
   2059 dnl input: recipient address (RCPT TO)
   2060 dnl output: see explanation at call
   2061 ######################################################################
   2062 SRcpt_ok
   2063 ifdef(`_LOOSE_RELAY_CHECK_',`dnl
   2064 R$*			$: $>CanonAddr $1
   2065 R$* < @ $* . >		$1 < @ $2 >			strip trailing dots',
   2066 `R$*			$: $>ParseRecipient $1		strip relayable hosts')
   2067 
   2068 ifdef(`_BESTMX_IS_LOCAL_',`dnl
   2069 ifelse(_BESTMX_IS_LOCAL_, `', `dnl
   2070 # unlimited bestmx
   2071 R$* < @ $* > $*			$: $1 < @ $2 @@ $(bestmx $2 $) > $3',
   2072 `dnl
   2073 # limit bestmx to $=B
   2074 R$* < @ $* $=B > $*		$: $1 < @ $2 $3 @@ $(bestmx $2 $3 $) > $4')
   2075 R$* $=O $* < @ $* @@ $=w . > $*	$@ $>"Rcpt_ok" $1 $2 $3
   2076 R$* < @ $* @@ $=w . > $*	$: $1 < @ $3 > $4
   2077 R$* < @ $* @@ $* > $*		$: $1 < @ $2 > $4')
   2078 
   2079 ifdef(`_BLACKLIST_RCPT_',`dnl
   2080 ifdef(`_ACCESS_TABLE_', `dnl
   2081 # blacklist local users or any host from receiving mail
   2082 R$*			$: <?> $1
   2083 dnl user is now tagged with @ to be consistent with check_mail
   2084 dnl and to distinguish users from hosts (com would be host, com@ would be user)
   2085 R<?> $+ < @ $=w >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <U:$1@> <D:$2>
   2086 R<?> $+ < @ $* >	$: <> <$1 < @ $2 >> $| <F:$1@$2> <D:$2>
   2087 R<?> $+			$: <> <$1> $| <U:$1@>
   2088 dnl $| is used as delimiter, otherwise false matches may occur: <user<@domain>>
   2089 dnl will only return user<@domain when "reversing" the args
   2090 R<> <$*> $| <$+>	$: <@> <$1> $| $>SearchList <+ To> $| <$2> <>
   2091 R<@> <$*> $| <$*>	$: <$2> <$1>		reverse result
   2092 R<?> <$*>		$: @ $1		mark address as no match
   2093 dnl we may have to filter here because otherwise some RHSs
   2094 dnl would be interpreted as generic error messages...
   2095 dnl error messages should be "tagged" by prefixing them with error: !
   2096 dnl that would make a lot of things easier.
   2097 R<$={Accept}> <$*>	$: @ $2		mark address as no match
   2098 ifdef(`_ACCESS_SKIP_', `dnl
   2099 R<SKIP> <$*>		$: @ $1		mark address as no match', `dnl')
   2100 ifdef(`_DELAY_COMPAT_8_10_',`dnl
   2101 dnl compatility with 8.11/8.10:
   2102 dnl we have to filter these because otherwise they would be interpreted
   2103 dnl as generic error message...
   2104 dnl error messages should be "tagged" by prefixing them with error: !
   2105 dnl that would make a lot of things easier.
   2106 dnl maybe we should stop checks already here (if SPAM_xyx)?
   2107 R<$={SpamTag}> <$*>	$: @ $2		mark address as no match')
   2108 R<REJECT> $*		$#error $@ 5.2.1 $: confRCPTREJ_MSG
   2109 R<DISCARD> $*		$#discard $: discard
   2110 R<QUARANTINE:$+> $*	$#error $@ quarantine $: $1
   2111 dnl error tag
   2112 R<ERROR:$-.$-.$-:$+> $*		$#error $@ $1.$2.$3 $: $4
   2113 R<ERROR:$+> $*		$#error $: $1
   2114 ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2115 dnl generic error from access map
   2116 R<$+> $*		$#error $: $1		error from access db
   2117 R@ $*			$1		remove mark', `dnl')', `dnl')
   2118 
   2119 ifdef(`_PROMISCUOUS_RELAY_', `divert(-1)', `dnl')
   2120 # authenticated via TLS?
   2121 R$*			$: $1 $| $>RelayTLS	client authenticated?
   2122 R$* $| $# $+		$# $2			error/ok?
   2123 R$* $| $*		$: $1			no
   2124 
   2125 R$*			$: $1 $| $>"Local_Relay_Auth" $&{auth_type}
   2126 dnl workspace: localpart<@domain> $| result of Local_Relay_Auth
   2127 R$* $| $# $*		$# $2
   2128 dnl if Local_Relay_Auth returns NO then do not check $={TrustAuthMech}
   2129 R$* $| NO		$: $1
   2130 R$* $| $*		$: $1 $| $&{auth_type}
   2131 dnl workspace: localpart<@domain> [ $| ${auth_type} ]
   2132 dnl empty ${auth_type}?
   2133 R$* $|			$: $1
   2134 dnl mechanism ${auth_type} accepted?
   2135 dnl use $# to override further tests (delay_checks): see check_rcpt below
   2136 R$* $| $={TrustAuthMech}	$# RELAY
   2137 dnl remove ${auth_type}
   2138 R$* $| $*		$: $1
   2139 dnl workspace: localpart<@domain> | localpart
   2140 ifelse(defn(`_NO_UUCP_'), `r',
   2141 `R$* ! $* < @ $* >	$: <REMOTE> $2 < @ BANG_PATH >
   2142 R$* ! $* 		$: <REMOTE> $2 < @ BANG_PATH >', `dnl')
   2143 # anything terminating locally is ok
   2144 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
   2145 R$+ < @ $* $=m >	$@ RELAY', `dnl')
   2146 R$+ < @ $=w >		$@ RELAY
   2147 ifdef(`_RELAY_HOSTS_ONLY_',
   2148 `R$+ < @ $=R >		$@ RELAY
   2149 ifdef(`_ACCESS_TABLE_', `dnl
   2150 ifdef(`_RELAY_FULL_ADDR_', `dnl
   2151 R$+ < @ $+ >		$: <$(access To:$1@$2 $: ? $)> <$1 < @ $2 >>
   2152 R<?> <$+ < @ $+ >>	$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>',`
   2153 R$+ < @ $+ >		$: <$(access To:$2 $: ? $)> <$1 < @ $2 >>')
   2154 dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
   2155 R<?> <$+ < @ $+ >>	$: <$(access $2 $: ? $)> <$1 < @ $2 >>',`dnl')',
   2156 `R$+ < @ $* $=R >	$@ RELAY
   2157 ifdef(`_ACCESS_TABLE_', `dnl
   2158 ifdef(`_RELAY_FULL_ADDR_', `dnl
   2159 R$+ < @ $+ >		$: $1 < @ $2 > $| $>SearchList <+ To> $| <F:$1@$2> <D:$2> <F:$1@> <>
   2160 R$+ < @ $+ > $| <$*>	$: <$3> <$1 <@ $2>>
   2161 R$+ < @ $+ > $| $*	$: <$3> <$1 <@ $2>>',
   2162 `R$+ < @ $+ >		$: $>D <$2> <?> <+ To> <$1 < @ $2 >>')')')
   2163 ifdef(`_ACCESS_TABLE_', `dnl
   2164 dnl workspace: <Result-of-lookup | ?> <localpart<@domain>>
   2165 R<RELAY> $*		$@ RELAY
   2166 ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2167 R<$*> <$*>		$: $2',`dnl')
   2168 
   2169 
   2170 ifdef(`_RELAY_MX_SERVED_', `dnl
   2171 # allow relaying for hosts which we MX serve
   2172 R$+ < @ $+ >		$: < : $(mxserved $2 $) : > $1 < @ $2 >
   2173 dnl this must not necessarily happen if the client is checked first...
   2174 R< : $* <TEMP> : > $*	$#TEMP $@ 4.4.0 $: "450 Can not check MX records for recipient host " $1
   2175 R<$* : $=w . : $*> $*	$@ RELAY
   2176 R< : $* : > $*		$: $2',
   2177 `dnl')
   2178 
   2179 # check for local user (i.e. unqualified address)
   2180 R$*			$: <?> $1
   2181 R<?> $* < @ $+ >	$: <REMOTE> $1 < @ $2 >
   2182 # local user is ok
   2183 dnl is it really? the standard requires user@domain, not just user
   2184 dnl but we should accept it anyway (maybe making it an option:
   2185 dnl RequireFQDN ?)
   2186 dnl postmaster must be accepted without domain (DRUMS)
   2187 ifdef(`_REQUIRE_QUAL_RCPT_', `dnl
   2188 R<?> postmaster		$@ OK
   2189 # require qualified recipient?
   2190 dnl prepend daemon_flags
   2191 R<?> $+			$: $&{daemon_flags} $| <?> $1
   2192 dnl workspace: ${daemon_flags} $| <?> localpart
   2193 dnl do not allow these at all or only from local systems?
   2194 dnl r flag? add client_name
   2195 R$* r $* $| <?> $+	$: < ? $&{client_name} > <?> $3
   2196 dnl no r flag: relay to local user (only local part)
   2197 # no qualified recipient required
   2198 R$* $| <?> $+		$@ RELAY
   2199 dnl client_name is empty
   2200 R<?> <?> $+		$@ RELAY
   2201 dnl client_name is local
   2202 R<? $=w> <?> $+		$@ RELAY
   2203 dnl client_name is not local
   2204 R<? $+> $+		$#error $@ 5.5.4 $: "553 Domain name required"', `dnl
   2205 dnl no qualified recipient required
   2206 R<?> $+			$@ RELAY')
   2207 dnl it is a remote user: remove mark and then check client
   2208 R<$+> $*		$: $2
   2209 dnl currently the recipient address is not used below
   2210 
   2211 ######################################################################
   2212 ### Relay_ok: is the relay/sender ok?
   2213 dnl input: ignored
   2214 dnl output: see explanation at call
   2215 ######################################################################
   2216 SRelay_ok
   2217 # anything originating locally is ok
   2218 # check IP address
   2219 R$*			$: $&{client_addr}
   2220 R$@			$@ RELAY		originated locally
   2221 R0			$@ RELAY		originated locally
   2222 R127.0.0.1		$@ RELAY		originated locally
   2223 RIPv6:::1		$@ RELAY		originated locally
   2224 R$=R $*			$@ RELAY		relayable IP address
   2225 ifdef(`_ACCESS_TABLE_', `dnl
   2226 R$*			$: $>A <$1> <?> <+ Connect> <$1>
   2227 R<RELAY> $* 		$@ RELAY		relayable IP address
   2228 ifdef(`_FFR_REJECT_IP_IN_CHECK_RCPT_',`dnl
   2229 dnl this will cause rejections in cases like:
   2230 dnl Connect:My.Host.Domain	RELAY
   2231 dnl Connect:My.Net		REJECT
   2232 dnl since in check_relay client_name is checked before client_addr
   2233 R<REJECT> $* 		$@ REJECT		rejected IP address')
   2234 ifdef(`_ATMPF_', `R<_ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2235 R<$*> <$*>		$: $2', `dnl')
   2236 R$*			$: [ $1 ]		put brackets around it...
   2237 R$=w			$@ RELAY		... and see if it is local
   2238 
   2239 ifdef(`_RELAY_DB_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
   2240 ifdef(`_RELAY_LOCAL_FROM_', `define(`_RELAY_MAIL_FROM_', `1')')dnl
   2241 ifdef(`_RELAY_MAIL_FROM_', `dnl
   2242 dnl input: {client_addr} or something "broken"
   2243 dnl just throw the input away; we do not need it.
   2244 # check whether FROM is allowed to use system as relay
   2245 R$*			$: <?> $>CanonAddr $&f
   2246 R<?> $+ < @ $+ . >	<?> $1 < @ $2 >		remove trailing dot
   2247 ifdef(`_RELAY_LOCAL_FROM_', `dnl
   2248 # check whether local FROM is ok
   2249 R<?> $+ < @ $=w >	$@ RELAY		FROM local', `dnl')
   2250 ifdef(`_RELAY_DB_FROM_', `dnl
   2251 R<?> $+ < @ $+ >	$: <@> $>SearchList <! From> $| <F:$1@$2> ifdef(`_RELAY_DB_FROM_DOMAIN_', ifdef(`_RELAY_HOSTS_ONLY_', `<E:$2>', `<D:$2>')) <>
   2252 R<@> <RELAY>		$@ RELAY		RELAY FROM sender ok
   2253 ifdef(`_ATMPF_', `R<@> <_ATMPF_>		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2254 ', `dnl
   2255 ifdef(`_RELAY_DB_FROM_DOMAIN_',
   2256 `errprint(`*** ERROR: _RELAY_DB_FROM_DOMAIN_ requires _RELAY_DB_FROM_
   2257 ')',
   2258 `dnl')
   2259 dnl')', `dnl')
   2260 dnl notice: the rulesets above do not leave a unique workspace behind.
   2261 dnl it does not matter in this case because the following rule ignores
   2262 dnl the input. otherwise these rules must "clean up" the workspace.
   2263 
   2264 # check client name: first: did it resolve?
   2265 dnl input: ignored
   2266 R$*			$: < $&{client_resolve} >
   2267 R<TEMP>			$#TEMP $@ 4.4.0 $: "450 Relaying temporarily denied. Cannot resolve PTR record for " $&{client_addr}
   2268 R<FORGED>		$#error $@ 5.7.1 $: "550 Relaying denied. IP name possibly forged " $&{client_name}
   2269 R<FAIL>			$#error $@ 5.7.1 $: "550 Relaying denied. IP name lookup failed " $&{client_name}
   2270 dnl ${client_resolve} should be OK, so go ahead
   2271 R$*			$: <@> $&{client_name}
   2272 dnl should not be necessary since it has been done for client_addr already
   2273 dnl this rule actually may cause a problem if {client_name} resolves to ""
   2274 dnl however, this should not happen since the forward lookup should fail
   2275 dnl and {client_resolve} should be TEMP or FAIL.
   2276 dnl nevertheless, removing the rule doesn't hurt.
   2277 dnl R<@>			$@ RELAY
   2278 dnl workspace: <@> ${client_name} (not empty)
   2279 # pass to name server to make hostname canonical
   2280 R<@> $* $=P 		$:<?>  $1 $2
   2281 R<@> $+			$:<?>  $[ $1 $]
   2282 dnl workspace: <?> ${client_name} (canonified)
   2283 R$* .			$1			strip trailing dots
   2284 ifdef(`_RELAY_ENTIRE_DOMAIN_', `dnl
   2285 R<?> $* $=m		$@ RELAY', `dnl')
   2286 R<?> $=w		$@ RELAY
   2287 ifdef(`_RELAY_HOSTS_ONLY_',
   2288 `R<?> $=R		$@ RELAY
   2289 ifdef(`_ACCESS_TABLE_', `dnl
   2290 R<?> $*			$: <$(access Connect:$1 $: ? $)> <$1>
   2291 R<?> <$*>		$: <$(access $1 $: ? $)> <$1>',`dnl')',
   2292 `R<?> $* $=R			$@ RELAY
   2293 ifdef(`_ACCESS_TABLE_', `dnl
   2294 R<?> $*			$: $>D <$1> <?> <+ Connect> <$1>',`dnl')')
   2295 ifdef(`_ACCESS_TABLE_', `dnl
   2296 R<RELAY> $*		$@ RELAY
   2297 ifdef(`_ATMPF_', `R<$* _ATMPF_> $*		$#TEMP $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2298 R<$*> <$*>		$: $2',`dnl')
   2299 dnl end of _PROMISCUOUS_RELAY_
   2300 divert(0)
   2301 ifdef(`_DELAY_CHECKS_',`dnl
   2302 # turn a canonical address in the form user<@domain>
   2303 # qualify unqual. addresses with $j
   2304 dnl it might have been only user (without <@domain>)
   2305 SFullAddr
   2306 R$* <@ $+ . >		$1 <@ $2 >
   2307 R$* <@ $* >		$@ $1 <@ $2 >
   2308 R$+			$@ $1 <@ $j >
   2309 
   2310 SDelay_TLS_Clt
   2311 # authenticated?
   2312 dnl code repeated here from Basic_check_mail
   2313 dnl only called from check_rcpt in delay mode if checkrcpt returns $#
   2314 R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
   2315 R$* $| $#$+		$#$2
   2316 dnl return result from checkrcpt
   2317 R$* $| $*		$# $1
   2318 R$*			$# $1
   2319 
   2320 SDelay_TLS_Clt2
   2321 # authenticated?
   2322 dnl code repeated here from Basic_check_mail
   2323 dnl only called from check_rcpt in delay mode if stopping due to Friend/Hater
   2324 R$*			$: $1 $| $>"tls_client" $&{verify} $| MAIL
   2325 R$* $| $#$+		$#$2
   2326 dnl return result from friend/hater check
   2327 R$* $| $*		$@ $1
   2328 R$*			$@ $1
   2329 
   2330 # call all necessary rulesets
   2331 Scheck_rcpt
   2332 dnl this test should be in the Basic_check_rcpt ruleset
   2333 dnl which is the correct DSN code?
   2334 # R$@			$#error $@ 5.1.3 $: "553 Recipient address required"
   2335 
   2336 R$+			$: $1 $| $>checkrcpt $1
   2337 dnl now we can simply stop checks by returning "$# xyz" instead of just "ok"
   2338 dnl on error (or discard) stop now
   2339 R$+ $| $#error $*	$#error $2
   2340 R$+ $| $#discard $*	$#discard $2
   2341 dnl otherwise call tls_client; see above
   2342 R$+ $| $#$*		$@ $>"Delay_TLS_Clt" $2
   2343 R$+ $| $*		$: <?> $>FullAddr $>CanonAddr $1
   2344 ifdef(`_SPAM_FH_',
   2345 `dnl lookup user@ and user@address
   2346 ifdef(`_ACCESS_TABLE_', `',
   2347 `errprint(`*** ERROR: FEATURE(`delay_checks', `argument') requires FEATURE(`access_db')
   2348 ')')dnl
   2349 dnl one of the next two rules is supposed to match
   2350 dnl this code has been copied from BLACKLIST... etc
   2351 dnl and simplified by omitting some < >.
   2352 R<?> $+ < @ $=w >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 > <U: $1@>
   2353 R<?> $+ < @ $* >	$: <> $1 < @ $2 > $| <F: $1@$2 > <D: $2 >
   2354 dnl R<?>		$@ something_is_very_wrong_here
   2355 # lookup the addresses only with Spam tag
   2356 R<> $* $| <$+>		$: <@> $1 $| $>SearchList <! Spam> $| <$2> <>
   2357 R<@> $* $| $*		$: $2 $1		reverse result
   2358 dnl', `dnl')
   2359 ifdef(`_SPAM_FRIEND_',
   2360 `# is the recipient a spam friend?
   2361 ifdef(`_SPAM_HATER_',
   2362 	`errprint(`*** ERROR: define either Hater or Friend -- not both.
   2363 ')', `dnl')
   2364 R<FRIEND> $+		$@ $>"Delay_TLS_Clt2" SPAMFRIEND
   2365 R<$*> $+		$: $2',
   2366 `dnl')
   2367 ifdef(`_SPAM_HATER_',
   2368 `# is the recipient no spam hater?
   2369 R<HATER> $+		$: $1			spam hater: continue checks
   2370 R<$*> $+		$@ $>"Delay_TLS_Clt2" NOSPAMHATER	everyone else: stop
   2371 dnl',`dnl')
   2372 
   2373 dnl run further checks: check_mail
   2374 dnl should we "clean up" $&f?
   2375 ifdef(`_FFR_MAIL_MACRO',
   2376 `R$*			$: $1 $| $>checkmail $&{mail_from}',
   2377 `R$*			$: $1 $| $>checkmail <$&f>')
   2378 dnl recipient (canonical format) $| result of checkmail
   2379 R$* $| $#$*		$#$2
   2380 dnl run further checks: check_relay
   2381 R$* $| $*		$: $1 $| $>checkrelay $&{client_name} $| $&{client_addr}
   2382 R$* $| $#$*		$#$2
   2383 R$* $| $*		$: $1
   2384 ', `dnl')
   2385 
   2386 ifdef(`_BLOCK_BAD_HELO_', `dnl
   2387 R$*			$: $1 $| <$&{auth_authen}>	Get auth info
   2388 dnl Bypass the test for users who have authenticated.
   2389 R$* $| <$+>		$: $1				skip if auth
   2390 R$* $| <$*>		$: $1 $| <$&{client_addr}> [$&s]	Get connection info
   2391 dnl Bypass for local clients -- IP address starts with $=R
   2392 R$* $| <$=R $*> [$*]	$: $1				skip if local client
   2393 dnl Bypass a "sendmail -bs" session, which use 0 for client ip address
   2394 R$* $| <0> [$*]		$: $1				skip if sendmail -bs
   2395 dnl Reject our IP - assumes "[ip]" is in class $=w
   2396 R$* $| <$*> $=w		$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
   2397 dnl Reject our hostname
   2398 R$* $| <$*> [$=w]	$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
   2399 dnl Pass anything else with a "." in the domain parameter
   2400 R$* $| <$*> [$+.$+]	$: $1				qualified domain ok
   2401 dnl Reject if there was no "." or only an initial or final "."
   2402 R$* $| <$*> [$*]	$#error $@ 5.7.1 $:"550 bogus HELO name used: " $&s
   2403 dnl Clean up the workspace
   2404 R$* $| $*		$: $1
   2405 ', `dnl')
   2406 
   2407 ifdef(`_ACCESS_TABLE_', `dnl', `divert(-1)')
   2408 ######################################################################
   2409 ###  F: LookUpFull -- search for an entry in access database
   2410 ###
   2411 ###	lookup of full key (which should be an address) and
   2412 ###	variations if +detail exists: +* and without +detail
   2413 ###
   2414 ###	Parameters:
   2415 ###		<$1> -- key
   2416 ###		<$2> -- default (what to return if not found in db)
   2417 dnl			must not be empty
   2418 ###		<$3> -- mark (must be <(!|+) single-token>)
   2419 ###			! does lookup only with tag
   2420 ###			+ does lookup with and without tag
   2421 ###		<$4> -- passthru (additional data passed unchanged through)
   2422 dnl returns:		<default> <passthru>
   2423 dnl 			<result> <passthru>
   2424 ######################################################################
   2425 
   2426 SF
   2427 dnl workspace: <key> <def> <o tag> <thru>
   2428 dnl full lookup
   2429 dnl    2    3  4    5
   2430 R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
   2431 dnl no match, try without tag
   2432 dnl   1    2      3    4
   2433 R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
   2434 dnl no match, +detail: try +*
   2435 dnl   1    2    3    4    5  6    7
   2436 R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
   2437 			$: <$(access $6`'_TAG_DELIM_`'$1+*@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
   2438 dnl no match, +detail: try +* without tag
   2439 dnl   1    2    3    4      5    6
   2440 R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
   2441 			$: <$(access $1+*@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
   2442 dnl no match, +detail: try without +detail
   2443 dnl   1    2    3    4    5  6    7
   2444 R<?> <$+ + $* @ $+> <$*> <$- $-> <$*>
   2445 			$: <$(access $6`'_TAG_DELIM_`'$1@$3 $: ? $)> <$1+$2@$3> <$4> <$5 $6> <$7>
   2446 dnl no match, +detail: try without +detail and without tag
   2447 dnl   1    2    3    4      5    6
   2448 R<?> <$+ + $* @ $+> <$*> <+ $-> <$*>
   2449 			$: <$(access $1@$3 $: ? $)> <$1+$2@$3> <$4> <+ $5> <$6>
   2450 dnl no match, return <default> <passthru>
   2451 dnl   1    2    3  4    5
   2452 R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
   2453 ifdef(`_ATMPF_', `dnl tempfail?
   2454 dnl            2    3  4    5
   2455 R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
   2456 dnl match, return <match> <passthru>
   2457 dnl    2    3  4    5
   2458 R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
   2459 
   2460 ######################################################################
   2461 ###  E: LookUpExact -- search for an entry in access database
   2462 ###
   2463 ###	Parameters:
   2464 ###		<$1> -- key
   2465 ###		<$2> -- default (what to return if not found in db)
   2466 dnl			must not be empty
   2467 ###		<$3> -- mark (must be <(!|+) single-token>)
   2468 ###			! does lookup only with tag
   2469 ###			+ does lookup with and without tag
   2470 ###		<$4> -- passthru (additional data passed unchanged through)
   2471 dnl returns:		<default> <passthru>
   2472 dnl 			<result> <passthru>
   2473 ######################################################################
   2474 
   2475 SE
   2476 dnl    2    3  4    5
   2477 R<$*> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
   2478 dnl no match, try without tag
   2479 dnl   1    2      3    4
   2480 R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
   2481 dnl no match, return default passthru
   2482 dnl   1    2    3  4    5
   2483 R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
   2484 ifdef(`_ATMPF_', `dnl tempfail?
   2485 dnl            2    3  4    5
   2486 R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
   2487 dnl match, return <match> <passthru>
   2488 dnl    2    3  4    5
   2489 R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
   2490 
   2491 ######################################################################
   2492 ###  U: LookUpUser -- search for an entry in access database
   2493 ###
   2494 ###	lookup of key (which should be a local part) and
   2495 ###	variations if +detail exists: +* and without +detail
   2496 ###
   2497 ###	Parameters:
   2498 ###		<$1> -- key (user@)
   2499 ###		<$2> -- default (what to return if not found in db)
   2500 dnl			must not be empty
   2501 ###		<$3> -- mark (must be <(!|+) single-token>)
   2502 ###			! does lookup only with tag
   2503 ###			+ does lookup with and without tag
   2504 ###		<$4> -- passthru (additional data passed unchanged through)
   2505 dnl returns:		<default> <passthru>
   2506 dnl 			<result> <passthru>
   2507 ######################################################################
   2508 
   2509 SU
   2510 dnl user lookups are always with trailing @
   2511 dnl    2    3  4    5
   2512 R<$+> <$*> <$- $-> <$*>		$: <$(access $4`'_TAG_DELIM_`'$1 $: ? $)> <$1> <$2> <$3 $4> <$5>
   2513 dnl no match, try without tag
   2514 dnl   1    2      3    4
   2515 R<?> <$+> <$*> <+ $-> <$*>	$: <$(access $1 $: ? $)> <$1> <$2> <+ $3> <$4>
   2516 dnl do not remove the @ from the lookup:
   2517 dnl it is part of the +detail@ which is omitted for the lookup
   2518 dnl no match, +detail: try +*
   2519 dnl   1    2      3    4  5    6
   2520 R<?> <$+ + $* @> <$*> <$- $-> <$*>
   2521 			$: <$(access $5`'_TAG_DELIM_`'$1+*@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
   2522 dnl no match, +detail: try +* without tag
   2523 dnl   1    2      3      4    5
   2524 R<?> <$+ + $* @> <$*> <+ $-> <$*>
   2525 			$: <$(access $1+*@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
   2526 dnl no match, +detail: try without +detail
   2527 dnl   1    2      3    4  5    6
   2528 R<?> <$+ + $* @> <$*> <$- $-> <$*>
   2529 			$: <$(access $5`'_TAG_DELIM_`'$1@ $: ? $)> <$1+$2@> <$3> <$4 $5> <$6>
   2530 dnl no match, +detail: try without +detail and without tag
   2531 dnl   1    2      3      4    5
   2532 R<?> <$+ + $* @> <$*> <+ $-> <$*>
   2533 			$: <$(access $1@ $: ? $)> <$1+$2@> <$3> <+ $4> <$5>
   2534 dnl no match, return <default> <passthru>
   2535 dnl   1    2    3  4    5
   2536 R<?> <$+> <$*> <$- $-> <$*>	$@ <$2> <$5>
   2537 ifdef(`_ATMPF_', `dnl tempfail?
   2538 dnl            2    3  4    5
   2539 R<$+ _ATMPF_> <$*> <$- $-> <$*>	$@ <_ATMPF_> <$5>', `dnl')
   2540 dnl match, return <match> <passthru>
   2541 dnl    2    3  4    5
   2542 R<$+> <$*> <$- $-> <$*>		$@ <$1> <$5>
   2543 
   2544 ######################################################################
   2545 ###  SearchList: search a list of items in the access map
   2546 ###	Parameters:
   2547 ###		<exact tag> $| <mark:address> <mark:address> ... <>
   2548 dnl	maybe we should have a @ (again) in front of the mark to
   2549 dnl	avoid errorneous matches (with error messages?)
   2550 dnl	if we can make sure that tag is always a single token
   2551 dnl	then we can omit the delimiter $|, otherwise we need it
   2552 dnl	to avoid errorneous matchs (first rule: D: if there
   2553 dnl	is that mark somewhere in the list, it will be taken).
   2554 dnl	moreover, we can do some tricks to enforce lookup with
   2555 dnl	the tag only, e.g.:
   2556 ###	where "exact" is either "+" or "!":
   2557 ###	<+ TAG>	lookup with and w/o tag
   2558 ###	<! TAG>	lookup with tag
   2559 dnl	Warning: + and ! should be in OperatorChars (otherwise there must be
   2560 dnl		a blank between them and the tag.
   2561 ###	possible values for "mark" are:
   2562 ###		D: recursive host lookup (LookUpDomain)
   2563 dnl		A: recursive address lookup (LookUpAddress) [not yet required]
   2564 ###		E: exact lookup, no modifications
   2565 ###		F: full lookup, try user+ext@domain and user@domain
   2566 ###		U: user lookup, try user+ext and user (input must have trailing @)
   2567 ###	return: <RHS of lookup> or <?> (not found)
   2568 ######################################################################
   2569 
   2570 # class with valid marks for SearchList
   2571 dnl if A is activated: add it
   2572 C{Src}E F D U ifdef(`_FFR_SRCHLIST_A', `A')
   2573 SSearchList
   2574 # just call the ruleset with the name of the tag... nice trick...
   2575 dnl       2       3    4
   2576 R<$+> $| <$={Src}:$*> <$*>	$: <$1> $| <$4> $| $>$2 <$3> <?> <$1> <>
   2577 dnl workspace: <o tag> $| <rest> $| <result of lookup> <>
   2578 dnl no match and nothing left: return
   2579 R<$+> $| <> $| <?> <>		$@ <?>
   2580 dnl no match but something left: continue
   2581 R<$+> $| <$+> $| <?> <>		$@ $>SearchList <$1> $| <$2>
   2582 dnl match: return
   2583 R<$+> $| <$*> $| <$+> <>	$@ <$3>
   2584 dnl return result from recursive invocation
   2585 R<$+> $| <$+>			$@ <$2>
   2586 dnl endif _ACCESS_TABLE_
   2587 divert(0)
   2588 
   2589 ######################################################################
   2590 ###  trust_auth: is user trusted to authenticate as someone else?
   2591 ###
   2592 ###	Parameters:
   2593 ###		$1: AUTH= parameter from MAIL command
   2594 ######################################################################
   2595 
   2596 dnl empty ruleset definition so it can be called
   2597 SLocal_trust_auth
   2598 Strust_auth
   2599 R$*			$: $&{auth_type} $| $1
   2600 # required by RFC 2554 section 4.
   2601 R$@ $| $*		$#error $@ 5.7.1 $: "550 not authenticated"
   2602 dnl seems to be useful...
   2603 R$* $| $&{auth_authen}		$@ identical
   2604 R$* $| <$&{auth_authen}>	$@ identical
   2605 dnl call user supplied code
   2606 R$* $| $*		$: $1 $| $>"Local_trust_auth" $2
   2607 R$* $| $#$*		$#$2
   2608 dnl default: error
   2609 R$*			$#error $@ 5.7.1 $: "550 " $&{auth_authen} " not allowed to act as " $&{auth_author}
   2610 
   2611 ######################################################################
   2612 ###  Relay_Auth: allow relaying based on authentication?
   2613 ###
   2614 ###	Parameters:
   2615 ###		$1: ${auth_type}
   2616 ######################################################################
   2617 SLocal_Relay_Auth
   2618 
   2619 ######################################################################
   2620 ###  srv_features: which features to offer to a client?
   2621 ###	(done in server)
   2622 ######################################################################
   2623 Ssrv_features
   2624 ifdef(`_LOCAL_SRV_FEATURES_', `dnl
   2625 R$*			$: $1 $| $>"Local_srv_features" $1
   2626 R$* $| $#$*		$#$2
   2627 R$* $| $*		$: $1', `dnl')
   2628 ifdef(`_ACCESS_TABLE_', `dnl
   2629 R$*		$: $>D <$&{client_name}> <?> <! SRV_FEAT_TAG> <>
   2630 R<?>$*		$: $>A <$&{client_addr}> <?> <! SRV_FEAT_TAG> <>
   2631 R<?>$*		$: <$(access SRV_FEAT_TAG`'_TAG_DELIM_ $: ? $)>
   2632 R<?>$*		$@ OK
   2633 ifdef(`_ATMPF_', `dnl tempfail?
   2634 R<$* _ATMPF_>$*	$#temp', `dnl')
   2635 R<$+>$*		$# $1')
   2636 
   2637 ######################################################################
   2638 ###  try_tls: try to use STARTTLS?
   2639 ###	(done in client)
   2640 ######################################################################
   2641 Stry_tls
   2642 ifdef(`_LOCAL_TRY_TLS_', `dnl
   2643 R$*			$: $1 $| $>"Local_try_tls" $1
   2644 R$* $| $#$*		$#$2
   2645 R$* $| $*		$: $1', `dnl')
   2646 ifdef(`_ACCESS_TABLE_', `dnl
   2647 R$*		$: $>D <$&{server_name}> <?> <! TLS_TRY_TAG> <>
   2648 R<?>$*		$: $>A <$&{server_addr}> <?> <! TLS_TRY_TAG> <>
   2649 R<?>$*		$: <$(access TLS_TRY_TAG`'_TAG_DELIM_ $: ? $)>
   2650 R<?>$*		$@ OK
   2651 ifdef(`_ATMPF_', `dnl tempfail?
   2652 R<$* _ATMPF_>$*	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2653 R<NO>$*		$#error $@ 5.7.1 $: "550 do not try TLS with " $&{server_name} " ["$&{server_addr}"]"')
   2654 
   2655 ######################################################################
   2656 ###  tls_rcpt: is connection with server "good" enough?
   2657 ###	(done in client, per recipient)
   2658 dnl called from deliver() before RCPT command
   2659 ###
   2660 ###	Parameters:
   2661 ###		$1: recipient
   2662 ######################################################################
   2663 Stls_rcpt
   2664 ifdef(`_LOCAL_TLS_RCPT_', `dnl
   2665 R$*			$: $1 $| $>"Local_tls_rcpt" $1
   2666 R$* $| $#$*		$#$2
   2667 R$* $| $*		$: $1', `dnl')
   2668 ifdef(`_ACCESS_TABLE_', `dnl
   2669 dnl store name of other side
   2670 R$*			$: $(macro {TLS_Name} $@ $&{server_name} $) $1
   2671 dnl canonify recipient address
   2672 R$+			$: <?> $>CanonAddr $1
   2673 dnl strip trailing dots
   2674 R<?> $+ < @ $+ . >	<?> $1 <@ $2 >
   2675 dnl full address?
   2676 R<?> $+ < @ $+ >	$: $1 <@ $2 > $| <F:$1@$2> <U:$1@> <D:$2> <E:>
   2677 dnl only localpart?
   2678 R<?> $+			$: $1 $| <U:$1@> <E:>
   2679 dnl look it up
   2680 dnl also look up a default value via E:
   2681 R$* $| $+	$: $1 $| $>SearchList <! TLS_RCPT_TAG> $| $2 <>
   2682 dnl found nothing: stop here
   2683 R$* $| <?>	$@ OK
   2684 ifdef(`_ATMPF_', `dnl tempfail?
   2685 R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2686 dnl use the generic routine (for now)
   2687 R$* $| <$+>	$@ $>"TLS_connection" $&{verify} $| <$2>')
   2688 
   2689 ######################################################################
   2690 ###  tls_client: is connection with client "good" enough?
   2691 ###	(done in server)
   2692 ###
   2693 ###	Parameters:
   2694 ###		${verify} $| (MAIL|STARTTLS)
   2695 ######################################################################
   2696 dnl MAIL: called from check_mail
   2697 dnl STARTTLS: called from smtp() after STARTTLS has been accepted
   2698 Stls_client
   2699 ifdef(`_LOCAL_TLS_CLIENT_', `dnl
   2700 R$*			$: $1 <?> $>"Local_tls_client" $1
   2701 R$* <?> $#$*		$#$2
   2702 R$* <?> $*		$: $1', `dnl')
   2703 ifdef(`_ACCESS_TABLE_', `dnl
   2704 dnl store name of other side
   2705 R$*		$: $(macro {TLS_Name} $@ $&{client_name} $) $1
   2706 dnl ignore second arg for now
   2707 dnl maybe use it to distinguish permanent/temporary error?
   2708 dnl if MAIL: permanent (STARTTLS has not been offered)
   2709 dnl if STARTTLS: temporary (offered but maybe failed)
   2710 R$* $| $*	$: $1 $| $>D <$&{client_name}> <?> <! TLS_CLT_TAG> <>
   2711 R$* $| <?>$*	$: $1 $| $>A <$&{client_addr}> <?> <! TLS_CLT_TAG> <>
   2712 dnl do a default lookup: just TLS_CLT_TAG
   2713 R$* $| <?>$*	$: $1 $| <$(access TLS_CLT_TAG`'_TAG_DELIM_ $: ? $)>
   2714 ifdef(`_ATMPF_', `dnl tempfail?
   2715 R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2716 R$*		$@ $>"TLS_connection" $1', `dnl
   2717 R$* $| $*	$@ $>"TLS_connection" $1')
   2718 
   2719 ######################################################################
   2720 ###  tls_server: is connection with server "good" enough?
   2721 ###	(done in client)
   2722 ###
   2723 ###	Parameter:
   2724 ###		${verify}
   2725 ######################################################################
   2726 dnl i.e. has the server been authenticated and is encryption active?
   2727 dnl called from deliver() after STARTTLS command
   2728 Stls_server
   2729 ifdef(`_LOCAL_TLS_SERVER_', `dnl
   2730 R$*			$: $1 $| $>"Local_tls_server" $1
   2731 R$* $| $#$*		$#$2
   2732 R$* $| $*		$: $1', `dnl')
   2733 ifdef(`_ACCESS_TABLE_', `dnl
   2734 dnl store name of other side
   2735 R$*		$: $(macro {TLS_Name} $@ $&{server_name} $) $1
   2736 R$*		$: $1 $| $>D <$&{server_name}> <?> <! TLS_SRV_TAG> <>
   2737 R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! TLS_SRV_TAG> <>
   2738 dnl do a default lookup: just TLS_SRV_TAG
   2739 R$* $| <?>$*	$: $1 $| <$(access TLS_SRV_TAG`'_TAG_DELIM_ $: ? $)>
   2740 ifdef(`_ATMPF_', `dnl tempfail?
   2741 R$* $| <$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2742 R$*		$@ $>"TLS_connection" $1', `dnl
   2743 R$*		$@ $>"TLS_connection" $1')
   2744 
   2745 ######################################################################
   2746 ###  TLS_connection: is TLS connection "good" enough?
   2747 ###
   2748 ###	Parameters:
   2749 ifdef(`_ACCESS_TABLE_', `dnl
   2750 ###		${verify} $| <Requirement> [<>]', `dnl
   2751 ###		${verify}')
   2752 ###		Requirement: RHS from access map, may be ? for none.
   2753 dnl	syntax for Requirement:
   2754 dnl	[(PERM|TEMP)+] (VERIFY[:bits]|ENCR:bits) [+extensions]
   2755 dnl	extensions: could be a list of further requirements
   2756 dnl		for now: CN:string	{cn_subject} == string
   2757 ######################################################################
   2758 STLS_connection
   2759 ifdef(`_ACCESS_TABLE_', `dnl', `dnl use default error
   2760 dnl deal with TLS handshake failures: abort
   2761 RSOFTWARE	$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake."
   2762 divert(-1)')
   2763 dnl common ruleset for tls_{client|server}
   2764 dnl input: ${verify} $| <ResultOfLookup> [<>]
   2765 dnl remove optional <>
   2766 R$* $| <$*>$*			$: $1 $| <$2>
   2767 dnl workspace: ${verify} $| <ResultOfLookup>
   2768 # create the appropriate error codes
   2769 dnl permanent or temporary error?
   2770 R$* $| <PERM + $={Tls} $*>	$: $1 $| <503:5.7.0> <$2 $3>
   2771 R$* $| <TEMP + $={Tls} $*>	$: $1 $| <403:4.7.0> <$2 $3>
   2772 dnl default case depends on TLS_PERM_ERR
   2773 R$* $| <$={Tls} $*>		$: $1 $| <ifdef(`TLS_PERM_ERR', `503:5.7.0', `403:4.7.0')> <$2 $3>
   2774 dnl workspace: ${verify} $| [<SMTP:ESC>] <ResultOfLookup>
   2775 # deal with TLS handshake failures: abort
   2776 RSOFTWARE $| <$-:$+> $* 	$#error $@ $2 $: $1 " TLS handshake failed."
   2777 dnl no <reply:dns> i.e. not requirements in the access map
   2778 dnl use default error
   2779 RSOFTWARE $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') TLS handshake failed."
   2780 # deal with TLS protocol errors: abort
   2781 RPROTOCOL $| <$-:$+> $* 	$#error $@ $2 $: $1 " STARTTLS failed."
   2782 dnl no <reply:dns> i.e. not requirements in the access map
   2783 dnl use default error
   2784 RPROTOCOL $| $* 		$#error $@ ifdef(`TLS_PERM_ERR', `5.7.0', `4.7.0') $: "ifdef(`TLS_PERM_ERR', `503', `403') STARTTLS failed."
   2785 R$* $| <$*> <VERIFY>		$: <$2> <VERIFY> <> $1
   2786 dnl separate optional requirements
   2787 R$* $| <$*> <VERIFY + $+>	$: <$2> <VERIFY> <$3> $1
   2788 R$* $| <$*> <$={Tls}:$->$*	$: <$2> <$3:$4> <> $1
   2789 dnl separate optional requirements
   2790 R$* $| <$*> <$={Tls}:$- + $+>$*	$: <$2> <$3:$4> <$5> $1
   2791 dnl some other value in access map: accept
   2792 dnl this also allows to override the default case (if used)
   2793 R$* $| $*			$@ OK
   2794 # authentication required: give appropriate error
   2795 # other side did authenticate (via STARTTLS)
   2796 dnl workspace: <SMTP:ESC> <{VERIFY,ENCR}[:BITS]> <[extensions]> ${verify}
   2797 dnl only verification required and it succeeded
   2798 R<$*><VERIFY> <> OK		$@ OK
   2799 dnl verification required and it succeeded but extensions are given
   2800 dnl change it to <SMTP:ESC> <REQ:0>  <extensions>
   2801 R<$*><VERIFY> <$+> OK		$: <$1> <REQ:0> <$2>
   2802 dnl verification required + some level of encryption
   2803 R<$*><VERIFY:$-> <$*> OK	$: <$1> <REQ:$2> <$3>
   2804 dnl just some level of encryption required
   2805 R<$*><ENCR:$-> <$*> $*		$: <$1> <REQ:$2> <$3>
   2806 dnl workspace:
   2807 dnl 1. <SMTP:ESC> <VERIFY [:bits]>  <[extensions]> {verify} (!= OK)
   2808 dnl 2. <SMTP:ESC> <REQ:bits>  <[extensions]>
   2809 dnl verification required but ${verify} is not set (case 1.)
   2810 R<$-:$+><VERIFY $*> <$*>	$#error $@ $2 $: $1 " authentication required"
   2811 R<$-:$+><VERIFY $*> <$*> FAIL	$#error $@ $2 $: $1 " authentication failed"
   2812 R<$-:$+><VERIFY $*> <$*> NO	$#error $@ $2 $: $1 " not authenticated"
   2813 R<$-:$+><VERIFY $*> <$*> NOT	$#error $@ $2 $: $1 " no authentication requested"
   2814 R<$-:$+><VERIFY $*> <$*> NONE	$#error $@ $2 $: $1 " other side does not support STARTTLS"
   2815 dnl some other value for ${verify}
   2816 R<$-:$+><VERIFY $*> <$*> $+	$#error $@ $2 $: $1 " authentication failure " $4
   2817 dnl some level of encryption required: get the maximum level (case 2.)
   2818 R<$*><REQ:$-> <$*>		$: <$1> <REQ:$2> <$3> $>max $&{cipher_bits} : $&{auth_ssf}
   2819 dnl compare required bits with actual bits
   2820 R<$*><REQ:$-> <$*> $-		$: <$1> <$2:$4> <$3> $(arith l $@ $4 $@ $2 $)
   2821 R<$-:$+><$-:$-> <$*> TRUE	$#error $@ $2 $: $1 " encryption too weak " $4 " less than " $3
   2822 dnl strength requirements fulfilled
   2823 dnl TLS Additional Requirements Separator
   2824 dnl this should be something which does not appear in the extensions itself
   2825 dnl @ could be part of a CN, DN, etc...
   2826 dnl use < > ? those are encoded in CN, DN, ...
   2827 define(`_TLS_ARS_', `++')dnl
   2828 dnl workspace:
   2829 dnl <SMTP:ESC> <REQ:bits> <extensions> result-of-compare
   2830 R<$-:$+><$-:$-> <$*> $*		$: <$1:$2 _TLS_ARS_ $5>
   2831 dnl workspace: <SMTP:ESC _TLS_ARS_ extensions>
   2832 dnl continue: check  extensions
   2833 R<$-:$+ _TLS_ARS_ >			$@ OK
   2834 dnl split extensions into own list
   2835 R<$-:$+ _TLS_ARS_ $+ >			$: <$1:$2> <$3>
   2836 R<$-:$+> < $+ _TLS_ARS_ $+ >		<$1:$2> <$3> <$4>
   2837 R<$-:$+> $+			$@ $>"TLS_req" $3 $| <$1:$2>
   2838 
   2839 ######################################################################
   2840 ###  TLS_req: check additional TLS requirements
   2841 ###
   2842 ###	Parameters: [<list> <of> <req>] $| <$-:$+>
   2843 ###		$-: SMTP reply code
   2844 ###		$+: Enhanced Status Code
   2845 dnl  further requirements for this ruleset:
   2846 dnl	name of "other side" is stored is {TLS_name} (client/server_name)
   2847 dnl
   2848 dnl	currently only CN[:common_name] is implemented
   2849 dnl	right now this is only a logical AND
   2850 dnl	i.e. all requirements must be true
   2851 dnl	how about an OR? CN must be X or CN must be Y or ..
   2852 dnl	use a macro to compute this as a trivial sequential
   2853 dnl	operations (no precedences etc)?
   2854 ######################################################################
   2855 STLS_req
   2856 dnl no additional requirements: ok
   2857 R $| $+		$@ OK
   2858 dnl require CN: but no CN specified: use name of other side
   2859 R<CN> $* $| <$+>		$: <CN:$&{TLS_Name}> $1 $| <$2>
   2860 dnl match, check rest
   2861 R<CN:$&{cn_subject}> $* $| <$+>		$@ $>"TLS_req" $1 $| <$2>
   2862 dnl CN does not match
   2863 dnl  1   2      3  4
   2864 R<CN:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " CN " $&{cn_subject} " does not match " $1
   2865 dnl cert subject
   2866 R<CS:$&{cert_subject}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
   2867 dnl CS does not match
   2868 dnl  1   2      3  4
   2869 R<CS:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Subject " $&{cert_subject} " does not match " $1
   2870 dnl match, check rest
   2871 R<CI:$&{cert_issuer}> $* $| <$+>	$@ $>"TLS_req" $1 $| <$2>
   2872 dnl CI does not match
   2873 dnl  1   2      3  4
   2874 R<CI:$+> $* $| <$-:$+>	$#error $@ $4 $: $3 " Cert Issuer " $&{cert_issuer} " does not match " $1
   2875 dnl return from recursive call
   2876 ROK			$@ OK
   2877 
   2878 ######################################################################
   2879 ###  max: return the maximum of two values separated by :
   2880 ###
   2881 ###	Parameters: [$-]:[$-]
   2882 ######################################################################
   2883 Smax
   2884 R:		$: 0
   2885 R:$-		$: $1
   2886 R$-:		$: $1
   2887 R$-:$-		$: $(arith l $@ $1 $@ $2 $) : $1 : $2
   2888 RTRUE:$-:$-	$: $2
   2889 R$-:$-:$-	$: $2
   2890 dnl endif _ACCESS_TABLE_
   2891 divert(0)
   2892 
   2893 ######################################################################
   2894 ###  RelayTLS: allow relaying based on TLS authentication
   2895 ###
   2896 ###	Parameters:
   2897 ###		none
   2898 ######################################################################
   2899 SRelayTLS
   2900 # authenticated?
   2901 dnl we do not allow relaying for anyone who can present a cert
   2902 dnl signed by a "trusted" CA. For example, even if we put verisigns
   2903 dnl CA in CertPath so we can authenticate users, we do not allow
   2904 dnl them to abuse our server (they might be easier to get hold of,
   2905 dnl but anyway).
   2906 dnl so here is the trick: if the verification succeeded
   2907 dnl we look up the cert issuer in the access map
   2908 dnl (maybe after extracting a part with a regular expression)
   2909 dnl if this returns RELAY we relay without further questions
   2910 dnl if it returns SUBJECT we perform a similar check on the
   2911 dnl cert subject.
   2912 ifdef(`_ACCESS_TABLE_', `dnl
   2913 R$*			$: <?> $&{verify}
   2914 R<?> OK			$: OK		authenticated: continue
   2915 R<?> $*			$@ NO		not authenticated
   2916 ifdef(`_CERT_REGEX_ISSUER_', `dnl
   2917 R$*			$: $(CERTIssuer $&{cert_issuer} $)',
   2918 `R$*			$: $&{cert_issuer}')
   2919 R$+			$: $(access CERTISSUER`'_TAG_DELIM_`'$1 $)
   2920 dnl use $# to stop further checks (delay_check)
   2921 RRELAY			$# RELAY
   2922 ifdef(`_CERT_REGEX_SUBJECT_', `dnl
   2923 RSUBJECT		$: <@> $(CERTSubject $&{cert_subject} $)',
   2924 `RSUBJECT		$: <@> $&{cert_subject}')
   2925 R<@> $+			$: <@> $(access CERTSUBJECT`'_TAG_DELIM_`'$1 $)
   2926 R<@> RELAY		$# RELAY
   2927 R$*			$: NO', `dnl')
   2928 
   2929 ######################################################################
   2930 ###  authinfo: lookup authinfo in the access map
   2931 ###
   2932 ###	Parameters:
   2933 ###		$1: {server_name}
   2934 ###		$2: {server_addr}
   2935 dnl	both are currently ignored
   2936 dnl if it should be done via another map, we either need to restrict
   2937 dnl functionality (it calls D and A) or copy those rulesets (or add another
   2938 dnl parameter which I want to avoid, it's quite complex already)
   2939 ######################################################################
   2940 dnl omit this ruleset if neither is defined?
   2941 dnl it causes DefaultAuthInfo to be ignored
   2942 dnl (which may be considered a good thing).
   2943 Sauthinfo
   2944 ifdef(`_AUTHINFO_TABLE_', `dnl
   2945 R$*		$: <$(authinfo AuthInfo:$&{server_name} $: ? $)>
   2946 R<?>		$: <$(authinfo AuthInfo:$&{server_addr} $: ? $)>
   2947 R<?>		$: <$(authinfo AuthInfo: $: ? $)>
   2948 R<?>		$@ no				no authinfo available
   2949 R<$*>		$# $1
   2950 dnl', `dnl
   2951 ifdef(`_ACCESS_TABLE_', `dnl
   2952 R$*		$: $1 $| $>D <$&{server_name}> <?> <! AuthInfo> <>
   2953 R$* $| <?>$*	$: $1 $| $>A <$&{server_addr}> <?> <! AuthInfo> <>
   2954 R$* $| <?>$*	$: $1 $| <$(access AuthInfo`'_TAG_DELIM_ $: ? $)> <>
   2955 R$* $| <?>$*	$@ no				no authinfo available
   2956 R$* $| <$*> <>	$# $2
   2957 dnl', `dnl')')
   2958 
   2959 ifdef(`_RATE_CONTROL_',`dnl
   2960 ######################################################################
   2961 ###  RateControl: 
   2962 ###	Parameters:	ignored
   2963 ###	return: $#error or OK
   2964 ######################################################################
   2965 SRateControl
   2966 ifdef(`_ACCESS_TABLE_', `dnl
   2967 R$*		$: <A:$&{client_addr}> <E:>
   2968 dnl also look up a default value via E:
   2969 R$+		$: $>SearchList <! ClientRate> $| $1 <>
   2970 dnl found nothing: stop here
   2971 R<?>		$@ OK
   2972 ifdef(`_ATMPF_', `dnl tempfail?
   2973 R<$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2974 dnl use the generic routine (for now)
   2975 R<0>		$@ OK		no limit
   2976 R<$+>		$: <$1> $| $(arith l $@ $1 $@ $&{client_rate} $)
   2977 dnl log this? Connection rate $&{client_rate} exceeds limit $1.
   2978 R<$+> $| TRUE	$#error $@ 4.3.2 $: _RATE_CONTROL_REPLY Connection rate limit exceeded.
   2979 ')')
   2980 
   2981 ifdef(`_CONN_CONTROL_',`dnl
   2982 ######################################################################
   2983 ###  ConnControl: 
   2984 ###	Parameters:	ignored
   2985 ###	return: $#error or OK
   2986 ######################################################################
   2987 SConnControl
   2988 ifdef(`_ACCESS_TABLE_', `dnl
   2989 R$*		$: <A:$&{client_addr}> <E:>
   2990 dnl also look up a default value via E:
   2991 R$+		$: $>SearchList <! ClientConn> $| $1 <>
   2992 dnl found nothing: stop here
   2993 R<?>		$@ OK
   2994 ifdef(`_ATMPF_', `dnl tempfail?
   2995 R<$* _ATMPF_>	$#error $@ 4.3.0 $: "451 Temporary system failure. Please try again later."', `dnl')
   2996 dnl use the generic routine (for now)
   2997 R<0>		$@ OK		no limit
   2998 R<$+>		$: <$1> $| $(arith l $@ $1 $@ $&{client_connections} $)
   2999 dnl log this: Open connections $&{client_connections} exceeds limit $1.
   3000 R<$+> $| TRUE	$#error $@ 4.3.2 $: _CONN_CONTROL_REPLY Too many open connections.
   3001 ')')
   3002 
   3003 undivert(9)dnl LOCAL_RULESETS
   3004 #
   3006 ######################################################################
   3007 ######################################################################
   3008 #####
   3009 `#####			MAIL FILTER DEFINITIONS'
   3010 #####
   3011 ######################################################################
   3012 ######################################################################
   3013 _MAIL_FILTERS_
   3014 #
   3016 ######################################################################
   3017 ######################################################################
   3018 #####
   3019 `#####			MAILER DEFINITIONS'
   3020 #####
   3021 ######################################################################
   3022 ######################################################################
   3023 undivert(7)dnl MAILER_DEFINITIONS
   3024 
   3025