You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

910 lines
30 KiB

#!/usr/bin/python -OO
# Copyright 2008 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.hexversion < 0x020403F0:
print "Sorry, requires Python 2.4.3 or higher."
exit(1)
import logging
import logging.handlers
import os
import getopt
import signal
import re
import glob
import socket
if os.name=='nt':
import platform
try:
import Cheetah
if Cheetah.Version[0] != '2':
raise ValueError
except:
print "Sorry, requires Python module Cheetah 2.0rc7 or higher."
exit(1)
import cherrypy
if not cherrypy.__version__.startswith("3.1."):
print "Sorry, requires Python module Cherrypy 3.1.x"
exit(1)
try:
from sqlite3 import version as sqlite3_version
except:
try:
from pysqlite2.sqlite3 import version as sqlite3_version
except:
print "Sorry, requires Python module sqlite3 (pysqlite2 in python2.4)"
if os.name != 'nt':
print "Try: apt-get install python-pysqlite2"
exit(1)
import sabnzbd
from sabnzbd.utils.configobj import ConfigObj, ConfigObjError
from sabnzbd.interface import *
from sabnzbd.constants import *
from sabnzbd.newsunpack import find_programs
from sabnzbd.misc import Get_User_ShellFolders, launch_a_browser, from_units, \
check_latest_version, Panic_Templ, Panic_Port, Panic_FWall, Panic, ExitSab, \
Notify, SplitHost, ConvertVersion
import sabnzbd.scheduler as scheduler
import sabnzbd.config as config
import sabnzbd.cfg
from threading import Thread
LOG_FLAG = False # Global for this module, signalling loglevel change
#------------------------------------------------------------------------------
signal.signal(signal.SIGINT, sabnzbd.sig_handler)
signal.signal(signal.SIGTERM, sabnzbd.sig_handler)
try:
import win32api
win32api.SetConsoleCtrlHandler(sabnzbd.sig_handler, True)
except ImportError:
if os.name == 'nt':
print "Sorry, requires Python module PyWin32."
exit(1)
def guard_loglevel():
""" Callback function for guarding loglevel """
global LOG_FLAG
LOG_FLAG = True
#------------------------------------------------------------------------------
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 len(self.store) >= self.size:
# Loose the oldest record
self.store.pop(0)
self.store.append(self.format(record))
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 (0= least, 2= most) [*]"
print " -w --weblogging <0..1> Set cherrypy logging (0= off, 1= on) [*]"
print
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]"
if os.name != 'nt':
print " -d --daemon Fork daemon process"
else:
print " -d --daemon Use when run as a service"
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 " --https Webserver uses HTTPS only"
def print_version():
print """
%s-%s
Copyright (C) 2008, 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):
"""Abort program because of CherryPy troubles
"""
logging.error("Failed to start web-interface")
Panic_Port(browserhost, cherryport)
sabnzbd.halt()
ExitSab(2)
def Web_Template(key, defweb, wdir):
""" Determine a correct web template set,
return full template path
"""
if wdir == None:
try:
wdir = key.get()
except:
wdir = ''
if not wdir:
wdir = defweb
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):
logging.warning('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_Templ(full_dir)
ExitSab(1)
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
else:
return ''
def GetProfileInfo(vista):
""" 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 os.name == 'nt':
# Ignore Win23 "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 os.name == 'nt':
specials = Get_User_ShellFolders()
try:
sabnzbd.DIR_APPDATA = '%s\\%s' % (specials['AppData'], DEF_WORKDIR)
sabnzbd.DIR_LCLDATA = '%s\\%s' % (specials['Local AppData'], DEF_WORKDIR)
sabnzbd.DIR_HOME = specials['Personal']
ok = True
except:
try:
if vista:
root = os.environ['AppData']
user = os.environ['USERPROFILE']
sabnzbd.DIR_APPDATA = '%s\\%s' % (root.replace('\\Roaming', '\\Local'), DEF_WORKDIR)
sabnzbd.DIR_HOME = '%s\\Documents' % 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('latin-1')
sabnzbd.DIR_HOME = sabnzbd.DIR_HOME.encode('latin-1')
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:
sabnzbd.DIR_APPDATA = '%s/Library/Application Support/SABnzbd' % (os.environ['HOME'])
sabnzbd.DIR_LCLDATA = sabnzbd.DIR_APPDATA
sabnzbd.DIR_HOME = os.environ['HOME']
ok = True
else:
# Unix/Linux
sabnzbd.DIR_APPDATA = '%s/.%s' % (os.environ['HOME'], DEF_WORKDIR)
sabnzbd.DIR_LCLDATA = sabnzbd.DIR_APPDATA
sabnzbd.DIR_HOME = os.environ['HOME']
ok = True
if not ok:
Panic("Cannot access the user profile.",
"Please start with sabnzbd.ini file in another location")
ExitSab(2)
def main():
AUTOBROWSER = None
sabnzbd.MY_FULLNAME = os.path.normpath(os.path.abspath(sys.argv[0]))
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)
# Need console logging for SABnzbd.py and SABnzbd-console.exe
consoleLogging = (not hasattr(sys, "frozen")) or (sabnzbd.MY_NAME.lower().find('-console') > 0)
LOGLEVELS = [ 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)
try:
opts, args = getopt.getopt(sys.argv[1:], "phdvncw:l:s:f:t:b:2:",
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=', \
'weblogging=', 'server=', 'templates', \
'template2', 'browser=', 'config-file=', 'delay=', 'force', 'version', 'https'])
except getopt.GetoptError:
print_help()
ExitSab(2)
fork = False
pause = False
f = None
cherryhost = None
cherryport = None
cherrypylogging = None
clean_up = False
logging_level = None
web_dir = None
web_dir2 = None
delay = 0.0
vista = False
vista64 = False
force_web = False
https = False
for o, a in opts:
if (o in ('-d', '--daemon')):
if os.name != 'nt':
fork = True
AUTOBROWSER = False
sabnzbd.DAEMON = True
consoleLogging = False
if o in ('-h', '--help'):
print_help()
ExitSab(0)
if o in ('-f', '--config-file'):
f = a
if o in ('-t', '--templates'):
web_dir = a
if o in ('-2', '--template2'):
web_dir2 = a
if o in ('-s', '--server'):
(cherryhost, cherryport) = SplitHost(a)
if o in ('-n', '--nobrowser'):
AUTOBROWSER = False
if o in ('-b', '--browser'):
try:
AUTOBROWSER = bool(int(a))
except:
AUTOBROWSER = True
if o in ('-c', '--clean'):
clean_up= True
if o in ('-w', '--weblogging'):
try:
cherrypylogging = int(a)
except:
cherrypylogging = -1
if cherrypylogging < 0 or cherrypylogging > 1:
print_help()
ExitSab(1)
if o in ('-l', '--logging'):
try:
logging_level = int(a)
except:
logging_level = -1
if logging_level < 0 or logging_level > 2:
print_help()
ExitSab(1)
if o in ('-v', '--version'):
print_version()
ExitSab(0)
if o in ('-p', '--pause'):
pause = True
if o in ('--delay'):
# For debugging of memory leak only!!
try:
delay = float(a)
except:
pass
if o in ('--force'):
force_web = True
if o in ('--https'):
https = True
# Detect Vista or higher
if os.name == 'nt':
if platform.platform().find('Windows-32bit') >= 0:
vista = True
vista64 = 'ProgramFiles(x86)' in os.environ
if f:
# INI file given, simplest case
f = os.path.normpath(os.path.abspath(f))
else:
# No ini file given, need profile data
GetProfileInfo(vista)
# Find out where INI file is
f = os.path.abspath(sabnzbd.DIR_PROG + '/' + DEF_INI_FILE)
if not os.path.exists(f):
f = os.path.abspath(sabnzbd.DIR_LCLDATA + '/' + DEF_INI_FILE)
# If INI file at non-std location, then use program dir as $HOME
if sabnzbd.DIR_LCLDATA != os.path.dirname(f):
sabnzbd.DIR_HOME = os.path.dirname(f)
# All system data dirs are relative to the place we found the INI file
sabnzbd.DIR_LCLDATA = os.path.dirname(f)
if not os.path.exists(f) 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.')
ExitSab(1)
sabnzbd.cfg.set_root_folders(sabnzbd.DIR_HOME, sabnzbd.DIR_LCLDATA, sabnzbd.DIR_PROG, sabnzbd.DIR_INTERFACES)
if not config.read_config(f):
Panic('"%s" is not a valid configuration file.' % f, \
'Specify a correct file or delete this file.')
ExitSab(1)
if cherrypylogging == None:
cherrypylogging = sabnzbd.cfg.LOG_WEB.get()
else:
sabnzbd.cfg.LOG_WEB.set(cherrypylogging)
if logging_level == None:
logging_level = sabnzbd.cfg.LOG_LEVEL.get()
else:
sabnzbd.cfg.LOG_LEVEL.set(logging_level)
ver, testRelease = ConvertVersion(sabnzbd.__version__)
if testRelease:
logging_level = 2
cherrypylogging = True
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= glob.glob(logdir + '/*')
for x in xlist:
if x.find(RSS_FILE_NAME) < 0:
os.remove(x)
try:
sabnzbd.LOGFILE = os.path.join(logdir, DEF_LOG_FILE)
logsize = sabnzbd.cfg.LOG_SIZE.get_int()
rollover_log = logging.handlers.RotatingFileHandler(\
sabnzbd.LOGFILE, 'a+',
logsize,
sabnzbd.cfg.LOG_BACKUPS.get())
rollover_log.setLevel(LOGLEVELS[logging_level])
format = '%(asctime)s::%(levelname)s::%(message)s'
rollover_log.setFormatter(logging.Formatter(format))
sabnzbd.LOGHANDLER = rollover_log
logger.addHandler(rollover_log)
logger.setLevel(LOGLEVELS[logging_level])
except IOError:
print "Error:"
print "Can't write to logfile"
ExitSab(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.setLevel(LOGLEVELS[logging_level])
console.setFormatter(logging.Formatter(format))
logger.addHandler(console)
except AttributeError:
pass
logging.info('--------------------------------')
logging.info('%s-%s (rev=%s)', sabnzbd.MY_NAME, sabnzbd.__version__, sabnzbd.__baseline__)
if os.name == 'nt':
suffix = ''
if vista: suffix = ' (=Vista)'
if vista64: suffix = ' (=Vista64)'
logging.info('Platform=%s%s Class=%s', platform.platform(), suffix, os.name)
else:
logging.info('Platform = %s', os.name)
logging.info('Python-version = %s', sys.version)
if testRelease:
logging.info('Test release, setting maximum logging levels')
if AUTOBROWSER != None:
sabnzbd.cfg.AUTOBROWSER.set(AUTOBROWSER)
else:
AUTOBROWSER = sabnzbd.cfg.AUTOBROWSER.get()
sabnzbd.cfg.DEBUG_DELAY.set(delay)
init_ok = sabnzbd.initialize(pause, clean_up, evalSched=True)
if not init_ok:
logging.error('Initializing %s-%s failed, aborting',
sabnzbd.MY_NAME, sabnzbd.__version__)
ExitSab(2)
find_programs(sabnzbd.DIR_PROG)
if sabnzbd.decoder.HAVE_YENC:
logging.info("_yenc module... found!")
else:
if hasattr(sys, "frozen"):
logging.warning("_yenc module... NOT found!")
else:
logging.info("_yenc module... NOT found!")
if sabnzbd.newsunpack.PAR2_COMMAND:
logging.info("par2 binary... found (%s)", sabnzbd.newsunpack.PAR2_COMMAND)
else:
logging.error("par2 binary... NOT found!")
if sabnzbd.newsunpack.PAR2C_COMMAND:
logging.info("par2-classic 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("unrar binary... NOT found")
if sabnzbd.newsunpack.ZIP_COMMAND:
logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
else:
logging.warning("unzip binary... NOT found!")
if os.name != 'nt':
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!")
if sabnzbd.newswrapper.HAVE_SSL:
logging.info("pyOpenSSL... found (%s)", sabnzbd.newswrapper.HAVE_SSL)
else:
logging.info("pyOpenSSL... NOT found - try apt-get install python-pyopenssl (SSL is optional)")
if cherryhost == None:
cherryhost = sabnzbd.cfg.CHERRYHOST.get()
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, use 0.0.0.0
cherryhost = '0.0.0.0'
info = socket.getaddrinfo(localhost, None)
for item in info:
ip = item[4][0]
if ip.find('169.254.') == 0:
pass # Is an APIPA
elif ip.find(':') >= 0:
ipv6 = True
elif ip.find('.') >= 0:
ipv4 = True
hostip = ip
break
if cherryhost == '':
if ipv6 and ipv4:
# To protect Firefox users, use numeric IP
cherryhost = hostip
browserhost = hostip
else:
cherryhost = socket.gethostname()
browserhost = cherryhost
elif cherryhost == '0.0.0.0':
# Just take the gamble for this
cherryhost = '0.0.0.0'
browserhost = localhost
elif cherryhost.find('[') >= 0 or cherryhost.find(':') >= 0:
# IPV6
browserhost = cherryhost
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 cherryhost.find('[') >= 0:
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 os.name == 'nt':
logging.warning("Please be aware the 0.0.0.0 hostname will need an IPv6 address for external access")
if cherryport == None:
cherryport= sabnzbd.cfg.CHERRYPORT.get_int()
else:
sabnzbd.cfg.CHERRYPORT.set(str(cherryport))
os.chdir(sabnzbd.DIR_PROG)
web_dir = Web_Template(sabnzbd.cfg.WEB_DIR, DEF_STDINTF, web_dir)
web_dir2 = Web_Template(sabnzbd.cfg.WEB_DIR2, '', web_dir2)
sabnzbd.WEB_DIR = web_dir
sabnzbd.WEB_DIR2 = web_dir2
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.WEB_COLOR.get(), web_dir)
sabnzbd.cfg.WEB_COLOR.set(sabnzbd.WEB_COLOR)
sabnzbd.WEB_COLOR2 = CheckColor(sabnzbd.cfg.WEB_COLOR2.get(), web_dir2)
sabnzbd.cfg.WEB_COLOR2.set(sabnzbd.WEB_COLOR2)
if fork and os.name != 'nt':
daemonize()
# Save the INI file
config.save_config(force=True)
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()
cherrylogtoscreen = False
sabnzbd.WEBLOGFILE = None
if cherrypylogging:
if logdir:
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY);
if not fork:
try:
x= sys.stderr.fileno
x= sys.stdout.fileno
cherrylogtoscreen = True
except:
cherrylogtoscreen = False
cherrypy.config.update({'server.environment': 'production',
'server.socket_host': cherryhost,
'server.socket_port': cherryport,
'server.logToScreen': cherrylogtoscreen,
'server.logFile': sabnzbd.WEBLOGFILE,
'engine.autoreload_frequency' : 100,
'engine.autoreload_on' : False,
'tools.encode.on' : True,
'tools.gzip.on' : True,
'tools.sessions.on' : True,
'server.show_tracebacks': True,
'error_page.401': sabnzbd.misc.error_page_401
})
ssl_ca = sabnzbd.cfg.SSL_CA.get_path()
ssl_key = sabnzbd.cfg.SSL_KEY.get_path()
if not (ssl_ca and os.path.exists(ssl_ca) and ssl_key and os.path.exists(ssl_key)):
ssl_ca = None
if https and not (ssl_ca and ssl_key):
logging.warning('Disabled HTTPS because of missing CA and KEY files')
https = False
if https:
cherrypy.config.update({'server.ssl_certificate' : ssl_ca,
'server.ssl_private_key' : ssl_key})
appconfig = {'/sabnzbd/api' : {'tools.basic_auth.on' : False},
'/sabnzbd/shutdown': {'streamResponse': True},
'/sabnzbd/static': {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir, 'static')}
}
if web_dir2:
appconfig['/sabnzbd/m/api'] = {'tools.basic_auth.on' : False}
appconfig['/sabnzbd/m/shutdown'] = {'streamResponse': True}
appconfig['/sabnzbd/m/static'] = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir2, 'static')}
if sabnzbd.cfg.USERNAME.get() and sabnzbd.cfg.PASSWORD.get():
appconfig['/sabnzbd'] = {'tools.basic_auth.on' : True, 'tools.basic_auth.realm' : 'SABnzbd',
'tools.basic_auth.users' : sabnzbd.interface.get_users, 'tools.basic_auth.encrypt' : sabnzbd.interface.encrypt_pwd}
login_page = LoginPage(web_dir, '/sabnzbd/', web_dir2, '/sabnzbd/m/')
cherrypy.tree.mount(login_page, '/', config=appconfig)
logging.info('Starting web-interface on %s:%s', cherryhost, cherryport)
sabnzbd.cfg.LOG_LEVEL.callback(guard_loglevel)
try:
# Use internal cherrypy check first to prevent ugly tracebacks
cherrypy.process.servers.check_port(cherryhost, cherryport)
cherrypy.engine.start()
except IOError, error:
if str(error) == 'Port not bound.':
if not force_web:
Panic_FWall(vista)
sabnzbd.halt()
ExitSab(2)
else:
Bail_Out(browserhost, cherryport)
except:
Bail_Out(browserhost, cherryport)
# Wait for server to become ready
cherrypy.engine.wait(cherrypy.process.wspbus.states.STARTED)
if https:
launch_a_browser("https://%s:%s/sabnzbd" % (browserhost, cherryport))
else:
launch_a_browser("http://%s:%s/sabnzbd" % (browserhost, cherryport))
Notify("SAB_Launched", None)
# Now's the time to check for a new version
check_latest_version()
# Have to keep this running, otherwise logging will terminate
timer = 0
while not sabnzbd.SABSTOP:
### 3 sec polling tasks
# Check for auto-restart request
if cherrypy.engine.execv:
cherrypy.engine._do_execv()
# Check for loglevel changes, ignore for non-final releases
if (not testRelease) and LOG_FLAG:
LOG_FLAG = False
logger.setLevel(LOGLEVELS[sabnzbd.cfg.LOG_LEVEL.get()])
### 30 sec polling tasks
if timer > 9:
timer = 0
# Keep Windows awake (if needed)
sabnzbd.keep_awake()
# Restart scheduler (if needed)
scheduler.restart()
# Save config (if needed)
config.save_config()
else:
timer += 1
time.sleep(3)
config.save_config()
Notify("SAB_Shutdown", None)
logging.info('Leaving SABnzbd')
sys.stderr.flush()
sys.stdout.flush()
os._exit(0)
#####################################################################
#
# Platform specific startup code
#
if not sabnzbd.DARWIN:
# Windows & Unix/Linux
if __name__ == '__main__':
main()
else:
# OSX
if __name__ == '__main__':
try:
from email import header
import cherrypy.filters.cachefilter
import cherrypy.filters.logdebuginfofilter
import cherrypy.filters.baseurlfilter
import cherrypy.filters.virtualhostfilter
import cherrypy.filters.decodingfilter
import cherrypy.filters.sessionauthenticatefilter
import cherrypy.filters.sessionfilter
import cherrypy.filters.staticfilter
import cherrypy.filters.nsgmlsfilter
import cherrypy.filters.tidyfilter
import cherrypy.filters.xmlrpcfilter
import cherrypy.filters.responseheadersfilter
import cherrypy.filters.encodingfilter
import Cheetah.DummyTransaction
import objc
from Foundation import *
from AppKit import *
from PyObjCTools import NibClassBuilder, AppHelper
NibClassBuilder.extractClasses("MainMenu")
class SABnzbdDelegate(NibClassBuilder.AutoBaseClass):
def applicationShouldTerminate_(self, sender):
logging.info('[osx] application terminating')
sabApp.stop()
return NSTerminateNow
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 ')
cherrypy.server.stop()
sabnzbd.halt()
logging.info('[osx] sabApp Quit - main thread stopped')
sabApp = startApp()
sabApp.start()
AppHelper.runEventLoop()
except:
main()