diff --git a/SickBeard.py b/SickBeard.py
index 73da7602..fed90122 100755
--- a/SickBeard.py
+++ b/SickBeard.py
@@ -385,6 +385,7 @@ def main():
# start IOLoop
IOLoop.current().start()
+ sickbeard.saveAndShutdown()
return
if __name__ == "__main__":
diff --git a/gui/slick/js/restart.js b/gui/slick/js/restart.js
index b98b0a8d..edc4074f 100644
--- a/gui/slick/js/restart.js
+++ b/gui/slick/js/restart.js
@@ -37,7 +37,7 @@ function is_alive() {
$('#restart_loading').hide();
$('#restart_success').show();
$('#refresh_message').show();
- window.location = sb_base_url+'/home';
+ window.location = sb_base_url+'/home/';
}
}
}, 'jsonp');
@@ -66,7 +66,7 @@ $(document).ready(function()
$('#restart_success').show();
$('#refresh_message').show();
}, 3000);
- setTimeout("window.location = sb_base_url+'/home'", 5000);
+ setTimeout("window.location = sb_base_url+'/home/'", 5000);
}
}
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 863cf05d..2226a0f6 100644
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -105,6 +105,7 @@ CUR_COMMIT_HASH = None
INIT_LOCK = Lock()
__INITIALIZED__ = False
started = False
+restarted = False
ACTUAL_LOG_DIR = None
LOG_DIR = None
@@ -1292,16 +1293,7 @@ def saveAll():
logger.log(u"Saving config file to disk")
save_config()
-def saveAndShutdown(restart=False):
- if not restart:
- logger.log('Shutting down tornado')
- try:
- IOLoop.current().stop()
- except RuntimeError:
- pass
- except:
- logger.log('Failed shutting down the server: %s' % traceback.format_exc(), logger.ERROR)
-
+def saveAndShutdown():
halt()
saveAll()
@@ -1309,7 +1301,7 @@ def saveAndShutdown(restart=False):
logger.log(u"Removing pidfile " + str(PIDFILE))
remove_pid_file(PIDFILE)
- if restart:
+ if restarted:
install_type = versionCheckScheduler.action.install_type
popen_list = []
@@ -1352,18 +1344,20 @@ def invoke_restart(soft=True):
def invoke_shutdown():
- invoke_command(saveAndShutdown)
+ invoke_command(webserveInit.shutdown)
def restart(soft=True):
+ global restarted
+
if soft:
halt()
saveAll()
logger.log(u"Re-initializing all data")
initialize()
-
else:
- saveAndShutdown(restart=True)
+ restarted=True
+ webserveInit.shutdown()
def save_config():
diff --git a/sickbeard/browser.py b/sickbeard/browser.py
index 94cf3eb8..1c790ff3 100644
--- a/sickbeard/browser.py
+++ b/sickbeard/browser.py
@@ -102,10 +102,10 @@ def foldersAtPath(path, includeParent=False):
class WebFileBrowser(RequestHandler):
def index(self, path=''):
- HTTPHeaders()['Content-Type'] = "application/json"
+ self.set_header("Content-Type", "application/json")
return self.finish(json.dumps(foldersAtPath(path, True)))
def complete(self, term):
- HTTPHeaders()['Content-Type'] = "application/json"
+ self.set_header("Content-Type", "application/json")
paths = [entry['path'] for entry in foldersAtPath(os.path.dirname(term)) if 'path' in entry]
return self.finish(json.dumps(paths))
diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py
index 50dcfc07..4f4dddaf 100644
--- a/sickbeard/webapi.py
+++ b/sickbeard/webapi.py
@@ -157,14 +157,14 @@ class Api(webserve.IndexHandler):
else:
t.apikey = "api key not generated"
- return self.finish(webserve._munge(t))
+ return webserve._munge(t)
def _out_as_json(self, dict):
""" set cherrypy response to json """
- self.h["content-type"] = "application/json;charset=UTF-8"
+ self.set_header("Content-Type", "application/json")
try:
out = json.dumps(dict, indent=self.intent, sort_keys=True)
- callback = self.h.get('callback' ,None) or self.h.get('jsonp' ,None)
+ callback = self.request.headers.get('callback', None) or self.request.headers.get('jsonp', None)
if callback != None:
out = callback + '(' + out + ');' # wrap with JSONP call if requested
except Exception, e: # if we fail to generate the output fake an error
@@ -175,7 +175,7 @@ class Api(webserve.IndexHandler):
def _grand_access(self, realKey, args, kwargs):
""" validate api key and log result """
- remoteIp = sickbeard.REMOTE_IP
+ remoteIp = self.request.remote_ip
apiKey = kwargs.get("apikey", None)
if not apiKey:
if args: # if we have keyless vars we assume first one is the api key, always !
@@ -1506,7 +1506,7 @@ class CMD_SickBeardPing(ApiCall):
def run(self):
""" check to see if sickbeard is running """
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
if sickbeard.started:
return _responds(RESULT_SUCCESS, {"pid": sickbeard.PID}, "Pong")
else:
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 7690fb6b..36086585 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -28,12 +28,8 @@ import re
import threading
import datetime
import random
-
-from Cheetah.Template import Template
import sys
-from tornado import gen
-from tornado.httputil import HTTPHeaders
-from tornado.web import RequestHandler, HTTPError, asynchronous, authenticated
+
import sickbeard
from sickbeard import config, sab
@@ -80,13 +76,19 @@ except ImportError:
from lib import adba
-#def _handle_reverse_proxy():
+from Cheetah.Template import Template
+from tornado import gen
+from tornado.web import RequestHandler, HTTPError, asynchronous, authenticated
+
+# def _handle_reverse_proxy():
# if sickbeard.HANDLE_REVERSE_PROXY:
# cherrypy.lib.cptools.proxy()
# cherrypy.tools.handle_reverse_proxy = cherrypy.Tool('before_handler', _handle_reverse_proxy)
+req_headers = None
+
def require_basic_auth(handler_class):
def wrap_execute(handler_execute):
def require_basic_auth(handler, kwargs):
@@ -115,15 +117,18 @@ def require_basic_auth(handler_class):
handler.clear_cookie("user")
get_auth()
+
def _execute(self, transforms, *args, **kwargs):
if not require_basic_auth(self, kwargs):
return False
return handler_execute(self, transforms, *args, **kwargs)
+
return _execute
handler_class._execute = wrap_execute(handler_class._execute)
return handler_class
+
class RedirectHandler(RequestHandler):
"""Redirects the client to the given URL for all GET requests.
@@ -133,47 +138,25 @@ class RedirectHandler(RequestHandler):
(r"/oldpath", web.RedirectHandler, {"url": "/newpath"}),
])
"""
+
def get(self, path):
self.redirect(path, permanent=True)
+
@require_basic_auth
class LoginHandler(RedirectHandler):
def get(self, path):
self.redirect(self.get_argument("next", u"/"))
+
@require_basic_auth
class IndexHandler(RedirectHandler):
def __init__(self, application, request, **kwargs):
super(IndexHandler, self).__init__(application, request, **kwargs)
+ global req_headers
- self.remote_ip = sickbeard.REMOTE_IP = self.request.headers.get('X-Forwarded-For',
- self.request.headers.get('X-Real-Ip', self.request.remote_ip))
-
- self.h = HTTPHeaders({"content-type": "text/html"})
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
- self.h['Content-Type'] = 'text/javascript'
- self.h['Access-Control-Allow-Origin'] = '*'
- self.h['Access-Control-Allow-Headers'] = 'x-requested-with'
-
- if 'Host' in self.h:
- if self.h['Host'][0] == '[':
- self.sbHost = re.match("^\[.*\]", self.h['Host'], re.X | re.M | re.S).group(0)
- else:
- self.sbHost = re.match("^[^:]+", self.h['Host'], re.X | re.M | re.S).group(0)
-
- if sickbeard.NZBS and sickbeard.NZBS_UID and sickbeard.NZBS_HASH:
- logger.log(u"NZBs.org has been replaced, please check the config to configure the new provider!",
- logger.ERROR)
- ui.notifications.error("NZBs.org Config Update",
- "NZBs.org has a new site. Please update your config with the api key from http://nzbs.org and then disable the old NZBs.org provider.")
-
- if "X-Forwarded-Host" in self.h:
- self.sbHost = self.h['X-Forwarded-Host']
- if "X-Forwarded-Port" in self.h:
- sbHttpPort = self.h['X-Forwarded-Port']
- self.sbHttpsPort = sbHttpPort
- if "X-Forwarded-Proto" in self.h:
- self.sbHttpsEnabled = True if self.h['X-Forwarded-Proto'] == 'https' else False
+ sickbeard.REMOTE_IP = self.request.remote_ip
+ req_headers = self.request.headers
def delist_arguments(self, args):
"""
@@ -200,8 +183,10 @@ class IndexHandler(RedirectHandler):
return inspect.isclass(c) and c.__module__ == pred.__module__
try:
- klass = [cls[1] for cls in inspect.getmembers(sys.modules[__name__], pred) + [(self.__class__.__name__, self.__class__)] if
- cls[0].lower() == method.lower() or method in cls[1].__dict__.keys()][0](self.application, self.request)
+ klass = [cls[1] for cls in
+ inspect.getmembers(sys.modules[__name__], pred) + [(self.__class__.__name__, self.__class__)] if
+ cls[0].lower() == method.lower() or method in cls[1].__dict__.keys()][0](self.application,
+ self.request)
except:
klass = None
@@ -416,7 +401,7 @@ class IndexHandler(RedirectHandler):
""" Provides a subscribeable URL for iCal subscriptions
"""
- logger.log(u"Receiving iCal request from %s" % self.request.remote.ip)
+ logger.log(u"Receiving iCal request from %s" % self.request.remote_ip)
poster_url = self.request.url().replace('ical', '')
@@ -479,18 +464,33 @@ class IndexHandler(RedirectHandler):
browser = WebFileBrowser
+
class PageTemplate(Template):
def __init__(self, *args, **KWs):
KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",
KWs['file'])
super(PageTemplate, self).__init__(*args, **KWs)
+ global req_headers
+
self.sbRoot = sickbeard.WEB_ROOT
self.sbHttpPort = sickbeard.WEB_PORT
self.sbHttpsPort = sickbeard.WEB_PORT
- self.sbHost = sickbeard.WEB_HOST
self.sbHttpsEnabled = sickbeard.ENABLE_HTTPS
self.sbHandleReverseProxy = sickbeard.HANDLE_REVERSE_PROXY
+ if req_headers['Host'][0] == '[':
+ self.sbHost = re.match("^\[.*\]", req_headers['Host'], re.X | re.M | re.S).group(0)
+ else:
+ self.sbHost = re.match("^[^:]+", req_headers['Host'], re.X | re.M | re.S).group(0)
+
+ if "X-Forwarded-Host" in req_headers:
+ self.sbHost = req_headers['X-Forwarded-Host']
+ if "X-Forwarded-Port" in req_headers:
+ sbHttpPort = req_headers['X-Forwarded-Port']
+ self.sbHttpsPort = sbHttpPort
+ if "X-Forwarded-Proto" in req_headers:
+ self.sbHttpsEnabled = True if req_headers['X-Forwarded-Proto'] == 'https' else False
+
logPageTitle = 'Logs & Errors'
if len(classes.ErrorViewer.errors):
logPageTitle += ' (' + str(len(classes.ErrorViewer.errors)) + ')'
@@ -1070,8 +1070,8 @@ class Manage(IndexHandler):
exceptions_list = []
curErrors += self.editShow(curShow, new_show_dir, anyQualities, bestQualities, exceptions_list,
- new_flatten_folders, new_paused, subtitles=new_subtitles, anime=new_anime,
- scene=new_scene, directCall=True)
+ new_flatten_folders, new_paused, subtitles=new_subtitles, anime=new_anime,
+ scene=new_scene, directCall=True)
if curErrors:
logger.log(u"Errors: " + str(curErrors), logger.ERROR)
@@ -1346,6 +1346,7 @@ ConfigMenu = [
{'title': 'Anime', 'path': 'config/anime/'},
]
+
class ConfigGeneral(IndexHandler):
def index(self, *args, **kwargs):
@@ -2432,8 +2433,8 @@ class ConfigAnime(IndexHandler):
self.redirect("/config/anime/")
-class Config(IndexHandler):
+class Config(IndexHandler):
def index(self, *args, **kwargs):
t = PageTemplate(file="config.tmpl")
t.submenu = ConfigMenu
@@ -2449,6 +2450,7 @@ class Config(IndexHandler):
notifications = ConfigNotifications
anime = ConfigAnime
+
def haveXBMC():
return sickbeard.USE_XBMC and sickbeard.XBMC_UPDATE_LIBRARY
@@ -2976,6 +2978,11 @@ class Home(IndexHandler):
else:
return "Error: Unsupported Request. Send jsonp request with 'callback' variable in the query string."
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
+ self.set_header('Content-Type', 'text/javascript')
+ self.set_header('Access-Control-Allow-Origin', '*')
+ self.set_header('Access-Control-Allow-Headers', 'x-requested-with')
+
if sickbeard.started:
return callback + '(' + json.dumps({"msg": str(sickbeard.PID)}) + ');'
else:
@@ -3031,7 +3038,7 @@ class Home(IndexHandler):
def testGrowl(self, host=None, password=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
host = config.clean_host(host, default_port=23053)
@@ -3048,7 +3055,7 @@ class Home(IndexHandler):
def testProwl(self, prowl_api=None, prowl_priority=0):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.prowl_notifier.test_notify(prowl_api, prowl_priority)
if result:
@@ -3058,7 +3065,7 @@ class Home(IndexHandler):
def testBoxcar(self, username=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.boxcar_notifier.test_notify(username)
if result:
@@ -3068,7 +3075,7 @@ class Home(IndexHandler):
def testBoxcar2(self, accesstoken=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.boxcar2_notifier.test_notify(accesstoken)
if result:
@@ -3078,7 +3085,7 @@ class Home(IndexHandler):
def testPushover(self, userKey=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.pushover_notifier.test_notify(userKey)
if result:
@@ -3088,13 +3095,13 @@ class Home(IndexHandler):
def twitterStep1(self, *args, **kwargs):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
return notifiers.twitter_notifier._get_authorization()
def twitterStep2(self, key):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.twitter_notifier._get_credentials(key)
logger.log(u"result: " + str(result))
@@ -3105,7 +3112,7 @@ class Home(IndexHandler):
def testTwitter(self, *args, **kwargs):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.twitter_notifier.test_notify()
if result:
@@ -3115,7 +3122,7 @@ class Home(IndexHandler):
def testXBMC(self, host=None, username=None, password=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
host = config.clean_hosts(host)
finalResult = ''
@@ -3131,7 +3138,7 @@ class Home(IndexHandler):
def testPLEX(self, host=None, username=None, password=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
finalResult = ''
for curHost in [x.strip() for x in host.split(",")]:
@@ -3146,7 +3153,7 @@ class Home(IndexHandler):
def testLibnotify(self, *args, **kwargs):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
if notifiers.libnotify_notifier.test_notify():
return "Tried sending desktop notification via libnotify"
@@ -3155,7 +3162,7 @@ class Home(IndexHandler):
def testNMJ(self, host=None, database=None, mount=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
host = config.clean_host(host)
result = notifiers.nmj_notifier.test_notify(urllib.unquote_plus(host), database, mount)
@@ -3166,7 +3173,7 @@ class Home(IndexHandler):
def settingsNMJ(self, host=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
host = config.clean_host(host)
result = notifiers.nmj_notifier.notify_settings(urllib.unquote_plus(host))
@@ -3178,7 +3185,7 @@ class Home(IndexHandler):
def testNMJv2(self, host=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
host = config.clean_host(host)
result = notifiers.nmjv2_notifier.test_notify(urllib.unquote_plus(host))
@@ -3189,7 +3196,7 @@ class Home(IndexHandler):
def settingsNMJv2(self, host=None, dbloc=None, instance=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
host = config.clean_host(host)
result = notifiers.nmjv2_notifier.notify_settings(urllib.unquote_plus(host), dbloc, instance)
@@ -3202,7 +3209,7 @@ class Home(IndexHandler):
def testTrakt(self, api=None, username=None, password=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.trakt_notifier.test_notify(api, username, password)
if result:
@@ -3212,7 +3219,7 @@ class Home(IndexHandler):
def loadShowNotifyLists(self, *args, **kwargs):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
with db.DBConnection() as myDB:
rows = myDB.select("SELECT show_id, show_name, notify_list FROM tv_shows ORDER BY show_name ASC")
@@ -3227,7 +3234,7 @@ class Home(IndexHandler):
def testEmail(self, host=None, port=None, smtp_from=None, use_tls=None, user=None, pwd=None, to=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
host = config.clean_host(host)
if notifiers.email_notifier.test_notify(host, port, smtp_from, use_tls, user, pwd, to):
@@ -3237,7 +3244,7 @@ class Home(IndexHandler):
def testNMA(self, nma_api=None, nma_priority=0):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.nma_notifier.test_notify(nma_api, nma_priority)
if result:
@@ -3247,7 +3254,7 @@ class Home(IndexHandler):
def testPushalot(self, authorizationToken=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.pushalot_notifier.test_notify(authorizationToken)
if result:
@@ -3257,7 +3264,7 @@ class Home(IndexHandler):
def testPushbullet(self, api=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.pushbullet_notifier.test_notify(api)
if result:
@@ -3267,7 +3274,7 @@ class Home(IndexHandler):
def getPushbulletDevices(self, api=None):
- self.h['Cache-Control'] = "max-age=0,no-cache,no-store"
+ self.set_header('Cache-Control', "max-age=0,no-cache,no-store")
result = notifiers.pushbullet_notifier.get_devices(api)
if result:
diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py
index 48281f6a..8f070cda 100644
--- a/sickbeard/webserveInit.py
+++ b/sickbeard/webserveInit.py
@@ -1,5 +1,7 @@
import os
+import traceback
import sickbeard
+from tornado.ioloop import IOLoop
import webserve
import webapi
@@ -93,9 +95,10 @@ def initWebServer(options={}):
# Load the app
app = Application([],
- debug=False,
+ debug=True,
gzip=True,
autoreload=True,
+ xheaders=False,
cookie_secret='61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=',
login_url='/login'
)
@@ -134,4 +137,13 @@ def initWebServer(options={}):
logger.log(u"Starting SickRage on " + protocol + "://" + str(options['host']) + ":" + str(
options['port']) + "/")
- server.listen(options['port'], options['host'])
\ No newline at end of file
+ server.listen(options['port'], options['host'])
+
+def shutdown():
+ logger.log('Shutting down tornado')
+ try:
+ IOLoop.current().stop()
+ except RuntimeError:
+ pass
+ except:
+ logger.log('Failed shutting down the server: %s' % traceback.format_exc(), logger.ERROR)
\ No newline at end of file