1
0
mirror of https://github.com/QRouland/UTPass.git synced 2025-02-24 04:14:55 +00:00

Add support for ssh clone

This commit is contained in:
Quentin Rouland 2025-02-21 15:50:27 +01:00
parent 5683db69c7
commit 884488b9ed
16 changed files with 558 additions and 184 deletions

View File

@ -12,8 +12,15 @@ extern "C" {
#include "jobs/gitjob.h" #include "jobs/gitjob.h"
Git::Git(): Git::Git():
m_sem(std::unique_ptr<QSemaphore>(new QSemaphore(1))) m_sem(std::unique_ptr<QSemaphore>(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(); git_libgit2_init();
} }
@ -26,7 +33,7 @@ Git::~Git()
bool Git::clone(QString url, QString path, cred_type mode) bool Git::clone(QString url, QString path, cred_type mode)
{ {
if (!this->m_sem->tryAcquire(1, 500)) { 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; return false;
} }
auto v = overload { auto v = overload {
@ -44,18 +51,26 @@ bool Git::clone(QString url, QString path, cred_type mode)
bool Git::cloneHttp(QString url, QString path) bool Git::cloneHttp(QString url, QString path)
{ {
qInfo() << "Call clone command Http " << url << " " << path; qInfo() << "[Git] Call clone command Http " << url << " " << path;
HTTP mode = {}; HTTP mode = {};
return this->clone(url, path, mode); return this->clone(url, path, mode);
} }
bool Git::cloneHttpPass(QString url, QString path, QString pass) 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 }; HTTPUserPass mode = { pass };
return this->clone(url, path, mode); 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) void Git::cloneResult(const bool err)
{ {
@ -66,3 +81,23 @@ void Git::cloneResult(const bool err)
} }
this->m_sem->release(); 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);
}

View File

@ -2,6 +2,7 @@
#define GIT_H #define GIT_H
#include "jobs/gitjob.h" #include "jobs/gitjob.h"
#include "qdebug.h"
#include <QUrl> #include <QUrl>
#include <QObject> #include <QObject>
#include <QSemaphore> #include <QSemaphore>
@ -16,6 +17,8 @@
class Git : public QObject class Git : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString privKey READ pubKeyPath)
Q_PROPERTY(QString pubKey READ privKeyPath)
private slots: private slots:
/** /**
@ -47,6 +50,7 @@ signals:
private: private:
std::unique_ptr<QSemaphore> m_sem; /**< Semaphore for managing concurrent operations. */ std::unique_ptr<QSemaphore> 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. * @brief Clones a repository from a specified URL.
@ -61,6 +65,27 @@ private:
*/ */
bool clone(QString url, QString path, cred_type mode); 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: public:
/** /**
* @brief Constructor for the Git class. * @brief Constructor for the Git class.
@ -76,6 +101,9 @@ public:
*/ */
~Git() override; ~Git() override;
Q_INVOKABLE bool importSshKey(QUrl source_path, bool is_private);
/** /**
* @brief Clones a repository over HTTP. * @brief Clones a repository over HTTP.
* *
@ -97,13 +125,22 @@ public:
* @param url The HTTP URL of the Git repository to clone. * @param url The HTTP URL of the Git repository to clone.
* @param path The destination path for the cloned repository. * @param path The destination path for the cloned repository.
* @param pass The password used for HTTP authentication. * @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); 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); * @brief Clones a repository over SSH with a key for authentication.
// Q_INVOKABLE bool clone_ssh_key(QString url, QString path, QString pub_key, QString priv_key, QString passphrase); *
* 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); // Q_INVOKABLE bool update(QUrl url, QString path);
// .... // ....

View File

@ -18,17 +18,7 @@ GitJob::~GitJob()
git_libgit2_shutdown(); 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, int GitJob::credentialsCB(git_cred **out, const char *url, const char *username_from_url,
unsigned int allowed_types, void *payload) unsigned int allowed_types, void *payload)

View File

@ -1,6 +1,7 @@
#ifndef GITJOB_H #ifndef GITJOB_H
#define GITJOB_H #define GITJOB_H
#include <QDir>
#include <QThread> #include <QThread>
extern "C" { extern "C" {
#include <git2.h> #include <git2.h>

View File

@ -9,5 +9,4 @@ void GitPlugin::registerTypes(const char *uri)
{ {
//@uri Git //@uri Git
qmlRegisterSingletonType<Git>(uri, 1, 0, "Git", [](QQmlEngine *, QJSEngine *) -> QObject * { return new Git; }); qmlRegisterSingletonType<Git>(uri, 1, 0, "Git", [](QQmlEngine *, QJSEngine *) -> QObject * { return new Git; });
} }

View File

@ -15,7 +15,7 @@ bool Utils::unzip(QUrl zip_url, QString dir_out_path)
if (!this->m_sem->tryAcquire(1, 500)) { if (!this->m_sem->tryAcquire(1, 500)) {
return false; 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)); auto job = new UnzipJob(zip_url, QDir(dir_out_path));
connect(job, &UnzipJob::resultReady, this, &Utils::unzipResult); connect(job, &UnzipJob::resultReady, this, &Utils::unzipResult);
connect(job, &UnzipJob::finished, job, &QObject::deleteLater); 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) void Utils::unzipResult(bool err)
{ {
qDebug() << "Unzip Result"; qDebug() << "[Utils] Unzip Result";
if (err) { if (err) {
qInfo() << "Unzip Failed"; qInfo() << "[Utils] Unzip Failed";
emit unzipFailed(); emit unzipFailed();
} else { } else {
qInfo() << "Unzip Succeed"; qInfo() << "[Utils] Unzip Succeed";
emit unzipSucceed(); emit unzipSucceed();
} }
this->m_sem->release(1); this->m_sem->release(1);
@ -42,7 +42,7 @@ void Utils::unzipResult(bool err)
QString Utils::manifestPath() QString Utils::manifestPath()
{ {
auto path = QDir(QDir::currentPath()).filePath("manifest_.json"); auto path = QDir(QDir::currentPath()).filePath("manifest_.json");
qInfo() << "Manifest path : " << path; qInfo() << "[Utils] Manifest path : " << path;
return path; return path;
} }
@ -56,3 +56,12 @@ bool Utils::rmDir(QUrl dir_url)
QDir dir(dir_url.toLocalFile()); QDir dir(dir_url.toLocalFile());
return dir.removeRecursively(); 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;
}

View File

@ -80,6 +80,14 @@ public:
*/ */
Q_INVOKABLE bool rmDir(QUrl dir_url); 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 #endif

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: utpass.qrouland\n" "Project-Id-Version: utpass.qrouland\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,6 +17,64 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n" "Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\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 #: ../qml/dialogs/ErrorDialog.qml:12
msgid "Error !" msgid "Error !"
msgstr "" msgstr ""
@ -75,82 +133,110 @@ msgstr ""
msgid "Released under the terms of the GNU GPL v3" msgid "Released under the terms of the GNU GPL v3"
msgstr "" 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" msgid "Info"
msgstr "" msgstr ""
#: ../qml/pages/PasswordList.qml:45 #: ../qml/pages/PasswordList.qml:79
msgid "No password found" msgid "No password found"
msgstr "" msgstr ""
#: ../qml/pages/PasswordList.qml:58 #: ../qml/pages/PasswordList.qml:92
msgid "You can import a password store by cloning or" msgid "You can import a password store by cloning or"
msgstr "" msgstr ""
#: ../qml/pages/PasswordList.qml:65 #: ../qml/pages/PasswordList.qml:99
msgid "importing a password store zip in the settings" msgid "importing a password store zip in the settings"
msgstr "" msgstr ""
#: ../qml/pages/PasswordList.qml:100 #: ../qml/pages/PasswordList.qml:176
msgid "Decryption failed !" msgid "Decryption failed !"
msgstr "" msgstr ""
#: ../qml/pages/PasswordList.qml:114 #: ../qml/pages/PasswordList.qml:198
msgid "Back" msgid "Back"
msgstr "" 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 #: ../qml/pages/headers/StackHeader.qml:9 UTPass.desktop.in.h:1
msgid "UTPass" msgid "UTPass"
msgstr "" msgstr ""
#: ../qml/pages/headers/MainHeader.qml:26 ../qml/pages/settings/Settings.qml:75 #: ../qml/pages/headers/MainHeader.qml:20
msgid "Settings" #: ../qml/pages/headers/MainHeader.qml:62
msgstr ""
#: ../qml/pages/headers/MainHeader.qml:57
msgid "Search" msgid "Search"
msgstr "" msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:42 #: ../qml/pages/headers/MainHeader.qml:31 ../qml/pages/settings/Settings.qml:73
#: ../qml/pages/settings/Settings.qml:58 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" msgid "Delete Password Store"
msgstr "" msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:55 #: ../qml/pages/settings/DeleteRepo.qml:56
msgid "You're are about to delete<br>the current Password Store.<br>Continue ?" msgid "You're are about to delete<br>the current Password Store.<br>Continue ?"
msgstr "" 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/ImportZip.qml:66
#: ../qml/pages/settings/InfoKeys.qml:174 #: ../qml/pages/settings/InfoKeys.qml:174
#: ../qml/pages/settings/git/ImportGitClone.qml:56
msgid "Yes" msgid "Yes"
msgstr "" msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:69 #: ../qml/pages/settings/DeleteRepo.qml:70
msgid "Password Store removal failed !" msgid "Password Store removal failed !"
msgstr "" msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:78 #: ../qml/pages/settings/DeleteRepo.qml:79
msgid "Password Store deleted !" msgid "Password Store deleted !"
msgstr "" msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:90 #: ../qml/pages/settings/ImportGitClone.qml:141
#: ../qml/pages/settings/InfoKeys.qml:216 msgid ""
msgid "Info Keys" "Importing a git repo will delete<br>any existing password store!"
"<br>Continue ?"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:61 #: ../qml/pages/settings/ImportGitClone.qml:155
msgid "Key import failed !" msgid "An error occured during git clone !"
msgstr "" 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 !" msgid "Key successfully imported !"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:81 #: ../qml/pages/settings/ImportKeyFile.qml:10
msgid "GPG Key Import" 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 "" msgstr ""
#: ../qml/pages/settings/ImportZip.qml:65 #: ../qml/pages/settings/ImportZip.qml:65
@ -162,11 +248,6 @@ msgstr ""
msgid "Password store import failed !" msgid "Password store import failed !"
msgstr "" 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 #: ../qml/pages/settings/ImportZip.qml:100
msgid "Zip Password Store Import" msgid "Zip Password Store Import"
msgstr "" msgstr ""
@ -203,59 +284,34 @@ msgstr ""
msgid "An Error occured getting GPG keys !" msgid "An Error occured getting GPG keys !"
msgstr "" 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" msgid "GPG"
msgstr "" msgstr ""
#: ../qml/pages/settings/Settings.qml:29 #: ../qml/pages/settings/Settings.qml:27
msgid "Import a GPG key file" msgid "Import a GPG key file"
msgstr "" msgstr ""
#: ../qml/pages/settings/Settings.qml:34 #: ../qml/pages/settings/Settings.qml:32
msgid "Show GPG keys" msgid "Show GPG keys"
msgstr "" msgstr ""
#: ../qml/pages/settings/Settings.qml:42 #: ../qml/pages/settings/Settings.qml:40
msgid "Password Store" msgid "Password Store"
msgstr "" msgstr ""
#: ../qml/pages/settings/Settings.qml:48 #: ../qml/pages/settings/Settings.qml:46
msgid "Import a Password Store using Git" msgid "Import a Password Store using Git"
msgstr "" msgstr ""
#: ../qml/pages/settings/Settings.qml:53 #: ../qml/pages/settings/Settings.qml:51
msgid "Import a Password Store Zip" msgid "Import a Password Store Zip"
msgstr "" msgstr ""
#: ../qml/pages/settings/Settings.qml:67 #: ../qml/pages/settings/Settings.qml:65
msgid "Warning: importing delete any exiting Password Store" msgid "Warning: importing delete any exiting Password Store"
msgstr "" 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<br>any existing password store!"
"<br>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 ""

View File

@ -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: "<username>@<hostname>"
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);
}
}
}

View File

@ -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
}
}

View File

@ -19,6 +19,7 @@ Page {
for (var i = 0; i < __passwords.length; i++) { for (var i = 0; i < __passwords.length; i++) {
if (__passwords[i].toUpperCase().indexOf(filter.toUpperCase()) > -1) if (__passwords[i].toUpperCase().indexOf(filter.toUpperCase()) > -1)
ret.push(__passwords[i]); ret.push(__passwords[i]);
} }
} }
return ret; return ret;
@ -179,6 +180,7 @@ Page {
Timer { Timer {
id: searchTimer id: searchTimer
interval: 500 interval: 500
onTriggered: __searchUpdateModel() onTriggered: __searchUpdateModel()
} }

View File

@ -2,11 +2,12 @@ import Lomiri.Components 1.3
import QtQuick 2.4 import QtQuick 2.4
PageHeader { PageHeader {
//property alias searchBarText: searchBar.text
//signal searchBarTextChanged(string text)
id: mainHeader id: mainHeader
property alias searchBar: searchBar property alias searchBar: searchBar
//property alias searchBarText: searchBar.text
//signal searchBarTextChanged(string text)
width: parent.width width: parent.width
height: units.gu(6) height: units.gu(6)
@ -20,9 +21,9 @@ PageHeader {
onTriggered: { onTriggered: {
searchBar.visible = !searchBar.visible; searchBar.visible = !searchBar.visible;
labelTitle.visible = !searchBar.visible; labelTitle.visible = !searchBar.visible;
if (searchBar.visible === true) { if (searchBar.visible === true)
searchBar.focus = true; searchBar.focus = true;
}
} }
}, },
Action { Action {

View File

@ -90,7 +90,7 @@ Page {
header: StackHeader { header: StackHeader {
id: deleteRepoPageHeader id: deleteRepoPageHeader
title: i18n.tr('Info Keys') title: i18n.tr('Delete Password Store')
} }
} }

View File

@ -8,20 +8,28 @@ import Lomiri.Components.Pickers 1.3
import Lomiri.Components.Popups 1.3 import Lomiri.Components.Popups 1.3
import Pass 1.0 import Pass 1.0
import QtQuick 2.4 import QtQuick 2.4
import Utils 1.0
Page { Page {
id: importGitClonePage id: importGitClonePage
property int __gitModeHTTP : 0
property int __gitModeHTTP_AUTH : 1
property int __gitModeSSH_KEY : 2
property string __repoUrl property string __repoUrl
function __loadForm() { function __loadForm() {
switch (combo.selectedIndex) { switch (combo.selectedIndex) {
case 0: case __gitModeHTTP:
importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttp.qml"); importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttp.qml");
break; break;
case 1: case __gitModeHTTP_AUTH:
importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttpAuth.qml"); importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttpAuth.qml");
break; break;
case __gitModeSSH_KEY:
importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneSshKey.qml");
break;
} }
} }
@ -29,6 +37,10 @@ Page {
Git.cloneSucceed.connect(function() { Git.cloneSucceed.connect(function() {
GitSettings.type = combo.selectedIndex; GitSettings.type = combo.selectedIndex;
GitSettings.repoUrl = importGitClonePage.__repoUrl; 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); PopupUtils.open(dialogGitCloneSuccess);
}); });
Git.cloneFailed.connect(function() { Git.cloneFailed.connect(function() {
@ -64,7 +76,7 @@ Page {
id: combo id: combo
width: parent.width width: parent.width
model: ["HTTP", "HTTP AUTH"] model: ["HTTP", "HTTP AUTH", "SSH KEY"]
onDelegateClicked: function(i) { onDelegateClicked: function(i) {
timer.setTimeout(function() { timer.setTimeout(function() {
__loadForm(); __loadForm();
@ -98,6 +110,25 @@ Page {
importGitClonePage.__repoUrl = url; importGitClonePage.__repoUrl = url;
}); });
importGitCloneForm.item.setRepoUrl(importGitClonePage.__repoUrl); 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;
}
} }
} }

View File

@ -1,84 +1,35 @@
import "../../dialogs" import "../../components"
import "../headers"
import Lomiri.Components 1.3
import Lomiri.Components.Popups 1.3
import Lomiri.Content 1.3
import Pass 1.0 import Pass 1.0
import QtQuick 2.4
import Utils 1.0
Page { ImportFile {
id: importKeyFilePage id: importKeyFilePage
property var activeTransfer headerTitle: i18n.tr("GPG Key Import")
dialogSuccessTxt : i18n.tr("Key successfully imported !")
dialogErrorTxt : i18n.tr("Key import failed !")
ContentPeerPicker { contentPicker.onPeerSelected: {
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; peer.selectionType = ContentTransfer.Single;
importKeyFilePage.activeTransfer = peer.request(); importKeyFilePage.activeTransfer = peer.request();
importKeyFilePage.activeTransfer.stateChanged.connect(function() { importKeyFilePage.activeTransfer.stateChanged.connect(function() {
if (importKeyFilePage.activeTransfer.state === ContentTransfer.Charged) { if (importKeyFilePage.activeTransfer.state === ContentTransfer.Charged) {
console.log("Charged"); console.log("Charged");
console.log(importKeyFilePage.activeTransfer.items[0].url); console.log(importKeyFilePage.activeTransfer.items[0].url);
var status = Pass.importGPGKey(importKeyFilePage.activeTransfer.items[0].url); Pass.importGPGKey(importKeyFilePage.activeTransfer.items[0].url);
Pass.importGPGKeySucceed.connect(function() { Pass.importGPGKeySucceed.connect(function() {
Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url);
importKeyFilePage.activeTransfer = null; importKeyFilePage.activeTransfer = null;
PopupUtils.open(dialogImportKeyPageSucess); PopupUtils.open(importKeyFilePage.dialogImportKeyPageSucess);
}); });
Pass.importGPGKeyFailed.connect(function(message) { Pass.importGPGKeyFailed.connect(function(message) {
Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url);
importKeyFilePage.activeTransfer = null; importKeyFilePage.activeTransfer = null;
PopupUtils.open(dialogImportKeyPageError); PopupUtils.open(importKeyFilePage.dialogImportKeyPageError);
}); });
} }
}); });
} }
onCancelPressed: {
pageStack.pop();
} }
}
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")
}
} }

View File

@ -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);
}
}
});
}
}
}