1
0
mirror of https://github.com/moparisthebest/SickRage synced 2024-12-12 11:02:21 -05:00

Merge remote-tracking branch 'origin/develop' into develop

Conflicts:
	sickbeard/webapi.py
This commit is contained in:
echel0n 2014-12-07 13:00:18 -08:00
commit e7de90f688
7 changed files with 320 additions and 358 deletions

View File

@ -45,7 +45,7 @@ function goListGroup(apikey, L7, L6, L5, L4, L3, L2, L1){
html += $.ajax({ html += $.ajax({
url: sbRoot + "/api/" + apikey + "/" + L1 + L2 + L3 + L4 + L5 + L6 + L7 + GlobalOptions, url: sbRoot + "/api/" + apikey + "/" + L1 + L2 + L3 + L4 + L5 + L6 + L7 + GlobalOptions,
async: false, async: false,
dataType: "html", dataType: "html"
}).responseText; }).responseText;
html += '</pre>'; html += '</pre>';

View File

@ -58,7 +58,7 @@ $(document).ready(function(){
$('#api_key').click(function(){ $('#api_key').select() }); $('#api_key').click(function(){ $('#api_key').select() });
$("#generate_new_apikey").click(function(){ $("#generate_new_apikey").click(function(){
$.get(sbRoot + '/config/general/generateKey', $.get(sbRoot + '/config/general/generateApiKey',
function(data){ function(data){
if (data.error != undefined) { if (data.error != undefined) {
alert(data.error); alert(data.error);

View File

@ -145,6 +145,7 @@ ANON_REDIRECT = None
USE_API = False USE_API = False
API_KEY = None API_KEY = None
API_ROOT = None
ENABLE_HTTPS = False ENABLE_HTTPS = False
HTTPS_CERT = None HTTPS_CERT = None
@ -480,7 +481,7 @@ def get_backlog_cycle_time():
def initialize(consoleLogging=True): def initialize(consoleLogging=True):
with INIT_LOCK: with INIT_LOCK:
global BRANCH, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, USE_API, API_KEY, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \ global BRANCH, GIT_REMOTE, GIT_REMOTE_URL, CUR_COMMIT_HASH, CUR_COMMIT_BRANCH, ACTUAL_LOG_DIR, LOG_DIR, WEB_PORT, WEB_LOG, ENCRYPTION_VERSION, WEB_ROOT, WEB_USERNAME, WEB_PASSWORD, WEB_HOST, WEB_IPV6, USE_API, API_KEY, API_ROOT, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \
HANDLE_REVERSE_PROXY, USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, RANDOMIZE_PROVIDERS, CHECK_PROPERS_INTERVAL, ALLOW_HIGH_PRIORITY, TORRENT_METHOD, \ HANDLE_REVERSE_PROXY, USE_NZBS, USE_TORRENTS, NZB_METHOD, NZB_DIR, DOWNLOAD_PROPERS, RANDOMIZE_PROVIDERS, CHECK_PROPERS_INTERVAL, ALLOW_HIGH_PRIORITY, TORRENT_METHOD, \
SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_CATEGORY_ANIME, SAB_HOST, \ SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, SAB_CATEGORY_ANIME, SAB_HOST, \
NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_CATEGORY_ANIME, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \ NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_CATEGORY_ANIME, NZBGET_PRIORITY, NZBGET_HOST, NZBGET_USE_HTTPS, backlogSearchScheduler, \

View File

@ -20,6 +20,7 @@ from __future__ import with_statement
import getpass import getpass
import os import os
import random
import re import re
import shutil import shutil
import socket import socket
@ -1433,3 +1434,27 @@ def get_size(start_path='.'):
def md5(text): def md5(text):
return hashlib.md5(ek.ss(text)).hexdigest() return hashlib.md5(ek.ss(text)).hexdigest()
def generateApiKey(*args, **kwargs):
""" Return a new randomized API_KEY
"""
try:
from hashlib import md5
except ImportError:
from md5 import md5
# Create some values to seed md5
t = str(time.time())
r = str(random.random())
# Create the md5 instance and give it the current time
m = md5(t)
# Update the md5 instance with the random variable
m.update(r)
# Return a hex digest of the md5, eg 49f68a5c8493ec2c0bf489821c21fc3b
logger.log(u"New API generated")
return m.hexdigest()

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
# along with SickRage. If not, see <http://www.gnu.org/licenses/>. # along with SickRage. If not, see <http://www.gnu.org/licenses/>.
from __future__ import with_statement from __future__ import with_statement
import random
import traceback import traceback
import os import os
@ -24,7 +25,6 @@ import time
import urllib import urllib
import re import re
import datetime import datetime
import random
import sys import sys
import sickbeard import sickbeard
@ -133,7 +133,7 @@ def haveTORRENT():
return False return False
class Menus: class Menus:
def HomeMenu(self): def HomeMenu(self, *args, **kwargs):
menu = [ menu = [
{'title': 'Add Shows', 'path': 'home/addShows/', }, {'title': 'Add Shows', 'path': 'home/addShows/', },
{'title': 'Manual Post-Processing', 'path': 'home/postprocess/'}, {'title': 'Manual Post-Processing', 'path': 'home/postprocess/'},
@ -146,7 +146,7 @@ class Menus:
return menu return menu
def ConfigMenu(self): def ConfigMenu(self, *args, **kwargs):
menu = [ menu = [
{'title': 'General', 'path': 'config/general/'}, {'title': 'General', 'path': 'config/general/'},
{'title': 'Backup/Restore', 'path': 'config/backuprestore/'}, {'title': 'Backup/Restore', 'path': 'config/backuprestore/'},
@ -160,7 +160,7 @@ class Menus:
return menu return menu
def ManageMenu(self): def ManageMenu(self, *args, **kwargs):
menu = [ menu = [
{'title': 'Backlog Overview', 'path': 'manage/backlogOverview/'}, {'title': 'Backlog Overview', 'path': 'manage/backlogOverview/'},
{'title': 'Manage Searches', 'path': 'manage/manageSearches/'}, {'title': 'Manage Searches', 'path': 'manage/manageSearches/'},
@ -179,7 +179,7 @@ class Menus:
return menu return menu
def ErrorLogsMenu(self): def ErrorLogsMenu(self, *args, **kwargs):
menu = [ menu = [
{'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'}, {'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'},
# { 'title': 'View Log', 'path': 'errorlogs/viewlog' }, # { 'title': 'View Log', 'path': 'errorlogs/viewlog' },
@ -236,7 +236,7 @@ class PageTemplate(Template):
return super(PageTemplate, self).compile(*args, **kwargs) return super(PageTemplate, self).compile(*args, **kwargs)
class BaseHandler(RequestHandler): class BaseHandler(RequestHandler):
def get_current_user(self): def get_current_user(self, *args, **kwargs):
if sickbeard.WEB_USERNAME and sickbeard.WEB_PASSWORD: if sickbeard.WEB_USERNAME and sickbeard.WEB_PASSWORD:
return self.get_secure_cookie('user') return self.get_secure_cookie('user')
else: else:
@ -249,7 +249,6 @@ class BaseHandler(RequestHandler):
t.message = message t.message = message
return t return t
# Make non basic auth option to get api key
class KeyHandler(RequestHandler): class KeyHandler(RequestHandler):
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
api_key = None api_key = None
@ -258,8 +257,8 @@ class KeyHandler(RequestHandler):
username = sickbeard.WEB_USERNAME username = sickbeard.WEB_USERNAME
password = sickbeard.WEB_PASSWORD password = sickbeard.WEB_PASSWORD
if (self.get_argument('u') == sickbeard.helpers.md5(username) or not username) \ if (self.get_argument('u', None) == username or not username) \
and (self.get_argument('p') == password or not password): and (self.get_argument('p', None) == password or not password):
api_key = sickbeard.API_KEY api_key = sickbeard.API_KEY
self.write({ self.write({
@ -270,24 +269,6 @@ class KeyHandler(RequestHandler):
logger.log('Failed doing key request: %s' % (traceback.format_exc()), logger.ERROR) logger.log('Failed doing key request: %s' % (traceback.format_exc()), logger.ERROR)
self.write({'success': False, 'error': 'Failed returning results'}) self.write({'success': False, 'error': 'Failed returning results'})
class UIHandler(RequestHandler):
def add_message(self):
ui.notifications.message('Test 1', 'This is test number 1')
ui.notifications.error('Test 2', 'This is test number 2')
return "ok"
def get_messages(self):
messages = {}
cur_notification_num = 1
for cur_notification in ui.notifications.get_notifications(self.request.remote_ip):
messages['notification-' + str(cur_notification_num)] = {'title': cur_notification.title,
'message': cur_notification.message,
'type': cur_notification.type}
cur_notification_num += 1
return json.dumps(messages)
class WebHandler(BaseHandler): class WebHandler(BaseHandler):
def write_error(self, status_code, **kwargs): def write_error(self, status_code, **kwargs):
if status_code == 404: if status_code == 404:
@ -323,7 +304,7 @@ class WebHandler(BaseHandler):
self.write('Could not load webui module') self.write('Could not load webui module')
return return
except: except:
page_not_found(self) page_not_found(self, *args, **kwargs)
return return
try: try:
@ -377,14 +358,53 @@ class LogoutHandler(BaseHandler):
@route('(.*)(/?)') @route('(.*)(/?)')
class WebRoot(WebHandler): class WebRoot(WebHandler):
def index(self): def index(self, *args, **kwargs):
self.redirect('/home/') self.redirect('/home/')
def robots_txt(self): def robots_txt(self, *args, **kwargs):
""" Keep web crawlers out """ """ Keep web crawlers out """
self.set_header('Content-Type', 'text/plain') self.set_header('Content-Type', 'text/plain')
return "User-agent: *\nDisallow: /" return "User-agent: *\nDisallow: /"
def apibuilder(self, *args, **kwargs):
t = PageTemplate(rh=self, file="apiBuilder.tmpl")
def titler(x):
if not x or sickbeard.SORT_ARTICLE:
return x
if x.lower().startswith('a '):
x = x[2:]
elif x.lower().startswith('an '):
x = x[3:]
elif x.lower().startswith('the '):
x = x[4:]
return x
t.sortedShowList = sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))
seasonSQLResults = {}
episodeSQLResults = {}
myDB = db.DBConnection(row_type="dict")
for curShow in t.sortedShowList:
seasonSQLResults[curShow.indexerid] = myDB.select(
"SELECT DISTINCT season FROM tv_episodes WHERE showid = ? ORDER BY season DESC", [curShow.indexerid])
for curShow in t.sortedShowList:
episodeSQLResults[curShow.indexerid] = myDB.select(
"SELECT DISTINCT season,episode FROM tv_episodes WHERE showid = ? ORDER BY season DESC, episode DESC",
[curShow.indexerid])
t.seasonSQLResults = seasonSQLResults
t.episodeSQLResults = episodeSQLResults
if len(sickbeard.API_KEY) == 32:
t.apikey = sickbeard.API_KEY
else:
t.apikey = "api key not generated"
return t
def showPoster(self, show=None, which=None): def showPoster(self, show=None, which=None):
# Redirect initial poster/banner thumb to default images # Redirect initial poster/banner thumb to default images
if which[0:6] == 'poster': if which[0:6] == 'poster':
@ -630,9 +650,28 @@ class WebRoot(WebHandler):
return ical return ical
@route('/ui/(.*)(/?)')
class UIHandler(WebRoot):
def add_message(self, *args, **kwargs):
ui.notifications.message('Test 1', 'This is test number 1')
ui.notifications.error('Test 2', 'This is test number 2')
return "ok"
def get_messages(self, *args, **kwargs):
messages = {}
cur_notification_num = 1
for cur_notification in ui.notifications.get_notifications(self.request.remote_ip):
messages['notification-' + str(cur_notification_num)] = {'title': cur_notification.title,
'message': cur_notification.message,
'type': cur_notification.type}
cur_notification_num += 1
return json.dumps(messages)
@route('/home/(.*)(/?)') @route('/home/(.*)(/?)')
class Home(WebRoot): class Home(WebRoot):
def index(self): def index(self, *args, **kwargs):
t = PageTemplate(rh=self, file="home.tmpl") t = PageTemplate(rh=self, file="home.tmpl")
if sickbeard.ANIME_SPLIT_HOME: if sickbeard.ANIME_SPLIT_HOME:
shows = [] shows = []
@ -3293,6 +3332,10 @@ class ConfigGeneral(Config):
t.submenu = Menus().ConfigMenu() t.submenu = Menus().ConfigMenu()
return t return t
def generateApiKey(self, *args, **kwargs):
return helpers.generateApiKey()
def saveRootDirs(self, rootDirString=None): def saveRootDirs(self, rootDirString=None):
sickbeard.ROOT_DIRS = rootDirString sickbeard.ROOT_DIRS = rootDirString
@ -3322,31 +3365,6 @@ class ConfigGeneral(Config):
sickbeard.save_config() sickbeard.save_config()
def generateKey(self, *args, **kwargs):
""" Return a new randomized API_KEY
"""
try:
from hashlib import md5
except ImportError:
from md5 import md5
# Create some values to seed md5
t = str(time.time())
r = str(random.random())
# Create the md5 instance and give it the current time
m = md5(t)
# Update the md5 instance with the random variable
m.update(r)
# Return a hex digest of the md5, eg 49f68a5c8493ec2c0bf489821c21fc3b
logger.log(u"New API generated")
return m.hexdigest()
def saveGeneral(self, log_dir=None, web_port=None, web_log=None, encryption_version=None, web_ipv6=None, def saveGeneral(self, log_dir=None, web_port=None, web_log=None, encryption_version=None, web_ipv6=None,
update_shows_on_start=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None, update_shows_on_start=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None,
launch_browser=None, web_username=None, launch_browser=None, web_username=None,
@ -4523,7 +4541,7 @@ class ConfigAnime(Config):
@route('/errorlogs/(.*)(/?)') @route('/errorlogs/(.*)(/?)')
class ErrorLogs(WebRoot): class ErrorLogs(WebRoot):
def index(self): def index(self, *args, **kwargs):
t = PageTemplate(rh=self, file="errorlogs.tmpl") t = PageTemplate(rh=self, file="errorlogs.tmpl")
t.submenu = Menus().ErrorLogsMenu() t.submenu = Menus().ErrorLogsMenu()

View File

@ -1,16 +1,13 @@
import os import os
import socket
import time
import threading import threading
import sys import sys
import sickbeard import sickbeard
import webserve
import webapi
from sickbeard.webserve import LoginHandler, LogoutHandler from sickbeard.webserve import LoginHandler, LogoutHandler, KeyHandler
from sickbeard.webapi import ApiHandler
from sickbeard import logger from sickbeard import logger
from sickbeard.helpers import create_https_certificates from sickbeard.helpers import create_https_certificates, generateApiKey
from tornado.web import Application, StaticFileHandler, HTTPError from tornado.web import Application, StaticFileHandler, HTTPError, RedirectHandler
from tornado.httpserver import HTTPServer from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.routes import route from tornado.routes import route
@ -63,8 +60,13 @@ class SRWebServer(threading.Thread):
self.video_root = None self.video_root = None
# web root # web root
self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/')) if self.options['web_root'] else '/' self.options['web_root'] = ('/' + self.options['web_root'].lstrip('/')).strip('/')
sickbeard.WEB_ROOT = self.options['web_root'].strip('/') sickbeard.WEB_ROOT = self.options['web_root']
# api root
if not sickbeard.API_KEY:
sickbeard.API_KEY = generateApiKey()
self.options['api_root'] = r'%s/api/%s/' % (sickbeard.WEB_ROOT, sickbeard.API_KEY)
# tornado setup # tornado setup
self.enable_https = self.options['enable_https'] self.enable_https = self.options['enable_https']
@ -97,20 +99,23 @@ class SRWebServer(threading.Thread):
# Main Handlers # Main Handlers
self.app.add_handlers(".*$", [ self.app.add_handlers(".*$", [
(r'%slogin(/?)' % self.options['web_root'], LoginHandler), (r'%s(/?)' % self.options['api_root'], ApiHandler),
(r'%slogout(/?)' % self.options['web_root'], LogoutHandler) (r'%s/getkey(/?)' % self.options['web_root'], KeyHandler),
(r'%s/api/builder' % self.options['web_root'], RedirectHandler, {"url": self.options['web_root'] + '/apibuilder/'}),
(r'%s/login(/?)' % self.options['web_root'], LoginHandler),
(r'%s/logout(/?)' % self.options['web_root'], LogoutHandler)
] + route.get_routes()) ] + route.get_routes())
# Static Path Handlers # Static Path Handlers
self.app.add_handlers(".*$", [ self.app.add_handlers(".*$", [
(r'%s(favicon\.ico)' % self.options['web_root'], MultiStaticFileHandler, (r'%s/(favicon\.ico)' % self.options['web_root'], MultiStaticFileHandler,
{'paths': [os.path.join(self.options['data_root'], 'images/ico/favicon.ico')]}), {'paths': [os.path.join(self.options['data_root'], 'images/ico/favicon.ico')]}),
(r'%s%s/(.*)(/?)' % (self.options['web_root'], 'images'), MultiStaticFileHandler, (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'images'), MultiStaticFileHandler,
{'paths': [os.path.join(self.options['data_root'], 'images'), {'paths': [os.path.join(self.options['data_root'], 'images'),
os.path.join(sickbeard.CACHE_DIR, 'images')]}), os.path.join(sickbeard.CACHE_DIR, 'images')]}),
(r'%s%s/(.*)(/?)' % (self.options['web_root'], 'css'), MultiStaticFileHandler, (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'css'), MultiStaticFileHandler,
{'paths': [os.path.join(self.options['data_root'], 'css')]}), {'paths': [os.path.join(self.options['data_root'], 'css')]}),
(r'%s%s/(.*)(/?)' % (self.options['web_root'], 'js'), MultiStaticFileHandler, (r'%s/%s/(.*)(/?)' % (self.options['web_root'], 'js'), MultiStaticFileHandler,
{'paths': [os.path.join(self.options['data_root'], 'js')]}), {'paths': [os.path.join(self.options['data_root'], 'js')]}),
]) ])