diff --git a/plugins/Git/git.cpp b/plugins/Git/git.cpp index ce29a1e..bf787e1 100644 --- a/plugins/Git/git.cpp +++ b/plugins/Git/git.cpp @@ -12,8 +12,15 @@ extern "C" { #include "jobs/gitjob.h" Git::Git(): - m_sem(std::unique_ptr(new QSemaphore(1))) + m_sem(std::unique_ptr(new QSemaphore(1))), + m_ssh_homedir (QStandardPaths::writableLocation( + QStandardPaths::AppDataLocation).append("/.ssh")) { + qDebug() << "[Git] SSH Home is " << m_ssh_homedir.absolutePath(); + QDir m_ssh_homedir(this->m_ssh_homedir); + if (!m_ssh_homedir.exists()) { + m_ssh_homedir.mkpath("."); + } git_libgit2_init(); } @@ -26,7 +33,7 @@ Git::~Git() bool Git::clone(QString url, QString path, cred_type mode) { if (!this->m_sem->tryAcquire(1, 500)) { - qWarning() << "Can acquire git semaphore a command is already running "; + qWarning() << "[Git] Can acquire git semaphore a command is already running "; return false; } auto v = overload { @@ -44,18 +51,26 @@ bool Git::clone(QString url, QString path, cred_type mode) bool Git::cloneHttp(QString url, QString path) { - qInfo() << "Call clone command Http " << url << " " << path; + qInfo() << "[Git] Call clone command Http " << url << " " << path; HTTP mode = {}; return this->clone(url, path, mode); } bool Git::cloneHttpPass(QString url, QString path, QString pass) { - qInfo() << "Call clone command HttpPass " << url << " " << path; + qInfo() << "[Git] Call clone command HttpPass " << url << " " << path; HTTPUserPass mode = { pass }; return this->clone(url, path, mode); } +bool Git::cloneSshKey(QString url, QString path, QString passphrase) +{ + qInfo() << "[Git] Call clone command HttpPass " << url << " " << path; + + SSHKey mode = { this->pubKeyPath(), this->privKeyPath(), passphrase }; + return this->clone(url, path, mode); +} + void Git::cloneResult(const bool err) { @@ -66,3 +81,23 @@ void Git::cloneResult(const bool err) } this->m_sem->release(); } + +bool Git::importSshKey(QUrl source_path, bool is_private){ + auto destination_path = is_private ? this->privKeyPath() : this->pubKeyPath(); + + QFile source_file(source_path.toLocalFile()); + + if (!source_file.exists()) { + qWarning() << "[Git] Source file does not exist."; + return false; + } + + QDir target_dir = QFileInfo(destination_path).absoluteDir(); + if (!target_dir.exists()) { + if (!target_dir.mkpath(".")) { + qWarning() << "[Git] Failed to create target directory."; + return false; + } + } + return source_file.copy(destination_path); +} diff --git a/plugins/Git/git.h b/plugins/Git/git.h index d86b1d5..5e2a979 100644 --- a/plugins/Git/git.h +++ b/plugins/Git/git.h @@ -2,6 +2,7 @@ #define GIT_H #include "jobs/gitjob.h" +#include "qdebug.h" #include #include #include @@ -16,6 +17,8 @@ class Git : public QObject { Q_OBJECT + Q_PROPERTY(QString privKey READ pubKeyPath) + Q_PROPERTY(QString pubKey READ privKeyPath) private slots: /** @@ -47,6 +50,7 @@ signals: private: std::unique_ptr m_sem; /**< Semaphore for managing concurrent operations. */ + QDir m_ssh_homedir; /**< Directory that contains the SSH keys (public and private). */ /** * @brief Clones a repository from a specified URL. @@ -61,6 +65,27 @@ private: */ bool clone(QString url, QString path, cred_type mode); +protected: + /** + * @brief Get the path to the public keyring. + * + * @return The file path to the public key. + */ + QString pubKeyPath() + { + return this->m_ssh_homedir.filePath("id_rsa.pub"); + } + + /** + * @brief Get the path to the secret keyring. + * + * @return The file path to the private key. + */ + QString privKeyPath() + { + return this->m_ssh_homedir.filePath("id_rsa"); + } + public: /** * @brief Constructor for the Git class. @@ -76,6 +101,9 @@ public: */ ~Git() override; + + Q_INVOKABLE bool importSshKey(QUrl source_path, bool is_private); + /** * @brief Clones a repository over HTTP. * @@ -97,13 +125,22 @@ public: * @param url The HTTP URL of the Git repository to clone. * @param path The destination path for the cloned repository. * @param pass The password used for HTTP authentication. - * @return `true` if the clone operation was successful, `false` otherwise. + * @return `true` if the clone job operation was successfully started, `false` otherwise. */ Q_INVOKABLE bool cloneHttpPass(QString url, QString path, QString pass); - // Future SSH support methods: - // Q_INVOKABLE bool clone_ssh_pass(QString url, QString path, QString pass); - // Q_INVOKABLE bool clone_ssh_key(QString url, QString path, QString pub_key, QString priv_key, QString passphrase); + /** + * @brief Clones a repository over SSH with a key for authentication. + * + * This method clones a Git repository from the specified ssh URL using the provided password for authentication, + * and saves it to the given destination path. + * + * @param url The HTTP URL of the Git repository to clone. + * @param path The destination path for the cloned repository. + * @param passphrase The passphrase used for SSH authentication. + * @return `true` if the clone job operation was successfully started, `false` otherwise. + */ + Q_INVOKABLE bool cloneSshKey(QString url, QString path, QString passphrase); // Q_INVOKABLE bool update(QUrl url, QString path); // .... diff --git a/plugins/Git/jobs/gitjob.cpp b/plugins/Git/jobs/gitjob.cpp index 3c6f809..18c5356 100644 --- a/plugins/Git/jobs/gitjob.cpp +++ b/plugins/Git/jobs/gitjob.cpp @@ -18,17 +18,7 @@ GitJob::~GitJob() git_libgit2_shutdown(); } -bool GitJob::getUsername(char **username, QString maybe_username, const char *username_from_url) -{ - if (username_from_url) { - *username = strdup(username_from_url); - return true; - } else if (!maybe_username.isNull()) { - *username = maybe_username.toLocal8Bit().data(); - return true; - } - return false; -} + int GitJob::credentialsCB(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *payload) diff --git a/plugins/Git/jobs/gitjob.h b/plugins/Git/jobs/gitjob.h index a56f74e..76b0e4b 100644 --- a/plugins/Git/jobs/gitjob.h +++ b/plugins/Git/jobs/gitjob.h @@ -1,6 +1,7 @@ #ifndef GITJOB_H #define GITJOB_H +#include #include extern "C" { #include diff --git a/plugins/Git/plugin.cpp b/plugins/Git/plugin.cpp index 1ebbf6f..7223253 100644 --- a/plugins/Git/plugin.cpp +++ b/plugins/Git/plugin.cpp @@ -9,5 +9,4 @@ void GitPlugin::registerTypes(const char *uri) { //@uri Git qmlRegisterSingletonType(uri, 1, 0, "Git", [](QQmlEngine *, QJSEngine *) -> QObject * { return new Git; }); - } diff --git a/plugins/Utils/utils.cpp b/plugins/Utils/utils.cpp index 737998e..e0a5b1a 100644 --- a/plugins/Utils/utils.cpp +++ b/plugins/Utils/utils.cpp @@ -15,7 +15,7 @@ bool Utils::unzip(QUrl zip_url, QString dir_out_path) if (!this->m_sem->tryAcquire(1, 500)) { return false; } - qInfo() << "Unzip path " << zip_url << " to " << dir_out_path; + qInfo() << "[Utils] Unzip path " << zip_url << " to " << dir_out_path; auto job = new UnzipJob(zip_url, QDir(dir_out_path)); connect(job, &UnzipJob::resultReady, this, &Utils::unzipResult); connect(job, &UnzipJob::finished, job, &QObject::deleteLater); @@ -26,13 +26,13 @@ bool Utils::unzip(QUrl zip_url, QString dir_out_path) void Utils::unzipResult(bool err) { - qDebug() << "Unzip Result"; + qDebug() << "[Utils] Unzip Result"; if (err) { - qInfo() << "Unzip Failed"; + qInfo() << "[Utils] Unzip Failed"; emit unzipFailed(); } else { - qInfo() << "Unzip Succeed"; + qInfo() << "[Utils] Unzip Succeed"; emit unzipSucceed(); } this->m_sem->release(1); @@ -42,7 +42,7 @@ void Utils::unzipResult(bool err) QString Utils::manifestPath() { auto path = QDir(QDir::currentPath()).filePath("manifest_.json"); - qInfo() << "Manifest path : " << path; + qInfo() << "[Utils] Manifest path : " << path; return path; } @@ -56,3 +56,12 @@ bool Utils::rmDir(QUrl dir_url) QDir dir(dir_url.toLocalFile()); return dir.removeRecursively(); } + +bool Utils::fileExists(QUrl path) +{ + QString p = path.toLocalFile(); + auto ret = QFileInfo::exists(p) && QFileInfo(p).isFile(); + qDebug() << "[Utils]" << path << " existing file :" << ret; + return ret; +} + diff --git a/plugins/Utils/utils.h b/plugins/Utils/utils.h index 5972449..2b84ce0 100644 --- a/plugins/Utils/utils.h +++ b/plugins/Utils/utils.h @@ -80,6 +80,14 @@ public: */ Q_INVOKABLE bool rmDir(QUrl dir_url); + /** + * @brief Verify that file exists at the specified URL. + * + * @param path The URL of the file to verfidy. + * @return `true` if the file exist; `false` otherwise. + */ + Q_INVOKABLE bool fileExists(QUrl path); + }; #endif diff --git a/po/utpass.qrouland.pot b/po/utpass.qrouland.pot index 7271416..8bb51ec 100644 --- a/po/utpass.qrouland.pot +++ b/po/utpass.qrouland.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: utpass.qrouland\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-04 17:49+0100\n" +"POT-Creation-Date: 2025-02-21 15:49+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,64 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" +#: ../qml/components/GitCloneHttp.qml:22 +#: ../qml/components/GitCloneHttpAuth.qml:22 +#: ../qml/components/GitCloneSshKey.qml:41 +msgid "Repo Url" +msgstr "" + +#: ../qml/components/GitCloneHttp.qml:47 +#: ../qml/components/GitCloneHttpAuth.qml:67 +#: ../qml/components/GitCloneSshKey.qml:143 +msgid "Clone" +msgstr "" + +#: ../qml/components/GitCloneHttpAuth.qml:42 +#: ../qml/components/GitCloneHttpAuth.qml:53 +msgid "Password" +msgstr "" + +#: ../qml/components/GitCloneSshKey.qml:61 +msgid "SSH private key" +msgstr "" + +#: ../qml/components/GitCloneSshKey.qml:70 +msgid "Import SSH private key" +msgstr "" + +#: ../qml/components/GitCloneSshKey.qml:79 +msgid "Delete SSH private key" +msgstr "" + +#: ../qml/components/GitCloneSshKey.qml:89 +msgid "SSH public key" +msgstr "" + +#: ../qml/components/GitCloneSshKey.qml:98 +msgid "Delete SSH public key" +msgstr "" + +#: ../qml/components/GitCloneSshKey.qml:107 +msgid "Import SSH public key" +msgstr "" + +#: ../qml/components/GitCloneSshKey.qml:118 +#: ../qml/components/GitCloneSshKey.qml:129 +msgid "Passphrase" +msgstr "" + +#: ../qml/components/ImportFile.qml:18 +msgid "Import succeeded !" +msgstr "" + +#: ../qml/components/ImportFile.qml:19 +msgid "Import failed !" +msgstr "" + +#: ../qml/components/ImportFile.qml:20 +msgid "File Import" +msgstr "" + #: ../qml/dialogs/ErrorDialog.qml:12 msgid "Error !" msgstr "" @@ -75,82 +133,110 @@ msgstr "" msgid "Released under the terms of the GNU GPL v3" msgstr "" -#: ../qml/pages/Info.qml:132 ../qml/pages/headers/MainHeader.qml:33 +#: ../qml/pages/Info.qml:132 ../qml/pages/headers/MainHeader.qml:38 msgid "Info" msgstr "" -#: ../qml/pages/PasswordList.qml:45 +#: ../qml/pages/PasswordList.qml:79 msgid "No password found" msgstr "" -#: ../qml/pages/PasswordList.qml:58 +#: ../qml/pages/PasswordList.qml:92 msgid "You can import a password store by cloning or" msgstr "" -#: ../qml/pages/PasswordList.qml:65 +#: ../qml/pages/PasswordList.qml:99 msgid "importing a password store zip in the settings" msgstr "" -#: ../qml/pages/PasswordList.qml:100 +#: ../qml/pages/PasswordList.qml:176 msgid "Decryption failed !" msgstr "" -#: ../qml/pages/PasswordList.qml:114 +#: ../qml/pages/PasswordList.qml:198 msgid "Back" msgstr "" -#: ../qml/pages/PasswordList.qml:121 ../qml/pages/headers/MainHeader.qml:9 +#: ../qml/pages/PasswordList.qml:205 ../qml/pages/headers/MainHeader.qml:14 #: ../qml/pages/headers/StackHeader.qml:9 UTPass.desktop.in.h:1 msgid "UTPass" msgstr "" -#: ../qml/pages/headers/MainHeader.qml:26 ../qml/pages/settings/Settings.qml:75 -msgid "Settings" -msgstr "" - -#: ../qml/pages/headers/MainHeader.qml:57 +#: ../qml/pages/headers/MainHeader.qml:20 +#: ../qml/pages/headers/MainHeader.qml:62 msgid "Search" msgstr "" -#: ../qml/pages/settings/DeleteRepo.qml:42 -#: ../qml/pages/settings/Settings.qml:58 +#: ../qml/pages/headers/MainHeader.qml:31 ../qml/pages/settings/Settings.qml:73 +msgid "Settings" +msgstr "" + +#: ../qml/pages/settings/DeleteRepo.qml:43 +#: ../qml/pages/settings/DeleteRepo.qml:93 +#: ../qml/pages/settings/Settings.qml:56 msgid "Delete Password Store" msgstr "" -#: ../qml/pages/settings/DeleteRepo.qml:55 +#: ../qml/pages/settings/DeleteRepo.qml:56 msgid "You're are about to delete
the current Password Store.
Continue ?" msgstr "" -#: ../qml/pages/settings/DeleteRepo.qml:56 +#: ../qml/pages/settings/DeleteRepo.qml:57 +#: ../qml/pages/settings/ImportGitClone.qml:142 #: ../qml/pages/settings/ImportZip.qml:66 #: ../qml/pages/settings/InfoKeys.qml:174 -#: ../qml/pages/settings/git/ImportGitClone.qml:56 msgid "Yes" msgstr "" -#: ../qml/pages/settings/DeleteRepo.qml:69 +#: ../qml/pages/settings/DeleteRepo.qml:70 msgid "Password Store removal failed !" msgstr "" -#: ../qml/pages/settings/DeleteRepo.qml:78 +#: ../qml/pages/settings/DeleteRepo.qml:79 msgid "Password Store deleted !" msgstr "" -#: ../qml/pages/settings/DeleteRepo.qml:90 -#: ../qml/pages/settings/InfoKeys.qml:216 -msgid "Info Keys" +#: ../qml/pages/settings/ImportGitClone.qml:141 +msgid "" +"Importing a git repo will delete
any existing password store!" +"
Continue ?" msgstr "" -#: ../qml/pages/settings/ImportKeyFile.qml:61 -msgid "Key import failed !" +#: ../qml/pages/settings/ImportGitClone.qml:155 +msgid "An error occured during git clone !" msgstr "" -#: ../qml/pages/settings/ImportKeyFile.qml:70 +#: ../qml/pages/settings/ImportGitClone.qml:164 +#: ../qml/pages/settings/ImportZip.qml:88 +msgid "Password store sucessfully imported !" +msgstr "" + +#: ../qml/pages/settings/ImportGitClone.qml:176 +msgid "Git Clone Import" +msgstr "" + +#: ../qml/pages/settings/ImportKeyFile.qml:8 +msgid "GPG Key Import" +msgstr "" + +#: ../qml/pages/settings/ImportKeyFile.qml:9 msgid "Key successfully imported !" msgstr "" -#: ../qml/pages/settings/ImportKeyFile.qml:81 -msgid "GPG Key Import" +#: ../qml/pages/settings/ImportKeyFile.qml:10 +msgid "Key import failed !" +msgstr "" + +#: ../qml/pages/settings/ImportSSHkey.qml:10 +msgid "SSH Key Import" +msgstr "" + +#: ../qml/pages/settings/ImportSSHkey.qml:11 +msgid "SSH Key successfully imported !" +msgstr "" + +#: ../qml/pages/settings/ImportSSHkey.qml:12 +msgid "SSH Key import failed !" msgstr "" #: ../qml/pages/settings/ImportZip.qml:65 @@ -162,11 +248,6 @@ msgstr "" msgid "Password store import failed !" msgstr "" -#: ../qml/pages/settings/ImportZip.qml:88 -#: ../qml/pages/settings/git/ImportGitClone.qml:78 -msgid "Password store sucessfully imported !" -msgstr "" - #: ../qml/pages/settings/ImportZip.qml:100 msgid "Zip Password Store Import" msgstr "" @@ -203,59 +284,34 @@ msgstr "" msgid "An Error occured getting GPG keys !" msgstr "" -#: ../qml/pages/settings/Settings.qml:23 +#: ../qml/pages/settings/InfoKeys.qml:216 +msgid "Info Keys" +msgstr "" + +#: ../qml/pages/settings/Settings.qml:21 msgid "GPG" msgstr "" -#: ../qml/pages/settings/Settings.qml:29 +#: ../qml/pages/settings/Settings.qml:27 msgid "Import a GPG key file" msgstr "" -#: ../qml/pages/settings/Settings.qml:34 +#: ../qml/pages/settings/Settings.qml:32 msgid "Show GPG keys" msgstr "" -#: ../qml/pages/settings/Settings.qml:42 +#: ../qml/pages/settings/Settings.qml:40 msgid "Password Store" msgstr "" -#: ../qml/pages/settings/Settings.qml:48 +#: ../qml/pages/settings/Settings.qml:46 msgid "Import a Password Store using Git" msgstr "" -#: ../qml/pages/settings/Settings.qml:53 +#: ../qml/pages/settings/Settings.qml:51 msgid "Import a Password Store Zip" msgstr "" -#: ../qml/pages/settings/Settings.qml:67 +#: ../qml/pages/settings/Settings.qml:65 msgid "Warning: importing delete any exiting Password Store" msgstr "" - -#: ../qml/pages/settings/git/GitCloneHttp.qml:16 -#: ../qml/pages/settings/git/GitCloneHttpAuth.qml:16 -msgid "Repo Url" -msgstr "" - -#: ../qml/pages/settings/git/GitCloneHttp.qml:40 -#: ../qml/pages/settings/git/GitCloneHttpAuth.qml:60 -msgid "Clone" -msgstr "" - -#: ../qml/pages/settings/git/GitCloneHttpAuth.qml:35 -#: ../qml/pages/settings/git/GitCloneHttpAuth.qml:46 -msgid "Password" -msgstr "" - -#: ../qml/pages/settings/git/ImportGitClone.qml:55 -msgid "" -"Importing a git repo will delete
any existing password store!" -"
Continue ?" -msgstr "" - -#: ../qml/pages/settings/git/ImportGitClone.qml:69 -msgid "An error occured during git clone !" -msgstr "" - -#: ../qml/pages/settings/git/ImportGitClone.qml:90 -msgid "Git Clone Import" -msgstr "" diff --git a/qml/components/GitCloneSshKey.qml b/qml/components/GitCloneSshKey.qml new file mode 100644 index 0000000..b0dc964 --- /dev/null +++ b/qml/components/GitCloneSshKey.qml @@ -0,0 +1,149 @@ +import Git 1.0 +import Lomiri.Components 1.3 +import Pass 1.0 +import Utils 1.0 +import QtQuick 2.4 + +Column { + + + property alias importSshPrivKeyButton : repoImportPrivKeyButton + property alias importSshPubKeyButton : repoImportPubKeyButton + property alias deleteSshPrivKeyButton : repoDeletePrivKeyButton + property alias deleteSshPubKeyButton : repoDeletePubKeyButton + property bool __sshPrivKeyAvailable : false + property bool __sshPubKeyAvailable : false + + signal repoUrlChanged(string url) + + function setRepoUrl(url) { + repoUrlInput.text = url; + } + + function update() { + __sshPrivKeyAvailable = Utils.fileExists(Git.privKey); + __sshPubKeyAvailable = Utils.fileExists(Git.pubKey); + } + + Component.onCompleted: { + update(); + } + + anchors.top: parent.fill + spacing: units.gu(1) + + Text { + id: repoUrlLabel + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + text: i18n.tr('Repo Url') + color: theme.palette.normal.backgroundText + } + + TextField { + id: repoUrlInput + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + placeholderText: "@" + onTextChanged: repoUrlChanged(repoUrlInput.text) + } + + Text { + id: repoPrivKeyLabel + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + text: i18n.tr('SSH private key') + color: theme.palette.normal.backgroundText + } + + Button { + id: repoImportPrivKeyButton + + width: parent.width + color: theme.palette.normal.positive + text: i18n.tr('Import SSH private key') + visible: !__sshPrivKeyAvailable + } + + Button { + id: repoDeletePrivKeyButton + + width: parent.width + color: theme.palette.normal.negative + text: i18n.tr('Delete SSH private key') + visible: __sshPrivKeyAvailable + } + + Text { + id: repoPubKeyLabel + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + text: i18n.tr('SSH public key') + color: theme.palette.normal.backgroundText + } + + Button { + id: repoDeletePubKeyButton + + width: parent.width + color: theme.palette.normal.negative + text: i18n.tr('Delete SSH public key') + visible: __sshPrivKeyAvailable + } + + Button { + id: repoImportPubKeyButton + + width: parent.width + color: theme.palette.normal.positive + text: i18n.tr('Import SSH public key') + visible: !__sshPrivKeyAvailable + } + + + Text { + id: repoPassphraseLabel + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + text: i18n.tr('Passphrase') + color: theme.palette.normal.backgroundText + } + + TextField { + id: repoPassphraseInput + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + echoMode: TextInput.Password + placeholderText: i18n.tr('Passphrase') + } + + Rectangle { + width: parent.width + height: units.gu(1) + color: theme.palette.normal.background + } + + Button { + id: buttonClone + + width: parent.width + color: theme.palette.normal.positive + text: i18n.tr('Clone') + onClicked: { + Git.cloneSSHKey(repoUrlInput.text, Pass.Passphrase_store, repoPassphraseInput.text); + } + } + +} diff --git a/qml/components/ImportFile.qml b/qml/components/ImportFile.qml new file mode 100644 index 0000000..5163b89 --- /dev/null +++ b/qml/components/ImportFile.qml @@ -0,0 +1,71 @@ +import "../dialogs" +import "../pages/headers" +import Lomiri.Components 1.3 +import Lomiri.Components.Popups 1.3 +import Lomiri.Content 1.3 +import Pass 1.0 +import QtQuick 2.4 +import Utils 1.0 + +Page { + id: importKeyFilePage + + property var activeTransfer + property alias contentPicker : contentPicker + property alias dialogImportKeyPageError : dialogImportKeyPageError + property alias dialogImportKeyPageSucess : dialogImportKeyPageSucess + + property string headerTitle : i18n.tr("Import succeeded !") + property string dialogErrorTxt : i18n.tr("Import failed !") + property string dialogSuccessTxt : i18n.tr("File Import") + + ContentPeerPicker { + id: contentPicker + anchors.top: importKeyHeader.bottom + anchors.bottom: parent.bottom + anchors.topMargin: importKeyFilePage.header.height + width: parent.width + visible: parent.visible + showTitle: false + contentType: ContentType.Text + handler: ContentHandler.Source + onCancelPressed: { + pageStack.pop(); + } + } + + ContentTransferHint { + id: transferHint + + anchors.fill: parent + activeTransfer: importKeyFilePage.activeTransfer + } + + Component { + id: dialogImportKeyPageError + + ErrorDialog { + textError: importKeyFilePage.dialogErrorTxt + } + + } + + Component { + id: dialogImportKeyPageSucess + + SuccessDialog { + textSuccess: importKeyFilePage.dialogSuccessTxt + onDialogClosed: { + pageStack.pop(); + } + } + + } + + header: StackHeader { + id: importKeyHeader + + title: importKeyFilePage.headerTitle + } + +} diff --git a/qml/pages/PasswordList.qml b/qml/pages/PasswordList.qml index 622cd28..2ee77e9 100644 --- a/qml/pages/PasswordList.qml +++ b/qml/pages/PasswordList.qml @@ -19,6 +19,7 @@ Page { for (var i = 0; i < __passwords.length; i++) { if (__passwords[i].toUpperCase().indexOf(filter.toUpperCase()) > -1) ret.push(__passwords[i]); + } } return ret; @@ -179,6 +180,7 @@ Page { Timer { id: searchTimer + interval: 500 onTriggered: __searchUpdateModel() } diff --git a/qml/pages/headers/MainHeader.qml b/qml/pages/headers/MainHeader.qml index 661493a..ce95b60 100644 --- a/qml/pages/headers/MainHeader.qml +++ b/qml/pages/headers/MainHeader.qml @@ -2,11 +2,12 @@ import Lomiri.Components 1.3 import QtQuick 2.4 PageHeader { + //property alias searchBarText: searchBar.text + //signal searchBarTextChanged(string text) + id: mainHeader property alias searchBar: searchBar - //property alias searchBarText: searchBar.text - //signal searchBarTextChanged(string text) width: parent.width height: units.gu(6) @@ -20,9 +21,9 @@ PageHeader { onTriggered: { searchBar.visible = !searchBar.visible; labelTitle.visible = !searchBar.visible; - if (searchBar.visible === true) { + if (searchBar.visible === true) searchBar.focus = true; - } + } }, Action { diff --git a/qml/pages/settings/DeleteRepo.qml b/qml/pages/settings/DeleteRepo.qml index aa23c75..ce766e9 100644 --- a/qml/pages/settings/DeleteRepo.qml +++ b/qml/pages/settings/DeleteRepo.qml @@ -90,7 +90,7 @@ Page { header: StackHeader { id: deleteRepoPageHeader - title: i18n.tr('Info Keys') + title: i18n.tr('Delete Password Store') } } diff --git a/qml/pages/settings/ImportGitClone.qml b/qml/pages/settings/ImportGitClone.qml index 9e70356..4d556ec 100644 --- a/qml/pages/settings/ImportGitClone.qml +++ b/qml/pages/settings/ImportGitClone.qml @@ -8,20 +8,28 @@ import Lomiri.Components.Pickers 1.3 import Lomiri.Components.Popups 1.3 import Pass 1.0 import QtQuick 2.4 - +import Utils 1.0 Page { id: importGitClonePage + property int __gitModeHTTP : 0 + property int __gitModeHTTP_AUTH : 1 + property int __gitModeSSH_KEY : 2 + + property string __repoUrl function __loadForm() { switch (combo.selectedIndex) { - case 0: - importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttp.qml"); - break; - case 1: - importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttpAuth.qml"); - break; + case __gitModeHTTP: + importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttp.qml"); + break; + case __gitModeHTTP_AUTH: + importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttpAuth.qml"); + break; + case __gitModeSSH_KEY: + importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneSshKey.qml"); + break; } } @@ -29,6 +37,10 @@ Page { Git.cloneSucceed.connect(function() { GitSettings.type = combo.selectedIndex; GitSettings.repoUrl = importGitClonePage.__repoUrl; + if(GitSettings.type != __gitModeSSH_KEY) { // ensure there no ssh key is kept if swicthing to another git mode + Utils.rmFile(Git.privKey); + Utils.rmFile(Git.pubKey); + } PopupUtils.open(dialogGitCloneSuccess); }); Git.cloneFailed.connect(function() { @@ -64,7 +76,7 @@ Page { id: combo width: parent.width - model: ["HTTP", "HTTP AUTH"] + model: ["HTTP", "HTTP AUTH", "SSH KEY"] onDelegateClicked: function(i) { timer.setTimeout(function() { __loadForm(); @@ -98,6 +110,25 @@ Page { importGitClonePage.__repoUrl = url; }); importGitCloneForm.item.setRepoUrl(importGitClonePage.__repoUrl); + + switch (combo.selectedIndex) { + case __gitModeHTTP: + break; + case __gitModeHTTP_AUTH: + break; + case __gitModeSSH_KEY: + importGitCloneForm.item.importSshPrivKeyButton.clicked.connect(function() { + pageStack.push(Qt.resolvedUrl("ImportSSHkey.qml"), { + "isPrivateKey": true + }); + }); + importGitCloneForm.item.importSshPubKeyButton.clicked.connect(function() { + pageStack.push(Qt.resolvedUrl("ImportSSHkey.qml"), { + "isPrivateKey": false + }); + }); + break; + } } } diff --git a/qml/pages/settings/ImportKeyFile.qml b/qml/pages/settings/ImportKeyFile.qml index 2671bb6..f4ccca1 100644 --- a/qml/pages/settings/ImportKeyFile.qml +++ b/qml/pages/settings/ImportKeyFile.qml @@ -1,84 +1,35 @@ -import "../../dialogs" -import "../headers" -import Lomiri.Components 1.3 -import Lomiri.Components.Popups 1.3 -import Lomiri.Content 1.3 +import "../../components" import Pass 1.0 -import QtQuick 2.4 -import Utils 1.0 -Page { +ImportFile { + id: importKeyFilePage - property var activeTransfer + headerTitle: i18n.tr("GPG Key Import") + dialogSuccessTxt : i18n.tr("Key successfully imported !") + dialogErrorTxt : i18n.tr("Key import failed !") - ContentPeerPicker { - anchors.top: importKeyHeader.bottom - anchors.bottom: parent.bottom - anchors.topMargin: importKeyFilePage.header.height - width: parent.width - visible: parent.visible - showTitle: false - contentType: ContentType.Text - handler: ContentHandler.Source - onPeerSelected: { - peer.selectionType = ContentTransfer.Single; - importKeyFilePage.activeTransfer = peer.request(); - importKeyFilePage.activeTransfer.stateChanged.connect(function() { - if (importKeyFilePage.activeTransfer.state === ContentTransfer.Charged) { - console.log("Charged"); - console.log(importKeyFilePage.activeTransfer.items[0].url); - var status = Pass.importGPGKey(importKeyFilePage.activeTransfer.items[0].url); - Pass.importGPGKeySucceed.connect(function() { - Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); - importKeyFilePage.activeTransfer = null; - PopupUtils.open(dialogImportKeyPageSucess); - }); - Pass.importGPGKeyFailed.connect(function(message) { - Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); - importKeyFilePage.activeTransfer = null; - PopupUtils.open(dialogImportKeyPageError); - }); - } - }); - } - onCancelPressed: { - pageStack.pop(); - } + contentPicker.onPeerSelected: { + { + peer.selectionType = ContentTransfer.Single; + importKeyFilePage.activeTransfer = peer.request(); + importKeyFilePage.activeTransfer.stateChanged.connect(function() { + if (importKeyFilePage.activeTransfer.state === ContentTransfer.Charged) { + console.log("Charged"); + console.log(importKeyFilePage.activeTransfer.items[0].url); + Pass.importGPGKey(importKeyFilePage.activeTransfer.items[0].url); + Pass.importGPGKeySucceed.connect(function() { + Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); + importKeyFilePage.activeTransfer = null; + PopupUtils.open(importKeyFilePage.dialogImportKeyPageSucess); + }); + Pass.importGPGKeyFailed.connect(function(message) { + Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); + importKeyFilePage.activeTransfer = null; + PopupUtils.open(importKeyFilePage.dialogImportKeyPageError); + }); + } + }); + } } - - ContentTransferHint { - id: transferHint - - anchors.fill: parent - activeTransfer: importKeyFilePage.activeTransfer - } - - Component { - id: dialogImportKeyPageError - - ErrorDialog { - textError: i18n.tr("Key import failed !") - } - - } - - Component { - id: dialogImportKeyPageSucess - - SuccessDialog { - textSuccess: i18n.tr("Key successfully imported !") - onDialogClosed: { - pageStack.pop(); - } - } - - } - - header: StackHeader { - id: importKeyHeader - - title: i18n.tr("GPG Key Import") - } - } diff --git a/qml/pages/settings/ImportSSHkey.qml b/qml/pages/settings/ImportSSHkey.qml new file mode 100644 index 0000000..fab5863 --- /dev/null +++ b/qml/pages/settings/ImportSSHkey.qml @@ -0,0 +1,34 @@ +import "../../components" +import Utils 1.0 +import Git 1.0 + +ImportFile { + id: importSSHKeyPage + + property bool isPrivateKey + + headerTitle: i18n.tr("SSH Key Import") + dialogSuccessTxt : i18n.tr("SSH Key successfully imported !") + dialogErrorTxt : i18n.tr("SSH Key import failed !") + + contentPicker.onPeerSelected: { + { + peer.selectionType = ContentTransfer.Single; + importSSHKeyPage.activeTransfer = peer.request(); + importSSHKeyPage.activeTransfer.stateChanged.connect(function() { + if (importSSHKeyPage.activeTransfer.state === ContentTransfer.Charged) { + console.log("Charged"); + console.log(importSSHKeyPage.activeTransfer.items[0].url); + var ret = Git.importSshKey(importSSHKeyPage.activeTransfer.items[0].url, isPrivateKey); + Utils.rmFile(importSSHKeyPage.activeTransfer.items[0].url); + importSSHKeyPage.activeTransfer = null; + if(ret) { + PopupUtils.open(importSSHKeyPage.dialogImportKeyPageSucess); + } else { + PopupUtils.open(importSSHKeyPage.dialogImportKeyPageError); + } + } + }); + } + } +}