#!/usr/bin/python -OO
# Copyright 2008-2017 The SABnzbd-Team <team@sabnzbd.org>
#
# 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 2
# of the License, 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
# MERCHANTABILITY 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

import sys
if sys.version_info[:2] < (2, 7) or sys.version_info[:2] >= (3, 0):
    print "Sorry, requires Python 2.7."
    sys.exit(1)

# Make sure UTF-8 is default 8bit encoding
if not hasattr(sys, "setdefaultencoding"):
    reload(sys)
try:
    sys.setdefaultencoding('utf-8')
except:
    print 'Sorry, you MUST add the SABnzbd folder to the PYTHONPATH environment variable'
    print 'or find another way to force Python to use UTF-8 for text encoding.'
    sys.exit(1)

import logging
import logging.handlers
import os
import getopt
import signal
import socket
import platform
import time
import re

try:
    import Cheetah
    if Cheetah.Version[0] != '2':
        raise ValueError
except ValueError:
    print "Sorry, requires Python module Cheetah 2.0rc7 or higher."
    sys.exit(1)
except:
    print "The Python module Cheetah is required"
    sys.exit(1)

import cherrypy
if [int(n) for n in cherrypy.__version__.split('.')] < [8, 1, 2]:
    print 'Sorry, requires Python module Cherrypy 8.1.2+ (use the included version)'
    sys.exit(1)

from cherrypy import _cpserver

SQLITE_DLL = True
try:
    from sqlite3 import version as sqlite3_version
except:
    try:
        from pysqlite2.dbapi2 import version as sqlite3_version
    except:
        if os.name != 'nt':
            print "Sorry, requires Python module sqlite3"
            print "Try: apt-get install python-pysqlite2"
            sys.exit(1)
        else:
            SQLITE_DLL = False

import locale
import __builtin__
try:
    locale.setlocale(locale.LC_ALL, "")
    __builtin__.__dict__['codepage'] = locale.getlocale()[1] or 'cp1252'
except:
    # Work-around for Python-ports with bad "locale" support
    __builtin__.__dict__['codepage'] = 'cp1252'

import sabnzbd
import sabnzbd.lang
import sabnzbd.interface
from sabnzbd.constants import *
import sabnzbd.newsunpack
from sabnzbd.misc import real_path, \
    check_latest_version, exit_sab, \
    split_host, get_ext, create_https_certificates, \
    windows_variant, ip_extract, set_serv_parms, get_serv_parms, globber_full
from sabnzbd.panic import panic_tmpl, panic_port, panic_host, panic_fwall, \
    panic_sqlite, panic, launch_a_browser, panic_xport
import sabnzbd.scheduler as scheduler
import sabnzbd.config as config
import sabnzbd.cfg
import sabnzbd.downloader
from sabnzbd.encoding import unicoder, deunicode
import sabnzbd.notifier as notifier
import sabnzbd.zconfig
import sabnzbd.utils.sslinfo

from threading import Thread

LOG_FLAG = False        # Global for this module, signaling loglevel change


try:
    import win32api
    import win32serviceutil
    import win32evtlogutil
    import win32event
    import win32service
    import pywintypes
    win32api.SetConsoleCtrlHandler(sabnzbd.sig_handler, True)
    from util.mailslot import MailSlot
    from util.apireg import get_connection_info, set_connection_info, del_connection_info
except ImportError:
    class MailSlot:
        pass
    if sabnzbd.WIN32:
        print "Sorry, requires Python module PyWin32."
        sys.exit(1)


def guard_loglevel():
    """ Callback function for guarding loglevel """
    global LOG_FLAG
    LOG_FLAG = True


class FilterCP3:
    # Filter out all CherryPy3-Access logging that we receive,
    # because we have the root logger

    def __init__(self):
        pass

    def filter(self, record):
        _cplogging = record.module == '_cplogging'
        # Python2.4 fix
        # record has no attribute called funcName under python 2.4
        if hasattr(record, 'funcName'):
            access = record.funcName == 'access'
        else:
            access = True
        return not (_cplogging and access)


class guiHandler(logging.Handler):
    """ Logging handler collects the last warnings/errors/exceptions
        to be displayed in the web-gui
    """

    def __init__(self, size):
        """ Initializes the handler """
        logging.Handler.__init__(self)
        self.size = size
        self.store = []

    def emit(self, record):
        """ Emit a record by adding it to our private queue """
        if record.levelname == 'WARNING':
            sabnzbd.LAST_WARNING = record.msg % record.args
        else:
            sabnzbd.LAST_ERROR = record.msg % record.args

        if len(self.store) >= self.size:
            # Loose the oldest record
            self.store.pop(0)
        try:
            self.store.append(self.format(record))
        except UnicodeDecodeError:
            # Catch elusive Unicode conversion problems
            pass

    def clear(self):
        self.store = []

    def count(self):
        return len(self.store)

    def last(self):
        if self.store:
            return self.store[len(self.store) - 1]
        else:
            return ""

    def content(self):
        """ Return an array with last records """
        return self.store


def print_help():
    print
    print "Usage: %s [-f <configfile>] <other options>" % sabnzbd.MY_NAME
    print
    print "Options marked [*] are stored in the config file"
    print
    print "Options:"
    print "  -f  --config-file <ini>  Location of config file"
    print "  -s  --server <srv:port>  Listen on server:port [*]"
    print "  -t  --templates <templ>  Template directory [*]"
    print "  -2  --template2 <templ>  Secondary template dir [*]"
    print
    print "  -l  --logging <0..2>     Set logging level (-1=off, 0= least, 2= most) [*]"
    print "  -w  --weblogging <0..2>  Set cherrypy logging (0= off, 1= on, 2= file-only) [*]"
    print
    print "  -b  --browser <0..1>     Auto browser launch (0= off, 1= on) [*]"
    if sabnzbd.WIN32:
        print "  -d  --daemon             Use when run as a service"
    else:
        print "  -d  --daemon             Fork daemon process"
        print "      --pid <path>         Create a PID file in the given folder (full path)"
        print "      --pidfile <path>     Create a PID file with the given name (full path)"
    print
    print "      --force              Discard web-port timeout (see Wiki!)"
    print "  -h  --help               Print this message"
    print "  -v  --version            Print version information"
    print "  -c  --clean              Remove queue, cache and logs"
    print "  -p  --pause              Start in paused mode"
    print "      --repair             Add orphaned jobs from the incomplete folder to the queue"
    print "      --repair-all         Try to reconstruct the queue from the incomplete folder"
    print "                           with full data reconstruction"
    print "      --https <port>       Port to use for HTTPS server"
    print "      --log-all            Log all article handling (for developers)"
    print "      --console            Force console logging for OSX app"
    print "      --new                Run a new instance of SABnzbd"
    print "      --ipv6_hosting <0|1> Listen on IPv6 address [::1]"


def print_version():
    print """
%s-%s

Copyright (C) 2008-2017, The SABnzbd-Team <team@sabnzbd.org>
SABnzbd comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under certain conditions. It is licensed under the
GNU GENERAL PUBLIC LICENSE Version 2 or (at your option) any later version.

""" % (sabnzbd.MY_NAME, sabnzbd.__version__)


def daemonize():
    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)
    except OSError:
        print "fork() failed"
        sys.exit(1)

    os.chdir(sabnzbd.DIR_PROG)
    os.setsid()
    # Make sure I can read my own files and shut out others
    prev = os.umask(0)
    os.umask(prev and int('077', 8))

    try:
        pid = os.fork()
        if pid > 0:
            sys.exit(0)
    except OSError:
        print "fork() failed"
        sys.exit(1)

    dev_null = file('/dev/null', 'r')
    os.dup2(dev_null.fileno(), sys.stdin.fileno())


def Bail_Out(browserhost, cherryport, err=''):
    """ Abort program because of CherryPy troubles """
    logging.error(T('Failed to start web-interface') + ' : ' + str(err))
    if not sabnzbd.DAEMON:
        if '13' in err:
            panic_xport(browserhost, cherryport)
        elif '49' in err:
            panic_host(browserhost, cherryport)
        else:
            panic_port(browserhost, cherryport)
    sabnzbd.halt()
    exit_sab(2)


def Web_Template(key, defweb, wdir):
    """ Determine a correct web template set, return full template path """
    if wdir is None:
        try:
            wdir = fix_webname(key())
        except:
            wdir = ''
    if not wdir:
        wdir = defweb
    if key:
        key.set(wdir)
    if not wdir:
        # No default value defined, accept empty path
        return ''

    full_dir = real_path(sabnzbd.DIR_INTERFACES, wdir)
    full_main = real_path(full_dir, DEF_MAIN_TMPL)
    logging.info("Web dir is %s", full_dir)

    if not os.path.exists(full_main):
        # Temporarily fix that allows missing Config
        if defweb == DEF_STDCONFIG:
            return ''
        # end temp fix
        logging.warning(T('Cannot find web template: %s, trying standard template'), full_main)
        full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
        full_main = real_path(full_dir, DEF_MAIN_TMPL)
        if not os.path.exists(full_main):
            logging.exception('Cannot find standard template: %s', full_dir)
            panic_tmpl(full_dir)
            exit_sab(1)

    # sabnzbd.lang.install_language(real_path(full_dir, DEF_INT_LANGUAGE), sabnzbd.cfg.language(), wdir)

    return real_path(full_dir, "templates")


def CheckColor(color, web_dir):
    """ Check existence of color-scheme """
    if color and os.path.exists(os.path.join(web_dir, 'static/stylesheets/colorschemes/' + color + '.css')):
        return color
    elif color and os.path.exists(os.path.join(web_dir, 'static/stylesheets/colorschemes/' + color)):
        return color
    else:
        return ''


def fix_webname(name):
    if name:
        name = deunicode(name)
        xname = name.title()
    else:
        xname = ''
    if xname in ('Default', ):
        return 'Glitter'
    elif xname in ('Glitter', 'Plush'):
        return xname
    elif xname in ('Smpl', 'Wizard'):
        return name.lower()
    elif xname in ('Config',):
        return 'Glitter'
    else:
        return name


def GetProfileInfo(vista_plus):
    """ Get the default data locations """
    ok = False
    if sabnzbd.DAEMON:
        # In daemon mode, do not try to access the user profile
        # just assume that everything defaults to the program dir
        sabnzbd.DIR_APPDATA = sabnzbd.DIR_PROG
        sabnzbd.DIR_LCLDATA = sabnzbd.DIR_PROG
        sabnzbd.DIR_HOME = sabnzbd.DIR_PROG
        if sabnzbd.WIN32:
            # Ignore Win32 "logoff" signal
            # This should work, but it doesn't
            # Instead the signal_handler will ignore the "logoff" signal
            # signal.signal(5, signal.SIG_IGN)
            pass
        ok = True
    elif sabnzbd.WIN32:
        try:
            from win32com.shell import shell, shellcon
            path = shell.SHGetFolderPath(0, shellcon.CSIDL_APPDATA, None, 0)
            sabnzbd.DIR_APPDATA = os.path.join(path, DEF_WORKDIR)
            path = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, None, 0)
            sabnzbd.DIR_LCLDATA = os.path.join(path, DEF_WORKDIR)
            sabnzbd.DIR_HOME = os.environ['USERPROFILE']
            ok = True
        except:
            try:
                if vista_plus:
                    root = os.environ['AppData']
                    user = os.environ['USERPROFILE']
                    sabnzbd.DIR_APPDATA = '%s\\%s' % (root.replace('\\Roaming', '\\Local'), DEF_WORKDIR)
                    sabnzbd.DIR_HOME = user
                else:
                    root = os.environ['USERPROFILE']
                    sabnzbd.DIR_APPDATA = '%s\\%s' % (root, DEF_WORKDIR)
                    sabnzbd.DIR_HOME = root

                try:
                    # Conversion to 8bit ASCII required for CherryPy
                    sabnzbd.DIR_APPDATA = sabnzbd.DIR_APPDATA.encode(codepage)
                    sabnzbd.DIR_HOME = sabnzbd.DIR_HOME.encode(codepage)
                    ok = True
                except:
                    # If unconvertible characters exist, use MSDOS name
                    try:
                        sabnzbd.DIR_APPDATA = win32api.GetShortPathName(sabnzbd.DIR_APPDATA)
                        sabnzbd.DIR_HOME = win32api.GetShortPathName(sabnzbd.DIR_HOME)
                        ok = True
                    except:
                        pass
                sabnzbd.DIR_LCLDATA = sabnzbd.DIR_APPDATA
            except:
                pass

    elif sabnzbd.DARWIN:
        home = os.environ.get('HOME')
        if home:
            sabnzbd.DIR_APPDATA = '%s/Library/Application Support/SABnzbd' % home
            sabnzbd.DIR_LCLDATA = sabnzbd.DIR_APPDATA
            sabnzbd.DIR_HOME = home
            ok = True

    else:
        # Unix/Linux
        home = os.environ.get('HOME')
        if home:
            sabnzbd.DIR_APPDATA = '%s/.%s' % (home, DEF_WORKDIR)
            sabnzbd.DIR_LCLDATA = sabnzbd.DIR_APPDATA
            sabnzbd.DIR_HOME = home
            ok = True

    if not ok:
        panic("Cannot access the user profile.",
              "Please start with sabnzbd.ini file in another location")
        exit_sab(2)


def print_modules():
    """ Log all detected optional or external modules """
    if sabnzbd.decoder.HAVE_YENC:
        logging.info("_yenc module... found!")
    else:
        logging.warning(T('_yenc module... NOT found!'))

    if sabnzbd.HAVE_CRYPTOGRAPHY:
        logging.info('Cryptography module (v%s)... found!', sabnzbd.HAVE_CRYPTOGRAPHY)
    else:
        logging.info('Cryptography module... NOT found!')

    if sabnzbd.newsunpack.PAR2_COMMAND:
        logging.info("par2 binary... found (%s)", sabnzbd.newsunpack.PAR2_COMMAND)
    else:
        logging.error(T('par2 binary... NOT found!'))

    if sabnzbd.newsunpack.PAR2C_COMMAND:
        logging.info("par2cmdline binary... found (%s)", sabnzbd.newsunpack.PAR2C_COMMAND)

    if sabnzbd.newsunpack.RAR_COMMAND:
        logging.info("UNRAR binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)

    else:
        logging.warning(T('unrar binary... NOT found'))

    if sabnzbd.newsunpack.ZIP_COMMAND:
        logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
    else:
        logging.info(T('unzip binary... NOT found!'))

    if sabnzbd.newsunpack.SEVEN_COMMAND:
        logging.info("7za binary... found (%s)", sabnzbd.newsunpack.SEVEN_COMMAND)
    else:
        logging.info(T('7za binary... NOT found!'))

    if not sabnzbd.WIN32:
        if sabnzbd.newsunpack.NICE_COMMAND:
            logging.info("nice binary... found (%s)", sabnzbd.newsunpack.NICE_COMMAND)
        else:
            logging.info("nice binary... NOT found!")
        if sabnzbd.newsunpack.IONICE_COMMAND:
            logging.info("ionice binary... found (%s)", sabnzbd.newsunpack.IONICE_COMMAND)
        else:
            logging.info("ionice binary... NOT found!")


def all_localhosts():
    """ Return all unique values of localhost in order of preference """
    ips = ['127.0.0.1']
    try:
        # Check whether IPv6 is available and enabled
        info = socket.getaddrinfo('::1', None)
        af, socktype, proto, _canonname, _sa = info[0]
        s = socket.socket(af, socktype, proto)
        s.close()
    except socket.error:
        return ips
    try:
        info = socket.getaddrinfo('localhost', None)
    except:
        # localhost does not resolve
        return ips
    ips = []
    for item in info:
        item = item[4][0]
        # Avoid problems on strange Linux settings
        if not isinstance(item, basestring):
            continue
        # Only return IPv6 when enabled
        if item not in ips and ('::1' not in item or sabnzbd.cfg.ipv6_hosting()):
            ips.append(item)
    return ips


def check_resolve(host):
    """ Return True if 'host' resolves """
    try:
        dummy = socket.getaddrinfo(host, None)
    except:
        # Does not resolve
        return False
    return True


def get_webhost(cherryhost, cherryport, https_port):
    """ Determine the webhost address and port,
        return (host, port, browserhost)
    """
    if cherryhost == '0.0.0.0' and not check_resolve('127.0.0.1'):
        cherryhost = ''
    elif cherryhost == '::' and not check_resolve('::1'):
        cherryhost = ''

    if cherryhost is None:
        cherryhost = deunicode(sabnzbd.cfg.cherryhost())
    else:
        sabnzbd.cfg.cherryhost.set(cherryhost)

    # Get IP address, but discard APIPA/IPV6
    # If only APIPA's or IPV6 are found, fall back to localhost
    ipv4 = ipv6 = False
    localhost = hostip = 'localhost'
    try:
        info = socket.getaddrinfo(socket.gethostname(), None)
    except:
        # Hostname does not resolve
        try:
            # Valid user defined name?
            info = socket.getaddrinfo(cherryhost, None)
        except:
            if cherryhost not in ('localhost', '127.0.0.1', '::1'):
                cherryhost = '0.0.0.0'
            try:
                info = socket.getaddrinfo(localhost, None)
            except:
                info = socket.getaddrinfo('127.0.0.1', None)
                localhost = '127.0.0.1'
    for item in info:
        ip = str(item[4][0])
        if ip.startswith('169.254.'):
            pass  # Automatic Private IP Addressing (APIPA)
        elif ':' in ip:
            ipv6 = True
        elif '.' in ip and not ipv4:
            ipv4 = True
            hostip = ip

    # A blank host will use the local ip address
    if cherryhost == '':
        if ipv6 and ipv4:
            # To protect Firefox users, use numeric IP
            cherryhost = hostip
            browserhost = hostip
        else:
            cherryhost = socket.gethostname()
            browserhost = cherryhost

    # 0.0.0.0 will listen on all ipv4 interfaces (no ipv6 addresses)
    elif cherryhost == '0.0.0.0':
        # Just take the gamble for this
        cherryhost = '0.0.0.0'
        browserhost = localhost

    # :: will listen on all ipv6 interfaces (no ipv4 addresses)
    elif cherryhost in ('::', '[::]'):
        cherryhost = cherryhost.strip('[').strip(']')
        # Assume '::1' == 'localhost'
        browserhost = localhost

    # IPV6 address
    elif '[' in cherryhost or ':' in cherryhost:
        browserhost = cherryhost

    # IPV6 numeric address
    elif cherryhost.replace('.', '').isdigit():
        # IPV4 numerical
        browserhost = cherryhost

    elif cherryhost == localhost:
        cherryhost = localhost
        browserhost = localhost

    else:
        # If on Vista and/or APIPA, use numerical IP, to help FireFoxers
        if ipv6 and ipv4:
            cherryhost = hostip
        browserhost = cherryhost

    # Some systems don't like brackets in numerical ipv6
        if sabnzbd.DARWIN:
            cherryhost = cherryhost.strip('[]')
        else:
            try:
                info = socket.getaddrinfo(cherryhost, None)
            except:
                cherryhost = cherryhost.strip('[]')

    if ipv6 and ipv4 and \
       (browserhost not in ('localhost', '127.0.0.1', '[::1]', '::1')):
        sabnzbd.AMBI_LOCALHOST = True
        logging.info("IPV6 has priority on this system, potential Firefox issue")

    if ipv6 and ipv4 and cherryhost == '' and sabnzbd.WIN32:
        logging.warning(T('Please be aware the 0.0.0.0 hostname will need an IPv6 address for external access'))

    if cherryhost == 'localhost' and not sabnzbd.WIN32 and not sabnzbd.DARWIN:
        # On the Ubuntu family, localhost leads to problems for CherryPy
        ips = ip_extract()
        if '127.0.0.1' in ips and '::1' in ips:
            cherryhost = '127.0.0.1'
            if ips[0] != '127.0.0.1':
                browserhost = '127.0.0.1'

    # This is to please Chrome on OSX
    if cherryhost == 'localhost' and sabnzbd.DARWIN:
        cherryhost = '127.0.0.1'
        browserhost = 'localhost'

    if cherryport is None:
        cherryport = sabnzbd.cfg.cherryport.get_int()
    else:
        sabnzbd.cfg.cherryport.set(str(cherryport))

    if https_port is None:
        https_port = sabnzbd.cfg.https_port.get_int()
    else:
        sabnzbd.cfg.https_port.set(str(https_port))
        # if the https port was specified, assume they want HTTPS enabling also
        sabnzbd.cfg.enable_https.set(True)

    if cherryport == https_port and sabnzbd.cfg.enable_https():
        sabnzbd.cfg.enable_https.set(False)
        # Should have a translated message, but that's not available yet
        logging.error(T('HTTP and HTTPS ports cannot be the same'))

    return cherryhost, cherryport, browserhost, https_port


def attach_server(host, port, cert=None, key=None, chain=None):
    """ Define and attach server, optionally HTTPS """
    if sabnzbd.cfg.ipv6_hosting() or '::1' not in host:
        http_server = cherrypy._cpserver.Server()
        http_server.bind_addr = (host, port)
        if cert and key:
            http_server.ssl_provider = 'builtin'
            http_server.ssl_certificate = cert
            http_server.ssl_private_key = key
            http_server.ssl_certificate_chain = chain
        http_server.subscribe()


def is_sabnzbd_running(url):
    """ Return True when there's already a SABnzbd instance running. """
    try:
        url = '%s&mode=version' % (url)
        # Do this without certificate verification, few installations will have that
        prev = sabnzbd.set_https_verification(False)
        ver = sabnzbd.newsunpack.get_from_url(url)
        sabnzbd.set_https_verification(prev)
        return (ver and (re.search(r'\d+\.\d+\.', ver) or ver.strip() == sabnzbd.__version__))
    except:
        return False


def find_free_port(host, currentport):
    """ Return a free port, 0 when nothing is free """
    n = 0
    while n < 10 and currentport <= 49151:
        try:
            cherrypy.process.servers.check_port(host, currentport, timeout=0.025)
            return currentport
        except:
            currentport += 5
            n += 1
    return 0


def check_for_sabnzbd(url, upload_nzbs, allow_browser=True):
    """ Check for a running instance of sabnzbd on this port
        allow_browser==True|None will launch the browser, False will not.
    """
    if allow_browser is None:
        allow_browser = True
    if is_sabnzbd_running(url):
        # Upload any specified nzb files to the running instance
        if upload_nzbs:
            from sabnzbd.utils.upload import upload_file
            prev = sabnzbd.set_https_verification(0)
            for f in upload_nzbs:
                upload_file(url, f)
            sabnzbd.set_https_verification(prev)
        else:
            # Launch the web browser and quit since sabnzbd is already running
            # Trim away everything after the final slash in the URL
            url = url[:url.rfind('/') + 1]
            launch_a_browser(url, force=allow_browser)
        exit_sab(0)
        return True
    return False


def evaluate_inipath(path):
    """ Derive INI file path from a partial path.
        Full file path: if file does not exist the name must contain a dot
        but not a leading dot.
        foldername is enough, the standard name will be appended.
    """
    if sabnzbd.WIN32:
        path = unicoder(path)
    path = os.path.normpath(os.path.abspath(path))
    inipath = os.path.join(path, DEF_INI_FILE)
    if os.path.isdir(path):
        return inipath
    elif os.path.isfile(path) or os.path.isfile(path + '.bak'):
        return path
    else:
        _dirpart, name = os.path.split(path)
        if name.find('.') < 1:
            return inipath
        else:
            return path


def cherrypy_logging(log_path, log_handler):
    """ Setup CherryPy logging """
    log = cherrypy.log
    log.access_file = ''
    log.error_file = ''
    # Max size of 512KB
    maxBytes = getattr(log, "rot_maxBytes", 524288)
    # cherrypy.log cherrypy.log.1 cherrypy.log.2
    backupCount = getattr(log, "rot_backupCount", 3)

    # Make a new RotatingFileHandler for the error log.
    fname = getattr(log, "rot_error_file", log_path)
    h = log_handler(fname, 'a', maxBytes, backupCount)
    h.setLevel(logging.DEBUG)
    h.setFormatter(cherrypy._cplogging.logfmt)
    log.error_log.addHandler(h)


def commandline_handler(frozen=True):
    """ Split win32-service commands are true parameters
        Returns:
            service, sab_opts, serv_opts, upload_nzbs
    """
    service = ''
    sab_opts = []
    serv_opts = [os.path.normpath(os.path.abspath(sys.argv[0]))]
    upload_nzbs = []

    # OSX binary: get rid of the weird -psn_0_123456 parameter
    for arg in sys.argv:
        if arg.startswith('-psn_'):
            sys.argv.remove(arg)
            break

    # Ugly hack to remove the extra "SABnzbd*" parameter the Windows binary
    # gets when it's restarted
    if len(sys.argv) > 1 and \
       'sabnzbd' in sys.argv[1].lower() and \
       not sys.argv[1].startswith('-'):
        slice = 2
    else:
        slice = 1

    # Prepend options from env-variable to options
    info = os.environ.get('SABnzbd', '').split()
    info.extend(sys.argv[slice:])

    try:
        opts, args = getopt.getopt(info, "phdvncw:l:s:f:t:b:2:",
                                   ['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=',
                                    'weblogging=', 'server=', 'templates', 'ipv6_hosting=',
                                    'template2', 'browser=', 'config-file=', 'force',
                                    'version', 'https=', 'autorestarted', 'repair', 'repair-all',
                                    'log-all', 'no-login', 'pid=', 'new', 'console', 'pidfile=',
                                    # Below Win32 Service options
                                    'password=', 'username=', 'startup=', 'perfmonini=', 'perfmondll=',
                                    'interactive', 'wait=',
                                    ])
    except getopt.GetoptError:
        print_help()
        exit_sab(2)

    # Check for Win32 service commands
    if args and args[0] in ('install', 'update', 'remove', 'start', 'stop', 'restart', 'debug'):
        service = args[0]
        serv_opts.extend(args)

    if not service:
        # Get and remove any NZB file names
        for entry in args:
            if get_ext(entry) in ('.nzb', '.zip', '.rar', '.gz', '.bz2'):
                upload_nzbs.append(os.path.abspath(entry))

    for opt, arg in opts:
        if opt in ('password', 'username', 'startup', 'perfmonini', 'perfmondll', 'interactive', 'wait'):
            # Service option, just collect
            if service:
                serv_opts.append(opt)
                if arg:
                    serv_opts.append(arg)
        else:
            if opt == '-f':
                arg = os.path.normpath(os.path.abspath(arg))
            sab_opts.append((opt, arg))

    return service, sab_opts, serv_opts, upload_nzbs


def get_f_option(opts):
    """ Return value of the -f option """
    for opt, arg in opts:
        if opt == '-f':
            return arg
    else:
        return None


def main():
    global LOG_FLAG
    import sabnzbd  # Due to ApplePython bug

    autobrowser = None
    autorestarted = False
    sabnzbd.MY_FULLNAME = sys.argv[0]
    sabnzbd.MY_NAME = os.path.basename(sabnzbd.MY_FULLNAME)
    fork = False
    pause = False
    inifile = None
    cherryhost = None
    cherryport = None
    https_port = None
    cherrypylogging = None
    clean_up = False
    logging_level = None
    web_dir = None
    web_dir2 = None
    vista_plus = False
    vista64 = False
    force_web = False
    repair = 0
    api_url = None
    no_login = False
    sabnzbd.RESTART_ARGS = [sys.argv[0]]
    pid_path = None
    pid_file = None
    new_instance = False
    osx_console = False
    ipv6_hosting = None

    _service, sab_opts, _serv_opts, upload_nzbs = commandline_handler()

    for opt, arg in sab_opts:
        if opt == '--servicecall':
            sabnzbd.MY_FULLNAME = arg
        elif opt in ('-d', '--daemon'):
            if not sabnzbd.WIN32:
                fork = True
            autobrowser = False
            sabnzbd.DAEMON = True
            sabnzbd.RESTART_ARGS.append(opt)
        elif opt in ('-f', '--config-file'):
            inifile = arg
            sabnzbd.RESTART_ARGS.append(opt)
            sabnzbd.RESTART_ARGS.append(arg)
        elif opt in ('-h', '--help'):
            print_help()
            exit_sab(0)
        elif opt in ('-t', '--templates'):
            web_dir = arg
        elif opt in ('-2', '--template2'):
            web_dir2 = arg
        elif opt in ('-s', '--server'):
            (cherryhost, cherryport) = split_host(arg)
        elif opt in ('-n', '--nobrowser'):
            autobrowser = False
        elif opt in ('-b', '--browser'):
            try:
                autobrowser = bool(int(arg))
            except:
                autobrowser = True
        elif opt in ('--autorestarted', ):
            autorestarted = True
        elif opt in ('-c', '--clean'):
            clean_up = True
        elif opt in ('-w', '--weblogging'):
            try:
                cherrypylogging = int(arg)
            except:
                cherrypylogging = -1
            if cherrypylogging < 0 or cherrypylogging > 2:
                print_help()
                exit_sab(1)
        elif opt in ('-l', '--logging'):
            try:
                logging_level = int(arg)
            except:
                logging_level = -2
            if logging_level < -1 or logging_level > 2:
                print_help()
                exit_sab(1)
        elif opt in ('-v', '--version'):
            print_version()
            exit_sab(0)
        elif opt in ('-p', '--pause'):
            pause = True
        elif opt in ('--force',):
            force_web = True
            sabnzbd.RESTART_ARGS.append(opt)
        elif opt in ('--https',):
            https_port = int(arg)
            sabnzbd.RESTART_ARGS.append(opt)
            sabnzbd.RESTART_ARGS.append(arg)
        elif opt in ('--repair',):
            repair = 1
            pause = True
        elif opt in ('--repair-all',):
            repair = 2
            pause = True
        elif opt in ('--log-all',):
            sabnzbd.LOG_ALL = True
        elif opt in ('--no-login',):
            no_login = True
        elif opt in ('--pid',):
            pid_path = arg
            sabnzbd.RESTART_ARGS.append(opt)
            sabnzbd.RESTART_ARGS.append(arg)
        elif opt in ('--pidfile',):
            pid_file = arg
            sabnzbd.RESTART_ARGS.append(opt)
            sabnzbd.RESTART_ARGS.append(arg)
        elif opt in ('--new',):
            new_instance = True
        elif opt in ('--console',):
            sabnzbd.RESTART_ARGS.append(opt)
            osx_console = True
        elif opt in ('--ipv6_hosting',):
            ipv6_hosting = arg

    sabnzbd.MY_FULLNAME = os.path.normpath(os.path.abspath(sabnzbd.MY_FULLNAME))
    sabnzbd.MY_NAME = os.path.basename(sabnzbd.MY_FULLNAME)
    sabnzbd.DIR_PROG = os.path.dirname(sabnzbd.MY_FULLNAME)
    sabnzbd.DIR_INTERFACES = real_path(sabnzbd.DIR_PROG, DEF_INTERFACES)
    sabnzbd.DIR_LANGUAGE = real_path(sabnzbd.DIR_PROG, DEF_LANGUAGE)
    org_dir = os.getcwd()

    if getattr(sys, 'frozen', None) == 'macosx_app':
        # Correct path if frozen with py2app (OSX)
        sabnzbd.MY_FULLNAME = sabnzbd.MY_FULLNAME.replace("/Resources/SABnzbd.py", "/MacOS/SABnzbd")

    # Need console logging for SABnzbd.py and SABnzbd-console.exe
    consoleLogging = (not hasattr(sys, "frozen")) or (sabnzbd.MY_NAME.lower().find('-console') > 0)
    consoleLogging = consoleLogging and not sabnzbd.DAEMON

    # No console logging needed for OSX app
    noConsoleLoggingOSX = (not osx_console) and (sabnzbd.DIR_PROG.find('.app/Contents/Resources') > 0)
    if noConsoleLoggingOSX:
        consoleLogging = 1

    LOGLEVELS = (logging.FATAL, logging.WARNING, logging.INFO, logging.DEBUG)

    # Setup primary logging to prevent default console logging
    gui_log = guiHandler(MAX_WARNINGS)
    gui_log.setLevel(logging.WARNING)
    format_gui = '%(asctime)s\n%(levelname)s\n%(message)s'
    gui_log.setFormatter(logging.Formatter(format_gui))
    sabnzbd.GUIHANDLER = gui_log

    # Create logger
    logger = logging.getLogger('')
    logger.setLevel(logging.WARNING)
    logger.addHandler(gui_log)

    # Detect Windows variant
    if sabnzbd.WIN32:
        vista_plus, vista64 = windows_variant()
        sabnzbd.WIN64 = vista64

    if not SQLITE_DLL:
        panic_sqlite(sabnzbd.MY_FULLNAME)
        exit_sab(2)

    if inifile:
        # INI file given, simplest case
        inifile = evaluate_inipath(inifile)
    else:
        # No ini file given, need profile data
        GetProfileInfo(vista_plus)
        # Find out where INI file is
        inifile = os.path.abspath(sabnzbd.DIR_LCLDATA + '/' + DEF_INI_FILE)

    # If INI file at non-std location, then use INI location as $HOME
    if sabnzbd.DIR_LCLDATA != os.path.dirname(inifile):
        sabnzbd.DIR_HOME = os.path.dirname(inifile)

    # All system data dirs are relative to the place we found the INI file
    sabnzbd.DIR_LCLDATA = os.path.dirname(inifile)

    if not os.path.exists(inifile) and not os.path.exists(inifile + '.bak') and not os.path.exists(sabnzbd.DIR_LCLDATA):
        try:
            os.makedirs(sabnzbd.DIR_LCLDATA)
        except IOError:
            panic('Cannot create folder "%s".' % sabnzbd.DIR_LCLDATA, 'Check specified INI file location.')
            exit_sab(1)

    sabnzbd.cfg.set_root_folders(sabnzbd.DIR_HOME, sabnzbd.DIR_LCLDATA)

    res, msg = config.read_config(inifile)
    if not res:
        panic(msg, 'Specify a correct file or delete this file.')
        exit_sab(1)

    # Set root folders for HTTPS server file paths
    sabnzbd.cfg.set_root_folders2()

    if ipv6_hosting is not None:
        sabnzbd.cfg.ipv6_hosting.set(ipv6_hosting)

    # Determine web host address
    cherryhost, cherryport, browserhost, https_port = get_webhost(cherryhost, cherryport, https_port)
    enable_https = sabnzbd.cfg.enable_https()

    # When this is a daemon, just check and bail out if port in use
    if sabnzbd.DAEMON:
        if enable_https and https_port:
            try:
                cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.025)
            except IOError, error:
                Bail_Out(browserhost, cherryport)
            except:
                Bail_Out(browserhost, cherryport, '49')
        try:
            cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.025)
        except IOError, error:
            Bail_Out(browserhost, cherryport)
        except:
            Bail_Out(browserhost, cherryport, '49')

    # Windows instance is reachable through registry
    url = None
    if sabnzbd.WIN32 and not new_instance:
        url = get_connection_info()
        if url and check_for_sabnzbd(url, upload_nzbs, autobrowser):
            exit_sab(0)

    # If an instance of sabnzbd(same version) is already running on this port, launch the browser
    # If another program or sabnzbd version is on this port, try 10 other ports going up in a step of 5
    # If 'Port is not bound' (firewall) do not do anything (let the script further down deal with that).
    notify_port_change = False

    # SSL
    if enable_https:
        port = https_port or cherryport
        try:
            cherrypy.process.servers.check_port(browserhost, port, timeout=0.025)
        except IOError, error:
            if str(error) == 'Port not bound.':
                pass
            else:
                if not url:
                    url = 'https://%s:%s/sabnzbd/api?' % (browserhost, port)
                if not sabnzbd.cfg.fixed_ports():
                    if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
                        newport = find_free_port(browserhost, port)
                        if newport > 0:
                            notify_port_change = True
                            # Save the new port
                            if https_port:
                                https_port = newport
                                sabnzbd.cfg.https_port.set(newport)
                            else:
                                # In case HTTPS == HTTP port
                                http_port = newport
                                sabnzbd.cfg.port.set(newport)
        except:
            Bail_Out(browserhost, cherryport, '49')

    # NonSSL check if there's no HTTPS or we only use 1 port
    if not (enable_https and not https_port):
        try:
            cherrypy.process.servers.check_port(browserhost, cherryport, timeout=0.025)
        except IOError, error:
            if str(error) == 'Port not bound.':
                pass
            else:
                if not url:
                    url = 'http://%s:%s/sabnzbd/api?' % (browserhost, cherryport)
                if not sabnzbd.cfg.fixed_ports():
                    if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
                        port = find_free_port(browserhost, cherryport)
                        if port > 0:
                            sabnzbd.cfg.cherryport.set(port)
                            notify_port_change = True
                            cherryport = port
        except:
            Bail_Out(browserhost, cherryport, '49')

    if logging_level is None:
        logging_level = sabnzbd.cfg.log_level()
    else:
        sabnzbd.cfg.log_level.set(logging_level)

    logdir = sabnzbd.cfg.log_dir.get_path()
    if fork and not logdir:
        print "Error:"
        print "I refuse to fork without a log directory!"
        sys.exit(1)

    if clean_up:
        xlist = globber_full(logdir)
        for x in xlist:
            if RSS_FILE_NAME not in x:
                try:
                    os.remove(x)
                except:
                    pass

    # Prevent the logger from raising exceptions
    # primarily to reduce the fallout of Python issue 4749
    logging.raiseExceptions = 0
    sabnzbd.LOGFILE = os.path.join(logdir, DEF_LOG_FILE)

    try:
        rollover_log = logging.handlers.RotatingFileHandler(
            sabnzbd.LOGFILE, 'a+',
            sabnzbd.cfg.log_size.get_int(),
            sabnzbd.cfg.log_backups())

        logformat = '%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s'
        rollover_log.setFormatter(logging.Formatter(logformat))
        rollover_log.addFilter(FilterCP3())
        sabnzbd.LOGHANDLER = rollover_log
        logger.addHandler(rollover_log)
        logger.setLevel(LOGLEVELS[logging_level + 1])

    except IOError:
        print "Error:"
        print "Can't write to logfile"
        exit_sab(2)

    if fork:
        try:
            x = sys.stderr.fileno
            x = sys.stdout.fileno
            ol_path = os.path.join(logdir, DEF_LOG_ERRFILE)
            out_log = file(ol_path, 'a+', 0)
            sys.stderr.flush()
            sys.stdout.flush()
            os.dup2(out_log.fileno(), sys.stderr.fileno())
            os.dup2(out_log.fileno(), sys.stdout.fileno())
        except AttributeError:
            pass

    else:
        try:
            x = sys.stderr.fileno
            x = sys.stdout.fileno

            if consoleLogging:
                console = logging.StreamHandler()
                console.addFilter(FilterCP3())
                console.setLevel(LOGLEVELS[logging_level + 1])
                console.setFormatter(logging.Formatter(logformat))
                logger.addHandler(console)
            if noConsoleLoggingOSX:
                logging.info('Console logging for OSX App disabled')
                so = file('/dev/null', 'a+')
                os.dup2(so.fileno(), sys.stdout.fileno())
                os.dup2(so.fileno(), sys.stderr.fileno())
        except AttributeError:
            pass

    logging.info('--------------------------------')
    logging.info('%s-%s (rev=%s)', sabnzbd.MY_NAME, sabnzbd.__version__, sabnzbd.__baseline__)
    logging.info('Full executable path = %s', sabnzbd.MY_FULLNAME)
    if sabnzbd.WIN32:
        suffix = ''
        if vista_plus:
            suffix = ' (=Vista+)'
        if vista64:
            suffix = ' (=Vista+ x64)'
        try:
            logging.info('Platform=%s%s Class=%s', platform.platform(), suffix, os.name)
        except:
            logging.info('Platform=%s <unknown> Class=%s', suffix, os.name)
    else:
        logging.info('Platform = %s', os.name)
    logging.info('Python-version = %s', sys.version)
    logging.info('Arguments = %s', sabnzbd.CMDLINE)
    try:
        logging.info('Preferred encoding = %s', locale.getpreferredencoding())
    except:
        logging.info('Preferred encoding = ERROR')


    if sabnzbd.cfg.log_level() > 1:
        from sabnzbd.getipaddress import localipv4, publicipv4, ipv6

        mylocalipv4 = localipv4()
        if mylocalipv4:
            logging.debug('My local IPv4 address = %s', mylocalipv4)
        else:
            logging.debug('Could not determine my local IPv4 address')

        mypublicipv4 = publicipv4()
        if mypublicipv4:
            logging.debug('My public IPv4 address = %s', mypublicipv4)
        else:
            logging.debug('Could not determine my public IPv4 address')

        myipv6 = ipv6()
        if myipv6:
            logging.debug('My IPv6 address = %s', myipv6)
        else:
            logging.debug('Could not determine my IPv6 address')

        # Measure and log system performance measured by pystone and - if possible - CPU model
        from sabnzbd.utils.getperformance import getpystone, getcpu
        pystoneperf = getpystone()
        if pystoneperf:
            logging.debug('CPU Pystone available performance is %s', pystoneperf)
        else:
            logging.debug('CPU Pystone available performance could not be calculated')
        cpumodel = getcpu()  # Linux only
        if cpumodel:
            logging.debug('CPU model name is %s', cpumodel)

    # OSX 10.5 I/O priority setting
    if sabnzbd.DARWIN:
        logging.info('[osx] IO priority setting')
        try:
            from ctypes import cdll
            libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
            boolSetResult = libc.setiopolicy_np(0, 1, 3)
            logging.info('[osx] IO priority set to throttle for process scope')
        except:
            logging.info('[osx] IO priority setting not supported')

    logging.info('Read INI file %s', inifile)

    if autobrowser is not None:
        sabnzbd.cfg.autobrowser.set(autobrowser)
    else:
        autobrowser = sabnzbd.cfg.autobrowser()

    if not sabnzbd.WIN_SERVICE and not getattr(sys, 'frozen', None) == 'macosx_app':
        signal.signal(signal.SIGINT, sabnzbd.sig_handler)
        signal.signal(signal.SIGTERM, sabnzbd.sig_handler)

    sabnzbd.initialize(pause, clean_up, evalSched=True, repair=repair)

    os.chdir(sabnzbd.DIR_PROG)

    web_dir = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
    web_dir2 = Web_Template(sabnzbd.cfg.web_dir2, '', fix_webname(web_dir2))
    web_dirc = Web_Template(None, DEF_STDCONFIG, '')

    wizard_dir = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')

    sabnzbd.WEB_DIR = web_dir
    sabnzbd.WEB_DIR2 = web_dir2
    sabnzbd.WEB_DIRC = web_dirc
    sabnzbd.WIZARD_DIR = wizard_dir

    sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), web_dir)
    sabnzbd.cfg.web_color.set(sabnzbd.WEB_COLOR)
    sabnzbd.WEB_COLOR2 = CheckColor(sabnzbd.cfg.web_color2(), web_dir2)
    sabnzbd.cfg.web_color2.set(sabnzbd.WEB_COLOR2)

    if fork and not sabnzbd.WIN32:
        daemonize()

    # Save the INI file
    config.save_config(force=True)

    if sabnzbd.cfg.win_menu() and not sabnzbd.DAEMON:
        if sabnzbd.WIN32:
            import sabnzbd.sabtray
            sabnzbd.WINTRAY = sabnzbd.sabtray.SABTrayThread()
        elif sabnzbd.LINUX_POWER and os.environ.get('DISPLAY'):
            try:
                import gtk
                import sabnzbd.sabtraylinux
                sabnzbd.LINUXTRAY = sabnzbd.sabtraylinux.StatusIcon()
            except:
                logging.info("pygtk2 not found. No SysTray.")

    # Find external programs
    sabnzbd.newsunpack.find_programs(sabnzbd.DIR_PROG)

    print_modules()

    logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version())
    logging.info("SSL supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols_labels()))

    cherrylogtoscreen = False
    sabnzbd.WEBLOGFILE = None

    if cherrypylogging:
        if logdir:
            sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
        # Define our custom logger for cherrypy errors
        cherrypy_logging(sabnzbd.WEBLOGFILE, logging.handlers.RotatingFileHandler)
        if not fork:
            try:
                x = sys.stderr.fileno
                x = sys.stdout.fileno
                if cherrypylogging == 1:
                    cherrylogtoscreen = True
            except:
                pass

    https_cert = sabnzbd.cfg.https_cert.get_path()
    https_key = sabnzbd.cfg.https_key.get_path()
    https_chain = sabnzbd.cfg.https_chain.get_path()
    if not (sabnzbd.cfg.https_chain() and os.path.exists(https_chain)):
        https_chain = None

    if enable_https:
        # If either the HTTPS certificate or key do not exist, make some self-signed ones.
        if not (https_cert and os.path.exists(https_cert)) or not (https_key and os.path.exists(https_key)):
            create_https_certificates(https_cert, https_key)

        if not (os.path.exists(https_cert) and os.path.exists(https_key)):
            logging.warning(T('Disabled HTTPS because of missing CERT and KEY files'))
            enable_https = False

    # Determine if this system has multiple definitions for 'localhost'
    hosts = all_localhosts()
    multilocal = len(hosts) > 1 and cherryhost in ('localhost', '0.0.0.0')

    # For 0.0.0.0 CherryPy will always pick IPv4, so make sure the secondary localhost is IPv6
    if multilocal and cherryhost == '0.0.0.0' and hosts[1] == '127.0.0.1':
        hosts[1] = '::1'

    # The Windows binary requires numeric localhost as primary address
    if cherryhost == 'localhost':
        cherryhost = hosts[0]

    if enable_https:
        if https_port:
            # Extra HTTP port for primary localhost
            attach_server(cherryhost, cherryport)
            if multilocal:
                # Extra HTTP port for secondary localhost
                attach_server(hosts[1], cherryport)
                # Extra HTTPS port for secondary localhost
                attach_server(hosts[1], https_port, https_cert, https_key, https_chain)
            cherryport = https_port
        elif multilocal:
            # Extra HTTPS port for secondary localhost
            attach_server(hosts[1], cherryport, https_cert, https_key)

        cherrypy.config.update({'server.ssl_module': 'builtin',
                                'server.ssl_certificate': https_cert,
                                'server.ssl_private_key': https_key,
                                'server.ssl_certificate_chain': https_chain})
    elif multilocal:
        # Extra HTTP port for secondary localhost
        attach_server(hosts[1], cherryport)

    if no_login:
        sabnzbd.cfg.username.set('')
        sabnzbd.cfg.password.set('')

    mime_gzip = ('text/*',
                 'application/javascript',
                 'application/x-javascript',
                 'application/json',
                 'application/xml',
                 'application/vnd.ms-fontobject',
                 'application/font*',
                 'image/svg+xml'
                 )
    cherrypy.config.update({'server.environment': 'production',
                            'server.socket_host': cherryhost,
                            'server.socket_port': cherryport,
                            'server.shutdown_timeout': 0,
                            'log.screen': cherrylogtoscreen,
                            'engine.autoreload.on': False,
                            'tools.encode.on': True,
                            'tools.gzip.on': True,
                            'tools.gzip.mime_types': mime_gzip,
                            'request.show_tracebacks': True,
                            'checker.check_localhost': bool(consoleLogging),
                            'error_page.401': sabnzbd.panic.error_page_401,
                            'error_page.404': sabnzbd.panic.error_page_404
                            })

    forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
    static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
    staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dirc, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
    wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(wizard_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}

    appconfig = {'/sabnzbd/api': {'tools.basic_auth.on': False},
                 '/api': {'tools.basic_auth.on': False},
                 '/m/api': {'tools.basic_auth.on': False},
                 '/rss': {'tools.basic_auth.on': False},
                 '/sabnzbd/rss': {'tools.basic_auth.on': False},
                 '/m/rss': {'tools.basic_auth.on': False},
                 '/sabnzbd/shutdown': {'streamResponse': True},
                 '/sabnzbd/static': static,
                 '/static': static,
                 '/sabnzbd/wizard/static': wizard_static,
                 '/wizard/static': wizard_static,
                 '/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(web_dirc, 'staticcfg', 'ico', 'favicon.ico')},
                 '/sabnzbd/staticcfg': staticcfg,
                 '/staticcfg': staticcfg
                 }

    if web_dir2:
        static2 = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir2, 'static'), 'tools.staticdir.content_types': forced_mime_types}
        appconfig['/sabnzbd/m/api'] = {'tools.basic_auth.on': False}
        appconfig['/sabnzbd/m/rss'] = {'tools.basic_auth.on': False}
        appconfig['/sabnzbd/m/shutdown'] = {'streamResponse': True}
        appconfig['/sabnzbd/m/static'] = static2
        appconfig['/m/static'] = static2
        appconfig['/sabnzbd/m/wizard/static'] = wizard_static
        appconfig['/m/wizard/static'] = wizard_static
        appconfig['/sabnzbd/m/staticcfg'] = staticcfg
        appconfig['/m/staticcfg'] = staticcfg

    login_page = sabnzbd.interface.MainPage(web_dir, '/', web_dir2, '/m/', web_dirc, first=2)
    cherrypy.tree.mount(login_page, '/', config=appconfig)

    # Set authentication for CherryPy
    sabnzbd.interface.set_auth(cherrypy.config)

    # Notify if port was changed
    if notify_port_change:
        logging.warning(T('Could not bind to configured port. Port changed to %s') % cherryport)

    logging.info('Starting web-interface on %s:%s', cherryhost, cherryport)

    sabnzbd.cfg.log_level.callback(guard_loglevel)

    try:
        cherrypy.engine.start()
    except:
        logging.error(T('Failed to start web-interface: '), exc_info=True)
        Bail_Out(browserhost, cherryport)

    # Wait for server to become ready
    cherrypy.engine.wait(cherrypy.process.wspbus.states.STARTED)
    sabnzbd.zconfig.set_bonjour(cherryhost, cherryport)

    mail = None
    if sabnzbd.WIN32:
        if enable_https:
            mode = 's'
        else:
            mode = ''
        api_url = 'http%s://%s:%s/sabnzbd/api?apikey=%s' % (mode, browserhost, cherryport, sabnzbd.cfg.api_key())

        if sabnzbd.WIN_SERVICE:
            mail = MailSlot()
            if mail.connect():
                logging.info('Connected to the SABHelper service')
                mail.send('api %s' % api_url)
            else:
                logging.error(T('Cannot reach the SABHelper service'))
                mail = None
        else:
            # Write URL directly to registry
            set_connection_info(api_url)

    if pid_path or pid_file:
        sabnzbd.pid_file(pid_path, pid_file, cherryport)

    # Start all SABnzbd tasks
    logging.info('Starting %s-%s', sabnzbd.MY_NAME, sabnzbd.__version__)
    try:
        sabnzbd.start()
    except:
        logging.exception("Failed to start %s-%s", sabnzbd.MY_NAME, sabnzbd.__version__)
        sabnzbd.halt()

    # Upload any nzb/zip/rar/nzb.gz/nzb.bz2 files from file association
    if upload_nzbs:
        from sabnzbd.utils.upload import add_local
        for f in upload_nzbs:
            add_local(f)

    # Set URL for browser
    if enable_https:
        browser_url = "https://%s:%s/sabnzbd" % (browserhost, cherryport)
    else:
        browser_url = "http://%s:%s/sabnzbd" % (browserhost, cherryport)
    sabnzbd.BROWSER_URL = browser_url

    if not autorestarted:
        launch_a_browser(browser_url)
        if sabnzbd.FOUNDATION:
            import sabnzbd.osxmenu
            sabnzbd.osxmenu.notify("SAB_Launched", None)
        notifier.send_notification('SABnzbd%s' % notifier.hostname(),
                                  T('SABnzbd %s started') % sabnzbd.__version__, 'startup')
        # Now's the time to check for a new version
        check_latest_version()
    autorestarted = False

    # Have to keep this running, otherwise logging will terminate
    timer = 0
    while not sabnzbd.SABSTOP:
        if sabnzbd.LAST_WARNING:
            msg = sabnzbd.LAST_WARNING
            sabnzbd.LAST_WARNING = None
            sabnzbd.notifier.send_notification(T('Warning'), msg, 'warning')
        if sabnzbd.LAST_ERROR:
            msg = sabnzbd.LAST_ERROR
            sabnzbd.LAST_ERROR = None
            sabnzbd.notifier.send_notification(T('Error'), msg, 'error')

        if sabnzbd.WIN_SERVICE:
            rc = win32event.WaitForMultipleObjects((sabnzbd.WIN_SERVICE.hWaitStop,
                                                    sabnzbd.WIN_SERVICE.overlapped.hEvent), 0, 3000)
            if rc == win32event.WAIT_OBJECT_0:
                if mail:
                    mail.send('stop')
                sabnzbd.save_state()
                logging.info('Leaving SABnzbd')
                sabnzbd.SABSTOP = True
                return
        else:
            time.sleep(3)

        # Check for loglevel changes
        if LOG_FLAG:
            LOG_FLAG = False
            level = LOGLEVELS[sabnzbd.cfg.log_level() + 1]
            logger.setLevel(level)
            if consoleLogging:
                console.setLevel(level)

        # 30 sec polling tasks
        if timer > 9:
            timer = 0
            # Keep OS awake (if needed)
            sabnzbd.keep_awake()
            # Restart scheduler (if needed)
            scheduler.restart()
            # Save config (if needed)
            config.save_config()
            # Check the threads
            if not sabnzbd.check_all_tasks():
                autorestarted = True
                sabnzbd.TRIGGER_RESTART = True
            # Notify guardian
            if sabnzbd.WIN_SERVICE and mail:
                mail.send('active')
        else:
            timer += 1

        # 3 sec polling tasks
        # Check for auto-restart request
        # Or special restart cases like Mac and WindowsService
        if sabnzbd.TRIGGER_RESTART:
            # Shutdown
            cherrypy.engine.exit()
            sabnzbd.halt()
            sabnzbd.SABSTOP = True

            if sabnzbd.downloader.Downloader.do.paused:
                sabnzbd.RESTART_ARGS.append('-p')
            if autorestarted:
                sabnzbd.RESTART_ARGS.append('--autorestarted')
            sys.argv = sabnzbd.RESTART_ARGS

            os.chdir(org_dir)
            # If OSX frozen restart of app instead of embedded python
            if getattr(sys, 'frozen', None) == 'macosx_app':
                # [[NSProcessInfo processInfo] processIdentifier]]
                # logging.info("%s" % (NSProcessInfo.processInfo().processIdentifier()))
                my_pid = os.getpid()
                my_name = sabnzbd.MY_FULLNAME.replace('/Contents/MacOS/SABnzbd', '')
                my_args = ' '.join(sys.argv[1:])
                cmd = 'kill -9 %s && open "%s" --args %s' % (my_pid, my_name, my_args)
                logging.info('Launching: ', cmd)
                os.system(cmd)

            elif sabnzbd.WIN_SERVICE and mail:
                logging.info('Asking the SABHelper service for a restart')
                mail.send('restart')
                mail.disconnect()
                return
            else:
                cherrypy.engine._do_execv()

    config.save_config()

    if sabnzbd.WINTRAY:
        sabnzbd.WINTRAY.terminate = True

    if sabnzbd.WIN_SERVICE and mail:
        mail.send('stop')
    if sabnzbd.WIN32:
        del_connection_info()
    if sabnzbd.FOUNDATION:
        sabnzbd.osxmenu.notify("SAB_Shutdown", None)
    logging.info('Leaving SABnzbd')
    sys.stderr.flush()
    sys.stdout.flush()
    sabnzbd.pid_file()
    if getattr(sys, 'frozen', None) == 'macosx_app':
        try:
            AppHelper.stopEventLoop()
        except:
            # Failing AppHelper libary!
            os._exit(0)
    else:
        notifier.send_notification('SABnzbd', T('SABnzbd shutdown finished'), 'startup')
        os._exit(0)


##############################################################################
# Windows Service Support
##############################################################################


if sabnzbd.WIN32:
    import servicemanager

    class SABnzbd(win32serviceutil.ServiceFramework):
        """ Win32 Service Handler """

        _svc_name_ = 'SABnzbd'
        _svc_display_name_ = 'SABnzbd Binary Newsreader'
        _svc_deps_ = ["EventLog", "Tcpip", "SABHelper"]
        _svc_description_ = 'Automated downloading from Usenet. ' \
                          'Set to "automatic" to start the service at system startup. ' \
                          'You may need to login with a real user account when you need ' \
                          'access to network shares.'

        def __init__(self, args):
            win32serviceutil.ServiceFramework.__init__(self, args)

            self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
            self.overlapped = pywintypes.OVERLAPPED()  # @UndefinedVariable
            self.overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None)
            sabnzbd.WIN_SERVICE = self

        def SvcDoRun(self):
            msg = 'SABnzbd-service %s' % sabnzbd.__version__
            self.Logger(servicemanager.PYS_SERVICE_STARTED, msg + ' has started')
            sys.argv = get_serv_parms(self._svc_name_)
            main()
            self.Logger(servicemanager.PYS_SERVICE_STOPPED, msg + ' has stopped')

        def SvcStop(self):
            self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
            win32event.SetEvent(self.hWaitStop)

        def Logger(self, state, msg):
            win32evtlogutil.ReportEvent(self._svc_display_name_,
                                        state, 0,
                                        servicemanager.EVENTLOG_INFORMATION_TYPE,
                                        (self._svc_name_, unicoder(msg)))

        def ErrLogger(self, msg, text):
            win32evtlogutil.ReportEvent(self._svc_display_name_,
                                        servicemanager.PYS_SERVICE_STOPPED, 0,
                                        servicemanager.EVENTLOG_ERROR_TYPE,
                                        (self._svc_name_, unicoder(msg)),
                                        unicoder(text))


def prep_service_parms(args):
    """ Prepare parameter list for service """

    # Must store our original path, because the Python Service launcher
    # won't give it to us.
    serv = [os.path.normpath(os.path.abspath(sys.argv[0]))]

    # Convert the tuples to list
    for arg in args:
        serv.append(arg[0])
        if arg[1]:
            serv.append(arg[1])

    # Make sure we run in daemon mode
    serv.append('-d')
    return serv


SERVICE_MSG = """
You may need to set additional Service parameters.
Run services.msc from a command prompt.

Don't forget to install the Service SABnzbd-helper.exe too!
"""


def HandleCommandLine(allow_service=True):
    """ Handle command line for a Windows Service
        Prescribed name that will be called by Py2Exe.
        You MUST set 'cmdline_style':'custom' in the package.py!
        Returns True when any service commands were detected.
    """
    service, sab_opts, serv_opts, _upload_nzbs = commandline_handler()
    if service and not allow_service:
        # The other frozen apps don't support Services
        print "For service support, use SABnzbd-service.exe"
        return True
    elif service:
        if service in ('install', 'update'):
            # In this case check for required parameters
            path = get_f_option(sab_opts)
            if not path:
                print 'The -f <path> parameter is required.\n' \
                      'Use: -f <path> %s' % service
                return True

            # First run the service installed, because this will
            # set the service key in the Registry
            win32serviceutil.HandleCommandLine(SABnzbd, argv=serv_opts)

            # Add our own parameter to the Registry
            sab_opts = prep_service_parms(sab_opts)
            if set_serv_parms(SABnzbd._svc_name_, sab_opts):
                print SERVICE_MSG
            else:
                print 'Cannot set required Registry info.'
        else:
            # Other service commands need no manipulation
            win32serviceutil.HandleCommandLine(SABnzbd)
    return bool(service)


##############################################################################
# Platform specific startup code
##############################################################################


if __name__ == '__main__':

    args = []
    for txt in sys.argv:
        if ' ' in txt:
            txt = '"%s"' % unicoder(txt)
        else:
            txt = unicoder(txt)
        args.append(txt)
    sabnzbd.CMDLINE = ' '.join(args)

    if sabnzbd.WIN32:
        if not HandleCommandLine(allow_service=not hasattr(sys, "frozen")):
            main()

    elif getattr(sys, 'frozen', None) == 'macosx_app':
        # OSX binary

        try:
            from PyObjCTools import AppHelper
            from sabnzbd.osxmenu import SABnzbdDelegate

            class startApp(Thread):

                def __init__(self):
                    logging.info('[osx] sabApp Starting - starting main thread')
                    Thread.__init__(self)

                def run(self):
                    main()
                    logging.info('[osx] sabApp Stopping - main thread quit ')
                    AppHelper.stopEventLoop()

                def stop(self):
                    logging.info('[osx] sabApp Quit - stopping main thread ')
                    sabnzbd.halt()
                    cherrypy.engine.exit()
                    sabnzbd.SABSTOP = True
                    logging.info('[osx] sabApp Quit - main thread stopped')

            sabApp = startApp()
            sabApp.start()
            AppHelper.runEventLoop()

        except:
            main()

    else:
        main()
