mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-30 08:23:37 +00:00
Change improve security.
Change improve security with DNS rebinding prevention, set "Allowed browser hostnames" at config/General/Web Interface. Change improve security with cross-site request forgery (xsrf) protection on web forms. Change improve security by sending header flag httponly with cookies Change improve security by sending header flag secure with SSL cookies Change improve test for creating self-signed SSL cert. Change force restart when switching SSL on/off. Change enable Tornado serve_traceback feature. Change PEP8 tweaks.
This commit is contained in:
parent
de32a1aa67
commit
f3310e29f2
24 changed files with 246 additions and 100 deletions
|
@ -1,5 +1,10 @@
|
|||
### 0.16.0 (2018-xx-xx xx:xx:xx UTC)
|
||||
|
||||
* Change improve security with cross-site request forgery (xsrf) protection on web forms
|
||||
* Change improve security by sending header flags httponly and secure with cookies
|
||||
* Change improve security with DNS rebinding prevention, set "Allowed browser hostnames" at config/General/Web Interface
|
||||
* Change improve test for creating self-signed SSL cert
|
||||
* Change force restart when switching SSL on/off
|
||||
* Change hachoir targa and mpeg_ts mime parser tags so they validate
|
||||
* Update backports/ssl_match_hostname 3.5.0.1 (r18) to 3.7.0.1 (r28)
|
||||
* Update cachecontrol library 0.12.3 (db54c40) to 0.12.4 (bd94f7e)
|
||||
|
|
53
SickBeard.py
53
SickBeard.py
|
@ -57,7 +57,7 @@ try:
|
|||
except ValueError:
|
||||
print('Sorry, requires Python module Cheetah 2.1.0 or newer.')
|
||||
sys.exit(1)
|
||||
except:
|
||||
except (StandardError, Exception):
|
||||
print('The Python module Cheetah is required')
|
||||
sys.exit(1)
|
||||
|
||||
|
@ -327,23 +327,25 @@ class SickGear(object):
|
|||
print(u'Unable to find "%s", all settings will be default!' % sickbeard.CONFIG_FILE)
|
||||
|
||||
sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
|
||||
stack_size = None
|
||||
try:
|
||||
stack_size = int(sickbeard.CFG['General']['stack_size'])
|
||||
except:
|
||||
except (StandardError, Exception):
|
||||
stack_size = None
|
||||
|
||||
if stack_size:
|
||||
try:
|
||||
threading.stack_size(stack_size)
|
||||
except (StandardError, Exception) as e:
|
||||
print('Stack Size %s not set: %s' % (stack_size, e.message))
|
||||
except (StandardError, Exception) as er:
|
||||
print('Stack Size %s not set: %s' % (stack_size, er.message))
|
||||
|
||||
# check all db versions
|
||||
for d, min_v, max_v, base_v, mo in [
|
||||
('failed.db', sickbeard.failed_db.MIN_DB_VERSION, sickbeard.failed_db.MAX_DB_VERSION, sickbeard.failed_db.TEST_BASE_VERSION, 'FailedDb'),
|
||||
('cache.db', sickbeard.cache_db.MIN_DB_VERSION, sickbeard.cache_db.MAX_DB_VERSION, sickbeard.cache_db.TEST_BASE_VERSION, 'CacheDb'),
|
||||
('sickbeard.db', sickbeard.mainDB.MIN_DB_VERSION, sickbeard.mainDB.MAX_DB_VERSION, sickbeard.mainDB.TEST_BASE_VERSION, 'MainDb')
|
||||
('failed.db', sickbeard.failed_db.MIN_DB_VERSION, sickbeard.failed_db.MAX_DB_VERSION,
|
||||
sickbeard.failed_db.TEST_BASE_VERSION, 'FailedDb'),
|
||||
('cache.db', sickbeard.cache_db.MIN_DB_VERSION, sickbeard.cache_db.MAX_DB_VERSION,
|
||||
sickbeard.cache_db.TEST_BASE_VERSION, 'CacheDb'),
|
||||
('sickbeard.db', sickbeard.mainDB.MIN_DB_VERSION, sickbeard.mainDB.MAX_DB_VERSION,
|
||||
sickbeard.mainDB.TEST_BASE_VERSION, 'MainDb')
|
||||
]:
|
||||
cur_db_version = db.DBConnection(d).checkDBVersion()
|
||||
|
||||
|
@ -409,19 +411,25 @@ class SickGear(object):
|
|||
self.webhost = (('0.0.0.0', '::')[sickbeard.WEB_IPV6], '')[sickbeard.WEB_IPV64]
|
||||
|
||||
# web server options
|
||||
self.web_options = {
|
||||
'port': int(self.start_port),
|
||||
'host': self.webhost,
|
||||
'data_root': os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
||||
'web_root': sickbeard.WEB_ROOT,
|
||||
'log_dir': self.log_dir,
|
||||
'username': sickbeard.WEB_USERNAME,
|
||||
'password': sickbeard.WEB_PASSWORD,
|
||||
'enable_https': sickbeard.ENABLE_HTTPS,
|
||||
'handle_reverse_proxy': sickbeard.HANDLE_REVERSE_PROXY,
|
||||
'https_cert': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
|
||||
'https_key': os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_KEY),
|
||||
}
|
||||
self.web_options = dict(
|
||||
host=self.webhost,
|
||||
port=int(self.start_port),
|
||||
web_root=sickbeard.WEB_ROOT,
|
||||
data_root=os.path.join(sickbeard.PROG_DIR, 'gui', sickbeard.GUI_NAME),
|
||||
log_dir=self.log_dir,
|
||||
username=sickbeard.WEB_USERNAME,
|
||||
password=sickbeard.WEB_PASSWORD,
|
||||
handle_reverse_proxy=sickbeard.HANDLE_REVERSE_PROXY,
|
||||
enable_https=False,
|
||||
https_cert=None,
|
||||
https_key=None,
|
||||
)
|
||||
if sickbeard.ENABLE_HTTPS:
|
||||
self.web_options.update(dict(
|
||||
enable_https=sickbeard.ENABLE_HTTPS,
|
||||
https_cert=os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_CERT),
|
||||
https_key=os.path.join(sickbeard.PROG_DIR, sickbeard.HTTPS_KEY)
|
||||
))
|
||||
|
||||
# start web server
|
||||
try:
|
||||
|
@ -596,7 +604,7 @@ class SickGear(object):
|
|||
# shutdown web server
|
||||
if self.webserver:
|
||||
logger.log('Shutting down Tornado')
|
||||
self.webserver.shutDown()
|
||||
self.webserver.shut_down()
|
||||
try:
|
||||
self.webserver.join(10)
|
||||
except (StandardError, Exception):
|
||||
|
@ -636,6 +644,7 @@ class SickGear(object):
|
|||
def exit(code):
|
||||
os._exit(code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.hexversion >= 0x020600F0:
|
||||
freeze_support()
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<div id="config-content">
|
||||
|
||||
<form id="configForm" action="saveAnime" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
<div id="config-content">
|
||||
|
||||
<form id="configForm" action="saveGeneral" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
|
||||
<ul>
|
||||
|
@ -589,6 +591,17 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="allowed_hosts">
|
||||
<span class="component-title">Allowed browser hostnames</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="allowed_hosts" id="allowed-hosts" value="$sg_str('ALLOWED_HOSTS')" class="form-control input-sm input300">
|
||||
<p>blank for insecure allow all</p>
|
||||
<div class="clear-left"><p>whitelist names that browse the interface (e.g. $request_host, my_hostname)</p></div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes">
|
||||
|
||||
</fieldset>
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
<div id="config">
|
||||
<div id="config-content">
|
||||
<form id="configForm" action="$sbRoot/config/notifications/save_notifications" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
<ul>
|
||||
<li><a href="#tabs-1">Home Theater / NAS</a></li>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<div id="config-content" class="linefix">
|
||||
|
||||
<form id="configForm" action="savePostProcessing" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
<ul>
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
<div id="config-content">
|
||||
|
||||
<form id="configForm" action="saveProviders" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
<ul>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<div id="config-content" class="linefix">
|
||||
|
||||
<form id="configForm" action="saveSearch" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
<ul>
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
<div id="config-content">
|
||||
|
||||
<form id="configForm" action="saveSubtitles" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
<ul>
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
<form action="editShow" method="post" id="editShow" style="width:894px">
|
||||
<input type="hidden" name="show" id="show" value="$show.indexerid">
|
||||
<input type="hidden" name="indexer" id="indexer" value="$show.indexer">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="config-components">
|
||||
<ul>
|
||||
|
|
|
@ -38,6 +38,7 @@ var config = { sortArticle: #echo ['!1','!0'][$sg_var('SORT_ARTICLE')]# }
|
|||
|
||||
<h3>Existing show folders</h3>
|
||||
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
|
||||
$xsrf_form_html
|
||||
|
||||
<span#if $kwargs.get('hash_dir', None)# class="hide"#end if#>
|
||||
<p>Tip: shows are added quicker when usable show nfo and xml metadata is found</p>
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#end if
|
||||
|
||||
<form id="addShowForm"#if $kwargs.get('action')# class="fullwidth"#end if# method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
|
||||
$xsrf_form_html
|
||||
|
||||
<fieldset class="sectionwrap step-one">
|
||||
<legend class="legendStep"><p>#if $use_provided_info#Using known show information#else#Find show at TV info source#end if#</p></legend>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
<form name="processForm" method="post" action="processEpisode">
|
||||
<input type="hidden" id="type" name="type" value="manual">
|
||||
$xsrf_form_html
|
||||
|
||||
<div id="postProcess" class="stepDiv">
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
<br />
|
||||
|
||||
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addRecommendedShow" accept-charset="utf-8">
|
||||
|
||||
$xsrf_form_html
|
||||
|
||||
<fieldset class="sectionwrap step-one">
|
||||
<legend class="legendStep"><p>Select a recommended show</p></legend>
|
||||
|
||||
|
@ -69,4 +70,4 @@
|
|||
|
||||
</div>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
|
||||
#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
|
||||
|
|
|
@ -44,9 +44,9 @@
|
|||
#end if
|
||||
</style>
|
||||
</head>
|
||||
<body><div class="login"><form action="" method="post"><div class="login-img center-block form-group"></div>
|
||||
<body><div class="login"><form action="" method="post">$xsrf_form_html<div class="login-img center-block form-group"></div>
|
||||
<div class="login-error"><div class="#if 'authfailed'==$resp then 'showme' else 'hide' #"><i class="error16"></i><span class="red-text">Authentication failed, please retry</span></div></div>
|
||||
<div class="form-group input-group"><span class="input-group-addon"><i class="icons icons-user" style=""></i></span><input name="username" class="form-control" placeholder="Username" type="text" autofocus></div>
|
||||
<div class="form-group input-group"><span class="input-group-addon"><i class="icons icons-lock" style=""></i></span><input name="password" class="form-control" placeholder="Password" type="password"></div>
|
||||
<div class="form-group"><label for="remember_me" class="login-remember"><input id="remember_me" name="remember_me" type="checkbox" value="1" checked="checked"><span>Remember me</span></label><input class="btn pull-right" name="submit" type="submit" value="Login"></div>
|
||||
</form></div></body></html>
|
||||
</form></div></body></html>
|
||||
|
|
|
@ -83,6 +83,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
|
|||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
<form name="bulkChangeForm" method="post" action="bulkChange">
|
||||
$xsrf_form_html
|
||||
|
||||
<table id="bulkChangeTable" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
|
||||
<thead>
|
||||
|
|
|
@ -62,6 +62,8 @@
|
|||
<script type="text/javascript" src="$sbRoot/js/manageEpisodeStatuses.js?v=$sbPID"></script>
|
||||
|
||||
<form action="$sbRoot/manage/changeEpisodeStatuses" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus">
|
||||
|
||||
<h3><span class="grey-text">$ep_count</span> episode#echo ('s', '')[1 == $ep_count]# marked <span class="grey-text">$statusStrings[$whichStatus].lower()</span> in <span class="grey-text">${len($sorted_show_ids)}</span> show#echo ('s', '')[1 == len($sorted_show_ids)]#</h3>
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
<script type="text/javascript" src="$sbRoot/js/massEdit.js?v=$sbPID"></script>
|
||||
|
||||
<form action="massEditSubmit" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<input type="hidden" name="toEdit" value="$showList">
|
||||
|
||||
<div class="optionWrapper">
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
<input type="hidden" id="selectSubLang" name="selectSubLang" value="$whichSubs">
|
||||
|
||||
<form action="$sbRoot/manage/downloadSubtitleMissed" method="post">
|
||||
$xsrf_form_html
|
||||
|
||||
<h2>Episodes without $subsLanguage subtitles.</h2>
|
||||
<br />
|
||||
Download missed subtitles for selected episodes <input class="btn btn-inline" type="submit" value="Go" />
|
||||
|
|
|
@ -266,8 +266,10 @@ $(document).ready(function () {
|
|||
});
|
||||
|
||||
function config_success(response) {
|
||||
if (response == 'reload') {
|
||||
if ('reload' == response) {
|
||||
window.location.reload(true);
|
||||
} else if ('restart' == response) {
|
||||
window.location.href = sbRoot + $('a.restart').attr('href')
|
||||
}
|
||||
$('.config_submitter').each(function () {
|
||||
$(this).removeAttr('disabled');
|
||||
|
|
|
@ -136,6 +136,7 @@ WEB_IPV64 = None
|
|||
|
||||
HANDLE_REVERSE_PROXY = False
|
||||
SEND_SECURITY_HEADERS = True
|
||||
ALLOWED_HOSTS = None
|
||||
PROXY_SETTING = None
|
||||
PROXY_INDEXERS = True
|
||||
|
||||
|
@ -608,7 +609,8 @@ def initialize(console_logging=True):
|
|||
HOME_SEARCH_FOCUS, USE_IMDB_INFO, IMDB_ACCOUNTS, DISPLAY_FREESPACE, SORT_ARTICLE, FUZZY_DATING, TRIM_ZERO, \
|
||||
DATE_PRESET, TIME_PRESET, TIME_PRESET_W_SECONDS, TIMEZONE_DISPLAY, \
|
||||
WEB_USERNAME, WEB_PASSWORD, CALENDAR_UNPROTECTED, USE_API, API_KEY, WEB_PORT, WEB_LOG, \
|
||||
ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, WEB_IPV6, WEB_IPV64, HANDLE_REVERSE_PROXY, SEND_SECURITY_HEADERS
|
||||
ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, WEB_IPV6, WEB_IPV64, HANDLE_REVERSE_PROXY, \
|
||||
SEND_SECURITY_HEADERS, ALLOWED_HOSTS
|
||||
# Gen Config/Advanced
|
||||
global BRANCH, CUR_COMMIT_BRANCH, GIT_REMOTE, CUR_COMMIT_HASH, GIT_PATH, CPU_PRESET, ANON_REDIRECT, \
|
||||
ENCRYPTION_VERSION, PROXY_SETTING, PROXY_INDEXERS, FILE_LOGGING_PRESET
|
||||
|
@ -814,6 +816,7 @@ def initialize(console_logging=True):
|
|||
|
||||
HANDLE_REVERSE_PROXY = bool(check_setting_int(CFG, 'General', 'handle_reverse_proxy', 0))
|
||||
SEND_SECURITY_HEADERS = bool(check_setting_int(CFG, 'General', 'send_security_headers', 1))
|
||||
ALLOWED_HOSTS = check_setting_str(CFG, 'General', 'allowed_hosts', '')
|
||||
|
||||
ROOT_DIRS = check_setting_str(CFG, 'General', 'root_dirs', '')
|
||||
if not re.match(r'\d+\|[^|]+(?:\|[^|]+)*', ROOT_DIRS):
|
||||
|
@ -1622,6 +1625,7 @@ def save_config():
|
|||
new_config['General']['https_key'] = HTTPS_KEY
|
||||
new_config['General']['handle_reverse_proxy'] = int(HANDLE_REVERSE_PROXY)
|
||||
new_config['General']['send_security_headers'] = int(SEND_SECURITY_HEADERS)
|
||||
new_config['General']['allowed_hosts'] = ALLOWED_HOSTS
|
||||
new_config['General']['use_nzbs'] = int(USE_NZBS)
|
||||
new_config['General']['use_torrents'] = int(USE_TORRENTS)
|
||||
new_config['General']['nzb_method'] = NZB_METHOD
|
||||
|
|
|
@ -639,10 +639,9 @@ def create_https_certificates(ssl_cert, ssl_key):
|
|||
Create self-signed HTTPS certificares and store in paths 'ssl_cert' and 'ssl_key'
|
||||
"""
|
||||
try:
|
||||
from OpenSSL import crypto # @UnresolvedImport
|
||||
from lib.certgen import createKeyPair, createCertRequest, createCertificate, TYPE_RSA, \
|
||||
serial # @UnresolvedImport
|
||||
except Exception as e:
|
||||
from OpenSSL import crypto
|
||||
from lib.certgen import createKeyPair, createCertRequest, createCertificate, TYPE_RSA, serial
|
||||
except (StandardError, Exception):
|
||||
logger.log(u"pyopenssl module missing, please install for https access", logger.WARNING)
|
||||
return False
|
||||
|
||||
|
@ -651,16 +650,17 @@ def create_https_certificates(ssl_cert, ssl_key):
|
|||
careq = createCertRequest(cakey, CN='Certificate Authority')
|
||||
cacert = createCertificate(careq, (careq, cakey), serial, (0, 60 * 60 * 24 * 365 * 10)) # ten years
|
||||
|
||||
cname = 'SickGear'
|
||||
pkey = createKeyPair(TYPE_RSA, 4096)
|
||||
req = createCertRequest(pkey, CN=cname)
|
||||
req = createCertRequest(pkey, CN='SickGear')
|
||||
cert = createCertificate(req, (cacert, cakey), serial, (0, 60 * 60 * 24 * 365 * 10)) # ten years
|
||||
|
||||
# Save the key and certificate to disk
|
||||
try:
|
||||
open(ssl_key, 'w').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
|
||||
open(ssl_cert, 'w').write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
|
||||
except:
|
||||
with open(ssl_key, 'w') as file_hd:
|
||||
file_hd.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
|
||||
with open(ssl_cert, 'w') as file_hd:
|
||||
file_hd.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
|
||||
except (StandardError, Exception):
|
||||
logger.log(u"Error creating SSL key and certificate", logger.ERROR)
|
||||
return False
|
||||
|
||||
|
@ -1350,6 +1350,64 @@ def maybe_plural(number=1):
|
|||
return ('s', '')[1 == number]
|
||||
|
||||
|
||||
def re_valid_hostname(with_allowed=True):
|
||||
return re.compile(r'(?i)(%slocalhost|.*\.local|%s|%s)$' % (
|
||||
'%s|' % (with_allowed
|
||||
and (sickbeard.ALLOWED_HOSTS and re.escape(sickbeard.ALLOWED_HOSTS).replace(',', '|') or '.*')
|
||||
or ''), socket.gethostname() or 'localhost', valid_ipaddr_expr()))
|
||||
|
||||
|
||||
def valid_ipaddr_expr():
|
||||
"""
|
||||
Returns a regular expression that will validate an ip address
|
||||
:return: Regular expression
|
||||
:rtype: String
|
||||
"""
|
||||
return r'(%s)' % '|'.join([re.sub('\s+(#.[^\r\n]+)?', '', x) for x in [
|
||||
# IPv4 address (accurate)
|
||||
# Matches 0.0.0.0 through 255.255.255.255
|
||||
'''
|
||||
(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])
|
||||
'''
|
||||
,
|
||||
# IPv6 address (standard and mixed)
|
||||
# 8 hexadecimal words, or 6 hexadecimal words followed by 4 decimal bytes All with optional leading zeros
|
||||
'''
|
||||
(?:(?<![:.\w])\[? # Anchor address
|
||||
(?:[A-F0-9]{1,4}:){6} # 6 words
|
||||
(?:[A-F0-9]{1,4}:[A-F0-9]{1,4} # 2 words
|
||||
| (?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3} # or 4 bytes
|
||||
(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])
|
||||
)(?![:.\w])) # Anchor address
|
||||
'''
|
||||
,
|
||||
# IPv6 address (compressed and compressed mixed)
|
||||
# 8 hexadecimal words, or 6 hexadecimal words followed by 4 decimal bytes
|
||||
# All with optional leading zeros. Consecutive zeros may be replaced with ::
|
||||
'''
|
||||
(?:(?<![:.\w])\[?(?: # Anchor address
|
||||
(?: # Mixed
|
||||
(?:[A-F0-9]{1,4}:){6} # Non-compressed
|
||||
|(?=(?:[A-F0-9]{0,4}:){2,6} # Compressed with 2 to 6 colons
|
||||
(?:[0-9]{1,3}\.){3}[0-9]{1,3} # and 4 bytes
|
||||
(?![:.\w])) # and anchored
|
||||
(([0-9A-F]{1,4}:){1,5}|:)((:[0-9A-F]{1,4}){1,5}:|:) # and at most 1 double colon
|
||||
|::(?:[A-F0-9]{1,4}:){5} # Compressed with 7 colons and 5 numbers
|
||||
)
|
||||
(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3} # 255.255.255.
|
||||
(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]) # 255
|
||||
| # Standard
|
||||
(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4} # Standard
|
||||
| # Compressed
|
||||
(?=(?:[A-F0-9]{0,4}:){0,7}[A-F0-9]{0,4} # Compressed with at most 7 colons
|
||||
(?![:.\w])) # and anchored
|
||||
(([0-9A-F]{1,4}:){1,7}|:)((:[0-9A-F]{1,4}){1,7}|:) # and at most 1 double colon
|
||||
|(?:[A-F0-9]{1,4}:){7}:|:(:[A-F0-9]{1,4}){7} # Compressed with 8 colons
|
||||
)(?![:.\w])) # Anchor address
|
||||
'''
|
||||
]])
|
||||
|
||||
|
||||
def build_dict(seq, key):
|
||||
return dict((d[key], dict(d, index=index)) for (index, d) in enumerate(seq))
|
||||
|
||||
|
|
|
@ -63,6 +63,7 @@ from sickbeard.indexermapper import MapStatus, save_mapping, map_indexers_to_sho
|
|||
from sickbeard.tv import show_not_found_retry_days, concurrent_show_not_found_days
|
||||
from tornado import gen
|
||||
from tornado.web import RequestHandler, StaticFileHandler, authenticated
|
||||
from tornado import escape
|
||||
from lib import adba
|
||||
from lib import subliminal
|
||||
from lib.dateutil import tz
|
||||
|
@ -87,8 +88,10 @@ except ImportError:
|
|||
|
||||
|
||||
class PageTemplate(Template):
|
||||
def __init__(self, headers, *args, **kwargs):
|
||||
def __init__(self, web_handler, *args, **kwargs):
|
||||
|
||||
headers = web_handler.request.headers
|
||||
self.xsrf_form_html = '<input name="_xsrf" type="hidden" value="%s">' % web_handler.xsrf_token
|
||||
self.sbHost = headers.get('X-Forwarded-Host')
|
||||
if None is self.sbHost:
|
||||
sbHost = headers.get('Host') or 'localhost'
|
||||
|
@ -207,7 +210,7 @@ class LoginHandler(BaseHandler):
|
|||
if self.get_current_user():
|
||||
self.redirect(self.get_argument('next', '/home/'))
|
||||
else:
|
||||
t = PageTemplate(headers=self.request.headers, file='login.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='login.tmpl')
|
||||
t.resp = self.get_argument('resp', '')
|
||||
self.set_status(401)
|
||||
self.finish(t.respond())
|
||||
|
@ -217,9 +220,12 @@ class LoginHandler(BaseHandler):
|
|||
password = sickbeard.WEB_PASSWORD
|
||||
|
||||
if (self.get_argument('username') == username) and (self.get_argument('password') == password):
|
||||
remember_me = int(self.get_argument('remember_me', default=0) or 0)
|
||||
params = dict(expires_days=(None, 30)[int(self.get_argument('remember_me', default=0) or 0) > 0],
|
||||
httponly=True)
|
||||
if sickbeard.ENABLE_HTTPS:
|
||||
params.update(dict(secure=True))
|
||||
self.set_secure_cookie('sickgear-session-%s' % helpers.md5_for_text(sickbeard.WEB_PORT),
|
||||
sickbeard.COOKIE_SECRET, expires_days=30 if remember_me > 0 else None)
|
||||
sickbeard.COOKIE_SECRET, **params)
|
||||
self.redirect(self.get_argument('next', '/home/'))
|
||||
else:
|
||||
next_arg = '&next=' + self.get_argument('next', '/home/')
|
||||
|
@ -405,7 +411,7 @@ class RepoHandler(BaseStaticFileHandler):
|
|||
return super(RepoHandler, self).get_content_type()
|
||||
|
||||
def index(self, basepath, filelist):
|
||||
t = PageTemplate(headers=self.request.headers, file='repo_index.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='repo_index.tmpl')
|
||||
t.basepath = basepath
|
||||
t.filelist = filelist
|
||||
return t.respond()
|
||||
|
@ -469,11 +475,11 @@ class RepoHandler(BaseStaticFileHandler):
|
|||
return fh.read().strip()
|
||||
|
||||
def render_kodi_repo_addon_xml(self):
|
||||
t = PageTemplate(headers=self.request.headers, file='repo_kodi_addon.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='repo_kodi_addon.tmpl')
|
||||
return t.respond().strip()
|
||||
|
||||
def render_kodi_repo_addons_xml(self):
|
||||
t = PageTemplate(headers=self.request.headers, file='repo_kodi_addons.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='repo_kodi_addons.tmpl')
|
||||
t.watchedstate_updater_addon_xml = re.sub(
|
||||
'(?m)^([\s]*<)', r'\t\1',
|
||||
'\n'.join(self.get_watchedstate_updater_addon_xml().split('\n')[1:])) # skip xml header
|
||||
|
@ -568,7 +574,7 @@ class WebHandler(BaseHandler):
|
|||
|
||||
def page_not_found(self):
|
||||
self.set_status(404)
|
||||
t = PageTemplate(headers=self.request.headers, file='404.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='404.tmpl')
|
||||
return t.respond()
|
||||
|
||||
@authenticated
|
||||
|
@ -580,10 +586,8 @@ class WebHandler(BaseHandler):
|
|||
except:
|
||||
self.finish(self.page_not_found())
|
||||
else:
|
||||
kwargss = self.request.arguments
|
||||
for arg, value in kwargss.items():
|
||||
if len(value) == 1:
|
||||
kwargss[arg] = value[0]
|
||||
kwargss = {k: v if not (isinstance(v, list) and 1 == len(v)) else v[0]
|
||||
for k, v in self.request.arguments.iteritems() if '_xsrf' != k}
|
||||
result = method(**kwargss)
|
||||
if result:
|
||||
self.finish(result)
|
||||
|
@ -770,7 +774,7 @@ class MainHandler(WebHandler):
|
|||
|
||||
# add localtime to the dict
|
||||
cache_obj = image_cache.ImageCache()
|
||||
t = PageTemplate(headers=self.request.headers, file='episodeView.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='episodeView.tmpl')
|
||||
t.fanart = {}
|
||||
for index, item in enumerate(sql_results):
|
||||
sql_results[index]['localtime'] = sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(item['airdate'],
|
||||
|
@ -1072,7 +1076,7 @@ r.close()
|
|||
self.redirect('/history/')
|
||||
|
||||
def _genericMessage(self, subject, message):
|
||||
t = PageTemplate(headers=self.request.headers, file='genericMessage.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='genericMessage.tmpl')
|
||||
t.submenu = self.HomeMenu()
|
||||
t.subject = subject
|
||||
t.message = message
|
||||
|
@ -1136,7 +1140,7 @@ class Home(MainHandler):
|
|||
self.redirect('/home/showlistView/')
|
||||
|
||||
def showlistView(self):
|
||||
t = PageTemplate(headers=self.request.headers, file='home.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='home.tmpl')
|
||||
t.showlists = []
|
||||
index = 0
|
||||
if sickbeard.SHOWLIST_TAGVIEW == 'custom':
|
||||
|
@ -1559,7 +1563,7 @@ class Home(MainHandler):
|
|||
|
||||
def viewchanges(self):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='viewchanges.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='viewchanges.tmpl')
|
||||
|
||||
t.changelist = [{'type': 'rel', 'ver': '', 'date': 'Nothing to display at this time'}]
|
||||
url = 'https://raw.githubusercontent.com/wiki/SickGear/SickGear/sickgear/CHANGES.md'
|
||||
|
@ -1603,7 +1607,7 @@ class Home(MainHandler):
|
|||
if str(pid) != str(sickbeard.PID):
|
||||
return self.redirect('/home/')
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='restart.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='restart.tmpl')
|
||||
t.shutdown = True
|
||||
|
||||
sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN)
|
||||
|
@ -1615,7 +1619,7 @@ class Home(MainHandler):
|
|||
if str(pid) != str(sickbeard.PID):
|
||||
return self.redirect('/home/')
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='restart.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='restart.tmpl')
|
||||
t.shutdown = False
|
||||
|
||||
sickbeard.events.put(sickbeard.events.SystemEvent.RESTART)
|
||||
|
@ -1664,7 +1668,7 @@ class Home(MainHandler):
|
|||
if None is season:
|
||||
return json.dumps(response)
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='inc_displayShow.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='inc_displayShow.tmpl')
|
||||
t.show = show_obj
|
||||
|
||||
my_db = db.DBConnection()
|
||||
|
@ -1697,7 +1701,7 @@ class Home(MainHandler):
|
|||
if showObj is None:
|
||||
return self._genericMessage('Error', 'Show not in show list')
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='displayShow.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='displayShow.tmpl')
|
||||
t.submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}]
|
||||
|
||||
try:
|
||||
|
@ -2162,7 +2166,7 @@ class Home(MainHandler):
|
|||
bestQualities = []
|
||||
|
||||
if not location and not anyQualities and not bestQualities and not flatten_folders:
|
||||
t = PageTemplate(headers=self.request.headers, file='editShow.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='editShow.tmpl')
|
||||
t.submenu = self.HomeMenu()
|
||||
|
||||
t.expand_ids = all([kwargs.get('tvsrc'), kwargs.get('srcid')])
|
||||
|
@ -2666,7 +2670,7 @@ class Home(MainHandler):
|
|||
# present season DESC episode DESC on screen
|
||||
ep_obj_rename_list.reverse()
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='testRename.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='testRename.tmpl')
|
||||
t.submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}]
|
||||
t.ep_obj_list = ep_obj_rename_list
|
||||
t.show = showObj
|
||||
|
@ -2922,7 +2926,7 @@ class Home(MainHandler):
|
|||
class HomePostProcess(Home):
|
||||
def index(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='home_postprocess.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='home_postprocess.tmpl')
|
||||
t.submenu = [x for x in self.HomeMenu() if 'postprocess' not in x['path']]
|
||||
return t.respond()
|
||||
|
||||
|
@ -2979,7 +2983,7 @@ class HomePostProcess(Home):
|
|||
class NewHomeAddShows(Home):
|
||||
def index(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='home_addShows.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='home_addShows.tmpl')
|
||||
t.submenu = self.HomeMenu()
|
||||
return t.respond()
|
||||
|
||||
|
@ -3194,7 +3198,7 @@ class NewHomeAddShows(Home):
|
|||
return s
|
||||
|
||||
def massAddTable(self, rootDir=None, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='home_massAddTable.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='home_massAddTable.tmpl')
|
||||
t.submenu = self.HomeMenu()
|
||||
t.kwargs = kwargs
|
||||
|
||||
|
@ -3316,7 +3320,7 @@ class NewHomeAddShows(Home):
|
|||
self.set_header('Pragma', 'no-cache')
|
||||
self.set_header('Expires', '0')
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='home_newShow.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='home_newShow.tmpl')
|
||||
t.submenu = self.HomeMenu()
|
||||
t.enable_anime_options = True
|
||||
t.enable_default_wanted = True
|
||||
|
@ -4003,7 +4007,7 @@ class NewHomeAddShows(Home):
|
|||
Display the new show page which collects a tvdb id, folder, and extra options and
|
||||
posts them to addNewShow
|
||||
"""
|
||||
t = PageTemplate(headers=self.request.headers, file='home_browseShows.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='home_browseShows.tmpl')
|
||||
t.submenu = self.HomeMenu()
|
||||
t.browse_type = browse_type
|
||||
t.browse_title = browse_title
|
||||
|
@ -4046,7 +4050,7 @@ class NewHomeAddShows(Home):
|
|||
"""
|
||||
Prints out the page to add existing shows from a root dir
|
||||
"""
|
||||
t = PageTemplate(headers=self.request.headers, file='home_addExistingShow.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='home_addExistingShow.tmpl')
|
||||
t.submenu = self.HomeMenu()
|
||||
t.enable_anime_options = False
|
||||
t.kwargs = kwargs
|
||||
|
@ -4279,7 +4283,7 @@ class Manage(MainHandler):
|
|||
return [x for x in menu if exclude not in x['title']]
|
||||
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='manage.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage.tmpl')
|
||||
t.submenu = self.ManageMenu('Bulk')
|
||||
return t.respond()
|
||||
|
||||
|
@ -4325,7 +4329,7 @@ class Manage(MainHandler):
|
|||
else:
|
||||
status_list = []
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='manage_episodeStatuses.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage_episodeStatuses.tmpl')
|
||||
t.submenu = self.ManageMenu('Episode')
|
||||
t.whichStatus = whichStatus
|
||||
|
||||
|
@ -4451,7 +4455,7 @@ class Manage(MainHandler):
|
|||
|
||||
def subtitleMissed(self, whichSubs=None):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='manage_subtitleMissed.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage_subtitleMissed.tmpl')
|
||||
t.submenu = self.ManageMenu('Subtitle')
|
||||
t.whichSubs = whichSubs
|
||||
|
||||
|
@ -4533,7 +4537,7 @@ class Manage(MainHandler):
|
|||
|
||||
def backlogOverview(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='manage_backlogOverview.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage_backlogOverview.tmpl')
|
||||
t.submenu = self.ManageMenu('Backlog')
|
||||
|
||||
showCounts = {}
|
||||
|
@ -4576,7 +4580,7 @@ class Manage(MainHandler):
|
|||
|
||||
def massEdit(self, toEdit=None):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='manage_massEdit.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage_massEdit.tmpl')
|
||||
t.submenu = self.ManageMenu()
|
||||
|
||||
if not toEdit:
|
||||
|
@ -4963,7 +4967,7 @@ class Manage(MainHandler):
|
|||
if toRemove:
|
||||
return self.redirect('/manage/failedDownloads/')
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='manage_failedDownloads.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage_failedDownloads.tmpl')
|
||||
t.over_limit = limit and len(sql_results) > limit
|
||||
t.failedResults = t.over_limit and sql_results[0:-1] or sql_results
|
||||
t.limit = str(limit)
|
||||
|
@ -4974,7 +4978,7 @@ class Manage(MainHandler):
|
|||
|
||||
class ManageSearches(Manage):
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='manage_manageSearches.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage_manageSearches.tmpl')
|
||||
# t.backlog_pi = sickbeard.backlogSearchScheduler.action.get_progress_indicator()
|
||||
t.backlog_paused = sickbeard.searchQueueScheduler.action.is_backlog_paused()
|
||||
t.backlog_running = sickbeard.searchQueueScheduler.action.is_backlog_in_progress()
|
||||
|
@ -5051,7 +5055,7 @@ class ManageSearches(Manage):
|
|||
|
||||
class showProcesses(Manage):
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='manage_showProcesses.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='manage_showProcesses.tmpl')
|
||||
t.queue_length = sickbeard.showQueueScheduler.action.queue_length()
|
||||
t.show_list = sickbeard.showList
|
||||
t.show_update_running = sickbeard.showQueueScheduler.action.isShowUpdateRunning() or sickbeard.showUpdateScheduler.action.amActive
|
||||
|
@ -5117,7 +5121,7 @@ class History(MainHandler):
|
|||
|
||||
def index(self, limit=100):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='history.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='history.tmpl')
|
||||
t.limit = limit
|
||||
|
||||
my_db = db.DBConnection(row_type='dict')
|
||||
|
@ -5570,7 +5574,7 @@ class Config(MainHandler):
|
|||
return [x for x in menu if exclude not in x['title']]
|
||||
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='config.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config.tmpl')
|
||||
t.submenu = self.ConfigMenu()
|
||||
|
||||
return t.respond()
|
||||
|
@ -5579,11 +5583,12 @@ class Config(MainHandler):
|
|||
class ConfigGeneral(Config):
|
||||
def index(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='config_general.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config_general.tmpl')
|
||||
t.submenu = self.ConfigMenu('General')
|
||||
t.show_tags = ', '.join(sickbeard.SHOW_TAGS)
|
||||
t.indexers = dict([(i, sickbeard.indexerApi().indexers[i]) for i in sickbeard.indexerApi().indexers
|
||||
if sickbeard.indexerApi(i).config['active']])
|
||||
t.request_host = escape.xhtml_escape(self.request.host_name)
|
||||
return t.respond()
|
||||
|
||||
def saveRootDirs(self, rootDirString=None):
|
||||
|
@ -5649,7 +5654,8 @@ class ConfigGeneral(Config):
|
|||
trash_remove_show=None, trash_rotate_logs=None, update_frequency=None, launch_browser=None, web_username=None,
|
||||
use_api=None, api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None, file_logging_preset=None,
|
||||
web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None,
|
||||
handle_reverse_proxy=None, send_security_headers=None, home_search_focus=None, display_freespace=None, sort_article=None, auto_update=None, notify_on_update=None,
|
||||
handle_reverse_proxy=None, send_security_headers=None, allowed_hosts=None,
|
||||
home_search_focus=None, display_freespace=None, sort_article=None, auto_update=None, notify_on_update=None,
|
||||
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None,
|
||||
fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
|
||||
indexer_timeout=None, rootDir=None, theme_name=None, default_home=None, use_imdb_info=None,
|
||||
|
@ -5717,6 +5723,7 @@ class ConfigGeneral(Config):
|
|||
sickbeard.TIMEZONE_DISPLAY = timezone_display
|
||||
|
||||
# Web interface
|
||||
restart = False
|
||||
reload_page = False
|
||||
if sickbeard.WEB_USERNAME != web_username:
|
||||
sickbeard.WEB_USERNAME = web_username
|
||||
|
@ -5731,6 +5738,7 @@ class ConfigGeneral(Config):
|
|||
sickbeard.WEB_PORT = config.to_int(web_port)
|
||||
# sickbeard.WEB_LOG is set in config.change_log_dir()
|
||||
|
||||
restart |= sickbeard.ENABLE_HTTPS != config.checkbox_to_value(enable_https)
|
||||
sickbeard.ENABLE_HTTPS = config.checkbox_to_value(enable_https)
|
||||
if not config.change_https_cert(https_cert):
|
||||
results += [
|
||||
|
@ -5743,6 +5751,10 @@ class ConfigGeneral(Config):
|
|||
sickbeard.WEB_IPV64 = config.checkbox_to_value(web_ipv64)
|
||||
sickbeard.HANDLE_REVERSE_PROXY = config.checkbox_to_value(handle_reverse_proxy)
|
||||
sickbeard.SEND_SECURITY_HEADERS = config.checkbox_to_value(send_security_headers)
|
||||
hosts = ','.join(filter(lambda name: not helpers.re_valid_hostname(with_allowed=False).match(name),
|
||||
config.clean_hosts(allowed_hosts).split(',')))
|
||||
if not hosts or self.request.host_name in hosts:
|
||||
sickbeard.ALLOWED_HOSTS = hosts
|
||||
|
||||
# Advanced
|
||||
sickbeard.GIT_REMOTE = git_remote
|
||||
|
@ -5767,6 +5779,11 @@ class ConfigGeneral(Config):
|
|||
else:
|
||||
ui.notifications.message('Configuration Saved', ek.ek(os.path.join, sickbeard.CONFIG_FILE))
|
||||
|
||||
if restart:
|
||||
self.clear_cookie('sickgear-session-%s' % helpers.md5_for_text(sickbeard.WEB_PORT))
|
||||
self.write('restart')
|
||||
reload_page = False
|
||||
|
||||
if reload_page:
|
||||
self.clear_cookie('sickgear-session-%s' % helpers.md5_for_text(sickbeard.WEB_PORT))
|
||||
self.write('reload')
|
||||
|
@ -5796,7 +5813,7 @@ class ConfigGeneral(Config):
|
|||
class ConfigSearch(Config):
|
||||
def index(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='config_search.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config_search.tmpl')
|
||||
t.submenu = self.ConfigMenu('Search')
|
||||
t.using_rls_ignore_words = [(show.indexerid, show.name)
|
||||
for show in sickbeard.showList if show.rls_ignore_words and
|
||||
|
@ -5908,7 +5925,7 @@ class ConfigSearch(Config):
|
|||
class ConfigPostProcessing(Config):
|
||||
def index(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='config_postProcessing.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config_postProcessing.tmpl')
|
||||
t.submenu = self.ConfigMenu('Processing')
|
||||
return t.respond()
|
||||
|
||||
|
@ -6092,7 +6109,7 @@ class ConfigPostProcessing(Config):
|
|||
|
||||
class ConfigProviders(Config):
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='config_providers.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config_providers.tmpl')
|
||||
t.submenu = self.ConfigMenu('Providers')
|
||||
return t.respond()
|
||||
|
||||
|
@ -6475,7 +6492,7 @@ class ConfigProviders(Config):
|
|||
class ConfigNotifications(Config):
|
||||
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='config_notifications.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config_notifications.tmpl')
|
||||
t.submenu = self.ConfigMenu('Notifications')
|
||||
t.root_dirs = []
|
||||
if sickbeard.ROOT_DIRS:
|
||||
|
@ -6774,7 +6791,7 @@ class ConfigNotifications(Config):
|
|||
|
||||
class ConfigSubtitles(Config):
|
||||
def index(self, *args, **kwargs):
|
||||
t = PageTemplate(headers=self.request.headers, file='config_subtitles.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config_subtitles.tmpl')
|
||||
t.submenu = self.ConfigMenu('Subtitle')
|
||||
return t.respond()
|
||||
|
||||
|
@ -6820,7 +6837,7 @@ class ConfigSubtitles(Config):
|
|||
class ConfigAnime(Config):
|
||||
def index(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='config_anime.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='config_anime.tmpl')
|
||||
t.submenu = self.ConfigMenu('Anime')
|
||||
return t.respond()
|
||||
|
||||
|
@ -6878,7 +6895,7 @@ class ErrorLogs(MainHandler):
|
|||
|
||||
def index(self, *args, **kwargs):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='errorlogs.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='errorlogs.tmpl')
|
||||
t.submenu = self.ErrorLogsMenu
|
||||
|
||||
return t.respond()
|
||||
|
@ -6906,7 +6923,7 @@ class ErrorLogs(MainHandler):
|
|||
|
||||
def viewlog(self, min_level=logger.MESSAGE, max_lines=500):
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='viewlogs.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='viewlogs.tmpl')
|
||||
t.submenu = self.ErrorLogsMenu
|
||||
|
||||
min_level = int(min_level)
|
||||
|
@ -6989,7 +7006,7 @@ class WebFileBrowser(MainHandler):
|
|||
class ApiBuilder(MainHandler):
|
||||
def index(self):
|
||||
""" expose the api-builder template """
|
||||
t = PageTemplate(headers=self.request.headers, file='apiBuilder.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='apiBuilder.tmpl')
|
||||
|
||||
def titler(x):
|
||||
return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE]
|
||||
|
@ -7029,7 +7046,7 @@ class Cache(MainHandler):
|
|||
if not sql_results:
|
||||
sql_results = []
|
||||
|
||||
t = PageTemplate(headers=self.request.headers, file='cache.tmpl')
|
||||
t = PageTemplate(web_handler=self, file='cache.tmpl')
|
||||
t.cacheResults = sql_results
|
||||
|
||||
return t.respond()
|
||||
|
|
|
@ -6,14 +6,13 @@ import webserve
|
|||
import webapi
|
||||
|
||||
from sickbeard import logger
|
||||
from sickbeard.helpers import create_https_certificates
|
||||
from sickbeard.helpers import create_https_certificates, re_valid_hostname
|
||||
from tornado.web import Application
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
|
||||
class WebServer(threading.Thread):
|
||||
def __init__(self, options={}, **kwargs):
|
||||
def __init__(self, options=None):
|
||||
threading.Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.alive = True
|
||||
|
@ -21,7 +20,7 @@ class WebServer(threading.Thread):
|
|||
self.io_loop = None
|
||||
self.server = None
|
||||
|
||||
self.options = options
|
||||
self.options = options or {}
|
||||
self.options.setdefault('port', 8081)
|
||||
self.options.setdefault('host', '0.0.0.0')
|
||||
self.options.setdefault('log_dir', None)
|
||||
|
@ -40,40 +39,60 @@ class WebServer(threading.Thread):
|
|||
self.https_key = self.options['https_key']
|
||||
|
||||
if self.enable_https:
|
||||
make_cert = False
|
||||
update_cfg = False
|
||||
for (attr, ext) in [('https_cert', '.crt'), ('https_key', '.key')]:
|
||||
ssl_path = getattr(self, attr, None)
|
||||
if ssl_path and not os.path.isfile(ssl_path):
|
||||
if not ssl_path.endswith(ext):
|
||||
setattr(self, attr, os.path.join(ssl_path, 'server%s' % ext))
|
||||
setattr(sickbeard, attr.upper(), 'server%s' % ext)
|
||||
make_cert = True
|
||||
|
||||
# If either the HTTPS certificate or key do not exist, make some self-signed ones.
|
||||
if not (self.https_cert and os.path.exists(self.https_cert))\
|
||||
or not (self.https_key and os.path.exists(self.https_key)):
|
||||
if make_cert:
|
||||
if not create_https_certificates(self.https_cert, self.https_key):
|
||||
logger.log(u'Unable to create CERT/KEY files, disabling HTTPS')
|
||||
update_cfg |= False is not sickbeard.ENABLE_HTTPS
|
||||
sickbeard.ENABLE_HTTPS = False
|
||||
self.enable_https = False
|
||||
else:
|
||||
update_cfg = True
|
||||
|
||||
if not (os.path.exists(self.https_cert) and os.path.exists(self.https_key)):
|
||||
if not (os.path.isfile(self.https_cert) and os.path.isfile(self.https_key)):
|
||||
logger.log(u'Disabled HTTPS because of missing CERT and KEY files', logger.WARNING)
|
||||
update_cfg |= False is not sickbeard.ENABLE_HTTPS
|
||||
sickbeard.ENABLE_HTTPS = False
|
||||
self.enable_https = False
|
||||
|
||||
if update_cfg:
|
||||
sickbeard.save_config()
|
||||
|
||||
# Load the app
|
||||
self.app = Application([],
|
||||
debug=False,
|
||||
serve_traceback=True,
|
||||
autoreload=False,
|
||||
gzip=True,
|
||||
compress_response=True,
|
||||
cookie_secret=sickbeard.COOKIE_SECRET,
|
||||
xsrf_cookies=True,
|
||||
login_url='%s/login/' % self.options['web_root'])
|
||||
|
||||
re_host_pattern = re_valid_hostname()
|
||||
|
||||
# webui login/logout handlers
|
||||
self.app.add_handlers('.*$', [
|
||||
self.app.add_handlers(re_host_pattern, [
|
||||
(r'%s/login(/?)' % self.options['web_root'], webserve.LoginHandler),
|
||||
(r'%s/logout(/?)' % self.options['web_root'], webserve.LogoutHandler),
|
||||
])
|
||||
|
||||
# Web calendar handler (Needed because option Unprotected calendar)
|
||||
self.app.add_handlers('.*$', [
|
||||
self.app.add_handlers(re_host_pattern, [
|
||||
(r'%s/calendar' % self.options['web_root'], webserve.CalendarHandler),
|
||||
])
|
||||
|
||||
# Static File Handlers
|
||||
self.app.add_handlers('.*$', [
|
||||
self.app.add_handlers(re_host_pattern, [
|
||||
# favicon
|
||||
(r'%s/(favicon\.ico)' % self.options['web_root'], webserve.BaseStaticFileHandler,
|
||||
{'path': os.path.join(self.options['data_root'], 'images/ico/favicon.ico')}),
|
||||
|
@ -100,7 +119,7 @@ class WebServer(threading.Thread):
|
|||
])
|
||||
|
||||
# Main Handler
|
||||
self.app.add_handlers('.*$', [
|
||||
self.app.add_handlers(re_host_pattern, [
|
||||
(r'%s/api/builder(/?)(.*)' % self.options['web_root'], webserve.ApiBuilder),
|
||||
(r'%s/api(/?.*)' % self.options['web_root'], webapi.Api),
|
||||
(r'%s/imagecache(/?.*)' % self.options['web_root'], webserve.CachedImages),
|
||||
|
@ -153,7 +172,7 @@ class WebServer(threading.Thread):
|
|||
# Ignore errors like 'ValueError: I/O operation on closed kqueue fd'. These might be thrown during a reload.
|
||||
pass
|
||||
|
||||
def shutDown(self):
|
||||
def shut_down(self):
|
||||
self.alive = False
|
||||
if None is not self.io_loop:
|
||||
self.io_loop.stop()
|
||||
|
|
Loading…
Reference in a new issue