1
0
mirror of https://github.com/QRouland/UTPass.git synced 2025-01-24 07:36:39 +00:00

Initial git clone feature

This commit is contained in:
Quentin Rouland 2025-01-10 13:48:38 +01:00
parent d33932be6d
commit 5e5a092da1
22 changed files with 334 additions and 91 deletions

View File

@ -3,6 +3,10 @@ cmake_minimum_required(VERSION 3.5.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
execute_process(
COMMAND dpkg-architecture -qDEB_HOST_ARCH
OUTPUT_VARIABLE CLICK_ARCH
@ -31,7 +35,8 @@ set(DATA_DIR /)
set(DESKTOP_FILE_NAME ${PROJECT_NAME}.desktop)
add_executable(${PROJECT_NAME} main.cpp)
add_executable(${PROJECT_NAME} main.cpp
qml/pages/settings/ImportGitClone.qml)
qt5_use_modules(${PROJECT_NAME} Gui Qml Quick)
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})

View File

@ -1,2 +1,3 @@
add_subdirectory(Git)
add_subdirectory(Pass)
add_subdirectory(Utils)

View File

@ -0,0 +1,37 @@
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(PLUGIN "Git")
set(
SRC
plugin.cpp
libgit.cpp
git.cpp
)
set(CMAKE_AUTOMOC ON)
execute_process(
COMMAND dpkg-architecture -qDEB_HOST_MULTIARCH
OUTPUT_VARIABLE ARCH_TRIPLET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(ARCH_TRIPLET STREQUAL "")
set(ARCH_TRIPLET x86_64-linux-gnu)
endif()
add_library(${PLUGIN} MODULE ${SRC})
set_target_properties(${PLUGIN} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGIN})
qt5_use_modules(${PLUGIN} Qml Quick DBus)
add_library(libgit2 SHARED IMPORTED)
set_property(TARGET libgit2 PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libgit2.so")
target_link_libraries(${PLUGIN} libgit2)
set(QT_IMPORTS_DIR "/lib/${ARCH_TRIPLET}")
install(TARGETS ${PLUGIN} DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN}/)
install(FILES qmldir DESTINATION ${QT_IMPORTS_DIR}/${PLUGIN}/)

18
plugins/Git/git.cpp Normal file
View File

@ -0,0 +1,18 @@
#include <QUrl>
#include <QtCore/QDir>
#include <QDebug>
#include "git.h"
#include "libgit.h"
Git::Git()
{}
bool Git::clone(QString url, QString path)
{
qInfo() << "Cloning " << url << "password_store to " << path;
QDir dir(path);
dir.removeRecursively(); // TODO see if we delete only after sucessfull clone / Will be change anyway when will add update etc..
return LibGit::instance()->clone(url, path);
}

20
plugins/Git/git.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef GIT_H
#define GIT_H
#include <QUrl>
#include <QObject>
class Git : public QObject
{
Q_OBJECT
public:
Git();
~Git() override = default;
Q_INVOKABLE bool clone(QString url, QString path);
// Q_INVOKABLE bool update(QUrl url, QString path);
};
#endif

51
plugins/Git/libgit.cpp Normal file
View File

@ -0,0 +1,51 @@
#include <QUrl>
#include <QDebug>
extern "C" {
#include <git2.h>
}
#include "libgit.h"
LibGit::LibGit()
{
git_libgit2_init();
}
LibGit::~LibGit() {
git_libgit2_shutdown();
}
int LibGit::credentials_cb(git_cred **out, const char *url, const char *username_from_url,
unsigned int allowed_types, void *payload)
{
int error;
const char *user, *pass;
/*
* Ask the user via the UI. On error, store the information and return GIT_EUSER which will be
* bubbled up to the code performing the fetch or push. Using GIT_EUSER allows the application
* to know it was an error from the application instead of libgit2.
*/
// if ((error = ask_user(&user, &pass, url, username_from_url, allowed_types)) < 0) {
// store_error(error);
// return GIT_EUSER;
// }
// user = "user";
// pass = "pass";
// return git_cred_userpass_plaintext_new(out, user, pass);
return GIT_EUSER;
}
bool LibGit::clone(QString url, QString path) {
qDebug("yo");
git_repository *repo = NULL;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
opts.fetch_opts.callbacks.credentials = *credentials_cb;
int ret = git_clone(&repo, url.toLocal8Bit().data(), path.toLocal8Bit().data(), &opts);
if (repo) {
git_repository_free(repo);
}
return ret == 0; // TODO Clean error handling to return specifics errors for the ui
}

32
plugins/Git/libgit.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef LIBGIT_H
#define LIBGIT_H
#include <QObject>
#include <QUrl>
extern "C" {
#include <git2/transport.h>
}
#include <memory>
class LibGit
{
private:
LibGit();
static int credentials_cb(git_cred **out, const char *url, const char *username_from_url,
unsigned int allowed_types, void *payload);
public:
~LibGit();
static std::shared_ptr<LibGit> instance()
{
static std::shared_ptr<LibGit> s{new LibGit};
return s;
}
LibGit(LibGit const &) = delete;
void operator=(LibGit const &) = delete;
bool clone(QString url, QString path);
};
#endif

10
plugins/Git/plugin.cpp Normal file
View File

@ -0,0 +1,10 @@
#include <QtQml>
#include "plugin.h"
#include "git.h"
void GitPlugin::registerTypes(const char *uri)
{
//@uri Git
qmlRegisterSingletonType<Git>(uri, 1, 0, "Git", [](QQmlEngine *, QJSEngine *) -> QObject * { return new Git; });
}

16
plugins/Git/plugin.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef GITPLUGIN_H
#define GITPLUGIN_H
#include <QQmlExtensionPlugin>
class GitPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID
"org.qt-project.Qt.QQmlExtensionInterface")
public:
void registerTypes(const char *uri) override;
};
#endif

2
plugins/Git/qmldir Normal file
View File

@ -0,0 +1,2 @@
module Git
plugin Git

View File

@ -4,7 +4,6 @@ set(PLUGIN "Pass")
set(
SRC
plugin.cpp
git.cpp
pass.cpp
gpg.cpp
passkeymodel.h
@ -43,10 +42,8 @@ set_property(TARGET libgpgmepp PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPL
add_library(libqgpgme SHARED IMPORTED)
set_property(TARGET libqgpgme PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libqgpgme.so")
add_library(libgit2 SHARED IMPORTED)
set_property(TARGET libgit2 PROPERTY IMPORTED_LOCATION "/usr/lib/${ARCH_TRIPLET}/libgit2.so")
target_link_libraries(${PLUGIN} gpgerror libassuan libgpgme libgpgmepp libqgpgme libgit2)
target_link_libraries(${PLUGIN} gpgerror libassuan libgpgme libgpgmepp libqgpgme)
set(QT_IMPORTS_DIR "/lib/${ARCH_TRIPLET}")

View File

@ -1,32 +0,0 @@
#include <QDebug>
#include <QUrl>
extern "C" {
#include <git2.h>
}
#include "git.h"
Git::Git()
{
git_libgit2_init();
}
Git::~Git() {
git_libgit2_shutdown();
}
bool Git::clone(QString url, QString path) {
git_repository *repo = NULL;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
int ret = git_clone(&repo, url.toLocal8Bit().data(), path.toLocal8Bit().data(), &opts);
if (repo) {
git_repository_free(repo);
}
return ret == 0; // TODO better error handling to return specifics errors for the ui
}

View File

@ -1,27 +0,0 @@
#ifndef GIT_H
#define GIT_H
#include <QObject>
#include <QUrl>
#include <memory>
class Git
{
private:
Git();
public:
~Git();
static std::shared_ptr<Git> instance()
{
static std::shared_ptr<Git> s{new Git};
return s;
}
Git(Git const &) = delete;
void operator=(Git const &) = delete;
bool clone(QString url, QString path);
};
#endif

View File

@ -13,6 +13,7 @@ class Gpg
{
private:
Gpg();
QObject *m_window;
QString findCommandPath(const QString &command);
@ -42,7 +43,7 @@ public:
};
QPair< Error, std::vector< Key > > getAllKeys(bool remote = false, bool include_sigs = {}, bool
QPair<Error, std::vector<Key >> getAllKeys(bool remote = false, bool include_sigs = {}, bool
validate = false);
QPair<Error, std::vector<Key>> getKeys( QString pattern_uid, bool remote = false,
bool include_sigs = false,

View File

@ -3,7 +3,6 @@
#include <QtCore/QDir>
#include "pass.h"
#include "git.h"
#include "gpg.h"
#include "passkeymodel.h"
@ -22,8 +21,9 @@ void Pass::init(QObject *window)
Gpg::instance()->setWindow(window);
QDir dir(m_password_store);
if (!dir.exists())
if (!dir.exists()) {
dir.mkpath(".");
}
qInfo() << "Password Store is :" << m_password_store;
}
@ -62,13 +62,3 @@ QVariant Pass::gpgGetAllKeysModel()
Gpg::instance()->getAllKeys().second));
}
QString Pass::getPasswordStore()
{
return m_password_store;
}
bool Pass::gitClone(QString url)
{
qInfo() << "Cloning . password_store from " << url;
return Git::instance()->clone(url, m_password_store);
}

View File

@ -9,6 +9,9 @@
class Pass : public QObject
{
Q_OBJECT
Q_PROPERTY(QString password_store READ password_store)
private:
QString m_password_store;
signals:
@ -16,18 +19,17 @@ signals:
void decryptCanceled();
void decryptFailed();
public:
Pass();
~Pass() override = default;
QString password_store() const { return m_password_store; }
Q_INVOKABLE void init(QObject *window);
Q_INVOKABLE QString getPasswordStore();
Q_INVOKABLE void decrypt(QUrl url);
Q_INVOKABLE bool gpgDeleteKeyId(QString id);
Q_INVOKABLE bool gpgImportKeyFromFile(QUrl url);
Q_INVOKABLE QVariant gpgGetAllKeysModel();
Q_INVOKABLE bool gitClone(QString url);
};
#endif

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: utpass.qrouland\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-01-07 13:36+0000\n"
"POT-Creation-Date: 2025-01-10 13:46+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -37,8 +37,8 @@ msgstr ""
msgid "Error !"
msgstr ""
#: ../qml/dialogs/ErrorDialog.qml:15 ../qml/dialogs/SuccessDialog.qml:15
msgid "OK"
#: ../qml/dialogs/ErrorDialog.qml:15
msgid "Close"
msgstr ""
#: ../qml/dialogs/PassphraseDialog.qml:7
@ -57,6 +57,10 @@ msgstr ""
msgid "Success !"
msgstr ""
#: ../qml/dialogs/SuccessDialog.qml:15
msgid "OK"
msgstr ""
#: ../qml/pages/Info.qml:11 ../qml/pages/headers/MainHeader.qml:58
msgid "Info"
msgstr ""
@ -103,6 +107,32 @@ msgstr ""
msgid "Settings"
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:15
msgid "Git Clone Import"
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:36
msgid "Repo Url"
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:44
msgid "Git repo url"
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:50
msgid "Clone"
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:68
msgid ""
"Importing a git repo will delete<br>any existing password store!"
"<br>Continue ?"
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:78
msgid "An error occured during git clone !"
msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:17
msgid "GPG Key Import"
msgstr ""
@ -177,9 +207,13 @@ msgid "Password Store"
msgstr ""
#: ../qml/pages/settings/Settings.qml:47
msgid "Import a Password Store using Git"
msgstr ""
#: ../qml/pages/settings/Settings.qml:51
msgid "Import a Password Store Zip"
msgstr ""
#: ../qml/pages/settings/Settings.qml:56
#: ../qml/pages/settings/Settings.qml:60
msgid "Warning: importing delete any exiting Password Store"
msgstr ""

View File

@ -69,9 +69,6 @@ Component {
id: passwordPageDecryptError
ErrorDialog {
textError: i18n.tr("Decryption failed !")
onDialogClosed: {
pageStack.pop()
}
}
}
}

View File

@ -3,7 +3,7 @@ import Lomiri.Components 1.3
import Lomiri.Components.Popups 1.3
Dialog {
id: dialogSuccess
id: dialogError
property string textError
@ -12,11 +12,11 @@ Dialog {
title: i18n.tr("Error !")
text: textError
Button {
text: i18n.tr("OK")
text: i18n.tr("Close")
color: LomiriColors.red
onClicked: function () {
dialogClosed()
PopupUtils.close(dialogSuccess)
PopupUtils.close(dialogError)
}
}
}

View File

@ -67,6 +67,6 @@ Page {
}
Component.onCompleted: {
passwordStorePath = "file:" + Pass.getPasswordStore()
passwordStorePath = "file:" + Pass.password_store
}
}

View File

@ -0,0 +1,85 @@
import QtQuick 2.4
import Lomiri.Components 1.3
import Lomiri.Components.Popups 1.3
import Git 1.0
import Pass 1.0
import "../headers"
import "../../components"
import "../../dialogs"
Page {
id: importGitClonePage
header: StackHeader {
id: importGitCloneHeader
title: i18n.tr('Git Clone Import')
}
Flow {
anchors.top: importGitCloneHeader.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.leftMargin: units.gu(2)
anchors.rightMargin: units.gu(2)
spacing: units.gu(1)
Rectangle {
width: parent.width
height: units.gu(1)
}
Text {
id: repoUrlLabe
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: parent.width
text: i18n.tr('Repo Url')
}
TextField {
id: textFieldInput
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: parent.width
placeholderText: i18n.tr('Git repo url')
}
Button {
id: buttonAdd
width: parent.width
text: i18n.tr('Clone')
onClicked: {
var ret = Git.clone(textFieldInput.text, Pass.password_store)
if(ret) {
pageStack.pop()
} else {
PopupUtils.open(importGitCloneError, importGitClonePage)
}
}
}
}
Component {
id: importGitCloneValidation
SimpleValidationDialog {
text: i18n.tr(
"Importing a git repo will delete<br>any existing password store!<br>Continue ?")
onCanceled: {
pageStack.pop()
}
}
}
Component {
id: importGitCloneError
ErrorDialog {
textError: i18n.tr("An error occured during git clone !")
}
}
Component.onCompleted: {
PopupUtils.open(importGitCloneValidation, importGitClonePage)
}
}

View File

@ -42,6 +42,10 @@ Page {
height: units.gu(4)
text: i18n.tr('Password Store')
}
PageStackLink {
page: Qt.resolvedUrl("ImportGitClone.qml")
text: i18n.tr('Import a Password Store using Git')
}
PageStackLink {
page: Qt.resolvedUrl("ImportZip.qml")
text: i18n.tr('Import a Password Store Zip')