mirror of
https://github.com/Mudlet/Mudlet
synced 2026-08-13 18:26:27 -04:00
#### Brief overview of PR changes/additions - Hoists the list `slot_deleteProfile()` checks a never-played profile against out of the function to `dlgConnectionProfiles::scmConnectionDetailFiles`, beside the file's other `scm*` constants, and points at it from both `writeProfileData()` implementations. - Adds a `ProfileDeletionSafetyTest` case that sets a profile up through the real dialog (New profile, name it, fill the connection form in, re-select it) and fails if anything it wrote is missing from the list. - Pins the deliberate exclusions too: a profile holding a stored password or a typed-in character name still asks before removal. #### Motivation for adding to Mudlet Nothing linked that list to the ~15 places profile data gets written, so it could silently go stale; because it is an allowlist a stale entry only ever costs an extra confirmation prompt, but the maintenance trap was worth closing. #### Other info (issues closed, discussion etc) Follows up https://github.com/Mudlet/Mudlet/pull/9722#discussion_r3740581754 on #9722 (fix: a profile named "." or ".." deletes every profile when removed). No behaviour change. **Test case:** `ctest -R ProfileDeletionSafetyTest` (20 cases). Removing an entry from the constant makes it fail naming the file; adding `login` makes the character-name case fail. Assisted-by: Claude:claude-opus-5
208 lines
9 KiB
C++
208 lines
9 KiB
C++
#ifndef MUDLET_DLGCONNECTIONPROFILES_H
|
|
#define MUDLET_DLGCONNECTIONPROFILES_H
|
|
|
|
/***************************************************************************
|
|
* Copyright (C) 2008-2011 by Heiko Koehn - KoehnHeiko@googlemail.com *
|
|
* Copyright (C) 2014 by Ahmed Charles - acharles@outlook.com *
|
|
* Copyright (C) 2016, 2020-2022 by Stephen Lyons *
|
|
* - slysven@virginmedia.com *
|
|
* *
|
|
* This program 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 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* 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 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., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
#include "ui_connection_profiles.h"
|
|
#include <optional>
|
|
#include <QRegularExpression>
|
|
#include <QTimer>
|
|
#include <QKeyEvent>
|
|
|
|
class QDir;
|
|
class QTabBar;
|
|
|
|
namespace pugi {
|
|
class xml_document;
|
|
}
|
|
|
|
class dlgConnectionProfiles : public QDialog, public Ui::connection_profiles
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
Q_DISABLE_COPY(dlgConnectionProfiles)
|
|
explicit dlgConnectionProfiles(QWidget* parent = nullptr);
|
|
~dlgConnectionProfiles();
|
|
|
|
void fillout_form();
|
|
QPair<bool, QString> writeProfileData(const QString& profile, const QString& item, const QString& what);
|
|
QString readProfileData(const QString& profile, const QString& item) const;
|
|
void accept() override;
|
|
QList<QListWidgetItem*> findData(const QListWidget& listWidget, const QVariant& what, const int role = Qt::UserRole) const;
|
|
QList<int> findProfilesBeginningWith(const QString&) const;
|
|
static const int csmNameRole{Qt::UserRole};
|
|
static QChar firstInvalidProfileNameChar(const QString& name);
|
|
static bool profileNameUsableAsIs(const QString& name);
|
|
static QString profileFolderPath(const QString& profilesPath, const QString& profile);
|
|
static const QString scmAllowedProfileNameChars;
|
|
static const QRegularExpression scmUnusableProfileNameChars;
|
|
// files whose presence alone does not warrant confirming a profile's removal
|
|
static const QStringList scmConnectionDetailFiles;
|
|
|
|
QString btn_connect_enabled_accessDesc;
|
|
QString btn_load_enabled_accessDesc;
|
|
QString btn_connOrLoad_disabled_accessDesc;
|
|
QString item_profile_accessName;
|
|
QString item_profile_accessDesc;
|
|
|
|
signals:
|
|
void signal_load_profile(QString profile_name, bool alsoConnect);
|
|
|
|
public slots:
|
|
void slot_updateName(const QString&);
|
|
void slot_saveName();
|
|
void slot_updateUrl(const QString&);
|
|
void slot_updatePort(const QString&);
|
|
void slot_updateSslTslPort(int state);
|
|
void slot_updateLogin(const QString&);
|
|
void slot_updatePassword(const QString&);
|
|
// Not used: void slot_updateWebsite(const QString&);
|
|
void slot_updateDescription();
|
|
|
|
void slot_itemClicked(QListWidgetItem*);
|
|
void slot_addProfile();
|
|
void slot_deleteProfile();
|
|
|
|
void slot_updateAutoConnect(int state);
|
|
void slot_updateAutoReconnect(int state);
|
|
void slot_load();
|
|
void slot_cancel();
|
|
void slot_copyProfile();
|
|
void slot_copyOnlySettingsOfProfile();
|
|
void indicatePackagesInstallOnConnect(QStringList packages);
|
|
|
|
|
|
protected:
|
|
bool eventFilter(QObject*, QEvent*) override;
|
|
void loadPasswordFromSettings(const QString& profile_name);
|
|
void ensurePasswordLoadedThenConnect(bool alsoConnect);
|
|
bool hasPendingKeychainOperation(const QString& profile_name) const;
|
|
|
|
|
|
private:
|
|
static bool copyFolder(const QString& sourceFolder, const QString& destFolder);
|
|
void dismissTutorialInvitation();
|
|
QString getDescription(const QString& profile_name) const;
|
|
bool validateConnect();
|
|
bool validateProfile();
|
|
void loadProfile(bool alsoConnect);
|
|
void copyProfileSettingsOnly(const QString& oldname, const QString& newname);
|
|
bool extractSettingsFromProfile(pugi::xml_document& newProfile, const QString& copySettingsFrom);
|
|
void saveProfileCopy(const QDir& newProfiledir, const pugi::xml_document& newProfileXml) const;
|
|
bool copyProfileWidget(QString& profile_name, QString& oldname, QListWidgetItem*& pItem) const;
|
|
struct CopiedProfileData
|
|
{
|
|
QString host;
|
|
QString port;
|
|
int sslTsl;
|
|
QString login;
|
|
QString website;
|
|
QString description;
|
|
};
|
|
CopiedProfileData captureProfileData() const;
|
|
void saveDefaultProfileCopy(const QString& profileName, const CopiedProfileData& data, const QString& oldPassword);
|
|
bool hasCustomIcon(const QString&) const;
|
|
void setProfileIcon() const;
|
|
void loadCustomProfile(const QString&) const;
|
|
void generateCustomProfile(const QString&) const;
|
|
void setCustomIcon(const QString&, QListWidgetItem*) const;
|
|
void setIconOfListedProfile(const QString& profileName, const QIcon& icon) const;
|
|
QString selectedProfileName() const;
|
|
template <typename L>
|
|
void loadSecuredPassword(const QString& profile, L callback);
|
|
void migrateSecuredPassword(const QString& oldProfile, const QString& newProfile);
|
|
void writeSecurePassword(const QString& profile, const QString& pass);
|
|
void deleteSecurePassword(const QString& profile);
|
|
void setupMudProfile(QListWidgetItem*, const QString& mudServer, const QString& serverDescription, const QString& iconFileName);
|
|
void reallyDeleteProfile(const QString& profile);
|
|
void showRemovalProblem(const QString& message);
|
|
void continueProfileSave(QListWidgetItem* pItem, const QString& newProfileName, const QString& newProfileHost, const QString& newProfilePort, const int newProfileSslTsl);
|
|
void setItemName(QListWidgetItem*, const QString&) const;
|
|
QIcon customIcon(const QString&, const std::optional<QColor>&) const;
|
|
void addLetterToProfileSearch(const int);
|
|
void clearNotificationArea();
|
|
void loadPasswordAsync(const QString& profileName);
|
|
void revealConnectionDetails();
|
|
bool showingOnlyMyProfiles() const;
|
|
|
|
static constexpr int scmMyGamesTab = 0;
|
|
static constexpr int scmAllGamesTab = 1;
|
|
|
|
// split into 3 properties so each one can be checked individually
|
|
// important for creation of a folder on disk, for example: name has
|
|
// to be valid, but other properties don't have to be
|
|
bool validName = false;
|
|
bool validUrl = false;
|
|
bool validPort = false;
|
|
|
|
QStringList mProfileList;
|
|
QPalette mRegularPalette;
|
|
QPalette mOKPalette;
|
|
QPalette mErrorPalette;
|
|
QPalette mReadOnlyPalette;
|
|
QAction* mpCopyProfile = nullptr;
|
|
// switches the profiles list between the user's own games and the full catalog
|
|
QTabBar* mpTabBar = nullptr;
|
|
QPushButton* offline_button = nullptr;
|
|
QPushButton* connect_button = nullptr;
|
|
QString mDiscordApplicationId;
|
|
QString mDiscordInviteURL;
|
|
QAction* mpAction_revealPassword;
|
|
// true for the duration of the 'Copy profile' action
|
|
bool mCopyingProfile = false;
|
|
// true while a profile is selected or refreshed programmatically, so that
|
|
// it is not mistaken for the user picking a game from the list
|
|
bool mProgrammaticProfileSelection = false;
|
|
// dialog height before it was shrunk to fit the welcome message
|
|
int mDialogHeightBeforeShrink = 0;
|
|
QString mDateTimeFormat;
|
|
QVector<QColor> mCustomIconColors;
|
|
QTimer mSearchTextTimer;
|
|
QString mSearchText;
|
|
QTimer* mPasswordSaveTimer = nullptr;
|
|
QPushButton* mpSkipToGamesButton = nullptr;
|
|
bool mTutorialDismissed = false;
|
|
|
|
// Async connection and password handling
|
|
QString mPendingPasswordSaveProfile;
|
|
QString mPendingProfileLoad; // Profile name waiting for password load
|
|
bool mPendingConnect = false; // Whether to connect (true) or just load (false)
|
|
bool mKeychainOperationInProgress = false; // Track if keychain op is active
|
|
|
|
|
|
private slots:
|
|
void slot_activeTabChanged(const int index);
|
|
void slot_skipToGamesList();
|
|
void slot_profileContextMenu(QPoint pos);
|
|
void slot_setCustomIcon();
|
|
void slot_setCustomColor();
|
|
void slot_resetCustomIcon();
|
|
void slot_togglePasswordVisibility(const bool);
|
|
void slot_reenableAllProfileItems();
|
|
void slot_loadPasswordAsync();
|
|
void slot_passwordTextChanged();
|
|
};
|
|
|
|
|
|
#endif // MUDLET_DLGCONNECTIONPROFILES_H
|