Change config dir, offer to migrate
Moment has different default directories and is controlled by different environment variables. On first startup, Moment detects Mirage directories and offers to copy them. It doesn't make sense to migrate logins without migrating encryption keys, so this is now included in migration. Themes are migrated as well. Only offer migration is only if both config and data directory are able to be migrated.
This commit is contained in:
parent
63a56d92fd
commit
2839f83dde
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,6 +16,8 @@ dist
|
|||
Makefile
|
||||
mirage
|
||||
mirage.pro.user
|
||||
moment
|
||||
moment.pro.user
|
||||
*.AppImage
|
||||
|
||||
tags
|
||||
|
|
|
@ -27,7 +27,7 @@ QRC_FILE = $$BUILD_DIR/resources.qrc
|
|||
RESOURCES += $$QRC_FILE
|
||||
HEADERS += $$glob_filenames(*.h) submodules/hsluv-c/src/hsluv.h
|
||||
SOURCES += $$glob_filenames(*.cpp) submodules/hsluv-c/src/hsluv.c
|
||||
TARGET = mirage
|
||||
TARGET = moment
|
||||
|
||||
unix:!macx {
|
||||
LIBS += -lX11 -lXss
|
||||
|
@ -61,12 +61,12 @@ no-x11 {
|
|||
executables.files = $$TARGET
|
||||
|
||||
shortcuts.path = $$PREFIX/share/applications
|
||||
shortcuts.files = packaging/mirage.desktop
|
||||
shortcuts.files = packaging/moment.desktop
|
||||
|
||||
icons256.path = $$PREFIX/share/icons/hicolor/256x256/apps
|
||||
icons256.files = packaging/mirage.png
|
||||
icons256.files = packaging/moment.png
|
||||
|
||||
examples.path = $$PREFIX/share/examples/mirage
|
||||
examples.path = $$PREFIX/share/examples/moment
|
||||
examples.files = src/config/settings.py
|
||||
|
||||
INSTALLS += executables shortcuts icons256 examples
|
||||
|
@ -101,7 +101,7 @@ for(file, $$list($$glob_filenames(*.py))) {
|
|||
}
|
||||
|
||||
QMAKE_CLEAN *= $$MOC_DIR $$OBJECTS_DIR $$RCC_DIR $$PYCACHE_DIRS $$QRC_FILE
|
||||
QMAKE_CLEAN *= $$BUILD_DIR $$TARGET Makefile mirage.pro.user .qmake.stash
|
||||
QMAKE_CLEAN *= $$BUILD_DIR $$TARGET Makefile moment.pro.user .qmake.stash
|
||||
QMAKE_CLEAN *= $$glob_filenames(*.pyc, *.qmlc, *.jsc, *.egg-info)
|
||||
QMAKE_CLEAN *= packaging/flatpak/flatpak-env
|
||||
QMAKE_CLEAN *= packaging/flatpak/flatpak-pip-generator
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
"""This package provides Mirage's backend side that can interact with the UI.
|
||||
"""This package provides Moment's backend side that can interact with the UI.
|
||||
|
||||
To learn more about how this package works, you might want to check the
|
||||
documentation in the following modules first:
|
||||
|
@ -12,7 +12,7 @@ documentation in the following modules first:
|
|||
- `nio_callbacks`
|
||||
"""
|
||||
|
||||
__app_name__ = "mirage"
|
||||
__display_name__ = "Mirage"
|
||||
__reverse_dns__ = "io.github.mirukana.mirage"
|
||||
__app_name__ = "moment"
|
||||
__display_name__ = "Moment"
|
||||
__reverse_dns__ = "xyz.mx-moment"
|
||||
__version__ = "0.7.2"
|
||||
|
|
|
@ -104,7 +104,7 @@ class Backend:
|
|||
media_cache: A matrix media cache for downloaded files.
|
||||
|
||||
presences: A `{user_id: Presence}` dict for storing presence info about
|
||||
matrix users registered on Mirage.
|
||||
matrix users registered on Moment.
|
||||
|
||||
mxc_events: A dict storing media `Event` model items for any account
|
||||
that have the same mxc URI
|
||||
|
@ -137,7 +137,7 @@ class Backend:
|
|||
DefaultDict(asyncio.Lock) # {room_id: lock}
|
||||
|
||||
cache_dir = Path(
|
||||
os.environ.get("MIRAGE_CACHE_DIR") or self.appdirs.user_cache_dir,
|
||||
os.environ.get("MOMENT_CACHE_DIR") or self.appdirs.user_cache_dir,
|
||||
)
|
||||
|
||||
self.media_cache: MediaCache = MediaCache(self, cache_dir)
|
||||
|
|
|
@ -95,7 +95,7 @@ class Media:
|
|||
<base download folder>/<homeserver domain>/
|
||||
<file title>_<mxc id>.<file extension>`
|
||||
```
|
||||
e.g. `~/.cache/mirage/downloads/matrix.org/foo_Hm24ar11i768b0el.png`.
|
||||
e.g. `~/.cache/moment/downloads/matrix.org/foo_Hm24ar11i768b0el.png`.
|
||||
"""
|
||||
|
||||
parsed = urlparse(self.mxc)
|
||||
|
@ -304,7 +304,7 @@ class Thumbnail(Media):
|
|||
<file title>_<mxc id>.<file extension>`
|
||||
```
|
||||
e.g.
|
||||
`~/.cache/mirage/thumbnails/matrix.org/32x32/foo_Hm24ar11i768b0el.png`.
|
||||
`~/.cache/moment/thumbnails/matrix.org/32x32/foo_Hm24ar11i768b0el.png`.
|
||||
"""
|
||||
|
||||
size = self.normalize_size(self.server_size or self.wanted_size)
|
||||
|
|
|
@ -197,7 +197,7 @@ class ConfigFile(UserFile):
|
|||
@property
|
||||
def path(self) -> Path:
|
||||
return Path(
|
||||
os.environ.get("MIRAGE_CONFIG_DIR") or
|
||||
os.environ.get("MOMENT_CONFIG_DIR") or
|
||||
self.backend.appdirs.user_config_dir,
|
||||
) / self.filename
|
||||
|
||||
|
@ -209,7 +209,7 @@ class UserDataFile(UserFile):
|
|||
@property
|
||||
def path(self) -> Path:
|
||||
return Path(
|
||||
os.environ.get("MIRAGE_DATA_DIR") or
|
||||
os.environ.get("MOMENT_DATA_DIR") or
|
||||
self.backend.appdirs.user_data_dir,
|
||||
) / self.filename
|
||||
|
||||
|
@ -460,7 +460,7 @@ class NewTheme(UserDataFile, PCNFile):
|
|||
@property
|
||||
def path(self) -> Path:
|
||||
data_dir = Path(
|
||||
os.environ.get("MIRAGE_DATA_DIR") or
|
||||
os.environ.get("MOMENT_DATA_DIR") or
|
||||
self.backend.appdirs.user_data_dir,
|
||||
)
|
||||
return data_dir / "themes" / self.filename
|
||||
|
@ -515,7 +515,7 @@ class Theme(UserDataFile):
|
|||
@property
|
||||
def path(self) -> Path:
|
||||
data_dir = Path(
|
||||
os.environ.get("MIRAGE_DATA_DIR") or
|
||||
os.environ.get("MOMENT_DATA_DIR") or
|
||||
self.backend.appdirs.user_data_dir,
|
||||
)
|
||||
return data_dir / "themes" / self.filename
|
||||
|
|
|
@ -29,7 +29,7 @@ class General:
|
|||
# Can be the name of a built-in theme (Mirage.qpl or Glass.qpl), or
|
||||
# the name (including extension) of a file in the user theme folder, which
|
||||
# is "$XDG_DATA_HOME/mirage/themes" if that environment variable is set,
|
||||
# else "~/.local/share/mirage/themes".
|
||||
# else "~/.local/share/moment/themes".
|
||||
# For Flatpak, it is
|
||||
# "~/.var/app/io.github.mirukana.mirage/data/mirage/themes".
|
||||
theme: str = "Midnight.qpl"
|
||||
|
@ -204,7 +204,7 @@ class Chat:
|
|||
auto_play_gif: bool = True
|
||||
|
||||
# When clicking on a file in the timeline, open it in an external
|
||||
# program instead of displaying it using Mirage's interface.
|
||||
# program instead of displaying it using Moment's interface.
|
||||
# On Linux, the xdg-open command is called.
|
||||
click_opens_externally: bool = False
|
||||
|
||||
|
@ -234,7 +234,7 @@ class Keys:
|
|||
# would need to be pressed.
|
||||
#
|
||||
# A list of default bindings can be found at:
|
||||
# https://github.com/mirukana/mirage/blob/master/docs/KEYBINDINGS.md
|
||||
# https://gitlab.com/mx-moment/moment/-/blob/main/docs/KEYBINDINGS.md
|
||||
|
||||
# Helper functions
|
||||
|
||||
|
@ -280,7 +280,7 @@ class Keys:
|
|||
qml_console = ["F1"]
|
||||
|
||||
# Start the Python backend debugger.
|
||||
# Mirage must be connected to a terminal for this to work.
|
||||
# Moment must be connected to a terminal for this to work.
|
||||
python_debugger = ["Shift+F1"]
|
||||
|
||||
# Start the Python backend debugger in remote access mode.
|
||||
|
|
|
@ -18,9 +18,9 @@ QtObject {
|
|||
-h, --help Show this help and exit
|
||||
|
||||
Environment variables:
|
||||
MIRAGE_CONFIG_DIR Override the default configuration folder
|
||||
MIRAGE_DATA_DIR Override the default application data folder
|
||||
MIRAGE_CACHE_DIR Override the default cache and downloads folder
|
||||
MOMENT_CONFIG_DIR Override the default configuration folder
|
||||
MOMENT_DATA_DIR Override the default application data folder
|
||||
MOMENT_CACHE_DIR Override the default cache and downloads folder
|
||||
http_proxy Override the General.proxy setting, see settings.py
|
||||
`
|
||||
|
||||
|
|
247
src/main.cpp
247
src/main.cpp
|
@ -17,6 +17,7 @@
|
|||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QLockFile>
|
||||
#include <QMessageBox>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
|
@ -97,6 +98,236 @@ void onExitSignal(int signum) {
|
|||
}
|
||||
|
||||
|
||||
void migrateFile(QDir source, QDir destination, QString fname) {
|
||||
if (! QFile::copy(source.filePath(fname), destination.filePath(fname)))
|
||||
qWarning() << "Could not migrate" << fname;
|
||||
}
|
||||
|
||||
|
||||
void migrateShallowDirectory(
|
||||
QDir sourceParent, QDir destinationParent, QString dname
|
||||
) {
|
||||
if (sourceParent.exists(dname)) {
|
||||
if (! destinationParent.mkpath(dname)) {
|
||||
qWarning() << "Could not create directory"
|
||||
<< destinationParent.filePath(dname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
QDir source(sourceParent.filePath(dname));
|
||||
QDir destination(destinationParent.filePath(dname));
|
||||
QFileInfoList files = source.entryInfoList();
|
||||
for (QFileInfo file : files) {
|
||||
if(file.fileName() == "." || file.fileName() == "..")
|
||||
continue;
|
||||
migrateFile(source, destination, file.fileName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void offerMigrateFromMirage(
|
||||
QDir configDirMoment, QDir configDirMirage,
|
||||
QDir dataDirMoment, QDir dataDirMirage
|
||||
) {
|
||||
QMessageBox dialog;
|
||||
dialog.setText("Would you like Moment to re-use your logins, "
|
||||
"encryption keys, configuration and themes from Mirage?");
|
||||
dialog.setInformativeText(
|
||||
"Will copy "+configDirMirage.path()+" → "+configDirMoment.path()
|
||||
+"\nWill copy "+dataDirMirage.path()+" → "+dataDirMoment.path());
|
||||
dialog.addButton(QMessageBox::Yes);
|
||||
dialog.addButton(QMessageBox::No);
|
||||
dialog.addButton(QMessageBox::Cancel);
|
||||
int result = dialog.exec();
|
||||
if (result == QMessageBox::Yes) {
|
||||
qWarning("Migrating config and data from Mirage");
|
||||
if (! configDirMoment.mkpath(".")) {
|
||||
qFatal("Could not create config directory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (! dataDirMoment.mkpath(".")) {
|
||||
qFatal("Could not create data directory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
migrateFile(configDirMirage, configDirMoment, "settings.py");
|
||||
migrateFile(configDirMirage, configDirMoment, "settings.gui.json");
|
||||
migrateFile(configDirMirage, configDirMoment, "accounts.json");
|
||||
migrateFile(dataDirMirage, dataDirMoment, "history.json");
|
||||
migrateFile(dataDirMirage, dataDirMoment, "state.json");
|
||||
migrateShallowDirectory(dataDirMirage, dataDirMoment, "themes");
|
||||
migrateShallowDirectory(dataDirMirage, dataDirMoment, "encryption");
|
||||
} else if (result == QMessageBox::No) {
|
||||
// Nothing to do. Proceed with starting the app
|
||||
qWarning("Not migrating");
|
||||
return;
|
||||
} else {
|
||||
// Neither "Yes" nor "No" was chosen.
|
||||
// We can't know what the user wants. Just quit.
|
||||
qWarning("Quitting. You can decide about migration next time.");
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool shouldMigrateFromMirage() {
|
||||
QString genericConfig = QStandardPaths::writableLocation(
|
||||
QStandardPaths::GenericConfigLocation);
|
||||
QString genericData = QStandardPaths::writableLocation(
|
||||
QStandardPaths::GenericDataLocation);
|
||||
|
||||
// Check whether Moment config already exists
|
||||
{
|
||||
QString customConfigDirMoment(
|
||||
qEnvironmentVariable("MOMENT_CONFIG_DIR")
|
||||
);
|
||||
if (! customConfigDirMoment.isEmpty()) {
|
||||
// MOMENT_CONFIG_DIR is set.
|
||||
// Moment would definitely use this as the config directory.
|
||||
if (QDir(customConfigDirMoment).exists())
|
||||
// But it already exists.
|
||||
// So this is not the first time Moment was started.
|
||||
return false;
|
||||
} else {
|
||||
// No MOMENT_CONFIG_DIR, so check the default config directory.
|
||||
if (QDir(genericConfig + "/moment").exists())
|
||||
// The default config folder exists.
|
||||
// So this is not the first time Moment was started.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether Moment data already exists
|
||||
{
|
||||
QString customDataDirMoment(qEnvironmentVariable("MOMENT_DATA_DIR"));
|
||||
if (! customDataDirMoment.isEmpty()) {
|
||||
// MOMENT_DATA_DIR is set.
|
||||
// Moment would definitely use this as the data directory.
|
||||
if (QDir(customDataDirMoment).exists())
|
||||
// But it already exists.
|
||||
// So this is not the first time Moment was started.
|
||||
return false;
|
||||
} else {
|
||||
// No MOMENT_DATA_DIR, so check the default data directory.
|
||||
if (QDir(genericData + "/moment").exists())
|
||||
// The default data folder exists.
|
||||
// So this is not the first time Moment was started.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether Mirage config exists
|
||||
{
|
||||
QString customConfigDirMirage(
|
||||
qEnvironmentVariable("MIRAGE_CONFIG_DIR")
|
||||
);
|
||||
if (! customConfigDirMirage.isEmpty()) {
|
||||
// MIRAGE_CONFIG_DIR is set.
|
||||
// Mirage would definitely use this as the config directory.
|
||||
if (! QDir(customConfigDirMirage).exists())
|
||||
// But this directory does not exist.
|
||||
// So there is nowhere to migrate from.
|
||||
return false;
|
||||
} else {
|
||||
// No MIRAGE_CONFIG_DIR, so check the default config directory.
|
||||
// Check /matrix-mirage (Debian) first, since it is more specific
|
||||
if (! QDir(genericConfig + "/matrix-mirage").exists())
|
||||
// Default Debian config folder does not exist,
|
||||
// so check whether the normal /mirage exists
|
||||
if (! QDir(genericConfig + "/mirage").exists())
|
||||
// No, neither /matrix-mirage nor /mirage exist.
|
||||
// So there is nowhere to migrate from.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We found out that there is no Moment config dir nor Moment data dir,
|
||||
// but there is a Mirage config dir which can be migrated from.
|
||||
// We could also check for Mirage data dir but it doesn't really matter.
|
||||
// User should definitely be prompted for migration.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void tryMigrateFromMirage() {
|
||||
QString genericConfig = QStandardPaths::writableLocation(
|
||||
QStandardPaths::GenericConfigLocation);
|
||||
QString genericData = QStandardPaths::writableLocation(
|
||||
QStandardPaths::GenericDataLocation);
|
||||
|
||||
QDir configDirMoment, configDirMirage, dataDirMoment, dataDirMirage;
|
||||
|
||||
QString customConfigDirMoment(qEnvironmentVariable("MOMENT_CONFIG_DIR"));
|
||||
configDirMoment = QDir(customConfigDirMoment.isEmpty()
|
||||
? genericConfig + "/moment" : customConfigDirMoment);
|
||||
|
||||
QString customDataDirMoment(qEnvironmentVariable("MOMENT_DATA_DIR"));
|
||||
dataDirMoment = QDir(customDataDirMoment.isEmpty()
|
||||
? genericData + "/moment" : customDataDirMoment);
|
||||
|
||||
QString customConfigDirMirage(qEnvironmentVariable("MIRAGE_CONFIG_DIR"));
|
||||
if (! customConfigDirMirage.isEmpty()) {
|
||||
// MIRAGE_CONFIG_DIR is set.
|
||||
// Mirage would definitely use this as the config directory.
|
||||
// So this is where we should migrate from.
|
||||
configDirMirage = QDir(customConfigDirMirage);
|
||||
} else {
|
||||
// No MIRAGE_CONFIG_DIR.
|
||||
// Check if Mirage default config directory exists
|
||||
// Check /matrix-mirage (Debian) first
|
||||
QDir dirDeb(genericConfig + "/matrix-mirage");
|
||||
if (dirDeb.exists()) {
|
||||
// Default Debian config dir exists.
|
||||
// So this is where we should migrate from.
|
||||
configDirMirage = dirDeb;
|
||||
} else {
|
||||
// No /matrix-mirage found, so check /mirage
|
||||
QDir dir(genericConfig + "/mirage");
|
||||
if (dir.exists())
|
||||
// Default config dir exists.
|
||||
// So this is where we should migrate from.
|
||||
configDirMirage = dir;
|
||||
else
|
||||
// No Mirage config dir found.
|
||||
// Do not migrate.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString customDataDirMirage(qEnvironmentVariable("MIRAGE_DATA_DIR"));
|
||||
if (! customDataDirMirage.isEmpty()) {
|
||||
// MIRAGE_DATA_DIR is set.
|
||||
// Mirage would definitely use this as the data directory.
|
||||
// So this is where we should migrate from.
|
||||
dataDirMirage = QDir(customDataDirMirage);
|
||||
} else {
|
||||
// No MIRAGE_DATA_DIR.
|
||||
// Check if Mirage default data directory exists
|
||||
// Check /matrix-mirage (Debian) first
|
||||
QDir dirDeb(genericData + "/matrix-mirage");
|
||||
if (dirDeb.exists()) {
|
||||
// Default Debian data dir exists.
|
||||
// So this is where we should migrate from.
|
||||
dataDirMirage = dirDeb;
|
||||
} else {
|
||||
// No /matrix-mirage found, so check /mirage
|
||||
QDir dir(genericData + "/mirage");
|
||||
if (dir.exists())
|
||||
// Default data dir exists.
|
||||
// So this is where we should migrate from.
|
||||
dataDirMirage = dir;
|
||||
else
|
||||
// No Mirage data dir found.
|
||||
// Do not migrate.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
offerMigrateFromMirage(
|
||||
configDirMoment, configDirMirage, dataDirMoment, dataDirMirage
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
bool setLockFile(QString configPath) {
|
||||
|
||||
QDir settingsFolder(configPath);
|
||||
|
@ -130,13 +361,19 @@ int main(int argc, char *argv[]) {
|
|||
qInstallMessageHandler(loggingHandler);
|
||||
|
||||
// Define some basic info about the app before creating the QApplication
|
||||
QApplication::setOrganizationName("mirage");
|
||||
QApplication::setApplicationName("mirage");
|
||||
QApplication::setApplicationDisplayName("Mirage");
|
||||
QApplication::setOrganizationName("moment");
|
||||
QApplication::setApplicationName("moment");
|
||||
QApplication::setApplicationDisplayName("Moment");
|
||||
QApplication::setApplicationVersion("0.7.2");
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
|
||||
QString customConfigDir(qEnvironmentVariable("MIRAGE_CONFIG_DIR"));
|
||||
// app needs to be constructed before attempting to migrate
|
||||
// because migrate displays a popup dialog
|
||||
QApplication app(argc, argv);
|
||||
|
||||
if (shouldMigrateFromMirage()) tryMigrateFromMirage();
|
||||
|
||||
QString customConfigDir(qEnvironmentVariable("MOMENT_CONFIG_DIR"));
|
||||
QString settingsFolder(
|
||||
customConfigDir.isEmpty() ?
|
||||
QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
|
||||
|
@ -144,8 +381,8 @@ int main(int argc, char *argv[]) {
|
|||
customConfigDir
|
||||
);
|
||||
|
||||
// Attempt to create a lockfile in the settings folder
|
||||
if (! setLockFile(settingsFolder)) return EXIT_SUCCESS;
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Register handlers for quit signals, e.g. SIGINT/Ctrl-C in unix terminals
|
||||
signal(SIGINT, onExitSignal);
|
||||
|
|
Loading…
Reference in New Issue
Block a user