backend/wbpublic/grtui/grtdb_connect_panel.cpp (982 lines of code) (raw):
/*
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2.0,
* as published by the Free Software Foundation.
*
* This program is designed to work with certain software (including
* but not limited to OpenSSL) that is licensed under separate terms, as
* designated in a particular file or component or in included license
* documentation. The authors of MySQL hereby grant you an additional
* permission to link the program and your derivative works with the
* separately licensed software that they have either included with
* the program or referenced in the documentation.
* This program 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, version 2.0, for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "grtdb_connect_panel.h"
#include "grtdb_connection_editor.h"
#include "mforms/fs_object_selector.h"
#include "grtdb/db_helpers.h"
#include "base/string_utilities.h"
#include "base/log.h"
#include "base/file_utilities.h"
#include "mforms/uistyle.h"
#include "mforms/utilities.h"
#include "mforms/checkbox.h"
#include "mforms/textbox.h"
#include "mforms/app.h"
#include <mforms/radiobutton.h>
#include "objimpl/wrapper/mforms_ObjectReference_impl.h"
#define MYSQL_RDBMS_ID "com.mysql.rdbms.mysql"
DEFAULT_LOG_DOMAIN("DbConnectPanel");
using namespace base;
using namespace grtui;
using namespace mforms;
DbConnectPanel::DbConnectPanel(DbConnectPanelFlags flags)
: Box(false),
_connection(nullptr),
_tab(mforms::TabViewSystemStandard),
_content(false),
_params_panel(mforms::TransparentPanel),
_params_table(nullptr),
_ssl_panel(mforms::TransparentPanel),
_ssl_table(nullptr),
_advanced_panel(mforms::TransparentPanel),
_advanced_table(nullptr),
_options_panel(mforms::TransparentPanel),
_options_table(nullptr),
_create_group(false),
_show_connection_combo((flags & DbConnectPanelShowConnectionCombo) != 0),
_show_manage_connections((flags & DbConnectPanelShowManageConnections) != 0),
_dont_set_default_connection((flags & DbConnectPanelDontSetDefaultConnection) != 0),
_last_active_tab(-1) {
_allow_edit_connections = false;
_initialized = false;
_updating = false;
_skip_schema_name = false;
_delete_connection_be = false;
set_spacing(4);
if (_show_connection_combo) {
_allow_edit_connections = false;
_label1.set_text(_("Stored Connection:"));
} else {
_allow_edit_connections = true;
_label1.set_text(_("Connection Name:"));
}
_label2.set_text(_("Database System:"));
_label3.set_text(_("Connection Method:"));
_label1.set_text_align(MiddleRight);
_label2.set_text_align(MiddleRight);
_label3.set_text_align(MiddleRight);
if (_show_connection_combo)
_desc1.set_text(_("Select from saved connection settings"));
else
_desc1.set_text(_("Type a name for the connection"));
_desc1.set_style(mforms::SmallHelpTextStyle);
_desc2.set_text(_("Select a RDBMS from the list of supported systems"));
_desc2.set_style(mforms::SmallHelpTextStyle);
_desc3.set_text(_("Method to use to connect to the RDBMS"));
_desc3.set_style(mforms::SmallHelpTextStyle);
_rdbms_sel.set_name("Database System");
_stored_connection_sel.set_name("Connection List");
_driver_sel.set_name("Connection Method");
if (_show_connection_combo)
scoped_connect(_stored_connection_sel.signal_changed(),
std::bind(&DbConnectPanel::change_active_stored_conn, this));
scoped_connect(_rdbms_sel.signal_changed(), std::bind(&DbConnectPanel::change_active_rdbms, this));
scoped_connect(_driver_sel.signal_changed(), std::bind(&DbConnectPanel::change_active_driver, this));
scoped_connect(_name_entry.signal_changed(), std::bind(&DbConnectPanel::change_connection_name, this));
_name_entry.set_name("Connection Name");
_name_entry.setInternalName("connect_panel:table");
_table.set_name("Connection information");
_table.set_row_count(flags & DbConnectPanelShowRDBMSCombo ? 4 : 2);
_table.set_column_count(3);
_table.set_column_spacing(4);
_table.set_row_spacing(4);
int row = 0;
if (flags & DbConnectPanelShowRDBMSCombo) {
_table.add(&_label2, 0, 1, row, row + 1, mforms::HFillFlag);
_table.add(&_rdbms_sel, 1, 2, row, row + 1, mforms::HExpandFlag | mforms::HFillFlag | mforms::VFillFlag);
_table.add(&_desc2, 2, 3, row, row + 1, mforms::HFillFlag);
row++;
_table.add(mforms::manage(new mforms::Label()), 0, 1, row, row + 1, mforms::HFillFlag);
row++;
}
if (!(flags & DbConnectPanelHideConnectionName)) {
if (_show_connection_combo) {
_table.add(&_label1, 0, 1, row, row + 1, mforms::HFillFlag);
_table.add(&_stored_connection_sel, 1, 2, row, row + 1,
mforms::HExpandFlag | mforms::HFillFlag | mforms::VFillFlag);
_table.add(&_desc1, 2, 3, row, row + 1, mforms::HFillFlag);
} else {
_table.add(&_label1, 0, 1, row, row + 1, mforms::HFillFlag);
_table.add(&_name_entry, 1, 2, row, row + 1, mforms::HExpandFlag | mforms::HFillFlag | mforms::VFillFlag);
_table.add(&_desc1, 2, 3, row, row + 1, mforms::HFillFlag);
}
row++;
}
_label3.set_size(-1, 30);
_table.add(&_label3, 0, 1, row, row + 1, mforms::HFillFlag);
_table.add(&_driver_sel, 1, 2, row, row + 1, mforms::HExpandFlag | mforms::HFillFlag | mforms::VFillFlag);
_table.add(&_desc3, 2, 3, row, row + 1, mforms::HFillFlag);
_tab.set_name("Connection Details");
_params_panel.set_name("Parameters");
_ssl_panel.set_name("SSL");
_advanced_panel.set_name("Advanced");
_options_panel.set_name("Options");
set_name("Connection");
setInternalName("connect_panel");
add(&_content, true, true);
_content.add(&_table, false, true);
_content.add(&_tab, true, true);
_warning.set_style(mforms::SmallHelpTextStyle);
_warning.set_front_color("#FF0000");
_content.add(&_warning, false, true);
}
DbConnectPanel::~DbConnectPanel() {
if (_delete_connection_be)
delete _connection;
}
void DbConnectPanel::connection_user_input(std::string &text_entry, bool &create_group, bool new_entry /*= true*/) {
std::size_t pos = text_entry.find_first_of("/");
if (pos == std::string::npos)
return;
create_group = false;
std::string group = text_entry.substr(0, pos);
std::string message = (new_entry) ? "Do you want to create connection inside the group"
: "Do you want to split the name and move the connection to the group";
int ret = mforms::Utilities::show_message(
"Place Connection in a Group.",
base::strfmt("You have used a forward slash in your connection name, which is used to separate a group from the "
"real connection name.\n"
"%s '%s'? If you select 'No' all forward slashes in the name will be replaced by underscores.",
message.c_str(), group.c_str()),
_("Yes"), _("No"));
if (ret == mforms::ResultOk) {
create_group = true;
return;
}
while (pos != std::string::npos) {
text_entry[pos] = '_';
pos = text_entry.find_first_of("/", pos + 1);
}
}
void DbConnectPanel::change_connection_name() {
if (_create_group)
return;
std::string text = _name_entry.get_string_value();
connection_user_input(text, _create_group);
_name_entry.set_value(text);
}
void DbConnectPanel::set_skip_schema_name(bool flag) {
_skip_schema_name = flag;
}
void DbConnectPanel::suspend_view_layout(bool flag) {
if (flag)
suspend_layout();
else
resume_layout();
}
void DbConnectPanel::init(DbConnection *conn, const db_mgmt_ConnectionRef &default_conn) {
_connection = conn;
_delete_connection_be = false;
_connection->set_control_callbacks(std::bind(&DbConnectPanel::suspend_view_layout, this, std::placeholders::_1),
std::bind(&DbConnectPanel::begin_layout, this),
std::bind(&DbConnectPanel::create_control, this, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
std::bind(&DbConnectPanel::end_layout, this));
if (default_conn.is_valid())
_anonymous_connection = default_conn;
else {
_anonymous_connection = db_mgmt_ConnectionRef(grt::Initialized);
_anonymous_connection->owner(_connection->get_db_mgmt());
}
if (!_allowed_rdbms.is_valid()) {
_allowed_rdbms = grt::ListRef<db_mgmt_Rdbms>(true);
_allowed_rdbms.ginsert(_connection->get_db_mgmt()->rdbms()[0]);
}
_rdbms_sel.clear();
for (grt::ListRef<db_mgmt_Rdbms>::const_iterator iter = _allowed_rdbms.begin(); iter != _allowed_rdbms.end(); ++iter)
_rdbms_sel.add_item((*iter)->caption());
_rdbms_sel.set_selected(0);
_initialized = true;
change_active_rdbms();
if (!_anonymous_connection->driver().is_valid())
_anonymous_connection->driver(selected_driver());
if (_stored_connection_sel.get_selected_index() == 0) {
if (default_conn.is_valid())
_connection->set_connection_and_update(_anonymous_connection);
else
_connection->set_connection_keeping_parameters(_anonymous_connection);
}
}
void DbConnectPanel::init(const db_mgmt_ManagementRef &mgmt, const grt::ListRef<db_mgmt_Rdbms> &allowed_rdbms,
const db_mgmt_ConnectionRef &default_conn) {
if (!mgmt.is_valid())
throw std::invalid_argument("DbConnectPanel::init() called with invalid db mgmt object");
_allowed_rdbms = allowed_rdbms;
DbConnection *conn = new DbConnection(
mgmt, default_conn.is_valid() ? default_conn->driver() : _allowed_rdbms[0]->defaultDriver(), _skip_schema_name);
init(conn, default_conn);
_delete_connection_be = true;
}
void DbConnectPanel::init(const db_mgmt_ManagementRef &mgmt, const db_mgmt_ConnectionRef &default_conn) {
if (!mgmt.is_valid())
throw std::invalid_argument("DbConnectPanel::init() called with invalid db mgmt object");
init(mgmt, mgmt->rdbms(), default_conn);
}
db_mgmt_ConnectionRef DbConnectPanel::get_connection(bool initInvalid) {
if (!_connection->get_connection().is_valid() && initInvalid) {
db_mgmt_ConnectionRef connection(grt::Initialized);
connection->owner(get_be()->get_db_mgmt());
connection->driver(selected_driver());
set_connection(connection);
change_active_stored_conn();
}
return _connection->get_connection();
}
grt::ListRef<db_mgmt_Connection> DbConnectPanel::connection_list() {
if (_rdbms_sel.get_item_count() > 0) {
int i = _rdbms_sel.get_selected_index();
if (i >= 0 && i < (int)_allowed_rdbms->count()) {
if (_allowed_rdbms[i]->id() == MYSQL_RDBMS_ID)
return _connection->get_db_mgmt()->storedConns();
else
return _connection->get_db_mgmt()->otherStoredConns();
}
}
db_mgmt_ConnectionRef conn(get_connection());
if (conn.is_valid() && conn->driver().is_valid() && conn->driver()->owner().is_valid() &&
conn->driver()->owner().id() == MYSQL_RDBMS_ID)
return _connection->get_db_mgmt()->storedConns();
else
return _connection->get_db_mgmt()->otherStoredConns();
}
void DbConnectPanel::set_connection(const db_mgmt_ConnectionRef &conn) {
const grt::ListRef<db_mgmt_Connection> list(connection_list());
int count = 0;
grt::ListRef<db_mgmt_Connection>::const_iterator iter = list.begin();
grt::StringRef conn_host = conn->hostIdentifier();
for (; iter != list.end(); ++iter) {
if (conn == (*iter)) {
_stored_connection_sel.set_selected(count + 1);
change_active_stored_conn();
break;
}
++count;
}
}
void DbConnectPanel::set_enabled(bool flag) {
_name_entry.set_enabled(flag);
_stored_connection_sel.set_enabled(flag);
_rdbms_sel.set_enabled(flag);
_driver_sel.set_enabled(flag);
for (std::list<mforms::View *>::const_iterator iter = _views.begin(); iter != _views.end(); ++iter)
(*iter)->set_enabled(flag);
}
void DbConnectPanel::set_default_host_name(const std::string &host, bool update) {
_default_host_name = host;
/*
if (update)
{
for (std::map<std::string, db_mgmt_ConnectionRef>::iterator iter= _anonymous_connections.begin();
iter != _anonymous_connections.end(); ++iter)
{
if (is_ssh_driver(iter->first))
iter->second->parameterValues().gset("sshHost", _default_host_name);
else
iter->second->parameterValues().gset("hostName", _default_host_name);
}
// force UI update
change_active_driver();
}*/
}
void DbConnectPanel::param_value_changed(mforms::View *sender, bool trim_whitespace) {
std::string param_name = sender->getInternalName();
if (!_allow_edit_connections && !_updating) {
// if stored connections combo is shown, copy the current connection params to the
// to anonymous connection and select it
// since stored connections are not editable in this case
_connection->set_connection_keeping_parameters(_anonymous_connection);
if (_stored_connection_sel.get_selected_index() != 0)
_stored_connection_sel.set_selected(0);
}
DbDriverParam *param = _connection->get_db_driver_param_handles()->get(param_name);
if (trim_whitespace)
param->set_value(grt::StringRef(base::trim(sender->get_string_value())));
else
param->set_value(grt::StringRef(sender->get_string_value()));
_connection->save_changes();
std::string error = _connection->validate_driver_params();
if (error != _last_validation)
_signal_validation_state_changed(error, error.empty());
_last_validation = error;
}
void DbConnectPanel::enum_param_value_changed(mforms::Selector *sender, std::vector<std::string> options) {
std::string param_name = sender->getInternalName();
if (!_allow_edit_connections && !_updating) {
// if stored connections combo is shown, copy the current connection params to the
// to anonymous connection and select it
// since stored connections are not editable in this case
_connection->set_connection_keeping_parameters(_anonymous_connection);
if (_stored_connection_sel.get_selected_index() != 0)
_stored_connection_sel.set_selected(0);
}
DbDriverParam *param = _connection->get_db_driver_param_handles()->get(param_name);
int i = sender->get_selected_index();
if (i >= 0)
param->set_value(grt::StringRef(options[i]));
else
param->set_value(grt::StringRef(""));
if (_connection) {
_connection->save_changes();
std::string error = _connection->validate_driver_params();
if (error != _last_validation)
_signal_validation_state_changed(error, error.empty());
_last_validation = error;
}
}
void DbConnectPanel::change_active_rdbms() {
if (_initialized && !_updating) {
if (!_allow_edit_connections) {
_connection->set_connection_keeping_parameters(_anonymous_connection);
if (_stored_connection_sel.get_selected_index() != 0)
_stored_connection_sel.set_selected(0);
}
db_mgmt_RdbmsRef active_rdbms(selected_rdbms());
if (active_rdbms.is_valid()) {
int i = 0;
int default_driver = -1;
_updating = true;
// refresh list of drivers
grt::ListRef<db_mgmt_Driver> drivers(active_rdbms->drivers());
_driver_sel.clear();
for (grt::ListRef<db_mgmt_Driver>::const_iterator iter = drivers.begin(); iter != drivers.end(); ++iter, ++i) {
_driver_sel.add_item((*iter)->caption());
if ((*iter) == active_rdbms->defaultDriver())
default_driver = i;
}
if (_show_connection_combo) {
// refresh list of stored connections
// this will select the driver for the default connection
refresh_stored_connections();
if (_stored_connection_sel.get_selected_index() > 0)
change_active_stored_conn();
else
_connection->set_driver_and_update(selected_driver());
} else {
// select the default driver for the rdbms
if (default_driver >= 0)
_driver_sel.set_selected(default_driver);
_connection->set_driver_and_update(selected_driver());
}
_updating = false;
} else
logWarning("DbConnectPanel: no active rdbms\n");
}
}
db_mgmt_RdbmsRef DbConnectPanel::selected_rdbms() {
int i = _rdbms_sel.get_selected_index();
if (i >= 0 && i < (int)_allowed_rdbms.count())
return _allowed_rdbms[i];
return db_mgmt_RdbmsRef();
}
db_mgmt_DriverRef DbConnectPanel::selected_driver() {
int i = _driver_sel.get_selected_index();
if (i >= 0 && i < (int)selected_rdbms()->drivers().count())
return selected_rdbms()->drivers()[i];
return db_mgmt_DriverRef();
}
void DbConnectPanel::change_active_driver() {
if (_initialized && !_updating) {
if (!_allow_edit_connections) {
_connection->set_connection_keeping_parameters(_anonymous_connection);
if (_stored_connection_sel.get_selected_index() != 0)
_stored_connection_sel.set_selected(0);
}
db_mgmt_DriverRef current_driver = _connection->driver();
db_mgmt_DriverRef new_driver = selected_driver();
if (new_driver == current_driver)
return;
_content.show(false);
// When switching to/from ssh based connections the value for the host name gets another
// semantic. In ssh based connections the sshHost is the remote server name (what is otherwise
// the host name) and the host name is relative to the ssh host (usually localhost).
db_mgmt_ConnectionRef actual_connection = get_connection(true);
if (*current_driver->name() == "MysqlNativeSSH") {
std::string machine = actual_connection->parameterValues().get_string("sshHost");
if (machine.find(':') != std::string::npos)
machine = machine.substr(0, machine.find(':'));
actual_connection->parameterValues().gset("hostName", machine);
} else if (*new_driver->name() == "MysqlNativeSSH") {
std::string machine = actual_connection->parameterValues().get_string("hostName");
actual_connection->parameterValues().gset("sshHost", machine + ":22");
actual_connection->parameterValues().gset("hostName", "127.0.0.1");
}
if (_driver_changed_cb)
_driver_changed_cb(new_driver);
_connection->set_driver_and_update(new_driver);
_content.show();
// db_mgmt_ConnectionRef conn(_connection->get_connection());
// grt::DictRef current_params(conn->parameterValues());
// // save current driver params
// for (grt::DictRef::const_iterator iter = current_params.begin(); iter != current_params.end(); ++iter)
// _parameters_per_driver[conn->driver()->name()]= grt::DictRef::cast_from(grt::copy_value(current_params,
// false));
// db_mgmt_DriverRef new_driver(selected_driver());
// // update params to driver-specific params
// if (_parameters_per_driver.find(new_driver->name()) == _parameters_per_driver.end())
// {
// grt::DictRef params(grt::DictRef::cast_from(grt::copy_value(current_params, false)));
//
// if (!_default_host_name.empty())
// {
// if (is_ssh_driver(new_driver->name()))
// params.gset("sshHost", _default_host_name);
// else
// params.gset("hostName", _default_host_name);
// }
//
// _parameters_per_driver[new_driver->name()]= params;
// }
// grt::replace_contents(conn->parameterValues(), _parameters_per_driver[new_driver->name()]);
{
// we update the validation msg
_last_validation = _connection->validate_driver_params();
// notify the frontend that the state has changed but don't show any error
// even if there is one
_signal_validation_state_changed("", _last_validation.empty());
}
}
}
void DbConnectPanel::refresh_stored_connections() {
grt::ListRef<db_mgmt_Connection> list(connection_list());
db_mgmt_RdbmsRef rdbms = selected_rdbms();
int selected_index = 0, i = 1;
_stored_connection_sel.clear();
_stored_connection_sel.add_item("");
for (grt::ListRef<db_mgmt_Connection>::const_iterator iter = list.begin(); iter != list.end(); ++iter) {
if (is_connectable_driver_type((*iter)->driver())) {
if (!rdbms.is_valid() || ((*iter)->driver().is_valid() && (*iter)->driver()->owner() == rdbms)) {
_stored_connection_sel.add_item((*iter)->name());
if (*(*iter)->isDefault() && !_dont_set_default_connection)
selected_index = i;
i++;
}
}
}
if (_show_manage_connections) {
_stored_connection_sel.add_item("-");
_stored_connection_sel.add_item(_("Manage Stored Connections..."));
}
if (_stored_connection_sel.get_selected_index() != selected_index)
_stored_connection_sel.set_selected(selected_index);
}
/**
Save the current connection with the given name.
*/
void DbConnectPanel::save_connection_as(const std::string &name) {
_connection->save_changes();
db_mgmt_ConnectionRef conn(_connection->get_connection());
grt::ListRef<db_mgmt_Connection> list(_connection->get_db_mgmt()->storedConns());
db_mgmt_ConnectionRef dup;
if ((dup = find_named_object_in_list(list, name, true, "name")).is_valid()) {
list->remove(dup);
}
list = _connection->get_db_mgmt()->otherStoredConns();
if ((dup = find_named_object_in_list(list, name, true, "name")).is_valid()) {
list->remove(dup);
}
conn->name(name);
conn->owner(_connection->get_db_mgmt());
connection_list().insert(conn);
refresh_stored_connections();
change_active_stored_conn();
}
bool DbConnectPanel::test_connection() {
std::string message = "Information related to this connection:\n\n";
bool failed = false;
try {
sql::DriverManager *dbc_drv_man = sql::DriverManager::getDriverManager();
db_mgmt_ConnectionRef connectionProperties = get_be()->get_connection();
if (!connectionProperties.is_valid()) {
db_mgmt_ConnectionRef connection(grt::Initialized);
connection->owner(get_be()->get_db_mgmt());
connection->driver(selected_driver());
set_connection(connection);
change_active_stored_conn();
connectionProperties = get_be()->get_connection();
}
std::string ssl_cipher;
message.append("Host: " + connectionProperties->parameterValues().get_string("hostName") + "\n");
message.append("Port: " + grt::IntegerRef(connectionProperties->parameterValues().get_int("port")).toString() +
"\n");
message.append("User: " + connectionProperties->parameterValues().get_string("userName") + "\n");
{
sql::ConnectionWrapper _dbc_conn = dbc_drv_man->getConnection(connectionProperties);
if (_dbc_conn.get() && !_dbc_conn->isClosed()) {
// check that we're connecting to a known and supported version of the server
std::string version;
{
std::unique_ptr<sql::Statement> stmt(_dbc_conn->createStatement());
std::unique_ptr<sql::ResultSet> result(stmt->executeQuery("SELECT version()"));
if (result->next())
version = result->getString(1);
}
if (!bec::is_supported_mysql_version(version)) {
logError("Unsupported server version: %s %s\n", _dbc_conn->getMetaData()->getDatabaseProductName().c_str(),
version.c_str());
// TODO: we have the same message in wb_context.cpp. Unite them.
if (mforms::Utilities::show_warning(
"Connection Warning",
base::strfmt("Incompatible/nonstandard server version or connection protocol detected (%s).\n\n"
"A connection to this database can be established but some MySQL Workbench features may "
"not work properly since the database is not fully compatible with the supported versions "
"of MySQL.\n\n"
"MySQL Workbench is developed and tested for MySQL Server versions 5.6, 5.7 and 8.0\n"
"Please note: there may be some incompatibilities with version 8.4.",
bec::sanitize_server_version_number(version).c_str()),
"Continue Anyway", "Cancel") != mforms::ResultOk)
return false;
}
// check ssl
{
std::unique_ptr<sql::Statement> stmt(_dbc_conn->createStatement());
std::unique_ptr<sql::ResultSet> result(stmt->executeQuery("SHOW SESSION STATUS LIKE 'Ssl_cipher'"));
if (result->next())
ssl_cipher = result->getString(2);
if (ssl_cipher.empty())
message.append("SSL: not enabled\n");
else
message.append("SSL: enabled with " + ssl_cipher + "\n");
}
} else {
message = "Connection Failed";
failed = true;
}
}
} catch (const std::exception &e) {
message = e.what();
failed = true;
}
bool ret_val = false;
if (message != "Operation Cancelled") {
std::string title;
if (failed) {
title = base::strfmt("Failed to Connect to %s",
bec::get_description_for_connection(get_be()->get_connection()).c_str());
mforms::Utilities::show_error(title, message, "OK");
} else {
message.append("\nA successful MySQL connection was made with\nthe parameters defined for this connection.");
mforms::Utilities::show_message("Successfully made the MySQL connection", message, "OK");
ret_val = true;
}
}
return ret_val;
}
void DbConnectPanel::set_active_stored_conn(const std::string &name) {
if (name.empty())
_connection->set_connection_keeping_parameters(_anonymous_connection);
else
set_active_stored_conn(find_named_object_in_list(connection_list(), name, true, "name"));
}
void DbConnectPanel::set_active_stored_conn(db_mgmt_ConnectionRef connection) {
_warning.set_text("");
if (!connection.is_valid())
connection = _anonymous_connection;
db_mgmt_DriverRef driver = connection->driver();
if (!driver.is_valid()) {
logError("Connection %s has no driver set\n", connection->name().c_str());
return;
}
db_mgmt_RdbmsRef rdbms = db_mgmt_RdbmsRef::cast_from(driver->owner());
// check if the rdbms of the connection is not the selected one (usually should be)
if (rdbms.is_valid() && selected_rdbms() != rdbms) {
size_t rdbms_index = find_object_index_in_list(_allowed_rdbms, rdbms->id());
_rdbms_sel.set_selected((int)rdbms_index);
change_active_rdbms();
}
// ensure the correct driver is selected in the selector
ssize_t driver_index = find_object_index_in_list(rdbms->drivers(), driver->id());
if (driver_index >= 0 && driver_index < _driver_sel.get_item_count())
_driver_sel.set_selected((int)driver_index);
// mark this connection as the active one for this rdbms type
if (!_dont_set_default_connection) {
grt::ListRef<db_mgmt_Connection> conns(connection_list());
for (size_t c = conns->count(), i = 0; i < c; i++) {
db_mgmt_ConnectionRef conn(conns[i]);
if (conn->driver().is_valid() && conn->driver()->owner() == rdbms)
conn->isDefault(0);
}
connection->isDefault(1);
}
// set the connection as the one being edited, updating the UI for it
_connection->set_connection_and_update(connection);
if (!_show_connection_combo)
_name_entry.set_value(connection->name());
}
void DbConnectPanel::change_active_stored_conn() {
static bool choosing = false;
if (_initialized && !choosing) {
_updating = true;
if (_show_manage_connections &&
_stored_connection_sel.get_selected_index() == _stored_connection_sel.get_item_count() - 1) {
choosing = true;
db_mgmt_ConnectionRef connection = open_editor();
refresh_stored_connections();
if (connection.is_valid())
_stored_connection_sel.set_selected(_stored_connection_sel.index_of_item_with_title(*connection->name()));
else
_stored_connection_sel.set_selected(0);
show(false);
set_active_stored_conn(connection);
show();
choosing = false;
} else {
std::string name = _stored_connection_sel.get_string_value();
show(false);
set_active_stored_conn(name);
show();
}
_updating = false;
// Revalidate connection parameters.
std::string error = _connection->validate_driver_params();
if (error != _last_validation)
_signal_validation_state_changed(error, error.empty());
_last_validation = error;
}
}
void DbConnectPanel::launch_ssl_wizard() {
mforms::Form *parent = get_parent_form();
grt::BaseListRef args(true);
args.ginsert(mforms_to_grt(parent, "Form"));
args.ginsert(get_connection(true));
args.ginsert(grt::StringRef(get_connection(true)->id()));
grt::GRT::get()->call_module_function("PyWbUtils", "generateCertificates", args);
_connection->update();
}
void DbConnectPanel::open_ssl_wizard_directory() {
std::string path = base::joinPath(mforms::App::get()->get_user_data_folder().c_str(), "certificates",
get_connection()->id().c_str(), "");
if (base::is_directory(path))
Utilities::open_url(path);
else
mforms::Utilities::show_warning(
_("Cannot Open Directory"),
_("The directory that should contain the files does not exist yet. Maybe you need to run the SSL Wizard first."),
_("OK"));
}
db_mgmt_ConnectionRef DbConnectPanel::open_editor() {
grt::ListRef<db_mgmt_Rdbms> rdbms_list(true);
rdbms_list.ginsert(selected_rdbms());
DbConnectionEditor editor(_connection->get_db_mgmt());
return editor.run(_connection->get_connection());
}
void DbConnectPanel::begin_layout() {
_last_active_tab = _tab.get_active_tab();
if (_params_table) {
_params_panel.remove(_params_table);
_tab.remove_page(&_params_panel);
}
if (_ssl_table) {
_ssl_panel.remove(_ssl_table);
_tab.remove_page(&_ssl_panel);
}
if (_advanced_table) {
_advanced_panel.remove(_advanced_table);
_tab.remove_page(&_advanced_panel);
}
if (_options_table) {
_options_panel.remove(_options_table);
_tab.remove_page(&_options_panel);
}
_params_table = mforms::manage(new mforms::Table());
_params_table->set_release_on_add();
_params_table->set_name("Parameters Table");
_params_table->setInternalName("params_table");
_params_table->set_column_count(3);
_params_table->set_row_spacing(MF_TABLE_ROW_SPACING);
_params_table->set_column_spacing(MF_TABLE_COLUMN_SPACING);
_params_table->set_padding(MF_PANEL_PADDING);
_ssl_table = mforms::manage(new mforms::Table());
_ssl_table->set_name("SSL Table");
_ssl_table->setInternalName("ssl_table");
_ssl_table->set_column_count(3);
_ssl_table->set_row_spacing(MF_TABLE_ROW_SPACING);
_ssl_table->set_column_spacing(MF_TABLE_COLUMN_SPACING);
_ssl_table->set_padding(MF_PANEL_PADDING);
_advanced_table = mforms::manage(new mforms::Table());
_advanced_table->set_name("Advanced Table");
_advanced_table->setInternalName("advanced_table");
_advanced_table->set_column_count(3);
_advanced_table->set_row_spacing(MF_TABLE_ROW_SPACING);
_advanced_table->set_column_spacing(MF_TABLE_COLUMN_SPACING);
_advanced_table->set_padding(MF_PANEL_PADDING);
_options_table = mforms::manage(new mforms::Table());
_options_table->set_name("Options Table");
_options_table->setInternalName("options_table");
_options_table->set_column_count(3);
_options_table->set_row_spacing(MF_TABLE_ROW_SPACING);
_options_table->set_column_spacing(MF_TABLE_COLUMN_SPACING);
_options_table->set_padding(MF_PANEL_PADDING);
_views.clear();
_param_rows.clear();
_ssl_rows.clear();
_advanced_rows.clear();
_options_rows.clear();
}
void DbConnectPanel::end_layout() {
if (!_param_rows.empty()) {
_params_panel.add(_params_table);
_tab.add_page(&_params_panel, _("Parameters"));
}
if (!_ssl_rows.empty()) {
_ssl_panel.add(_ssl_table);
_tab.add_page(&_ssl_panel, _("SSL"));
}
if (!_advanced_rows.empty()) {
_advanced_panel.add(_advanced_table);
_tab.add_page(&_advanced_panel, _("Advanced"));
}
if (!_options_rows.empty()) {
_options_panel.add(_options_table);
_tab.add_page(&_options_panel, _("Options"));
}
if (_last_active_tab != -1)
_tab.set_active_tab(_last_active_tab);
}
void DbConnectPanel::set_keychain_password(DbDriverParam *param, bool clear) {
std::string storageKey;
std::string userName;
grt::DictRef paramValues(get_connection(true)->parameterValues());
std::vector<std::string> tokens =
base::split(param->object()->paramTypeDetails().get_string("storageKeyFormat"), "::");
if (tokens.size() == 2) {
userName = tokens[0];
storageKey = tokens[1];
} else {
logError("Invalid storage key format for option %s\n", param->object().id().c_str());
return;
}
for (grt::DictRef::const_iterator iter = paramValues.begin(); iter != paramValues.end(); ++iter) {
storageKey = base::replaceString(storageKey, "%" + iter->first + "%", iter->second.toString());
userName = base::replaceString(userName, "%" + iter->first + "%", iter->second.toString());
}
if (storageKey.substr(0, 3) == "ssh") { // This is ssh, we should check if port was given, if not, we will append the default 22.
auto pos = storageKey.find_last_of(":");
if (pos == std::string::npos) {
storageKey.append(":22");
}
}
if (userName.empty()) {
mforms::Utilities::show_warning(_("Cannot Set Password"), _("Please fill the username to be used."), _("OK"));
return;
}
if (clear) {
try {
mforms::Utilities::forget_password(storageKey, userName);
} catch (std::exception &exc) {
mforms::Utilities::show_error("Clear Password", base::strfmt("Could not clear password: %s", exc.what()), "OK");
}
} else {
std::string password;
try {
if (mforms::Utilities::ask_for_password("Store Password For Connection", storageKey, userName, password))
mforms::Utilities::store_password(storageKey, userName, password);
} catch (std::exception &exc) {
mforms::Utilities::show_error("Store Password", base::strfmt("Could not store password: %s", exc.what()), "OK");
}
}
}
void DbConnectPanel::create_control(::DbDriverParam *driver_param, const ::ControlType ctrl_type,
const ControlBounds &bounds, const std::string &caption) {
bool is_new_line = false;
Table *table = NULL;
Box *box = NULL;
std::vector<mforms::Box *> *rows = NULL;
switch (driver_param->object()->layoutAdvanced()) {
case 0:
rows = &_param_rows;
table = _params_table;
break;
case 1:
rows = &_advanced_rows;
table = _advanced_table;
break;
case 2:
rows = &_ssl_rows;
table = _ssl_table;
break;
case 3:
rows = &_options_rows;
table = _options_table;
break;
default:
return;
}
if (bounds.top >= (int)rows->size()) {
is_new_line = true;
table->set_row_count((int)rows->size() + 1);
if (ctrl_type == ::ctCheckBox && table != _params_table) {
rows->push_back(box = mforms::manage(new Box(false)));
box->set_spacing(0);
} else {
rows->push_back(box = mforms::manage(new Box(true)));
box->set_spacing(4);
}
_views.push_back(box);
mforms::TableItemFlags flags;
flags = mforms::HExpandFlag | mforms::HFillFlag | mforms::VFillFlag;
if (driver_param->get_type() == DbDriverParam::ptText)
flags = flags | mforms::VExpandFlag | mforms::VFillFlag;
table->add(mforms::manage(box), 1, 2, bounds.top, bounds.top + 1, flags);
} else
box = (*rows)[bounds.top];
switch (ctrl_type) {
case ::ctLabel: {
Label *label = new Label();
label->set_text(caption);
label->set_name(driver_param->get_accessibility_name());
label->set_text_align(mforms::TopRight);
if (is_new_line)
table->add(mforms::manage(label), 0, 1, bounds.top, bounds.top + 1, mforms::HFillFlag | mforms::VFillFlag);
else
box->add(mforms::manage(label), false, true);
_views.push_back(label);
break;
}
case ::ctDescriptionLabel: {
Label *label = new Label();
label->set_text(caption);
label->set_name(driver_param->get_accessibility_name());
label->set_text_align(mforms::TopLeft);
label->set_style(mforms::SmallHelpTextStyle);
label->set_wrap_text(true);
label->set_size(250, -1);
table->add(mforms::manage(label), 2, 3, bounds.top, bounds.top + 1, mforms::HFillFlag | mforms::VFillFlag);
_views.push_back(label);
break;
}
case ::ctButton: {
Button *btn = new Button();
btn->set_text(caption);
btn->set_size(bounds.width, 30);
box->add(mforms::manage(btn), false, true);
_views.push_back(btn);
if (driver_param->object()->name() == "sslWizard") {
scoped_connect(btn->signal_clicked(), std::bind(&DbConnectPanel::launch_ssl_wizard, this));
} else if (driver_param->object()->name() == "openSSLWizardDirectory") {
scoped_connect(btn->signal_clicked(), std::bind(&DbConnectPanel::open_ssl_wizard_directory, this));
}
break;
}
case ::ctCheckBox: {
CheckBox *ctrl = new CheckBox();
ctrl->set_name(driver_param->get_accessibility_name());
ctrl->setInternalName(driver_param->get_control_name());
ctrl->set_text(caption);
// value
{
grt::StringRef value = driver_param->get_value_repr();
if (value.is_valid())
ctrl->set_active(*value != "" && *value != "0" && *value != "NULL");
}
scoped_connect(ctrl->signal_clicked(), std::bind(&DbConnectPanel::param_value_changed, this, ctrl, false));
box->add(mforms::manage(ctrl), false, true);
_views.push_back(ctrl);
break;
}
case ::ctKeychainPassword: {
Button *btn = new Button();
#ifdef _MSC_VER
btn->set_text("Store in Vault ...");
btn->set_tooltip(_("Store the password for this connection in a secured vault"));
#else
btn->set_text("Store in Keychain ...");
btn->set_tooltip(_("Store the password for this connection in the system's keychain"));
#endif
box->add(mforms::manage(btn), false, true);
_views.push_back(btn);
scoped_connect(btn->signal_clicked(),
std::bind(&DbConnectPanel::set_keychain_password, this, driver_param, false));
btn = new Button();
btn->set_text("Clear");
btn->set_size(100, -1);
#ifdef _MSC_VER
btn->set_tooltip(_("Remove the previously stored password from the secured vault"));
#else
btn->set_tooltip(_("Remove the previously stored password from the system's keychain"));
#endif
box->add(mforms::manage(btn), false, true);
_views.push_back(btn);
scoped_connect(btn->signal_clicked(),
std::bind(&DbConnectPanel::set_keychain_password, this, driver_param, true));
break;
}
case ::ctTextBox: {
bool is_password = ::DbDriverParam::ptPassword == driver_param->get_type();
TextEntry *ctrl = new TextEntry(is_password ? PasswordEntry : NormalEntry);
ctrl->set_name(driver_param->get_accessibility_name());
ctrl->setInternalName(driver_param->get_control_name());
// value
{
grt::StringRef value = driver_param->getValue();
if (value.is_valid())
ctrl->set_value(*value);
}
ctrl->set_size(bounds.width, -1);
scoped_connect(ctrl->signal_changed(), std::bind(&DbConnectPanel::param_value_changed, this, ctrl, true));
box->add(mforms::manage(ctrl), true, true);
_views.push_back(ctrl);
break;
}
case ::ctText: {
TextBox *ctrl = new TextBox(mforms::VerticalScrollBar);
ctrl->set_name(driver_param->get_accessibility_name());
ctrl->setInternalName(driver_param->get_control_name());
// value
{
grt::StringRef value = driver_param->get_value_repr();
if (value.is_valid())
ctrl->set_value(*value);
}
ctrl->set_size(bounds.width, -1);
scoped_connect(ctrl->signal_changed(), std::bind(&DbConnectPanel::param_value_changed, this, ctrl, false));
box->add(mforms::manage(ctrl), true, true);
_views.push_back(ctrl);
break;
}
case ::ctFileSelector: {
FsObjectSelector *ctrl = new FsObjectSelector();
ctrl->set_name(driver_param->get_accessibility_name());
ctrl->setInternalName(driver_param->get_control_name());
// value
grt::StringRef value = driver_param->get_value_repr();
std::string initial_value = "";
if (value.is_valid())
initial_value = *value;
ctrl->set_size(bounds.width, -1);
ctrl->initialize(initial_value, mforms::OpenFile, "", true,
std::bind(&DbConnectPanel::param_value_changed, this, ctrl, true));
box->add(mforms::manage(ctrl), true, true);
_views.push_back(ctrl);
break;
}
case ::ctDirSelector: {
FsObjectSelector *ctrl = new FsObjectSelector();
ctrl->set_name(driver_param->get_accessibility_name());
ctrl->setInternalName(driver_param->get_control_name());
// value
grt::StringRef value = driver_param->get_value_repr();
std::string initial_value = "";
if (value.is_valid())
initial_value = *value;
ctrl->set_size(bounds.width, -1);
ctrl->initialize(initial_value, mforms::OpenDirectory, "", true,
std::bind(&DbConnectPanel::param_value_changed, this, ctrl, true));
box->add(mforms::manage(ctrl), true, true);
_views.push_back(ctrl);
break;
}
case ::ctEnumSelector: {
mforms::Selector *ctrl = new Selector();
ctrl->set_name(driver_param->get_accessibility_name());
ctrl->setInternalName(driver_param->get_control_name());
std::vector<std::pair<std::string, std::string> > options;
std::vector<std::string> option_ids;
std::string value = driver_param->get_value_repr();
int idx = -1;
try {
options = driver_param->get_enum_options();
} catch (std::exception &e) {
logError("Error calling get_enum_options() for param %s: %s", driver_param->get_control_name().c_str(),
e.what());
mforms::Utilities::show_error(
"Connection Setup",
base::strfmt("An error occurred while retrieving values for option '%s' from '%s'.\n\n%s",
driver_param->object()->name().c_str(), selected_driver()->name().c_str(), e.what()),
"OK", "", "");
}
for (size_t i = 0; i < options.size(); i++) {
ctrl->add_item(options[i].second);
option_ids.push_back(options[i].first);
if (value == options[i].first)
idx = (int)i;
}
if (idx >= 0)
ctrl->set_selected(idx);
enum_param_value_changed(ctrl, option_ids);
scoped_connect(ctrl->signal_changed(),
std::bind(&DbConnectPanel::enum_param_value_changed, this, ctrl, option_ids));
box->add(mforms::manage(ctrl), true, true);
_views.push_back(ctrl);
break;
}
case ::ctEnumOption: {
std::vector<std::pair<std::string, std::string> > options;
std::string value;
try {
options = driver_param->get_enum_options();
value = _connection->get_connection()->parameterValues().get("kerberosMode").toString();
} catch (std::exception &e) {
logError("Error calling get_enum_options() for param %s: %s", driver_param->get_control_name().c_str(),
e.what());
mforms::Utilities::show_error(
"Connection Setup",
base::strfmt("An error occurred while retrieving values for option '%s' from '%s'.\n\n%s",
driver_param->object()->name().c_str(), selected_driver()->name().c_str(), e.what()),
"OK", "", "");
}
Box *inner_box = new Box(false);
unsigned int group_id = mforms::RadioButton::new_id();
for (auto &option: options) {
mforms::RadioButton *rb = new mforms::RadioButton(group_id);
rb->set_text(option.second);
rb->set_name(driver_param->get_accessibility_name().toString() + "_" + option.first);
rb->setInternalName(driver_param->get_control_name().toString() + "_" + option.first);
rb->set_active(option.first == value);
#ifndef _MSC_VER
if (option.first == "1")
rb->set_enabled(false);
#endif
scoped_connect(rb->signal_clicked(), [=](){
_connection->get_connection()->parameterValues().gset("kerberosMode", option.first);
});
inner_box->add(mforms::manage(rb), false, true);
}
box->add(mforms::manage(inner_box), true, true);
break;
}
default:
logWarning("Unknown param type for %s\n", driver_param->get_control_name().c_str());
break;
}
}
//--------------------------------------------------------------------------------------------------
bool DbConnectPanel::is_connectable_driver_type(db_mgmt_DriverRef driver) {
if (driver.is_valid()) {
std::string d = driver->id();
if (driver->owner().is_valid()) {
if (driver->owner()->id() != MYSQL_RDBMS_ID || d == "com.mysql.rdbms.mysql.driver.native" ||
d == "com.mysql.rdbms.mysql.driver.native_socket" || d == "com.mysql.rdbms.mysql.driver.native_sshtun") {
return true;
}
}
}
return false;
}