check_content_types - check content types


This module parses a MIME message into its components and compares the content types of all parts with the contents of config/content_types. It returns OK, DENY or DECLINED on the first match, or DECLINED if there is no match.

The content-types from the message have all LFWS replaced by a single space. For nested parts, all the content-types leading to this part are concatenated, separated by tabs. Thus, a text/plain part of a signed multipart message may be represented as:

    multipart-signed; micalg=pgp-sha1;
    boundary="boundary-2"<TAB>text/plain; charset=iso-8859-1

(all on one line, of course)

This allows distinguishing between e.g., an HTML-only message and a multipart/alternative message containing an HTML version.


The content_type files contains one pattern and result per line, e.g.

    m{application/x-evil}       DENY
    m{text/plain}               DECLINED
    m{application/x-ikwid}      OK

The patterns are perl regular expressions including the quotes. Thus, the first example would also match ``x-application/x-evillage'', which may or may not be intended and it will not match ``APPLICATION/X-EVIL'', which almost certainly isn't. Thus some care in writing the regexps is necessary.

The result values are the same as for qpsmtpd plugins:

accept the message and skip all further plugins which might want to inspect the contents of the message. This could be used to skip virus filters for text/plain messages, for example.
reject the message with a ``552 Message denied'' response.
no decision whether the message should be accepted or rejected. The message will be passed on to the next plugin.

Therefore, somewhat unintuitively, the normal return value for acceptable content types is DECLINED.

Some more examples:


would match every part of type application/x-evil.


would match only a message of type text plain, not text/plain parts of a multipart message. OTOH,


would match only text plain parts of multipart messages with a funny capitalization.

You can also match on arbitrary parameters, e.g.


would match all parts in a certain alphabet which I cannot read.


The patterns are used to construct a sub which is then compiled and executed. Thus anyone with write access to the config/content_types file can execute arbitrary code as user smtpd. This doesn't matter since presumably the person who can write this file can add arbitrary plugins, anyway, but if this module is ever changed to permit per-user configuration, proper checking must be added.


data_post: check_content_type($self, $transaction)


None known (yet).


Nothing yet. Looks pretty complete to me except that interdependencies (e.g., match if there is a part of type X and a part of type Y) cannot be expressed. I think such checks are better left to specialized modules.


Peter J. Holzer <>