diff --git a/server/src/main/java/ctbrec/recorder/server/AbstractCtbrecServlet.java b/server/src/main/java/ctbrec/recorder/server/AbstractCtbrecServlet.java index 2ec49a83..100fe6b2 100644 --- a/server/src/main/java/ctbrec/recorder/server/AbstractCtbrecServlet.java +++ b/server/src/main/java/ctbrec/recorder/server/AbstractCtbrecServlet.java @@ -1,5 +1,7 @@ package ctbrec.recorder.server; +import static javax.servlet.http.HttpServletResponse.*; + import java.io.BufferedReader; import java.io.IOException; import java.security.InvalidKeyException; @@ -7,13 +9,19 @@ import java.security.NoSuchAlgorithmException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.Hmac; public abstract class AbstractCtbrecServlet extends HttpServlet { - boolean checkAuthentication(HttpServletRequest req, String body) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException { + private static final Logger LOG = LoggerFactory.getLogger(AbstractCtbrecServlet.class); + + boolean checkAuthentication(HttpServletRequest req, String body) throws IOException, InvalidKeyException, NoSuchAlgorithmException { boolean authenticated = false; if(Config.getInstance().getSettings().key != null) { String reqParamHmac = req.getParameter("hmac"); @@ -48,4 +56,14 @@ public abstract class AbstractCtbrecServlet extends HttpServlet { } return body.toString().trim(); } + + void sendResponse(HttpServletResponse resp, int httpStatus, String message) { + try { + resp.setStatus(httpStatus); + resp.getWriter().print(message); + } catch (IOException e) { + LOG.error("Couldn't write response", e); + resp.setStatus(SC_INTERNAL_SERVER_ERROR); + } + } } diff --git a/server/src/main/java/ctbrec/recorder/server/ConfigServlet.java b/server/src/main/java/ctbrec/recorder/server/ConfigServlet.java index 8dcd632b..d3b63cf8 100644 --- a/server/src/main/java/ctbrec/recorder/server/ConfigServlet.java +++ b/server/src/main/java/ctbrec/recorder/server/ConfigServlet.java @@ -3,6 +3,7 @@ package ctbrec.recorder.server; import static javax.servlet.http.HttpServletResponse.*; import java.io.IOException; +import java.lang.reflect.Field; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -11,6 +12,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,10 +27,7 @@ public class ConfigServlet extends AbstractCtbrecServlet { private Settings settings; public enum DataType { - STRING, - BOOLEAN, - INTEGER, - DOUBLE + STRING, BOOLEAN, INTEGER, LONG, DOUBLE } public ConfigServlet(Config config) { @@ -42,7 +41,7 @@ public class ConfigServlet extends AbstractCtbrecServlet { boolean authenticated = checkAuthentication(req, body(req)); if (!authenticated) { resp.setStatus(SC_UNAUTHORIZED); - String response = "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}"; + String response = "HMAC does not match"; resp.getWriter().write(response); return; } @@ -52,13 +51,13 @@ public class ConfigServlet extends AbstractCtbrecServlet { addParameter("ffmpegFileSuffix", "File Suffix", DataType.STRING, settings.ffmpegFileSuffix, json); addParameter("ffmpegMergedDownloadArgs", "FFmpeg Parameters", DataType.STRING, settings.ffmpegMergedDownloadArgs, json); addParameter("httpPort", "HTTP port", DataType.INTEGER, settings.httpPort, json); - addParameter("httpsPort", "HTTPS port", DataType.INTEGER, settings.httpSecurePort, json); + addParameter("httpSecurePort", "HTTPS port", DataType.INTEGER, settings.httpSecurePort, json); addParameter("httpUserAgent", "User-Agent", DataType.STRING, settings.httpUserAgent, json); addParameter("httpUserAgentMobile", "Mobile User-Agent", DataType.STRING, settings.httpUserAgentMobile, json); addParameter("generatePlaylist", "Generate Playlist", DataType.BOOLEAN, settings.generatePlaylist, json); addParameter("maximumResolution", "Maximum Resolution", DataType.INTEGER, settings.maximumResolution, json); addParameter("minimumLengthInSeconds", "Minimum Length (secs)", DataType.INTEGER, settings.minimumLengthInSeconds, json); - addParameter("minimumSpaceLeftInBytes", "Leave Space On Device (GiB)", DataType.INTEGER, settings.minimumSpaceLeftInBytes, json); + addParameter("minimumSpaceLeftInBytes", "Leave Space On Device (GiB)", DataType.LONG, settings.minimumSpaceLeftInBytes, json); addParameter("onlineCheckIntervalInSecs", "Online Check Interval (secs)", DataType.INTEGER, settings.onlineCheckIntervalInSecs, json); addParameter("postProcessing", "Post-Processing", DataType.STRING, settings.postProcessing, json); addParameter("postProcessingThreads", "Post-Processing Threads", DataType.INTEGER, settings.postProcessingThreads, json); @@ -89,25 +88,81 @@ public class ConfigServlet extends AbstractCtbrecServlet { } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException { try { - boolean authenticated = checkAuthentication(req, body(req)); + resp.setContentType("application/json"); + String body = body(req); + boolean authenticated = checkAuthentication(req, body); if (!authenticated) { - resp.setStatus(SC_UNAUTHORIZED); - String response = "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}"; - resp.getWriter().write(response); + String response = "HMAC does not match"; + sendResponse(resp, SC_UNAUTHORIZED, response); return; } - String postBody = body(req); - LOG.info("POST {}", postBody); - //JSONObject json = new JSONObject(postBody); - resp.setStatus(SC_OK); - resp.setContentType("application/json"); - resp.getWriter().print("{\"status\": \"success\"}"); - } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { - resp.setStatus(SC_INTERNAL_SERVER_ERROR); + JSONArray json = new JSONArray(body); + changeConfig(json); + sendResponse(resp, SC_OK, "{\"status\": \"success\"}"); + } catch (InvalidKeyException | NoSuchAlgorithmException e) { + LOG.error("Couldn't authenticate request", e); + sendResponse(resp, SC_INTERNAL_SERVER_ERROR, "Couldn't authenticate request"); + } catch (JSONException e) { + LOG.error("Couldn't parse request", e); + sendResponse(resp, SC_BAD_REQUEST, "JSON array expected"); + } catch (IOException e) { + LOG.error("Couldn't read request", e); + sendResponse(resp, SC_INTERNAL_SERVER_ERROR, "Couldn't read request"); + } catch (ConfigWriteException e) { + LOG.error("Couldn't write config", e); + sendResponse(resp, SC_INTERNAL_SERVER_ERROR, "Couldn't write config: " + e.getLocalizedMessage()); } + } + private void changeConfig(JSONArray json) throws ConfigWriteException { + try { + for (int i = 0; i < json.length(); i++) { + JSONObject property = json.getJSONObject(i); + String key = property.optString("key"); + DataType type = DataType.valueOf(property.optString("type")); + Object value = property.get("value"); + Object typeCorrectedValue = correctType(type, value); + LOG.debug("{}: {}", key, value); + + Field field = Settings.class.getField(key); + field.set(settings, typeCorrectedValue); + } + config.save(); + } catch (Exception e) { + throw new ConfigWriteException(e); + } + } + + private Object correctType(DataType type, Object value) { + Object corrected = value; + if (value == null) { + return null; + } + switch (type) { + case INTEGER: + corrected = Integer.parseInt(value.toString()); + break; + case LONG: + corrected = Long.parseLong(value.toString()); + break; + case BOOLEAN: + corrected = Boolean.parseBoolean(value.toString()); + break; + case DOUBLE: + corrected = Double.parseDouble(value.toString()); + break; + default: + break; + } + return corrected; + } + + private static class ConfigWriteException extends Exception { + public ConfigWriteException(Exception e) { + super(e); + } } } diff --git a/server/src/main/resources/html/static/config.js b/server/src/main/resources/html/static/config.js index 19e55ed8..780207be 100644 --- a/server/src/main/resources/html/static/config.js +++ b/server/src/main/resources/html/static/config.js @@ -11,12 +11,12 @@ function loadConfig() { } }).done(function(data, textStatus, jqXHR) { if (textStatus === 'success') { + while (observableSettingsArray().length > 0) { + observableSettingsArray.pop(); + } for (let i = 0; i < data.length; i++) { let param = data[i]; param.ko_value = ko.observable(param.value); - param.ko_value.subscribe(function(newValue) { - console.log("The person's new name is " + newValue); - }); observableSettingsArray.push(param); } } else { @@ -36,7 +36,6 @@ function loadConfig() { function saveConfig() { try { let msg = JSON.stringify(observableSettingsArray()); - console.log(msg); $.ajax({ type : 'POST', url : '../config', @@ -49,17 +48,21 @@ function saveConfig() { data : msg }).done(function(data, textStatus, jqXHR) { if (textStatus === 'success') { - //$.notify('Configuration saved', 'info'); + loadConfig(); + $.notify('Configuration saved. You might have to restart the server.', 'info'); } else { if (console) console.log('request failed', data); + $.notify('Error: ' + jqXHR.responseText, 'error'); } }).fail(function(jqXHR, textStatus, errorThrown) { if (console) console.log(jqXHR, textStatus, errorThrown); + $.notify(errorThrown + ': ' + jqXHR.responseText, 'error'); }); } catch (e) { if (console) console.log('Unexpected error', e); + $.notify(errorThrown + ': ' + jqXHR.responseText, 'error'); } } \ No newline at end of file diff --git a/server/src/main/resources/html/static/index.html b/server/src/main/resources/html/static/index.html index 957582de..66dc1d28 100644 --- a/server/src/main/resources/html/static/index.html +++ b/server/src/main/resources/html/static/index.html @@ -77,7 +77,7 @@