Next: , Previous: , Up: MFL   [Contents][Index]

4.22 Exceptional Conditions

When the running program encounters a condition it is not able to handle, it signals an exception. To illustrate the concept, let’s consider the execution of the following code fragment:

  if primitive_hasmx(domainpart($f))
    accept
  fi

The function primitive_hasmx (see primitive_hasmx) tests whether the domain name given as its argument has any ‘MX’ records. It should return a boolean value. However, when querying the Domain Name System, it may fail to get a definite result. For example, the DNS server can be down or temporary unavailable. In other words, primitive_hasmx can be in a situation when, instead of returning ‘yes’ or ‘no’, it has to return ‘don't know’. It has no way of doing so, therefore it signals an exception.

Each exception is identified by exception type, an integer number associated with it.


Next: , Up: Exceptions   [Contents][Index]

4.22.1 Built-in Exceptions

The first 22 exception numbers are reserved for built-in exceptions. These are declared in module status.mfl. The following table summarizes all built-in exception types implemented by mailfromd version 9.0. Exceptions are listed in lexicographic order.

Exception: e_badmmq

The called function cannot finish its task because an incompatible message modification function was called at some point before it. For details, MMQ and dkim_sign.

Exception: e_dbfailure

General database failure. For example, the database cannot be opened. This exception can be signaled by any function that queries any DBM database.

Exception: e_divzero

Division by zero.

Exception: e_exists

This exception is emitted by dbinsert built-in if the requested key is already present in the database (see dbinsert).

Exception: e_eof

Function reached end of file while reading. See I/O functions, for a description of functions that can signal this exception.

Exception: e_failure

A general failure has occurred. In particular, this exception is signaled by DNS lookup functions when any permanent failure occurs. This exception can be signaled by any DNS-related function (hasmx, poll, etc.) or operation (mx matches).

Exception: e_format

Invalid input format. This exception is signaled if input data to a function are improperly formatted. In version 9.0 it is signaled by message_burst function if its input message is not formatted according to RFC 934. See Message digest functions.

Exception: e_ilseq

Illegal byte sequence. Signaled when a string cannot be converted between character sets because a sequence of bytes was encountered that is not defined for the source character set or cannot be represented in the destination character set.

See MIME decoding, for details.

Exception: e_inval

Arguments supplied to a function are invalid.

Exception: e_invcidr

Invalid CIDR notation. This is signaled by match_cidr function when its second argument is not a valid CIDR.

Exception: e_invip

Invalid IP address. This is signaled by match_cidr function when its first argument is not a valid IP address.

Exception: e_invtime

Invalid time interval specification. It is signaled by interval function if its argument is not a valid time interval (see time interval specification).

Exception: e_io

An error occurred during the input-output operation. See I/O functions, for a description of functions that can signal this exception.

Exception: e_macroundef

A Sendmail macro is undefined.

Exception: e_not_found

Required entity is not found. It is raised, for example, by message_find_header, when the requested header is not present in the message and by DNS resolver functions when unable to resolve host name or IP address.

Exception: e_range

The supplied argument is outside the allowed range. This is signalled, for example, by substring function (see substring).

Exception: e_regcomp

Regular expression cannot be compiled. This can happen when a regular expression (a right-hand argument of a matches operator) is built at the runtime and the produced string is an invalid regex.

Exception: e_ston_conv

String-to-number conversion failed. This can be signaled when a string is used in numeric context which cannot be converted to the numeric data type. For example:

 set x "10a"
 set y x / 2

In this code fragment, line 2 will raise the e_ston_conv exception, since ‘10a’ cannot be converted to a number.

Exception: e_success

This is not an exception in the strict sense of the word, but a constant indicating success.

Exception: e_temp_failure

A temporary failure has occurred. This can be signaled by DNS-related functions or operations.

Exception: e_too_many

Raised by various DNS functions when they encounter a long chain of CNAME records when trying to resolve a hostname. See CNAME chains.

Exception: e_url

The supplied URL is invalid. See Interfaces to Third-Party Programs.


Next: , Previous: , Up: Exceptions   [Contents][Index]

4.22.2 User-defined Exceptions

You can define your own exception types using the dclex statement:

dclex type

In this statement, type must be a valid MFL identifier, not used for another constant (see Constants). The dclex statement defines a new exception identified by the constant type and allocates a new exception number for it.

The type can subsequently be used in throw and catch statements, for example:

dclex myrange

number fact(number val)
  returns number
do
  if val < 0
    throw myrange "fact argument is out of range"
  fi
  …
done

Previous: , Up: Exceptions   [Contents][Index]

4.22.3 Exception Handling

Normally when an exception is signalled, the program execution is terminated and the MTA is returned a tempfail status. Additional information regarding the exception is then output to the logging channel (see Logging and Debugging). However, the user can intercept any exception by installing his own exception-handling routines.

An exception-handling routine is introduced by a try–catch statement, which has the following syntax:

try
do
  stmtlist
done
catch exception-list
do
  handler-body
done

where stmtlist and handler-body are sequences of MFL statements and exception-list is the list of exception types, separated by the word or. A special exception-list*’ is allowed and means all exceptions.

This construct works as follows. First, the statements from stmtlist are executed. If the execution finishes successfully, control is passed to the first statement after the ‘catch’ block. Otherwise, if an exception is signalled and this exception is listed in exception-list, the execution is passed to the handler-body. If the exception is not listed in exception-list, it is handled as usual.

The following example shows a ‘try--catch’ construct used for handling eventual exceptions, signalled by primitive_hasmx.

try
do
  if primitive_hasmx(domainpart($f))
    accept
  else
    reject
  fi
done
catch e_failure or e_temp_failure
do
  echo "primitive_hasmx failed"
  continue
done

The ‘try--catch’ statement can appear anywhere inside a function or a handler, but it cannot appear outside of them. It can also be nested within another ‘try--catch’, in either of its parts. Upon exit from a function or milter handler, all exceptions are restored to the state they had when it has been entered.

A catch block can also be used alone, without preceding try part. Such a construct is called a standalone catch. It is mostly useful for setting global exception handlers in a begin statement (see begin/end). When used within a usual function or handler, the exception handlers set by a standalone catch remain in force until either another standalone catch appears further in the same function or handler, or an end of the function is encountered, whichever occurs first.

A standalone catch defined within a function must return from it by executing return statement. If it does not do that explicitly, the default value of 1 is returned. A standalone catch defined within a milter handler must end execution with any of the following actions: accept, continue, discard, reject, tempfail. By default, continue is used.

It is not recommended to mix ‘try--catch’ constructs and standalone catches. If a standalone catch appears within a ‘try--catch’ statement, its scope of visibility is undefined.

Upon entry to a handler-body, two implicit positional arguments are defined, which can be referenced in handler-body as $1 and $217. The first argument gives the numeric code of the exception that has occurred. The second argument is a textual string containing a human-readable description of the exception.

The following is an improved version of the previous example, which uses these parameters to supply more information about the failure:

try
do
  if primitive_hasmx(domainpart($f))
    accept
  else
    reject
  fi
done
catch e_failure or e_temp_failure
do
  echo "Caught exception $1: $2"
  continue
done

The following example defines the function hasmx that returns true if the domain part of its argument has any ‘MX’ records, and false if it does not or if an exception occurs 18.

func hasmx (string s)
  returns number
do
  try
  do
    return primitive_hasmx(domainpart(s))
  done
  catch *
  do
    return 0
  done
done

The same function can written using standalone catch:

func hasmx (string s)
  returns number
do
  catch *
  do
    return 0
  done
  return primitive_hasmx(domainpart(s))
done

All variables remain visible within catch body, with the exception of positional arguments of the enclosing handler. To access positional arguments of a handler from the catch body, assign them to local variables prior to the ‘try--catch’ construct, e.g.:

prog header
do
  string hname $1
  string hvalue $2
  try
  do
    …
  done
  catch *
  do
    echo "Exception $1 while processing header %hname: %hvalue"
    echo $2
    tempfail
  done

You can also generate (or raise) exceptions explicitly in the code, using throw statement:

throw excode descr

The arguments correspond exactly to the positional parameters of the catch statement: excode gives the numeric code of the exception, descr gives its textual description. This statement can be used in complex scripts to create non-local exits from deeply nested statements.

Notice, that the the excode argument must be an immediate value: an exception identifier (either a built-in one or one declared previously using a dclex statement).


Footnotes

(17)

As of mailfromd version 9.0, there is also a third implicit argument, which holds the value of program counter where the exception occurred. Currently it is considered to be an implementation artifact. Filter writers are discouraged from relying on it.

(18)

This function is part of the mailfromd library, See hasmx.


Previous: , Up: Exceptions   [Contents][Index]