mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-06 01:53:37 +00:00
Merge pull request #1080 from JackDandy/feature/ChangeMoreSecurity
Change improve security.
This commit is contained in:
commit
dfdc5caec4
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