// // DomainServerSettingsManager.cpp // domain-server/src // // Created by Stephen Birarda on 2014-06-24. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include #include #include #include #include #include #include #include #include "DomainServerSettingsManager.h" const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/web/settings/describe.json"; const QString SETTINGS_JSON_FILE_RELATIVE_PATH = "/resources/settings.json"; DomainServerSettingsManager::DomainServerSettingsManager() : _descriptionObject(), _settingsMap() { // load the description object from the settings description QFile descriptionFile(QCoreApplication::applicationDirPath() + SETTINGS_DESCRIPTION_RELATIVE_PATH); descriptionFile.open(QIODevice::ReadOnly); _descriptionObject = QJsonDocument::fromJson(descriptionFile.readAll()).object(); // load the existing config file to get the current values QFile configFile(QCoreApplication::applicationDirPath() + SETTINGS_JSON_FILE_RELATIVE_PATH); if (configFile.exists()) { configFile.open(QIODevice::ReadOnly); _settingsMap = QJsonDocument::fromJson(configFile.readAll()).toVariant().toMap(); } } const QString DESCRIPTION_SETTINGS_KEY = "settings"; const QString SETTING_DEFAULT_KEY = "default"; bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connection, const QUrl &url) { if (connection->requestOperation() == QNetworkAccessManager::GetOperation && url.path() == "/settings.json") { // this is a GET operation for our settings // check if there is a query parameter for settings affecting a particular type of assignment const QString SETTINGS_TYPE_QUERY_KEY = "type"; QUrlQuery settingsQuery(url); QString typeValue = settingsQuery.queryItemValue(SETTINGS_TYPE_QUERY_KEY); QJsonObject responseObject; if (typeValue.isEmpty()) { // combine the description object and our current settings map responseObject["descriptions"] = _descriptionObject; responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); } else { // convert the string type value to a QJsonValue QJsonValue queryType = QJsonValue(typeValue.toInt()); const QString AFFECTED_TYPES_JSON_KEY = "assignment-types"; // enumerate the groups in the description object to find which settings to pass foreach(const QString& group, _descriptionObject.keys()) { QJsonObject groupObject = _descriptionObject[group].toObject(); QJsonObject groupSettingsObject = groupObject[DESCRIPTION_SETTINGS_KEY].toObject(); QJsonObject groupResponseObject; foreach(const QString& settingKey, groupSettingsObject.keys()) { QJsonObject settingObject = groupSettingsObject[settingKey].toObject(); QJsonArray affectedTypesArray = settingObject[AFFECTED_TYPES_JSON_KEY].toArray(); if (affectedTypesArray.isEmpty()) { affectedTypesArray = groupObject[AFFECTED_TYPES_JSON_KEY].toArray(); } if (affectedTypesArray.contains(queryType)) { // this is a setting we should include in the responseObject // we need to check if the settings map has a value for this setting QVariant variantValue; QVariant settingsMapGroupValue = _settingsMap.value(group); if (!settingsMapGroupValue.isNull()) { variantValue = settingsMapGroupValue.toMap().value(settingKey); } if (variantValue.isNull()) { // no value for this setting, pass the default groupResponseObject[settingKey] = settingObject[SETTING_DEFAULT_KEY]; } else { groupResponseObject[settingKey] = QJsonValue::fromVariant(variantValue); } } } if (!groupResponseObject.isEmpty()) { // set this group's object to the constructed object responseObject[group] = groupResponseObject; } } } connection->respond(HTTPConnection::StatusCode200, QJsonDocument(responseObject).toJson(), "application/json"); return true; } return false; } bool DomainServerSettingsManager::handleAuthenticatedHTTPRequest(HTTPConnection *connection, const QUrl &url) { if (connection->requestOperation() == QNetworkAccessManager::PostOperation && url.path() == "/settings.json") { // this is a POST operation to change one or more settings QJsonDocument postedDocument = QJsonDocument::fromJson(connection->requestContent()); QJsonObject postedObject = postedDocument.object(); // we recurse one level deep below each group for the appropriate setting recurseJSONObjectAndOverwriteSettings(postedObject, _settingsMap, _descriptionObject); // store whatever the current _settingsMap is to file persistToFile(); // return success to the caller QString jsonSuccess = "{\"status\": \"success\"}"; connection->respond(HTTPConnection::StatusCode200, jsonSuccess.toUtf8(), "application/json"); return true; } return false; } const QString SETTING_DESCRIPTION_TYPE_KEY = "type"; void DomainServerSettingsManager::recurseJSONObjectAndOverwriteSettings(const QJsonObject& postedObject, QVariantMap& settingsVariant, QJsonObject descriptionObject) { foreach(const QString& key, postedObject.keys()) { QJsonValue rootValue = postedObject[key]; // we don't continue if this key is not present in our descriptionObject if (descriptionObject.contains(key)) { if (rootValue.isString()) { if (rootValue.toString().isEmpty()) { // this is an empty value, clear it in settings variant so the default is sent settingsVariant.remove(key); } else { if (descriptionObject[key].toObject().contains(SETTING_DESCRIPTION_TYPE_KEY)) { // for now this means that this is a double, so set it as a double settingsVariant[key] = rootValue.toString().toDouble(); } else { settingsVariant[key] = rootValue.toString(); } } } else if (rootValue.isBool()) { settingsVariant[key] = rootValue.toBool(); } else if (rootValue.isObject()) { // there's a JSON Object to explore, so attempt to recurse into it QJsonObject nextDescriptionObject = descriptionObject[key].toObject(); if (nextDescriptionObject.contains(DESCRIPTION_SETTINGS_KEY)) { if (!settingsVariant.contains(key)) { // we don't have a map below this key yet, so set it up now settingsVariant[key] = QVariantMap(); } QVariantMap& thisMap = *reinterpret_cast(settingsVariant[key].data()); recurseJSONObjectAndOverwriteSettings(rootValue.toObject(), thisMap, nextDescriptionObject[DESCRIPTION_SETTINGS_KEY].toObject()); if (thisMap.isEmpty()) { // we've cleared all of the settings below this value, so remove this one too settingsVariant.remove(key); } } } } } } QByteArray DomainServerSettingsManager::getJSONSettingsMap() const { return QJsonDocument::fromVariant(_settingsMap).toJson(); } void DomainServerSettingsManager::persistToFile() { QFile settingsFile(QCoreApplication::applicationDirPath() + SETTINGS_JSON_FILE_RELATIVE_PATH); if (settingsFile.open(QIODevice::WriteOnly)) { settingsFile.write(getJSONSettingsMap()); } else { qCritical("Could not write to JSON settings file. Unable to persist settings."); } }