#!/usr/bin/python
#
# vim: tabstop=4 expandtab shiftwidth=4 noautoindent
#
# nymserv.py - A Basic Nymserver for delivering messages to a shared mailbox
# such as alt.anonymous.messages.
#
# Copyright (C) 2012 Steve Crook <steve@mixmin.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program.  If not, see <http://www.gnu.org/licenses/>.

import sys
import os.path
import logging
import signal
from Daemon import Daemon
from Config import config
import timing
import Mail
import Pool
import IDLog
import EncodePacket
import KeyManager


class MyDaemon(Daemon):
    def run(self):
        """Override the run function from the standard Python Daemon.  Did I
           say standard?  There isn't a standard Daemon for Python2.x but this
           is probably as close as it gets.
        """
        # First, load up the keyrings.
        pubring = KeyManager.Pubring()
        secring = KeyManager.Secring()
        # Packet encoding requires the Public Keyring in order to encrypt
        # messages to other remailers.
        encode = EncodePacket.Mixmaster(pubring)
        # Fire up the PacketID log that prevents replay attacks on Mixmaster
        # messages.  This (and the Chunk Manager) are only used within the
        # DecodePacket process but initializing them here enables us to
        # cleanly close them when we catch a SIGTERM.
        idlog = IDLog.PacketID()
        # Chunkmgr handles reassembly of multipart messages.  This function
        # is only performed on exit messages but as destinations can be
        # whitelisted, even Middleman remailers can perform exit functions.
        chunkmgr = IDLog.ChunkID()
        # The mail function reads the incoming mail queue and performs any
        # processing required to turn each inbound message into an outbound
        # message in the pool.
        mail = Mail.MailMessage(pubring, secring, idlog, encode, chunkmgr)
        # The pool process handles the random selection of messages from the
        # pool and the actual sending of them.  It requies PacketEncode
        # functionality in order to generate dummies.
        pool = Pool.Pool(encode)
        # Sleep dictates how many seconds between each loop of inbound mail
        # checking.  Pool processing is also considered after each sleep interval
        # but it only performed if the configured pool-interval has expired.
        sleep = timing.dhms_secs(config.get('general', 'interval'))
        self.idlog = idlog
        self.chunkmgr = chunkmgr
        # Catch SIGTERM signals so we can close files cleanly before
        # terminating.
        signal.signal(signal.SIGTERM, self.signal_handler)
        # Begin an endless loop of mail and pool processing.
        while True:
            idlog.prune()
            chunkmgr.prune()
            mail.iterate_mailbox()
            pool.process()
            idlog.sync()
            chunkmgr.sync()
            log.debug("Sleeping for %s seconds", sleep)
            try:
                timing.sleep(sleep)
            except KeyboardInterrupt:
                self.idlog.close()
                self.chunkmgr.close()
                sys.exit(0)

    def signal_handler(self, signum, frame):
        log.info("Caught SIGTERM, exiting cleanly.")
        # Reset SIGTERM to its default handler, otherwise stop() will
        # endlessly loop.
        signal.signal(signum, signal.SIG_DFL)
        self.idlog.close()
        self.chunkmgr.close()
        self.stop()



if (__name__ == "__main__"):
    logfmt = config.get('logging', 'format')
    datefmt = config.get('logging', 'datefmt')
    loglevels = {'debug': logging.DEBUG, 'info': logging.INFO,
                 'warn': logging.WARN, 'error': logging.ERROR}
    log = logging.getLogger("Pymaster")
    log.setLevel(loglevels[config.get('logging', 'level')])
    filename = os.path.join(config.get('paths', 'log'), 'pymaster.log')
    handler = logging.FileHandler(filename, mode='a')
    handler.setFormatter(logging.Formatter(fmt=logfmt, datefmt=datefmt))
    log.addHandler(handler)

    d = MyDaemon(config.get('general', 'pidfile'),
                 stderr=os.path.join(config.get('paths', 'log'), 'error.log'))
    if len(sys.argv) <= 1:
        sys.stdout.write("Usage: --start\n")
        sys.exit(0)
    command = sys.argv[1]
    if command == "--start":
        d.start()
