   _       ___  ____ ____  ____   _         _        ___            _
  |_|_ _  / _ \/ ___/ ___||  _ \ | |_ _ __ | |_ _ __|__ \ _ _  _ _ | |_ _ __
  _|_||_|| | | \___ \___ \| |_) || | ' `  \| __| '_ \ _) | ' \| ' \| __| '_ \
 |_||_|_|| |_| |___) |__) |  __/ | | || || | |_| |_) / _/| || | || | |_| |_) |
  |_|_|_| \___/|____/____/|_|    |_|_||_||_|\__| .__/____|_||_|_||_|\__| .__/
                                               |_|                     |_|
  OSSP lmtp2nntp - Mail to News Gateway

  INSTALLATION
  ============

  To install OSSP lmtp2nntp into /path/to/lmtp2nntp/{sbin,man}/ perform
  the following steps in your shell:

    $ ./configure --prefix=/path/to/lmtp2nntp
    $ make
    $ make check
    $ make install

  CONFIGURATION
  =============

  In order to use OSSP lmtp2nntp, the program has to be integrated into
  a Mail Transfer Agent (MTA) which speaks Local Mail Transfer Protocol
  (LMTP). OSSP lmtp2nntp was developed and tested with Sendmail 8.12 and
  Postfix 2.0, but should also work with other MTAs who provide an LMTP
  interface. The detailed integration into Sendmail and Postfix is shown
  below.

  Sendmail
  --------

  1. Create a new mailer definition macro file

     cf/mailer/lmtp2nntp.m4
     |
     | PUSHDIVERT(-1)
     | 
     | _DEFIFNOT(`LMTP2NNTP_MAILER_PATH',  `/usr/local/sbin/lmtp2nntp')
     | _DEFIFNOT(`LMTP2NNTP_MAILER_FLAGS', `mDFMuXz0')
     | _DEFIFNOT(`LMTP2NNTP_MAILER_MAX',   `8388608')
     | _DEFIFNOT(`LMTP2NNTP_MAILER_ARGS',  `--include /usr/local/etc/lmtp2nntp.conf')
     | 
     | POPDIVERT
     | 
     | #########################################
     | ###   LMTP2NNTP Mailer specification  ###
     | #########################################
     | 
     | Mlmtp2nntp, P=LMTP2NNTP_MAILER_PATH, F=LMTP2NNTP_MAILER_FLAGS,
     |             S=EnvFromSMTP/HdrFromSMTP, R=MasqSMTP, E=\r\n,
     |             L=990, T=DNS/RFC822/SMTP, M=LMTP2NNTP_MAILER_MAX,
     |             A=LMTP2NNTP_MAILER_ARGS

  2. Use the new mailer definition macro within master control

     Sendmail and lmtp2nntp can talk LMTP to each other through
     stdin/stdout. In this case Sendmail spawns a child whenever it
     wants to talk and no communication channel is already established.
     The new process execs lmtp2nntp and finally the Sendmail parent and
     the lmtp2nntp child are connected through a local pipe.

     sendmail.m4 entry for local pipe
     |
     | dnl #  Mailer: LMTP2NNTP (mail to news gateway using pipe)
     | dnl define(`LMTP2NNTP_MAILER_FLAGS', `mDFMuXz0')
     | dnl define(`LMTP2NNTP_MAILER_PATH',  `/usr/local/sbin/lmtp2nntp')
     | dnl define(`LMTP2NNTP_MAILER_ARGS',  `lmtp2nntp --include /usr/local/etc/lmtp2nntp.conf')

     Alternatively, Sendmail and lmtp2nntp can talk LMTP to each other
     through a socket. Both BSD network and UNIX domain sockets are
     supported.  In this case Sendmail connects to lmtp2nntp through the
     network. It is a requirement to start lmtp2nntp in daemon mode as
     described in the "daemonize" section below.

     sendmail.m4 entry for network socket
     |
     | dnl #  Mailer: LMTP2NNTP (mail to news gateway using socket)
     | define(`LMTP2NNTP_MAILER_FLAGS', `mDFMuXz0')
     | define(`LMTP2NNTP_MAILER_PATH',  `[IPC]')

     sendmail.m4 entry for UNIX domain socket
     |
     | define(`LMTP2NNTP_MAILER_ARGS',  `FILE /usr/local/var/lmtp2nntp/lmtp2nntp.socket')

     sendmail.m4 entry for BSD network socket
     |
     | define(`LMTP2NNTP_MAILER_ARGS',  `TCP 127.0.0.1 24')

     In all cases, tell M4 to divert configuration information based
     your preferences into sendmail.cf.  This defines the new
     "lmtp2nntp" mailer in your Sendmail configuration. Additional steps
     are required to activate the configuration.

     sendmail.m4
     |
     | MAILER(lmtp2nntp)


  3. Activate the virtual user table and mailer table in your Sendmail
     M4 configuration 

     sendmail.m4
     |
     | FEATURE(mailertable,   `hash -o /path/to/mailertable')
     | FEATURE(virtusertable, `hash -o /path/to/virtusertable')

  4. Add the following entries to your virtual user table 
     and your mailer table

     virtusertable
     |
     | posting+*@gateway.example.com %2@lmtp2nntp.invalid

     mailertable
     |
     | lmtp2nntp.invalid lmtp2nntp:news

     Notice that we use the RFC2606 top-level domain name ".invalid"
     for the internal mapping between virtual user table and the
     mailer table. This is neccessary because Sendmail cannot directly
     map individual recipient mailbox addresses to outgoing mailers.

     The token following the colon (news.example.com) in mailertable is
     passed to mailer as $h. The instructions regarding sendmail.m4
     above ignore that fact and assume the address of the NNTP server is
     configured in lmtp2nntp.conf. However, only the "pipe" mode
     discussed above is able to catch that information from Sendmail.
     Simply add '--destination $h' to LMTP2NNTP_MAILER_ARGS and omit
     'destination' from the config file. 

     In this example configuration we assume that the machine
     "gateway.example.com" is the FQDN of machine running Sendmail and
     OSSP lmtp2nntp and "news" is the remote Usenet News server speaking
     NNTP. This means that mails addressed to
     "posting+foo.bar@gateway.example.com" are posted via NNTP as news
     articles into the newsgroup foo.bar on server "news".

     Additionally, if the MX RR for "news.example.com" points to the
     address of "gateway.example.com" you can add the following
     additional entry to your virtual user table in order to let
     "news.example.com" virtually provide mail to news gateway
     functionality:

     virtusertable
     |
     | @news.example.com %1@lmtp2nntp.invalid

     Notice that there are more delivery modes supported by OSSP
     lmtp2nntp. Refer to the lmtp2nntp(1) manual page for more details.

  Postfix
  -------

  1. Make sure that Postfix was built with PCRE map support
     (see README.PCRE in the original Postfix source distribution).
     It basically means building and installing PCRE from
     ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ and
     configuring Postfix with at least:

     | make makefiles \
     |     CCARGS="-I/path/to/pcre/include -DHAS_PCRE"
     |     AUXLIBS="-L/path/to/pcre/lib -lpcre"

  2. In the Postfix main configuration file (main.cf) you have
     to enable the virtual user and transport mapping tables:

     main.cf
     |
     | virtual_maps   = pcre:/usr/local/etc/virtual
     | transport_maps = hash:/usr/local/etc/transport

  3. Add the following entry to the Postfix virtual user mapping table

     /usr/local/etc/virtual
     |
     | /^posting\+(.*)@gateway\.example\.com$/ $1@lmtp2nntp.invalid

  4. Add the following entry to the Postfix transport mapping table

     /usr/local/etc/transport
     |
     | lmtp2nntp.invalid  lmtp:unix:/path/to/lmtp2nntp.news.example.com

     Additionally convert the transport mapping table into the hash
     table format Postfix reads (not required for the virtual user
     mapping table above, because it is PCRE based).

     $ postmap /usr/local/etc/transport

  5. Assuming you use the "lmtp2nntpd" run command script as described
     in the next section "Daemonize", start lmtp2nntp daemon and reload
     Postfix to activate the new setup Note that Postfix does not
     support LMTP across local pipes as Sendmail does so running a
     daemon is mandatory.

     $ lmtp2nntpd start
     $ postfix reload

  Daemonize
  ---------

  1. Start lmtp2nntp as a daemon for delivering and listen for incoming
     connections.

     lmtp2nntpd
     |
     | #!/bin/sh
     | LMTP2NNTP_PATH='/usr/local/sbin/lmtp2nntp'
     | LMTP2NNTP_CONF='/usr/local/etc/lmtp2nntp.conf'
     | case $1 in
     |     start )
     |         $LMTP2NNTP_PATH --daemonize --include $LMTP2NNTP_CONF
     |         ;;
     |     stop )
     |         $LMTP2NNTP_PATH --kill      --include $LMTP2NNTP_CONF
     |         ;;
     | esac

  Configuration
  -------------

     lmtp2nntp.conf
     |
     | ##
     | ##  lmtp2nntp.conf -- Mail to News Gateway Configuration
     | ##
     | 
     | #   --- DAEMON ---
     | user               @l_musr@
     | childsmax          3
     | pidfile            "@l_prefix@/var/lmtp2nntp/lmtp2nntp.pid"
     | 
     | #   --- LMTP SERVER ---
     | 
     | #   server communication on TCP socket (address:port)
     | #acl               127.0.0.1/32
     | #bind              127.0.0.1:24
     | 
     | #   server communication on Unix domain socket (path:permissions)
     | bind               "@l_prefix@/var/lmtp2nntp/lmtp2nntp.socket:660"
     | 
     | #   server communication on stdin/stdout (remove DAEMON section above)
     | #bind              -
     | 
     | #   server communication limits
     | timeoutlmtp        0
     | timeoutlmtpaccept  0
     | timeoutlmtpread    10
     | timeoutlmtpwrite   10
     | size               8388608
     | 
     | #   ---- NNTP CLIENT ----
     | 
     | #   client communication via TCP socket
     | #client            127.0.0.1
     | destination        127.0.0.1:nntp
     | 
     | #   client communication limits
     | timeoutnntp        30
     | timeoutnntpconnect 60
     | timeoutnntpread    60
     | timeoutnntpwrite   60
     | operationmode      post
     | 
     | #   ---- GATEWAY OPERATION ----
     | 
     | #   processing mode
     | groupmode          envelope
     | mailfrom           ".*"
     | nodename           "gateway"
     | restrictheader     "X-Gateway:.*lmtp2nntp"
     | newsgroup          "local.test"
     | 
     | #   remove headers (using empty value)
     | headerrule         '500:^(-EF|Path|Received|To|Cc|Bcc|):$1:'
     | 
     | #   merge values from duplicate headers and separte them with "comma space"
     | headerrule         '510:^(Reply-To):$1:[${msg.header.${1}[#]}${msg.header.${1}[#+1]:+, }]'
     | 
     | #   replace empty Subject with text "None"
     | headerrule         '520::Subject:${msg.header.Subject:-None}'
     | 
     | #   create Message-ID if omitted or illegal (no or more than one '@')
     | headerrule         '530::Message-ID:${msg.header.Message-ID:-@@:s/^.*@.*@.*$//:%createmessageid}'
     | 
     | #   append a header to inhibit bang path addressing for mail replies
     | headerrule         '540::Path:lmtp2nntp!not-for-mail'
     | 
     | #   append a header to avoid message loops with news2mail gateways - see restrictheader
     | headerrule         '610::X-Gateway:lmtp2nntp'
     | 
     | #   append a header to approve posting to a moderated newsgroup
     | headerrule         '620::Approved:gateway@example.com'
     | 
     | #   ---- LOGGING ----
     | 
     | #   normal operation
     | l2spec             'info:  prefix(prefix="%b %d %H:%M:%S <%L> lmtp2nntp[%P]: ", timezone=local) \
     |                         -> buffer(size=65536) \
     |                         -> file(path="@l_prefix@/var/lmtp2nntp/lmtp2nntp.log", append=1, perm=0640)'
     | 
     | #   normal and debug operation
     | #l2spec            'noop -> { \
     |                     info:  prefix(prefix="%b %d %H:%M:%S <%L> lmtp2nntp[%P]: ", timezone=local) \
     |                         -> buffer(size=65536) \
     |                         -> file(path="@l_prefix@/var/lmtp2nntp/lmtp2nntp.log", append=1, perm=0640); \
     |                     debug: prefix(prefix="%b %d %H:%M:%S <%L> lmtp2nntp[%P]: ", timezone=local) \
     |                         -> file(path="@l_prefix@/var/lmtp2nntp/lmtp2nntp.debug", append=0, perm=0640) }'
     | 
     | #   raw debug operation only
     | #l2spec            'debug: fd(fd=2)'
