FEAT(client): Allow specifying config file via commandline switch

This adds the switch -c/--config to the mumble client. If -c is
followed by a filename, this file will be read instead of the
standard config. A new config option database= has been added to
the 'General' section of the ini file. This can be used to specify
a different database which is necessary to run multiple completely
separate Mumble instances at the same time.

Additionaly the (undocumented) function to merge another ini file
by providing it as a parameter has been removed as it was
conflicting with the added functionality.

FIXES #3953
This commit is contained in:
Popkornium18 2020-07-31 20:18:51 +02:00
parent 3a07016b8f
commit 4edae462fa
7 changed files with 116 additions and 52 deletions

View File

@ -40,11 +40,10 @@ static bool execQueryAndLogFailure(QSqlQuery &query, const QString &queryString)
}
Database::Database(const QString &dbname) {
db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbname);
bool Database::findOrCreateDatabase()
{
QSettings qs;
QStringList datapaths;
int i;
datapaths << g.qdBasePath.absolutePath();
datapaths << QStandardPaths::writableLocation(QStandardPaths::DataLocation);
@ -55,42 +54,65 @@ Database::Database(const QString &dbname) {
datapaths << QDir::currentPath();
datapaths << qApp->applicationDirPath();
datapaths << qs.value(QLatin1String("InstPath")).toString();
bool found = false;
datapaths.removeAll(QLatin1String(""));
datapaths.removeDuplicates();
for (i = 0; (i < datapaths.size()) && ! found; i++) {
if (!datapaths[i].isEmpty()) {
// Try the legacy path first, and use it if it exists.
// If it doesn't, use the new, non-hidden version.
QFile legacyDatabaseFile(datapaths[i] + QLatin1String("/.mumble.sqlite"));
if (legacyDatabaseFile.exists()) {
db.setDatabaseName(legacyDatabaseFile.fileName());
found = db.open();
// Try to find an existing database
foreach(const QString &datapath, datapaths) {
// Try the legacy path first, and use it if it exists.
// If it doesn't, use the new, non-hidden version.
QFile legacyDatabaseFile(datapath + QLatin1String("/.mumble.sqlite"));
if (legacyDatabaseFile.exists()) {
db.setDatabaseName(legacyDatabaseFile.fileName());
if (db.open()) {
return true;
}
if (found) {
break;
}
QFile databaseFile(datapaths[i] + QLatin1String("/mumble.sqlite"));
if (databaseFile.exists()) {
db.setDatabaseName(databaseFile.fileName());
found = db.open();
}
QFile databaseFile(datapath + QLatin1String("/mumble.sqlite"));
if (databaseFile.exists()) {
db.setDatabaseName(databaseFile.fileName());
if (db.open()) {
return true;
}
}
}
if (! found) {
for (i = 0; (i < datapaths.size()) && ! found; i++) {
if (!datapaths[i].isEmpty()) {
QDir::root().mkpath(datapaths[i]);
QFile f(datapaths[i] + QLatin1String("/mumble.sqlite"));
db.setDatabaseName(f.fileName());
found = db.open();
// There is no existing database, so we create one
foreach(const QString &datapath, datapaths) {
QDir::root().mkpath(datapath);
QFile f(datapath + QLatin1String("/mumble.sqlite"));
db.setDatabaseName(f.fileName());
if (db.open()) {
return true;
}
}
return false;
}
Database::Database(const QString &dbname) {
db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbname);
if (!g.s.qsDatabaseLocation.isEmpty()) {
QFile configuredLocation(g.s.qsDatabaseLocation);
if (configuredLocation.exists()) {
db.setDatabaseName(g.s.qsDatabaseLocation);
db.open();
} else {
int result = QMessageBox::critical(nullptr, QLatin1String("Mumble"), tr("The database file '%1' set in the configuration file does not exist. Do you want to create a new database file at this location?").arg(g.s.qsDatabaseLocation), QMessageBox::Yes | QMessageBox::No);
if (result == QMessageBox::Yes) {
db.setDatabaseName(g.s.qsDatabaseLocation);
db.open();
} else {
qFatal("Database: File not found");
}
}
}
if (! found) {
QMessageBox::critical(nullptr, QLatin1String("Mumble"), tr("Mumble failed to initialize a database in any\nof the possible locations."), QMessageBox::Ok | QMessageBox::Default, QMessageBox::NoButton);
qFatal("Database: Failed initialization");
if (!db.isOpen()) {
if (findOrCreateDatabase()) {
g.s.qsDatabaseLocation = db.databaseName();
} else {
QMessageBox::critical(nullptr, QLatin1String("Mumble"), tr("Mumble failed to initialize a database in any of the possible locations."), QMessageBox::Ok | QMessageBox::Default, QMessageBox::NoButton);
qFatal("Database: Failed initialization");
}
}
QFileInfo fi(db.databaseName());

View File

@ -25,6 +25,10 @@ class Database : public QObject {
Q_DISABLE_COPY(Database)
QSqlDatabase db;
/// This function is called when no database location is configured
/// in the config file. It tries to find an existing database file and
/// creates a new one if none was found.
bool findOrCreateDatabase();
public:
Database(const QString &dbname);
~Database() Q_DECL_OVERRIDE;

View File

@ -65,7 +65,7 @@ static void migrateDataDir() {
}
#endif // Q_OS_WIN
Global::Global() {
Global::Global(const QString &qsConfigPath) {
mw = 0;
db = 0;
p = 0;
@ -119,29 +119,37 @@ Global::Global() {
bDebugDumpInput = false;
bDebugPrintQueue = false;
QStringList qsl;
qsl << QCoreApplication::instance()->applicationDirPath();
qsl << QStandardPaths::writableLocation(QStandardPaths::DataLocation);
#if defined(Q_OS_WIN)
QString appdata;
wchar_t appData[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, appData))) {
appdata = QDir::fromNativeSeparators(QString::fromWCharArray(appData));
if (!appdata.isEmpty()) {
appdata.append(QLatin1String("/Mumble"));
qsl << appdata;
}
}
#endif
foreach(const QString &dir, qsl) {
QFile inifile(QString::fromLatin1("%1/mumble.ini").arg(dir));
if (inifile.exists() && inifile.permissions().testFlag(QFile::WriteUser)) {
qdBasePath.setPath(dir);
qs = new QSettings(inifile.fileName(), QSettings::IniFormat);
break;
if (!qsConfigPath.isEmpty()) {
QFile inifile(qsConfigPath);
qs = new QSettings(inifile.fileName(), QSettings::IniFormat);
} else {
QStringList qsl;
qsl << QCoreApplication::instance()->applicationDirPath();
qsl << QStandardPaths::writableLocation(QStandardPaths::DataLocation);
#if defined(Q_OS_WIN)
if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, appData))) {
appdata = QDir::fromNativeSeparators(QString::fromWCharArray(appData));
if (!appdata.isEmpty()) {
appdata.append(QLatin1String("/Mumble"));
qsl << appdata;
}
}
#endif
foreach(const QString &dir, qsl) {
QFile inifile(QString::fromLatin1("%1/mumble.ini").arg(dir));
if (inifile.exists() && inifile.permissions().testFlag(QFile::WriteUser)) {
qdBasePath.setPath(dir);
qs = new QSettings(inifile.fileName(), QSettings::IniFormat);
break;
}
}
}

View File

@ -108,7 +108,7 @@ public:
bool bHappyEaster;
static const char ccHappyEaster[];
Global();
Global(const QString &qsConfigPath = QString());
~Global();
};

View File

@ -674,6 +674,8 @@ void Settings::load(QSettings* settings_ptr) {
// Config updates
SAVELOAD(uiUpdateCounter, "lastupdate");
SAVELOAD(qsDatabaseLocation, "databaselocation");
SAVELOAD(bMute, "audio/mute");
SAVELOAD(bDeaf, "audio/deaf");
LOADENUM(atTransmit, "audio/transmit");
@ -1031,6 +1033,8 @@ void Settings::save() {
// Config updates
SAVELOAD(uiUpdateCounter, "lastupdate");
SAVELOAD(qsDatabaseLocation, "databaselocation");
SAVELOAD(bMute, "audio/mute");
SAVELOAD(bDeaf, "audio/deaf");
SAVELOAD(atTransmit, "audio/transmit");

View File

@ -430,6 +430,9 @@ struct Settings {
// Config updates
unsigned int uiUpdateCounter;
/// Path to SQLite-DB
QString qsDatabaseLocation;
// Nonsaved
LoopMode lmLoopMode;
float dPacketLoss;

View File

@ -125,7 +125,26 @@ int main(int argc, char **argv) {
}
#endif
Global::g_global_struct = new Global();
// This argument has to be parsed first, since it's value is needed to create the global struct,
// which other switches are modifying. If it is parsed first, the order of the arguments does not matter.
QStringList args = a.arguments();
const int index = std::max(args.lastIndexOf(QLatin1String("-c")), args.lastIndexOf(QLatin1String("--config")));
if (index >= 0) {
if (index + 1 < args.count()) {
QFile inifile(args.at(index + 1));
if (inifile.exists() && inifile.permissions().testFlag(QFile::WriteUser)) {
Global::g_global_struct = new Global(args.at(index + 1));
} else {
printf("%s", qPrintable(MainWindow::tr("Configuration file %1 does not exist or is not writable.\n").arg(args.at(index + 1))));
return 1;
}
} else {
qCritical("Missing argument for --config!");
return 1;
}
} else {
Global::g_global_struct = new Global();
}
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
// For Qt >= 5.10 we use QRandomNumberGenerator that is seeded automatically
@ -143,8 +162,8 @@ int main(int argc, char **argv) {
bool bRpcMode = false;
QString rpcCommand;
QUrl url;
if (a.arguments().count() > 1) {
QStringList args = a.arguments();
for (int i = 1; i < args.count(); ++i) {
if (args.at(i) == QLatin1String("-h") || args.at(i) == QLatin1String("--help")
#if defined(Q_OS_WIN)
@ -165,6 +184,10 @@ int main(int argc, char **argv) {
" -h, --help Show this help text and exit.\n"
" -m, --multiple\n"
" Allow multiple instances of the client to be started.\n"
" -c, --config\n"
" Specify an alternative configuration file.\n"
" If you use this to run multiple instances of Mumble at once,\n"
" make sure to set an alternative 'database' value in the config.\n"
" -n, --noidentity\n"
" Suppress loading of identity files (i.e., certificates.)\n"
" -jn, --jackname <arg>\n"