15.1.363. tablet_qt/menulib/serversettingswindow.cpp¶
/*
Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
Created by Rudolf Cardinal (rnc1001@cam.ac.uk).
This file is part of CamCOPS.
CamCOPS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
CamCOPS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/
#include "serversettingswindow.h"
#include <QString>
#include "common/aliases_camcops.h"
#include "common/varconst.h"
#include "core/camcopsapp.h"
#include "lib/convert.h"
#include "lib/stringfunc.h"
#include "lib/uifunc.h"
#include "questionnairelib/commonoptions.h"
#include "questionnairelib/namevalueoptions.h"
#include "questionnairelib/questionnaire.h"
#include "questionnairelib/questionnairefunc.h"
#include "questionnairelib/quhorizontalline.h"
#include "questionnairelib/qulineedit.h"
#include "questionnairelib/qulineeditinteger.h"
#include "questionnairelib/qulineeditint64.h"
#include "questionnairelib/qumcq.h"
#include "questionnairelib/qutext.h"
#include "widgets/openablewidget.h"
// ============================================================================
// Server configuration
// ============================================================================
ServerSettingsWindow::ServerSettingsWindow(CamcopsApp& app) :
m_app(app)
{
}
OpenableWidget* ServerSettingsWindow::editor()
{
// The general options here: have the Questionnaire save directly to the
// StoredVar but in a way that's not permanent and allows "recall/reload"
// upon cancel; or temporary local storage with writing to the StoredVar
// on OK. The latter is a generally better principle. Since C++ lambda
// functions don't extend the life of things they reference (see "dangling
// references" in the spec), the best way is by having the CamcopsApp
// (whose lifespan is guaranteed to be long enough) to do the storage,
// and having a fieldref to a cached storedvar.
// A third option is to use a database transaction (and forgo or invalidate
// any copies of storedvars maintained in memory).
const QString DEPRECATED("(†) ");
m_app.clearCachedVars(); // ... in case any are left over
FieldRefPtr address_fr = m_app.storedVarFieldRef(varconst::SERVER_ADDRESS);
const QString address_t = tr("Server address");
const QString address_h = tr("host name or IP address");
FieldRefPtr port_fr = m_app.storedVarFieldRef(varconst::SERVER_PORT);
const QString port_t = tr("Server port for HTTPS");
const QString port_h = tr("default 443");
FieldRefPtr path_fr = m_app.storedVarFieldRef(varconst::SERVER_PATH);
const QString path_t = tr("Path on server");
const QString path_h = tr("no leading /; e.g. camcops/database");
FieldRefPtr timeout_fr = m_app.storedVarFieldRef(varconst::SERVER_TIMEOUT_MS);
const QString timeout_t = tr("Network timeout (ms)");
const QString timeout_h = tr("e.g. 50000");
#ifdef DEBUG_OFFER_HTTP_TO_SERVER
FieldRefPtr https_fr = m_app.storedVarFieldRef(varconst::DEBUG_USE_HTTPS_TO_SERVER);
const QString https_t = tr("Use HTTPS to server?");
const QString https_h = tr("You should <b>only</b> disable this for debugging!");
#endif
FieldRefPtr ssl_fr = m_app.storedVarFieldRef(
varconst::VALIDATE_SSL_CERTIFICATES);
const QString ssl_t = tr("Validate HTTPS (TLS/SSL) certificates?");
const QString ssl_h = tr("Should always be YES for security-conscious "
"systems.");
FieldRefPtr ssl_proto_fr = m_app.storedVarFieldRef(varconst::SSL_PROTOCOL);
const QString ssl_proto_t = tr("HTTPS (TLS/SSL) protocol?");
const QString ssl_proto_h = tr("Stick with the default unless your server "
"can’t cope with it.");
const NameValueOptions options_ssl_protocol{
// http://doc.qt.io/qt-5/qssl.html#SslProtocol-enum
{tr("Known secure [default]"), convert::SSLPROTODESC_SECUREPROTOCOLS},
{DEPRECATED + tr("SSL v3"), convert::SSLPROTODESC_SSLV3},
{DEPRECATED + tr("SSL v2"), convert::SSLPROTODESC_SSLV2},
{DEPRECATED + tr("TLS v1.0"), convert::SSLPROTODESC_TLSV1_0},
{DEPRECATED + tr("TLS v1.0 or later"), convert::SSLPROTODESC_TLSV1_0_OR_LATER},
{tr("TLS v1.1"), convert::SSLPROTODESC_TLSV1_1},
{tr("TLS v1.1 or later"), convert::SSLPROTODESC_TLSV1_1_OR_LATER},
{tr("TLS v1.2"), convert::SSLPROTODESC_TLSV1_2},
{tr("TLS v1.2 or later"), convert::SSLPROTODESC_TLSV1_2_OR_LATER},
{DEPRECATED + tr("SSLv2, SSLv3, or TLSv1.0"), convert::SSLPROTODESC_ANYPROTOCOL},
{DEPRECATED + tr("TLS v1.0 or SSL v3"), convert::SSLPROTODESC_TLSV1_SSLV3},
};
const QString ssl_proto_explanation = DEPRECATED + "Insecure, deprecated.";
FieldRefPtr storepw_fr = m_app.storedVarFieldRef(varconst::STORE_SERVER_PASSWORD);
const QString storepw_t = tr("Store user’s server password?");
const QString storepw_h = tr(
"NO = fractionally more secure; YES = more convenient/"
"fractionally less secure, but still AES-256-encrypted.");
FieldRefPtr uploadmethod_fr = m_app.storedVarFieldRef(
varconst::UPLOAD_METHOD);
const QString uploadmethod_t = tr("Upload method");
const NameValueOptions options_upload_method{
{tr("Multi-step (original)"), varconst::UPLOAD_METHOD_MULTISTEP},
{tr("Always one-step (faster)"), varconst::UPLOAD_METHOD_ONESTEP},
{tr("One-step if small enough (default)"), varconst::UPLOAD_METHOD_BYSIZE},
};
FieldRefPtr maxsizeonestep_fr = m_app.storedVarFieldRef(
varconst::MAX_DBSIZE_FOR_ONESTEP_UPLOAD);
const QString maxsizeonestep_t = tr(
"Maximum (approximate) database size for one-step upload (bytes)");
const QString maxsizeonestep_h = tr("e.g. 2000000 for ~2Mb");
QuPagePtr page(new QuPage{
questionnairefunc::defaultGridRawPointer({
{
stringfunc::makeTitle(address_t, address_h, true),
(new QuLineEdit(address_fr))->setHint(
stringfunc::makeHint(address_t, address_h))
->setWidgetInputMethodHints(Qt::ImhNoAutoUppercase |
Qt::ImhNoPredictiveText)
},
{
stringfunc::makeTitle(port_t, port_h, true),
new QuLineEditInteger(port_fr,
uiconst::IP_PORT_MIN, uiconst::IP_PORT_MAX)
},
{
stringfunc::makeTitle(path_t, path_h, true),
(new QuLineEdit(path_fr))->setHint(
stringfunc::makeHint(path_t, path_h))
->setWidgetInputMethodHints(Qt::ImhNoAutoUppercase |
Qt::ImhNoPredictiveText)
},
{
stringfunc::makeTitle(timeout_t, timeout_h, true),
new QuLineEditInteger(timeout_fr,
uiconst::NETWORK_TIMEOUT_MS_MIN,
uiconst::NETWORK_TIMEOUT_MS_MAX)
},
}, 1, 1),
#ifdef DEBUG_OFFER_HTTP_TO_SERVER
new QuText(stringfunc::makeTitle(https_t, https_h)),
(new QuMcq(https_fr, CommonOptions::yesNoBoolean()))
->setHorizontal(true)
->setAsTextButton(true),
#endif
new QuText(stringfunc::makeTitle(ssl_t, ssl_h)),
(new QuMcq(ssl_fr, CommonOptions::yesNoBoolean()))
->setHorizontal(true)
->setAsTextButton(true),
new QuText(stringfunc::makeTitle(ssl_proto_t, ssl_proto_h)),
(new QuMcq(ssl_proto_fr, options_ssl_protocol))
->setHorizontal(true)
->setAsTextButton(true),
new QuText(ssl_proto_explanation),
new QuHorizontalLine(),
new QuText(stringfunc::makeTitle(storepw_t, storepw_h)),
(new QuMcq(storepw_fr, CommonOptions::yesNoBoolean()))
->setHorizontal(true)
->setAsTextButton(true),
new QuHorizontalLine(),
new QuText(stringfunc::makeTitle(uploadmethod_t)),
(new QuMcq(uploadmethod_fr, options_upload_method))
->setHorizontal(true)
->setAsTextButton(true),
questionnairefunc::defaultGridRawPointer({
{
stringfunc::makeTitle(maxsizeonestep_t, maxsizeonestep_h, true),
(new QuLineEditInt64(maxsizeonestep_fr))->setHint(
stringfunc::makeHint(maxsizeonestep_t, maxsizeonestep_h))
}
}, 1, 1),
});
page->setTitle(tr("Configure server settings"));
page->setType(QuPage::PageType::Config);
page->registerValidator(
std::bind(&ServerSettingsWindow::validateServerSettings,
this, std::placeholders::_1,
std::placeholders::_2)
);
auto questionnaire = new Questionnaire(m_app, {page});
questionnaire->setFinishButtonIconToTick();
connect(questionnaire, &Questionnaire::completed,
this, &ServerSettingsWindow::serverSettingsSaved);
connect(questionnaire, &Questionnaire::cancelled,
&m_app, &CamcopsApp::clearCachedVars);
return questionnaire;
}
bool ServerSettingsWindow::validateServerSettings(QStringList& errors,
const QuPage* page)
{
Q_UNUSED(page)
// Note that we are using cached server variables.
const QString hostname = m_app.getCachedVar(varconst::SERVER_ADDRESS).toString();
if (hostname.contains("/")) {
errors.append("No forward slashes ('/') permitted in server hostname");
return false;
}
return true;
}
void ServerSettingsWindow::serverSettingsSaved()
{
// User has edited server settings and then clicked OK.
const bool server_details_changed = (
m_app.cachedVarChanged(varconst::SERVER_ADDRESS)
|| m_app.cachedVarChanged(varconst::SERVER_PORT)
|| m_app.cachedVarChanged(varconst::SERVER_PATH)
);
if (server_details_changed) {
uifunc::alert(
tr("Server details have changed. You should consider "
"re-registering with the server."),
tr("Registration advised")
);
}
if (!m_app.storingServerPassword()) {
// Wipe the stored password
m_app.setCachedVar(varconst::SERVER_USERPASSWORD_OBSCURED, "");
}
m_app.saveCachedVars();
}