Browse Source

Remove tapi and replace by api

pull/1468/head
Safihre 5 years ago
parent
commit
51c9da73fe
  1. 2
      interfaces/Config/templates/_inc_header_uc.tmpl
  2. 6
      interfaces/Config/templates/config_general.tmpl
  3. 2
      interfaces/Config/templates/config_notify.tmpl
  4. 2
      interfaces/Config/templates/config_server.tmpl
  5. 6
      interfaces/Config/templates/config_sorting.tmpl
  6. 2
      interfaces/Config/templates/config_switches.tmpl
  7. 2
      interfaces/Glitter/templates/static/javascripts/glitter.basic.js
  8. 2
      interfaces/Glitter/templates/static/javascripts/glitter.main.js
  9. 2
      interfaces/Plush/templates/_inc_modals.tmpl
  10. 6
      interfaces/Plush/templates/static/javascripts/config.js
  11. 64
      interfaces/Plush/templates/static/javascripts/plush.js
  12. 2
      interfaces/wizard/static/javascript/checkserver.js
  13. 25
      sabnzbd/api.py
  14. 268
      sabnzbd/interface.py
  15. 2
      tests/testhelper.py

2
interfaces/Config/templates/_inc_header_uc.tmpl

@ -46,7 +46,7 @@
var sabSession = '$session';
var rootURL = '${root}'
var urlBase = '${url_base}'
var folderBrowseUrl = '${root}tapi?mode=browse&output=json&apikey=$session';
var folderBrowseUrl = '${root}api?mode=browse&output=json&apikey=$session';
var folderSeperator = '#if $os.sep == '\\' then '\\\\' else '/'#'
// Translations

6
interfaces/Config/templates/config_general.tmpl

@ -289,7 +289,7 @@
if (confirm("$T('Plush-confirm')")) {
$.ajax({
type: "POST",
url: "../../tapi",
url: "../../api",
data: {mode:'config', name:'set_apikey', apikey: \$('#apikey').val()},
success: function(msg){
\$('#apikey').val(msg);
@ -302,7 +302,7 @@
if (confirm("$T('Plush-confirm')")) {
$.ajax({
type: "POST",
url: "../../tapi",
url: "../../api",
data: { mode:'config', name:'set_nzbkey', apikey: \$('#apikey').val() },
success: function(msg){
\$('#nzbkey').val(msg);
@ -334,7 +334,7 @@
// Submit request and then restart
$.ajax({
type: "POST",
url: "../../tapi",
url: "../../api",
data: { mode: 'config', name: 'regenerate_certs', apikey: \$('#apikey').val() },
success: function(msg) {
do_restart()

2
interfaces/Config/templates/config_notify.tmpl

@ -405,7 +405,7 @@
// Get the request
\$.ajax({
type: "GET",
url: "../../tapi",
url: "../../api",
data: data
}).then(function(data) {
// Remove disabled and make the box

2
interfaces/Config/templates/config_server.tmpl

@ -491,7 +491,7 @@
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
\$.ajax({
type: "POST",
url: "../../tapi",
url: "../../api",
data: "mode=config&output=json&name=test_server&" + \$(this).parents('form:first').serialize()
}).then(function(data) {
// Let's replace the link

6
interfaces/Config/templates/config_sorting.tmpl

@ -475,7 +475,7 @@
\$('#previewtv-result').addClass("loading");
\$.ajax({
type: "GET",
url: "../../tapi",
url: "../../api",
data: {mode:'eval_sort', value: 'series', name: \$('#tvsamplename').val(), title: \$tvsortstring, apikey: '$session', output: 'json' },
success: function(data){
\$('#previewtv-result').removeClass("loading failure").html(data.result);
@ -496,7 +496,7 @@
\$('#previewmovie-result').addClass("loading");
\$.ajax({
type: "GET",
url: "../../tapi",
url: "../../api",
data: {mode:'eval_sort', value: 'movie', name: \$('#moviesamplename').val(), title: \$moviesortstring, movieextra: \$('#movieextra').val(), apikey: '$session', output: 'json' },
success: function(data){
\$('#previewmovie-result').removeClass("loading failure").html(data.result);
@ -517,7 +517,7 @@
\$('#previewdate-result').addClass("loading");
\$.ajax({
type: "GET",
url: "../../tapi",
url: "../../api",
data: {mode:'eval_sort', value: 'date', name: \$('#datesamplename').val(), title: \$datesortstring, apikey: '$session', output: 'json' },
success: function(data){
\$('#previewdate-result').removeClass("loading failure").html(data.result);

2
interfaces/Config/templates/config_switches.tmpl

@ -535,7 +535,7 @@
// Send request
\$.ajax({
type: "GET",
url: "../../tapi",
url: "../../api",
data: "mode=set_config_default&session=${session}&output=json&keyword=" + key_container.join('&keyword=')
}).then(function(data) {
// Reload page

2
interfaces/Glitter/templates/static/javascripts/glitter.basic.js

@ -30,7 +30,7 @@ function callAPI(data) {
data.output = "json";
data.apikey = apiKey;
var ajaxQuery = $.ajax({
url: "./tapi",
url: "./api",
type: "GET",
cache: false,
data: data,

2
interfaces/Glitter/templates/static/javascripts/glitter.main.js

@ -717,7 +717,7 @@ function ViewModel() {
// Add this one
$.ajax({
url: "./tapi",
url: "./api",
type: "POST",
cache: false,
processData: false,

2
interfaces/Plush/templates/_inc_modals.tmpl

@ -74,7 +74,7 @@ $T('Plush-containerWidth'):
<input type="text" id="addID_input" size="31" />
<input type="submit" id="addID" value="$T('Plush-fetch')" class="juiButton" />
<div id="add_nzb_hr"><hr></div>
<form action="tapi" id="uploadNZBForm" method="post" enctype="multipart/form-data">
<form action="api" id="uploadNZBForm" method="post" enctype="multipart/form-data">
<input type="hidden" name="apikey" value="$session" />
<input type="hidden" name="mode" value="addfile" />
<input type="file" id="uploadNZBFile" name="name" size="12" style="width:180px;" />

6
interfaces/Plush/templates/static/javascripts/config.js

@ -134,7 +134,7 @@ jQuery(document).ready(function($){
if (confirm($(this).attr('rel'))) {
$.ajax({
type: "POST",
url: "../../tapi",
url: "../../api",
data: {mode:'config', name:'set_apikey', apikey: $('#apikey').val()},
success: function(msg){
$('#apikey,#session').val(msg);
@ -147,7 +147,7 @@ jQuery(document).ready(function($){
if (confirm($(this).attr('rel'))) {
$.ajax({
type: "POST",
url: "../../tapi",
url: "../../api",
data: {mode:'config', name:'set_nzbkey', apikey: $('#apikey').val()},
success: function(msg){
$('#nzbkey,#session').val(msg);
@ -166,7 +166,7 @@ jQuery(document).ready(function($){
$(event.target).next('span').addClass('loading');
$.ajax({
type: "POST",
url: "../../tapi",
url: "../../api",
data: "mode=config&name=test_server&"+ $(event.target).parents('form:first').serialize() +"&apikey="+$('#apikey').val(),
success: function(msg){
alert(msg);

64
interfaces/Plush/templates/static/javascripts/plush.js

@ -43,7 +43,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {
mode: 'addurl',
name: $("#addID_input").val(),
@ -80,7 +80,7 @@ jQuery(function($){
/*$('#fetch_newzbin_bookmarks').click(function(){
$.ajax({
type: "POST",
url: "tapi",
url: "api",
data: {mode:'newzbin', name:'get_bookmarks', apikey: $.plush.apikey},
success: function(result){
$.plush.RefreshQueue();
@ -190,7 +190,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'config', name:'set_speedlimit', value: speedLimit, apikey: $.plush.apikey}
});
// Update
@ -248,7 +248,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'change_complete_action', value: $(this).val(), apikey: $.plush.apikey}
});
});
@ -270,7 +270,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'delete', value:value, del_files:del_files, search: $('#queueSearchBox').val(), apikey: $.plush.apikey},
success: function(){
$.colorbox.close();
@ -291,7 +291,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'retry_all', apikey: $.plush.apikey},
success: function(){
$.colorbox.close();
@ -316,7 +316,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'sort', sort: sort, dir: dir, apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -331,7 +331,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'config', name:'set_pause', value: minutes, apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -342,7 +342,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'newzbin', name:'get_bookmarks', apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -353,7 +353,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'reset_quota', apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -364,7 +364,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'rss_now', apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -375,7 +375,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'watched_now', apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -386,7 +386,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'resume_pp', apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -557,7 +557,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'resume', apikey: $.plush.apikey}
});
} else {
@ -566,7 +566,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'pause', apikey: $.plush.apikey}
});
}
@ -599,7 +599,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'pause', value: pid, apikey: $.plush.apikey}
});
} else {
@ -607,7 +607,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'resume', value: pid, apikey: $.plush.apikey}
});
}
@ -632,7 +632,7 @@ jQuery(function($){
$.plush.pendingQueueRefresh = true;
$.ajax({
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'delete', value: delid, apikey: $.plush.apikey},
success: function(){
if ( $("#queueTable tr:visible").length - 1 < 1 ) { // don't leave stranded on non-page
@ -652,7 +652,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'priority', value: nzbid, value2: $(this).val(), apikey: $.plush.apikey},
success: function(newPos){
// reposition the nzb if necessary (new position is returned by the API)
@ -675,7 +675,7 @@ jQuery(function($){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode: cval, value: val, value2: $(this).val(), apikey: $.plush.apikey},
success: function(resp){
// each category can define different priority/processing/script -- must be accounted for
@ -764,7 +764,7 @@ $.plush.queueprevslots = $.plush.queuenoofslots; // for the next refresh
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'switch', value: row.id, value2: val2, apikey: $.plush.apikey},
success: function(result){
// change priority of the nzb if necessary (priority is returned by API)
@ -850,7 +850,7 @@ $("a","#multiops_inputs").click(function(e){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:$('#multi_status').val(), value: nzo_ids, apikey: $.plush.apikey}
});
@ -858,7 +858,7 @@ $("a","#multiops_inputs").click(function(e){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode: 'change_cat', value: nzo_ids, value2: $('#multi_cat').val(), apikey: $.plush.apikey}
});
@ -866,7 +866,7 @@ $("a","#multiops_inputs").click(function(e){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'priority', value: nzo_ids, value2: $('#multi_priority').val(), apikey: $.plush.apikey}
});
@ -874,7 +874,7 @@ $("a","#multiops_inputs").click(function(e){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode: 'change_opts', value: nzo_ids, value2: $('#multi_pp').val(), apikey: $.plush.apikey}
});
@ -882,7 +882,7 @@ $("a","#multiops_inputs").click(function(e){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode: 'change_script', value: nzo_ids, value2: $('#multi_script').val(), apikey: $.plush.apikey}
});
@ -913,7 +913,7 @@ $("a","#multiops_inputs").click(function(e){
if (!$.plush.confirmDeleteQueue || confirm($.plush.Tconfirmation)){
$.ajax({
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'delete', value: nzo_ids, apikey: $.plush.apikey},
success: $.plush.RefreshQueue
});
@ -953,7 +953,7 @@ $("a","#multiops_inputs").click(function(e){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'history', name:'delete', value:value, del_files:del_files, search: $('#historySearchBox').val(), apikey: $.plush.apikey},
success: function(){
$.colorbox.close();
@ -1043,7 +1043,7 @@ $("a","#multiops_inputs").click(function(e){
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:mode, name:'delete', value: delid, del_files: del_files, apikey: $.plush.apikey},
success: function(){
if ( $("#historyTable tr:visible").length - 1 < 1 ) { // don't leave stranded on non-page
@ -1133,7 +1133,7 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'rating', value: nzo_id, type: videoAudio, setting: $(this).val(), apikey: $.plush.apikey},
success: $.plush.Refresh
});
@ -1145,7 +1145,7 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'rating', value: nzo_id, type: 'vote', setting: upDown, apikey: $.plush.apikey},
success: $.plush.Refresh
});
@ -1170,7 +1170,7 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
$.ajax({
headers: {"Cache-Control": "no-cache"},
type: "POST",
url: "tapi",
url: "api",
data: {mode:'queue', name:'rating', value: nzo_id, type: 'flag', setting: flag, detail: _detail, apikey: $.plush.apikey},
success: $.plush.RefreshHistory
});

2
interfaces/wizard/static/javascript/checkserver.js

@ -16,7 +16,7 @@ $(document).ready(function() {
$("#serverTest").click(function() {
$('#serverResponse').html(txtChecking);
$.getJSON(
"../tapi?mode=config&name=test_server&output=json",
"../api?mode=config&name=test_server&output=json",
$("form").serialize(),
function(result) {
if (result.value.result) {

25
sabnzbd/api.py

@ -85,16 +85,35 @@ if os.name == 'nt':
else:
PATHEXT = []
def api_handler(kwargs):
""" API Dispatcher """
if cfg.api_logging():
# Was it proxy forwarded?
xff = cherrypy.request.headers.get('X-Forwarded-For')
if xff:
logging.debug('API-call from %s (X-Forwarded-For: %s) [%s] %s', cherrypy.request.remote.ip,
xff, cherrypy.request.headers.get('User-Agent', '??'), kwargs)
else:
logging.debug('API-call from %s [%s] %s', cherrypy.request.remote.ip,
cherrypy.request.headers.get('User-Agent', '??'), kwargs)
# Clean-up the arguments
mode = kwargs.get('mode', '')
output = kwargs.get('output')
name = kwargs.get('name', '')
output = kwargs.get('output')
if isinstance(mode, list):
mode = mode[0]
if isinstance(name, list):
name = name[0]
if isinstance(output, list):
output = output[0]
if mode not in ('version', 'auth'):
msg = sabnzbd.interface.check_apikey(kwargs)
if msg:
return report(output, msg)
response = _api_table.get(mode, (_api_undefined, 2))[0](name, output, kwargs)
return response
@ -1531,7 +1550,7 @@ def build_header(webdir='', output=None, trans_functions=True):
header['power_options'] = sabnzbd.WIN32 or sabnzbd.DARWIN or sabnzbd.LINUX_POWER
header['pp_pause_event'] = sabnzbd.scheduler.pp_pause_event()
header['session'] = cfg.api_key()
header['apikey'] = cfg.api_key()
header['new_release'], header['new_rel_url'] = sabnzbd.NEW_VERSION
header['version'] = sabnzbd.__version__

268
sabnzbd/interface.py

@ -64,9 +64,8 @@ from sabnzbd.constants import MEBI, DEF_SKIN_COLORS, \
from sabnzbd.lang import list_languages
from sabnzbd.api import list_scripts, list_cats, del_from_section, \
api_handler, build_queue, build_status, retry_job, retry_all_jobs, \
build_header, build_history, format_bytes, report, del_hist_job, Ttemplate, \
build_queue_header
api_handler, build_queue, build_status, retry_job, build_header, build_history, \
format_bytes, report, del_hist_job, Ttemplate, build_queue_header
##############################################################################
# Global constants
@ -76,10 +75,10 @@ from sabnzbd.api import list_scripts, list_cats, del_from_section, \
##############################################################################
# Security functions
##############################################################################
def secured_expose(wrap_func=None, check_configlock=False, check_session_key=False):
def secured_expose(wrap_func=None, check_configlock=False, check_api_key=False):
""" Wrapper for both cherrypy.expose and login/access check """
if not wrap_func:
return functools.partial(secured_expose, check_configlock=check_configlock, check_session_key=check_session_key)
return functools.partial(secured_expose, check_configlock=check_configlock, check_api_key=check_api_key)
# Expose to cherrypy
wrap_func.exposed = True
@ -101,7 +100,7 @@ def secured_expose(wrap_func=None, check_configlock=False, check_session_key=Fal
return 'Access denied'
# Verify login status, only for non-key pages
if not check_login() and not check_session_key:
if not check_login() and not check_api_key:
raise Raiser('/login/')
# Verify host used for the visit
@ -109,9 +108,9 @@ def secured_expose(wrap_func=None, check_configlock=False, check_session_key=Fal
cherrypy.response.status = 403
return 'Access denied - Hostname verification failed: https://sabnzbd.org/hostname-check'
# Some pages need correct session key
if check_session_key:
msg = check_session(kwargs)
# Some pages need correct API key
if check_api_key:
msg = check_apikey(kwargs)
if msg:
return msg
@ -250,65 +249,46 @@ def set_auth(conf):
conf.update({'tools.auth_basic.on': False})
def check_session(kwargs):
""" Check session key """
if not check_access():
return 'Access denied'
key = kwargs.get('session')
if not key:
key = kwargs.get('apikey')
msg = None
if not key:
log_warning_and_ip(T('Missing Session key'))
msg = T('Error: Session Key Required')
elif key != cfg.api_key():
log_warning_and_ip(T('Error: Session Key Incorrect'))
msg = T('Error: Session Key Incorrect')
return msg
def check_apikey(kwargs, nokey=False):
""" Check api key or nzbkey
def check_apikey(kwargs):
""" Check API-key or NZB-key
Return None when OK, otherwise an error message
"""
output = kwargs.get('output')
mode = kwargs.get('mode', '')
name = kwargs.get('name', '')
# Lookup required access level
# Lookup required access level, returns 4 for config-things
req_access = sabnzbd.api.api_level(mode, name)
if req_access == 1 and check_access(1):
# NZB-only actions
pass
elif not check_access(req_access):
return report(output, 'Access denied')
return 'Access denied'
# First check APIKEY, if OK that's sufficient
if not (cfg.disable_key() or nokey):
# First check API-key, if OK that's sufficient
if not cfg.disable_key():
key = kwargs.get('apikey')
if not key:
key = kwargs.get('session')
if not key:
if cfg.api_warnings():
log_warning_and_ip(T('API Key missing, please enter the api key from Config->General into your 3rd party program:'))
return report(output, 'API Key Required')
return 'API Key Required'
elif req_access == 1 and key == cfg.nzb_key():
return None
elif key == cfg.api_key():
return None
else:
log_warning_and_ip(T('API Key incorrect, Use the api key from Config->General in your 3rd party program:'))
return report(output, 'API Key Incorrect')
return 'API Key Incorrect'
# No active APIKEY, check web credentials instead
# No active API-key, check web credentials instead
if cfg.username() and cfg.password():
if check_login() or (kwargs.get('ma_username') == cfg.username() and kwargs.get('ma_password') == cfg.password()):
pass
else:
if cfg.api_warnings():
log_warning_and_ip(T('Authentication missing, please enter username/password from Config->General into your 3rd party program:'))
return report(output, 'Missing authentication')
return 'Missing authentication'
return None
@ -396,7 +376,7 @@ class MainPage:
# Redirect to the setup wizard
raise cherrypy.HTTPRedirect('%s/wizard/' % cfg.url_base())
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def shutdown(self, **kwargs):
# Check for PID
pid_in = kwargs.get('pid')
@ -406,55 +386,26 @@ class MainPage:
sabnzbd.shutdown_program()
return T('SABnzbd shutdown finished')
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def pause(self, **kwargs):
scheduler.plan_resume(0)
Downloader.do.pause()
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def resume(self, **kwargs):
scheduler.plan_resume(0)
sabnzbd.unpause_all()
raise Raiser(self.__root)
@cherrypy.expose
def tapi(self, **kwargs):
""" Handler for API over http, for template use """
msg = check_apikey(kwargs)
if msg:
return msg
return api_handler(kwargs)
@cherrypy.expose
def api(self, **kwargs):
""" Handler for API over http, with explicit authentication parameters """
if cfg.api_logging():
# Was it proxy forwarded?
xff = cherrypy.request.headers.get('X-Forwarded-For')
if xff:
logging.debug('API-call from %s (X-Forwarded-For: %s) [%s] %s', cherrypy.request.remote.ip,
xff, cherrypy.request.headers.get('User-Agent', '??'), kwargs)
else:
logging.debug('API-call from %s [%s] %s', cherrypy.request.remote.ip,
cherrypy.request.headers.get('User-Agent', '??'), kwargs)
mode = kwargs.get('mode', '')
if isinstance(mode, list):
mode = mode[0]
kwargs['mode'] = mode
name = kwargs.get('name', '')
if isinstance(name, list):
name = name[0]
kwargs['name'] = name
if mode not in ('version', 'auth'):
msg = check_apikey(kwargs)
if msg:
return msg
""" Redirect to API-handler """
return api_handler(kwargs)
@secured_expose
def scriptlog(self, **kwargs):
""" Duplicate of scriptlog of History, needed for some skins """
""" Needed for all skins, URL is fixed due to postproc """
# No session key check, due to fixed URLs
name = kwargs.get('name')
if name:
@ -463,7 +414,7 @@ class MainPage:
else:
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def retry(self, **kwargs):
""" Duplicate of retry of History, needed for some skins """
job = kwargs.get('job', '')
@ -476,7 +427,7 @@ class MainPage:
del_hist_job(job, del_files=True)
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def retry_pp(self, **kwargs):
# Duplicate of History/retry_pp to please the SMPL skin :(
retry_job(kwargs.get('job'), kwargs.get('nzbfile'), kwargs.get('password'))
@ -749,7 +700,7 @@ class NzoPage:
return template.respond()
else:
# Job no longer exists, go to main page
raise Raiser(cherrypy.lib.httputil.urljoin(self.__root, '../queue/'))
raise Raiser(urllib.parse.urljoin(self.__root, '../queue/'))
def nzo_details(self, info, pnfo_list, nzo_id):
slot = {}
@ -849,7 +800,7 @@ class NzoPage:
if priority is not None and nzo.priority != int(priority):
NzbQueue.do.set_priority(nzo_id, priority)
raise Raiser(cherrypy.lib.httputil.urljoin(self.__root, '../queue/'))
raise Raiser(urllib.parse.urljoin(self.__root, '../queue/'))
def bulk_operation(self, nzo_id, kwargs):
self.__cached_selection = kwargs
@ -874,9 +825,9 @@ class NzoPage:
NzbQueue.do.move_bottom_bulk(nzo_id, nzf_ids)
if NzbQueue.do.get_nzo(nzo_id):
url = cherrypy.lib.httputil.urljoin(self.__root, nzo_id)
url = urllib.parse.urljoin(self.__root, nzo_id)
else:
url = cherrypy.lib.httputil.urljoin(self.__root, '../queue')
url = urllib.parse.urljoin(self.__root, '../queue')
if url and not url.endswith('/'):
url += '/'
raise Raiser(url)
@ -899,7 +850,7 @@ class QueuePage:
searchList=[info], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def delete(self, **kwargs):
uid = kwargs.get('uid')
del_files = int_conv(kwargs.get('del_files'))
@ -907,12 +858,12 @@ class QueuePage:
NzbQueue.do.remove(uid, add_to_history=False, delete_all_data=del_files)
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def purge(self, **kwargs):
NzbQueue.do.remove_all(kwargs.get('search'))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def change_queue_complete_action(self, **kwargs):
""" Action or script to be performed once the queue has been completed
Scripts are prefixed with 'script_'
@ -921,7 +872,7 @@ class QueuePage:
sabnzbd.change_queue_complete_action(action)
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def switch(self, **kwargs):
uid1 = kwargs.get('uid1')
uid2 = kwargs.get('uid2')
@ -929,7 +880,7 @@ class QueuePage:
NzbQueue.do.switch(uid1, uid2)
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def change_opts(self, **kwargs):
nzo_id = kwargs.get('nzo_id')
pp = kwargs.get('pp', '')
@ -937,7 +888,7 @@ class QueuePage:
NzbQueue.do.change_opts(nzo_id, int(pp))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def change_script(self, **kwargs):
nzo_id = kwargs.get('nzo_id')
script = kwargs.get('script', '')
@ -947,7 +898,7 @@ class QueuePage:
NzbQueue.do.change_script(nzo_id, script)
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def change_cat(self, **kwargs):
nzo_id = kwargs.get('nzo_id')
cat = kwargs.get('cat', '')
@ -958,51 +909,51 @@ class QueuePage:
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def shutdown(self, **kwargs):
sabnzbd.shutdown_program()
return T('SABnzbd shutdown finished')
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def pause(self, **kwargs):
scheduler.plan_resume(0)
Downloader.do.pause()
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def resume(self, **kwargs):
scheduler.plan_resume(0)
sabnzbd.unpause_all()
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def pause_nzo(self, **kwargs):
uid = kwargs.get('uid', '')
NzbQueue.do.pause_multiple_nzo(uid.split(','))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def resume_nzo(self, **kwargs):
uid = kwargs.get('uid', '')
NzbQueue.do.resume_multiple_nzo(uid.split(','))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def set_priority(self, **kwargs):
NzbQueue.do.set_priority(kwargs.get('nzo_id'), kwargs.get('priority'))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def sort_by_avg_age(self, **kwargs):
NzbQueue.do.sort_queue('avg_age', kwargs.get('dir'))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def sort_by_name(self, **kwargs):
NzbQueue.do.sort_queue('name', kwargs.get('dir'))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def sort_by_size(self, **kwargs):
NzbQueue.do.sort_queue('size', kwargs.get('dir'))
raise queueRaiser(self.__root, kwargs)
@ -1054,13 +1005,13 @@ class HistoryPage:
searchList=[history], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def purge(self, **kwargs):
history_db = sabnzbd.get_db_connection()
history_db.remove_history()
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def delete(self, **kwargs):
job = kwargs.get('job')
del_files = int_conv(kwargs.get('del_files'))
@ -1070,44 +1021,11 @@ class HistoryPage:
del_hist_job(job, del_files=del_files)
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def retry_pp(self, **kwargs):
retry_job(kwargs.get('job'), kwargs.get('nzbfile'), kwargs.get('password'))
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
def retry_all(self, **kwargs):
retry_all_jobs()
raise queueRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True)
def reset(self, **kwargs):
# sabnzbd.reset_byte_counter()
raise queueRaiser(self.__root, kwargs)
@secured_expose
def scriptlog(self, **kwargs):
""" Duplicate of scriptlog of History, needed for some skins """
# No session key check, due to fixed URLs
name = kwargs.get('name')
if name:
history_db = sabnzbd.get_db_connection()
return ShowString(history_db.get_name(name), history_db.get_script_log(name))
else:
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
def retry(self, **kwargs):
job = kwargs.get('job', '')
url = kwargs.get('url', '').strip()
pp = kwargs.get('pp')
cat = kwargs.get('cat')
script = kwargs.get('script')
if url:
sabnzbd.add_url(url, pp, script, cat, nzbname=kwargs.get('nzbname'))
del_hist_job(job, del_files=True)
raise Raiser(self.__root)
##############################################################################
class ConfigPage:
@ -1151,14 +1069,14 @@ class ConfigPage:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def restart(self, **kwargs):
logging.info('Restart requested by interface')
# Do the shutdown async to still send goodbye to browser
Thread(target=sabnzbd.trigger_restart, kwargs={'timeout': 1}).start()
return T('&nbsp<br />SABnzbd shutdown finished.<br />Wait for about 5 second and then click the button below.<br /><br /><strong><a href="..">Refresh</a></strong><br />')
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def repair(self, **kwargs):
logging.info('Queue repair requested by interface')
sabnzbd.request_repair()
@ -1191,7 +1109,7 @@ class ConfigFolders:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def saveDirectories(self, **kwargs):
for kw in LIST_DIRPAGE:
value = kwargs.get(kw)
@ -1257,7 +1175,7 @@ class ConfigSwitches:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def saveSwitches(self, **kwargs):
for kw in SWITCH_LIST:
item = config.get_config('misc', kw)
@ -1311,7 +1229,7 @@ class ConfigSpecial:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def saveSpecial(self, **kwargs):
for kw in SPECIAL_BOOL_LIST + SPECIAL_VALUE_LIST + SPECIAL_LIST_LIST:
item = config.get_config('misc', kw)
@ -1407,7 +1325,7 @@ class ConfigGeneral:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def saveGeneral(self, **kwargs):
# Handle general options
for kw in GENERAL_LIST:
@ -1486,33 +1404,33 @@ class ConfigServer:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def addServer(self, **kwargs):
return handle_server(kwargs, self.__root, True)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def saveServer(self, **kwargs):
return handle_server(kwargs, self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def testServer(self, **kwargs):
return handle_server_test(kwargs, self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def delServer(self, **kwargs):
kwargs['section'] = 'servers'
kwargs['keyword'] = kwargs.get('server')
del_from_section(kwargs)
raise Raiser(self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def clrServer(self, **kwargs):
server = kwargs.get('server')
if server:
BPSMeter.do.clear_server(server)
raise Raiser(self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def toggleServer(self, **kwargs):
server = kwargs.get('server')
if server:
@ -1693,7 +1611,7 @@ class ConfigRss:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def save_rss_rate(self, **kwargs):
""" Save changed RSS automatic readout rate """
cfg.rss_rate.set(kwargs.get('rss_rate'))
@ -1701,7 +1619,7 @@ class ConfigRss:
scheduler.restart()
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def upd_rss_feed(self, **kwargs):
""" Update Feed level attributes,
legacy version: ignores 'enable' parameter
@ -1722,7 +1640,7 @@ class ConfigRss:
self.__show_eval_button = True
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def save_rss_feed(self, **kwargs):
""" Update Feed level attributes """
try:
@ -1739,7 +1657,7 @@ class ConfigRss:
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def toggle_rss_feed(self, **kwargs):
""" Toggle automatic read-out flag of Feed """
try:
@ -1754,7 +1672,7 @@ class ConfigRss:
else:
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def add_rss_feed(self, **kwargs):
""" Add one new RSS feed definition """
feed = Strip(kwargs.get('feed')).strip('[]')
@ -1783,7 +1701,7 @@ class ConfigRss:
else:
raise Raiser(self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def upd_rss_filter(self, **kwargs):
""" Wrapper, so we can call from api.py """
self.internal_upd_rss_filter(**kwargs)
@ -1819,7 +1737,7 @@ class ConfigRss:
self.__show_eval_button = True
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def del_rss_feed(self, *args, **kwargs):
""" Remove complete RSS feed """
kwargs['section'] = 'rss'
@ -1828,7 +1746,7 @@ class ConfigRss:
sabnzbd.rss.clear_feed(kwargs.get('feed'))
raise Raiser(self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def del_rss_filter(self, **kwargs):
""" Wrapper, so we can call from api.py """
self.internal_del_rss_filter(**kwargs)
@ -1846,7 +1764,7 @@ class ConfigRss:
self.__show_eval_button = True
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def download_rss_feed(self, *args, **kwargs):
""" Force download of all matching jobs in a feed """
if 'feed' in kwargs:
@ -1858,14 +1776,14 @@ class ConfigRss:
self.__evaluate = True
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def clean_rss_jobs(self, *args, **kwargs):
""" Remove processed RSS jobs from UI """
sabnzbd.rss.clear_downloaded(kwargs['feed'])
self.__evaluate = True
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def test_rss_feed(self, *args, **kwargs):
""" Read the feed content again and show results """
if 'feed' in kwargs:
@ -1878,7 +1796,7 @@ class ConfigRss:
self.__show_eval_button = False
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def eval_rss_feed(self, *args, **kwargs):
""" Re-apply the filters to the feed """
if 'feed' in kwargs:
@ -1890,7 +1808,7 @@ class ConfigRss:
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def download(self, **kwargs):
""" Download NZB from provider (Download button) """
feed = kwargs.get('feed')
@ -1909,7 +1827,7 @@ class ConfigRss:
sabnzbd.rss.flag_downloaded(feed, url)
raise rssRaiser(self.__root, kwargs)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def rss_now(self, *args, **kwargs):
""" Run an automatic RSS run now """
scheduler.force_rss()
@ -2037,7 +1955,7 @@ class ConfigScheduling:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def addSchedule(self, **kwargs):
servers = config.get_servers()
minute = kwargs.get('minute')
@ -2085,7 +2003,7 @@ class ConfigScheduling:
scheduler.restart(force=True)
raise Raiser(self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def delSchedule(self, **kwargs):
schedules = cfg.schedules()
line = kwargs.get('line')
@ -2096,7 +2014,7 @@ class ConfigScheduling:
scheduler.restart(force=True)
raise Raiser(self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def toggleSchedule(self, **kwargs):
schedules = cfg.schedules()
line = kwargs.get('line')
@ -2144,14 +2062,14 @@ class ConfigCats:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def delete(self, **kwargs):
kwargs['section'] = 'categories'
kwargs['keyword'] = kwargs.get('name')
del_from_section(kwargs)
raise Raiser(self.__root)
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def save(self, **kwargs):
name = kwargs.get('name', '*')
if name == '*':
@ -2198,7 +2116,7 @@ class ConfigSorting:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def saveSorting(self, **kwargs):
try:
kwargs['movie_categories'] = kwargs['movie_cat']
@ -2244,22 +2162,22 @@ class Status:
searchList=[header], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def reset_quota(self, **kwargs):
BPSMeter.do.reset_quota(force=True)
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def disconnect(self, **kwargs):
Downloader.do.disconnect()
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def refresh_conn(self, **kwargs):
# No real action, just reload the page
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def showlog(self, **kwargs):
try:
sabnzbd.LOGHANDLER.flush()
@ -2298,46 +2216,46 @@ class Status:
cherrypy.response.headers['Content-Disposition'] = 'attachment;filename="sabnzbd.log"'
return log_data
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def clearwarnings(self, **kwargs):
sabnzbd.GUIHANDLER.clear()
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def change_loglevel(self, **kwargs):
cfg.log_level.set(kwargs.get('loglevel'))
config.save_config()
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def unblock_server(self, **kwargs):
Downloader.do.unblock(kwargs.get('server'))
# Short sleep so that UI shows new server status
time.sleep(1.0)
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def delete(self, **kwargs):
orphan_delete(kwargs)
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def delete_all(self, **kwargs):
orphan_delete_all()
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def add(self, **kwargs):
orphan_add(kwargs)
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def add_all(self, **kwargs):
orphan_add_all()
raise Raiser(self.__root)
@secured_expose(check_session_key=True)
@secured_expose(check_api_key=True)
def dashrefresh(self, **kwargs):
# This function is run when Refresh button on Dashboard is clicked
# Put the time consuming dashboard functions here; they only get executed when the user clicks the Refresh button
@ -2577,7 +2495,7 @@ class ConfigNotify:
searchList=[conf], compilerSettings=CHEETAH_DIRECTIVES)
return template.respond()
@secured_expose(check_session_key=True, check_configlock=True)
@secured_expose(check_api_key=True, check_configlock=True)
def saveEmail(self, **kwargs):
ajax = kwargs.get('ajax')

2
tests/testhelper.py

@ -102,7 +102,7 @@ def set_platform(platform):
def get_url_result(url="", host=SAB_HOST, port=SAB_PORT):
""" Do basic request to web page """
arguments = {"session": "apikey"}
arguments = {"apikey": "apikey"}
return requests.get("http://%s:%s/%s/" % (host, port, url), params=arguments).text

Loading…
Cancel
Save