1
0
mirror of https://github.com/QRouland/UTPass.git synced 2026-01-10 19:36:57 +00:00

9 Commits
main ... 0.0.4

25 changed files with 468 additions and 297 deletions

View File

@@ -1,4 +1,9 @@
# 0.0.3: Git Initial Support and Move to RNP # 0.0.4
- Fix white screen issue on 24.04
- Add search functionality
- Some minor fixes and improvements
# 0.0.3
- Port app to Focal - Port app to Focal
- Improve UI : - Improve UI :
* Follow human interface guidelines * Follow human interface guidelines
@@ -9,14 +14,14 @@
- Add Git HTTP and HTTP AUTH clone for password store import feature - Add Git HTTP and HTTP AUTH clone for password store import feature
- Add delete password store feature - Add delete password store feature
# 0.0.2 : Added translations # 0.0.2
- Added French by Anne17 and Reda - Added French by Anne17 and Reda
- Added Catalan by Joan CiberSheep - Added Catalan by Joan CiberSheep
- Added Spanish by Advocatux and Reda - Added Spanish by Advocatux and Reda
Thanks to all the translators ! Thanks to all the translators !
# 0.0.1 : Initial Release # 0.0.1
- Import of gpg keys via file - Import of gpg keys via file
- Suppression of gpg keys - Suppression of gpg keys
- Import of password via a password store zip - Import of password via a password store zip

View File

@@ -1,6 +1,6 @@
# UTPass # UTPass
A Ubuntu Touch password management app aiming to be compatible with [ZX2C4s pass command line application](https://www.passwordstore.org/) the standard unix password manager. A Ubuntu Touch password management app aiming to be read-only compatible with [ZX2C4s pass command line application](https://www.passwordstore.org/) the standard unix password manager.
## Installation ## Installation
@@ -9,10 +9,6 @@ UTPass is avalaible on the [OpenStore](https://open-store.io)
[![OpenStore](https://open-store.io/badges/en_US.png)](https://open-store.io/app/utpass.qrouland) [![OpenStore](https://open-store.io/badges/en_US.png)](https://open-store.io/app/utpass.qrouland)
## Features
The goal is to be closest possible of the features offer by [ZX2C4s pass command line application](https://www.passwordstore.org/).
See [Features wiki page](https://taiga.rdrive.ovh/project/utpass/wiki/contributing) for details.
## Export/Import ## Export/Import
@@ -23,8 +19,7 @@ Export gpg private keys in order to decrypt passwords:
gpg --output keys.gpg --export-secret-keys <key> gpg --output keys.gpg --export-secret-keys <key>
``` ```
If your password store is already hosted in a Git repository that provides HTTP or HTTP with authentication for cloning (we're working to have support for SSH soon), you can clone your password store directly from the app. If your password store is already hosted in a Git repository with HTTP (SSH is not supported yet), you can clone your password store directly from the app. Otherwise, follow these steps to export it as a ZIP file for importing.
Otherwise, follow these steps to export it to a ZIP file for importing.
Export passwords to a ZIP archive, assuming they reside in the *.password-store* folder: Export passwords to a ZIP archive, assuming they reside in the *.password-store* folder:
``` ```

View File

@@ -41,4 +41,5 @@ install_lib:
- "libhttp_parser.so*" - "libhttp_parser.so*"
- "libssh2.so*" - "libssh2.so*"
- "libquazip5.so*" - "libquazip5.so*"
- "libjson-c.so*"

View File

@@ -10,7 +10,7 @@
"content-hub": "UTPass.contenthub" "content-hub": "UTPass.contenthub"
} }
}, },
"version": "0.0.4-dev", "version": "0.0.4",
"maintainer": "Quentin Rouland <quentin@qrouland.com>", "maintainer": "Quentin Rouland <quentin@qrouland.com>",
"framework" : "ubuntu-sdk-20.04" "framework" : "ubuntu-sdk-20.04"
} }

View File

@@ -6,6 +6,7 @@ set(
plugin.cpp plugin.cpp
git.cpp git.cpp
utils.h utils.h
error.h
jobs/clonejob.cpp jobs/clonejob.cpp
jobs/gitjob.cpp jobs/gitjob.cpp
) )

55
plugins/Git/error.h Normal file
View File

@@ -0,0 +1,55 @@
// error.h
#ifndef ERROR_H
#define ERROR_H
#include <string>
extern "C" {
#include <git2.h>
}
// Enum for Git-specific errors (e.g., from git_clone)
enum class GitCloneErrorCode {
Successful = 0,
UnexpectedError, ///< Unknown or unexpected error
InvalidUrl, ///< Malformed URL error
NoUsername, ///< Missing username error
AuthentificationError, ///< Authentification error
UrlTypeDoNotMatchCreds,
};
/**
* Convert a git_error to a GitCloneErrorCode
* @param error A pointer to the git_error structure
* @return Corresponding GitCloneErrorCode integer value
*/
inline GitCloneErrorCode gitErrorToGitCloneErrorCode(const git_error* error)
{
if (error == nullptr) {
return GitCloneErrorCode::Successful; ///< Default error if null
}
if (error->message != nullptr) {
if (std::string(error->message) == "malformed URL") {
return GitCloneErrorCode::InvalidUrl; ///< Invalid URL error
}
if (std::string(error->message) == "remote requested authentication but did not negotiate mechanisms") {
return GitCloneErrorCode::AuthentificationError; ///< Invalid URL error
}
}
return GitCloneErrorCode::UnexpectedError; ///< Default to UnexpectedError
}
/**
* Maps the given GitCloneErrorCode to its corresponding integer.
*
* @param error The GitCloneErrorCode value to convert.
* @return Corresponding GitCloneErrorCode integer value.
*/
constexpr int code_err(GitCloneErrorCode err)
{
return static_cast<int>(err);
}
#endif // ERROR_H

View File

@@ -1,3 +1,4 @@
#include "error.h"
#include <QUrl> #include <QUrl>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QDebug> #include <QDebug>
@@ -14,7 +15,7 @@ extern "C" {
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( m_ssh_homedir (QStandardPaths::writableLocation(
QStandardPaths::AppDataLocation).append("/.ssh")) QStandardPaths::AppDataLocation).append("/.ssh"))
{ {
qDebug() << "[Git] SSH Home is " << m_ssh_homedir.absolutePath(); qDebug() << "[Git] SSH Home is " << m_ssh_homedir.absolutePath();
QDir m_ssh_homedir(this->m_ssh_homedir); QDir m_ssh_homedir(this->m_ssh_homedir);
@@ -71,18 +72,19 @@ bool Git::cloneSshKey(QString url, QString path, QString passphrase)
return this->clone(url, path, mode); return this->clone(url, path, mode);
} }
void Git::cloneResult(const bool err) void Git::cloneResult(const int err_code, const QString message)
{ {
if (err) { if (err_code == code_err(GitCloneErrorCode::Successful)) {
emit cloneFailed(); // TODO error message
} else {
emit cloneSucceed(); emit cloneSucceed();
} else {
emit cloneFailed(err_code, message);
} }
this->m_sem->release(); this->m_sem->release();
} }
bool Git::importSshKey(QUrl source_path, bool is_private){ bool Git::importSshKey(QUrl source_path, bool is_private)
{
auto destination_path = is_private ? this->privKeyPath() : this->pubKeyPath(); auto destination_path = is_private ? this->privKeyPath() : this->pubKeyPath();
QFile source_file(source_path.toLocalFile()); QFile source_file(source_path.toLocalFile());

View File

@@ -1,8 +1,8 @@
#ifndef GIT_H #ifndef GIT_H
#define GIT_H #define GIT_H
#include "error.h"
#include "jobs/gitjob.h" #include "jobs/gitjob.h"
#include "qdebug.h"
#include <QUrl> #include <QUrl>
#include <QObject> #include <QObject>
#include <QSemaphore> #include <QSemaphore>
@@ -28,10 +28,10 @@ private slots:
* process finishes. It emits the appropriate signal based on whether the clone operation succeeded * process finishes. It emits the appropriate signal based on whether the clone operation succeeded
* or failed. * or failed.
* *
* @param err A boolean indicating whether an error occurred during cloning. `true` if the clone failed, * @param err A err_code indicating whether an error occurred during cloning.
* `false` if it succeeded. * @param err An error message.
*/ */
void cloneResult(const bool err); void cloneResult(const int err_code, const QString message);
signals: signals:
/** /**
@@ -44,9 +44,11 @@ signals:
/** /**
* @brief Signal emitted when the cloning operation fails. * @brief Signal emitted when the cloning operation fails.
* *
* @param err_code The error code
* @param msg The deffautl error message from libgit
* This signal is emitted when an error occurs during the cloning operation. * This signal is emitted when an error occurs during the cloning operation.
*/ */
void cloneFailed(); void cloneFailed(int err_code, QString msg);
private: private:
std::unique_ptr<QSemaphore> m_sem; /**< Semaphore for managing concurrent operations. */ std::unique_ptr<QSemaphore> m_sem; /**< Semaphore for managing concurrent operations. */

View File

@@ -3,12 +3,14 @@
#include <QUrl> #include <QUrl>
#include <QDebug> #include <QDebug>
#include <QObject> #include <QObject>
#include <memory>
#include <type_traits> #include <type_traits>
extern "C" { extern "C" {
#include <git2.h> #include <git2.h>
} }
#include "clonejob.h" #include "clonejob.h"
#include "../error.h"
CloneJob::CloneJob(QString url, QString path, cred_type cred): CloneJob::CloneJob(QString url, QString path, cred_type cred):
GitJob(cred), GitJob(cred),
@@ -18,69 +20,92 @@ CloneJob::CloneJob(QString url, QString path, cred_type cred):
this->setObjectName("CloneJob"); this->setObjectName("CloneJob");
} }
void CloneJob::run() void CloneJob::run()
{ {
auto tmp_dir = this->cloneSetup(); auto tmp_dir = this->cloneSetup();
auto ret = this->clone(this->m_url, tmp_dir.absolutePath(), this->m_cred, this->credentialsCB); const auto [errorCode, errorMessage] = this->clone(m_url, tmp_dir.absolutePath(), this->m_cred, this->credentialsCB);
if (ret) {
if (errorCode == GitCloneErrorCode::Successful) {
this->moveToDestination(tmp_dir, this->m_path); this->moveToDestination(tmp_dir, this->m_path);
} }
this->cloneCleanUp(tmp_dir); this->cloneCleanUp(tmp_dir);
emit resultReady(code_err(errorCode), errorMessage);
emit resultReady(!ret); // TODO Clean error handling to return specifics errors for the ui
} }
QDir CloneJob::cloneSetup() QDir CloneJob::cloneSetup()
{ {
QDir tmp_dir(QStandardPaths::writableLocation( QStandardPaths::CacheLocation).append("/clone")); QDir tmp_dir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation).append("/clone"));
tmp_dir.removeRecursively(); tmp_dir.removeRecursively(); // Clean the directory before use
qDebug() << "[CloneJob] Temp dir path is " << tmp_dir.absolutePath(); qDebug() << "[CloneJob] Temp dir path is" << tmp_dir.absolutePath();
return tmp_dir; return tmp_dir;
} }
bool CloneJob::cloneCleanUp(QDir tmp_dir) bool CloneJob::cloneCleanUp(QDir tmp_dir)
{ {
return tmp_dir.removeRecursively(); if (!tmp_dir.removeRecursively()) {
qWarning() << "[CloneJob] Failed to clean up temporary directory:" << tmp_dir.absolutePath();
return false;
}
return true;
} }
bool CloneJob::moveToDestination(QDir tmp_dir, QString path) bool CloneJob::moveToDestination(QDir tmp_dir, const QString& path)
{ {
qDebug() << "[CloneJob] Removing password_store " << path; qDebug() << "[CloneJob] Removing existing destination:" << path;
QDir destination_dir(path); QDir destination_dir(path);
destination_dir.removeRecursively(); destination_dir.removeRecursively(); // Clean the destination directory
qDebug() << "[CloneJob] Moving cloned content to destination dir"; qDebug() << "[CloneJob] Moving cloned content to destination dir";
QDir dir;
qDebug() << "[CloneJob]" << tmp_dir.absolutePath() << " to " << destination_dir.absolutePath(); if (!QDir().rename(tmp_dir.absolutePath(), destination_dir.absolutePath())) {
return dir.rename(tmp_dir.absolutePath(), destination_dir.absolutePath()); // TODO Better error handling qWarning() << "[CloneJob] Failed to move directory from" << tmp_dir.absolutePath() << "to" <<
destination_dir.absolutePath();
return false;
}
return true;
} }
bool CloneJob::clone(QString url, QString path, cred_type cred, git_cred_acquire_cb cb) const QPair<GitCloneErrorCode, QString> CloneJob::clone(QString url, QString path, cred_type cred,
git_cred_acquire_cb cb)
{ {
git_repository *repo = NULL; git_repository *repo = nullptr; // Use nullptr for type safety
git_clone_options opts = GIT_CLONE_OPTIONS_INIT; git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
PayloadCB payload = PayloadCB(false, cred); PayloadCB payload(false, cred);
opts.fetch_opts.callbacks.credentials = cb; opts.fetch_opts.callbacks.credentials = cb;
opts.fetch_opts.callbacks.payload = &payload; opts.fetch_opts.callbacks.payload = &payload;
int ret = git_clone(&repo, url.toLocal8Bit().data(), path.toLocal8Bit().data(), &opts); int ret = git_clone(&repo, url.toLocal8Bit().data(), path.toLocal8Bit().data(), &opts);
if (ret == GIT_EUSER ) {
qDebug() << "[CloneJob] CallBack Error"; // Map the application specific cb errors if any
} else if (ret != 0) { if (ret == GIT_EUSER) {
auto err = git_error_last(); // TODO Better error handling for return ui messages if (payload.err == ErrorCodeCB::NoUsername)
if (err) { return {GitCloneErrorCode::NoUsername, "no username provided in URL"};
qDebug() << "[CloneJob]" << git_error_last()->message; if (payload.err == ErrorCodeCB::InvalidCreds)
} return {GitCloneErrorCode::AuthentificationError, "authentification error"};
if (payload.err == ErrorCodeCB::UrlTypeDoNotMatchCreds)
return {GitCloneErrorCode::UrlTypeDoNotMatchCreds, "invalid creds types for provided url"};
return {GitCloneErrorCode::UnexpectedError, "unexcepted error occured"};
} }
const git_error* err = git_error_last(); // Retrieve the last error git error
// Log error details for debugging
if (err) {
qDebug() << "[CloneJob] Error class:" << err->klass;
qDebug() << "[CloneJob] Error message:" << err->message;
}
// Check if the repository was successfully created and free it
if (repo) { if (repo) {
git_repository_free(repo); git_repository_free(repo);
} }
return ret == 0;
}
// Return the error code mapped from git_error_last
return { gitErrorToGitCloneErrorCode(err), err ? QString::fromUtf8(err->message) : "success"};
}

View File

@@ -6,6 +6,7 @@ extern "C" {
#include <git2.h> #include <git2.h>
} }
#include "gitjob.h" #include "gitjob.h"
#include "../error.h"
/** /**
* @class CloneJob * @class CloneJob
@@ -26,14 +27,13 @@ class CloneJob : public GitJob
signals: signals:
/** /**
* @brief Signal emitted when the cloning operation is complete. * @brief Signal emitted when the cloning operation is complete.
* *
* This signal is emitted once the cloning operation finishes. * This signal is emitted once the cloning operation finishes.
* *
* @param err A boolean indicating whether an error occurred during cloning. * @param err A Git error.
* `true` if an error occurred, `false` if the clone was successful. */
*/ void resultReady(const int err_code, const QString message);
void resultReady(const bool err);
private: private:
QString m_url; ///< The URL of the Git repository to clone. QString m_url; ///< The URL of the Git repository to clone.
@@ -58,7 +58,7 @@ private:
* @param tmp_dir The temporary directory where the repository was cloned. * @param tmp_dir The temporary directory where the repository was cloned.
* @return `true` if the move was successful, `false` otherwise. * @return `true` if the move was successful, `false` otherwise.
*/ */
static bool moveToDestination(QDir tmp_dir, QString path); static bool moveToDestination(QDir tmp_dir, const QString& path);
/** /**
* @brief Tears down the temporary directory after cloning. * @brief Tears down the temporary directory after cloning.
@@ -84,9 +84,10 @@ private:
* @param path The destination path for the cloned repository. * @param path The destination path for the cloned repository.
* @param cred The credentials to use for the cloning operation. * @param cred The credentials to use for the cloning operation.
* @param cb The callback function for acquiring credentials during cloning. * @param cb The callback function for acquiring credentials during cloning.
* @return `true` if the cloning process was successful, `false` otherwise. * @return A Git error, result of the operation.
*/ */
static bool clone(QString url, QString path, cred_type cred, git_cred_acquire_cb cb); static const QPair<GitCloneErrorCode, QString> clone(QString url, QString path, cred_type cred, git_cred_acquire_cb cb);
public: public:
/** /**

View File

@@ -28,11 +28,13 @@ int GitJob::credentialsCB(git_cred **out, const char *url, const char *username_
if (!username_from_url) { // we required here that the username must be provided directly in url (maybe improve later on) if (!username_from_url) { // we required here that the username must be provided directly in url (maybe improve later on)
qWarning() << "[GitJob] credentials_cb : no username provided"; qWarning() << "[GitJob] credentials_cb : no username provided";
p->err = ErrorCodeCB::NoUsername;
return (int) GIT_EUSER; return (int) GIT_EUSER;
} }
if (p->called) { if (p->called) {
qWarning() << "[GitJob] credentials_cb : cb already called, probably invalid creds"; qWarning() << "[GitJob] credentials_cb : cb already called, probably invalid creds";
p->err = ErrorCodeCB::InvalidCreds;
return (int) GIT_EUSER; return (int) GIT_EUSER;
} }
p->called = true; p->called = true;
@@ -45,26 +47,24 @@ int GitJob::credentialsCB(git_cred **out, const char *url, const char *username_
qWarning() << "[GitJob] credentialsCB : callback should never be call for HTTP"; qWarning() << "[GitJob] credentialsCB : callback should never be call for HTTP";
return (int) GIT_EUSER; return (int) GIT_EUSER;
}, },
[allowed_types, &out, &username_from_url](const HTTPUserPass & x) [allowed_types, &out, &username_from_url, &p](const HTTPUserPass & x)
{ {
qDebug() << "[GitJob] credentialsCB : HTTPUserPass "; qDebug() << "[GitJob] credentialsCB : HTTPUserPass ";
if (!(allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT)) { if (!(allowed_types & GIT_CREDTYPE_USERPASS_PLAINTEXT)) {
qWarning() << "[GitJob] credentials_cb : allowed_types is invalid for HTTPUserPass creds"; qWarning() << "[GitJob] credentials_cb : allowed_types is invalid for HTTPUserPass creds";
p->err = ErrorCodeCB::UrlTypeDoNotMatchCreds;
return (int) GIT_EUSER; return (int) GIT_EUSER;
} }
return git_cred_userpass_plaintext_new(out, username_from_url, x.pass.toLocal8Bit().constData()); return git_cred_userpass_plaintext_new(out, username_from_url, x.pass.toLocal8Bit().constData());
}, },
[allowed_types, &out, &username_from_url](const SSHKey & x) [allowed_types, &out, &username_from_url, &p](const SSHKey & x)
{ {
qDebug() << "[GitJob] credentialsCB : SSHKey "; qDebug() << "[GitJob] credentialsCB : SSHKey ";
if (!(allowed_types & GIT_CREDTYPE_SSH_KEY)) { if (!(allowed_types & GIT_CREDTYPE_SSH_KEY)) {
qWarning() << "[GitJob] credentials_cb : allowed_types is invalid for SSHKey creds"; qWarning() << "[GitJob] credentials_cb : allowed_types is invalid for SSHKey creds";
p->err = ErrorCodeCB::UrlTypeDoNotMatchCreds;
return (int) GIT_EUSER; return (int) GIT_EUSER;
} }
qDebug() << "[GitJob] username_from_url :" << username_from_url;
qDebug() << "[GitJob] pub_key :" << x.pub_key.toLocal8Bit().constData();
qDebug() << "[GitJob] priv_key :" << x.priv_key.toLocal8Bit().constData();
qDebug() << "[GitJob] passphrase :" << x.passphrase.toLocal8Bit().constData();
return git_cred_ssh_key_new(out, username_from_url, x.pub_key.toLocal8Bit().constData(), return git_cred_ssh_key_new(out, username_from_url, x.pub_key.toLocal8Bit().constData(),
x.priv_key.toLocal8Bit().constData(), x.passphrase.toLocal8Bit().constData()); x.priv_key.toLocal8Bit().constData(), x.passphrase.toLocal8Bit().constData());
} }

View File

@@ -31,12 +31,19 @@ struct SSHKey {
*/ */
typedef std::variant<HTTP, HTTPUserPass, SSHKey> cred_type; typedef std::variant<HTTP, HTTPUserPass, SSHKey> cred_type;
enum class ErrorCodeCB {
None = 0,
NoUsername,
UrlTypeDoNotMatchCreds,
InvalidCreds,
};
struct PayloadCB
{ struct PayloadCB {
bool called; bool called;
cred_type creds; cred_type creds;
PayloadCB(bool ca, cred_type cr): called(ca), creds(cr) {} ErrorCodeCB err;
PayloadCB(bool ca, cred_type cr): called(ca), creds(cr), err(ErrorCodeCB::None) {}
}; };
/** /**

View File

@@ -1,7 +1,7 @@
#ifndef UTILS_H #ifndef UTILS_H
#define UTILS_H #define UTILS_H
#define UNUSED(x) (void)(x) #define UNUSED(x) (void)(x) // Stub used to hide some warnings
/** /**
* @brief A utility structure for enabling function overloading with template-based classes. * @brief A utility structure for enabling function overloading with template-based classes.

View File

@@ -6,66 +6,58 @@ extern "C" {
#include "rnp/rnp_err.h" #include "rnp/rnp_err.h"
} }
enum class ErrorCodeShow { // Enum for general error codes
Success= 0, enum class ErrorCode {
UnexceptedError, Error = 1, ///< Generic error code
BadPassphrase,
NoKeyFound,
DecryptFailed
}; };
int rnpErrorToErrorCodeShow(int rnpErrorCode) { // Enum for errors related to showing errors (e.g., password issues, key issues)
enum class ErrorCodeShow {
UnexpectedError = 1, ///< Unknown or unexpected error
BadPassphrase, ///< Invalid passphrase error
NoKeyFound, ///< Key not found error
DecryptFailed ///< Decryption failure error
};
/**
* Convert an RNP error code to a corresponding ErrorCodeShow
* @param rnpErrorCode The RNP error code
* @return Corresponding ErrorCodeShow integer value
*/
inline ErrorCodeShow rnpErrorToErrorCodeShow(int rnpErrorCode)
{
switch (rnpErrorCode) { switch (rnpErrorCode) {
case RNP_SUCCESS:
return static_cast<int>(ErrorCodeShow::Success);
case RNP_ERROR_BAD_PASSWORD: case RNP_ERROR_BAD_PASSWORD:
return static_cast<int>(ErrorCodeShow::BadPassphrase); return ErrorCodeShow::BadPassphrase; ///< Bad passphrase error
case RNP_ERROR_KEY_NOT_FOUND: case RNP_ERROR_KEY_NOT_FOUND:
case RNP_ERROR_NO_SUITABLE_KEY: case RNP_ERROR_NO_SUITABLE_KEY:
return static_cast<int>(ErrorCodeShow::NoKeyFound); return ErrorCodeShow::NoKeyFound; ///< No key found error
case RNP_ERROR_DECRYPT_FAILED: case RNP_ERROR_DECRYPT_FAILED:
return static_cast<int>(ErrorCodeShow::DecryptFailed); return ErrorCodeShow::DecryptFailed; ///< Decryption failure error
default: default:
return static_cast<int>(ErrorCodeShow::UnexceptedError); return ErrorCodeShow::UnexpectedError; ///< Default to unexpected error
} }
} }
// Enum for errors related to importing key files
enum class ErrorCodeImportKeyFile { enum class ErrorCodeImportKeyFile {
Success= 0, UnexpectedError = 1, ///< Unknown or unexpected error
UnexceptedError, BadFormat, ///< Bad format error when importing the key file
BadFormat,
}; };
int rnpErrorToErrorCodeImportKeyFile(int rnpErrorCode) { /**
switch (rnpErrorCode) { * Convert an RNP error code to a corresponding ErrorCodeImportKeyFile
case RNP_SUCCESS: * @param rnpErrorCode The RNP error code
return static_cast<int>(ErrorCodeShow::Success); * @return Corresponding ErrorCodeImportKeyFile integer value
case RNP_ERROR_BAD_FORMAT: */
return static_cast<int>(ErrorCodeImportKeyFile::BadFormat); inline ErrorCodeImportKeyFile rnpErrorToErrorCodeImportKeyFile(int rnpErrorCode)
default:
return static_cast<int>(ErrorCodeImportKeyFile::UnexceptedError);
}
}
enum class ErrorCodeUnexvepted {
Success= 0,
UnexceptedError,
};
int rnpErrorToErrorCodeGeneric(int rnpErrorCode) {
switch (rnpErrorCode) {
case RNP_SUCCESS:
return static_cast<int>(ErrorCodeShow::Success);
default:
return static_cast<int>(ErrorCodeImportKeyFile::UnexceptedError);
}
}
enum class ErrorCode
{ {
Success= 0, switch (rnpErrorCode) {
Error, case RNP_ERROR_BAD_FORMAT:
}; return ErrorCodeImportKeyFile::BadFormat; ///< Bad format error
default:
return ErrorCodeImportKeyFile::UnexpectedError; ///< Default to unexpected error
}
}
#endif // ERROR_H #endif // ERROR_H

View File

@@ -109,7 +109,7 @@ bool Pass::show(QUrl url)
void Pass::slotShowError(rnp_result_t err) void Pass::slotShowError(rnp_result_t err)
{ {
qInfo() << "[Pass] Show Failed"; qInfo() << "[Pass] Show Failed";
emit showFailed(rnpErrorToErrorCodeShow(err), rnp_result_to_string(err)); emit showFailed((int) rnpErrorToErrorCodeShow(err), rnp_result_to_string(err));
this->m_sem->release(1); this->m_sem->release(1);
} }
@@ -137,7 +137,7 @@ bool Pass::deletePasswordStore()
} }
void Pass::slotDeletePasswordStoreResult(bool err) void Pass::slotDeletePasswordStoreResult(bool err)
{ {
if (err) { if (err) {
qInfo() << "[Pass] Delete Password Store Failed"; qInfo() << "[Pass] Delete Password Store Failed";
emit deletePasswordStoreFailed(static_cast<int>(ErrorCode::Error), "Failed to delete password store"); emit deletePasswordStoreFailed(static_cast<int>(ErrorCode::Error), "Failed to delete password store");
@@ -168,7 +168,7 @@ bool Pass::deleteGPGKey(PassKeyModel* key)
void Pass::slotDeleteGPGKeyError(rnp_result_t err) void Pass::slotDeleteGPGKeyError(rnp_result_t err)
{ {
qInfo() << "[Pass] Delete GPG key Failed"; qInfo() << "[Pass] Delete GPG key Failed";
emit deleteGPGKeyFailed(rnpErrorToErrorCodeGeneric(err), rnp_result_to_string(err)); emit deleteGPGKeyFailed(static_cast<int>(ErrorCode::Error), rnp_result_to_string(err));
this->m_sem->release(1); this->m_sem->release(1);
} }
@@ -198,7 +198,7 @@ bool Pass::importGPGKey(QUrl url)
void Pass::slotImportGPGKeyError(rnp_result_t err) void Pass::slotImportGPGKeyError(rnp_result_t err)
{ {
qInfo() << "[Pass] Import GPG Key Failed"; qInfo() << "[Pass] Import GPG Key Failed";
emit importGPGKeyFailed(rnpErrorToErrorCodeImportKeyFile(err), rnp_result_to_string(err)); emit importGPGKeyFailed((int) rnpErrorToErrorCodeImportKeyFile(err), rnp_result_to_string(err));
this->m_sem->release(1); this->m_sem->release(1);
} }
@@ -229,7 +229,7 @@ void Pass::slotGetAllGPGKeysError(rnp_result_t err)
{ {
qInfo() << "[Pass] Get all GPG Keys Failed"; qInfo() << "[Pass] Get all GPG Keys Failed";
this->m_keyring_model = nullptr; this->m_keyring_model = nullptr;
emit getAllGPGKeysFailed(rnpErrorToErrorCodeGeneric(err), rnp_result_to_string(err)); emit getAllGPGKeysFailed(static_cast<int>(ErrorCode::Error), rnp_result_to_string(err));
this->m_sem->release(1); this->m_sem->release(1);
} }

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-03-14 10:08+0100\n" "POT-Creation-Date: 2026-01-05 19:03+0000\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"
@@ -194,7 +194,7 @@ msgid "You're are about to delete<br>the current Password Store.<br>Continue ?"
msgstr "" msgstr ""
#: ../qml/pages/settings/DeleteRepo.qml:57 #: ../qml/pages/settings/DeleteRepo.qml:57
#: ../qml/pages/settings/ImportGitClone.qml:142 #: ../qml/pages/settings/ImportGitClone.qml:169
#: ../qml/pages/settings/ImportZip.qml:66 #: ../qml/pages/settings/ImportZip.qml:66
#: ../qml/pages/settings/InfoKeys.qml:174 #: ../qml/pages/settings/InfoKeys.qml:174
msgid "Yes" msgid "Yes"
@@ -208,50 +208,71 @@ msgstr ""
msgid "Password Store deleted !" msgid "Password Store deleted !"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:141 #: ../qml/pages/settings/ImportGitClone.qml:56
msgid "An unexpected error occurred during the clone operation."
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:59
msgid "Invalid URL for the current clone operation."
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:62
msgid "Username is missing in the URL."
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:65
msgid "Authentication error, credentials may be invalid."
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:68
msgid ""
"Credentials type does not match the URL for the current clone operation."
msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:168
msgid "" msgid ""
"Importing a git repo will delete<br>any existing password store!" "Importing a git repo will delete<br>any existing password store!"
"<br>Continue ?" "<br>Continue ?"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:155 #: ../qml/pages/settings/ImportGitClone.qml:182
msgid "An error occured during git clone !" msgid "An error occured during git clone !"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:164 #: ../qml/pages/settings/ImportGitClone.qml:192
#: ../qml/pages/settings/ImportZip.qml:88 #: ../qml/pages/settings/ImportZip.qml:88
msgid "Password store sucessfully imported !" msgid "Password store sucessfully imported !"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportGitClone.qml:176 #: ../qml/pages/settings/ImportGitClone.qml:204
msgid "Git Clone Import" msgid "Git Clone Import"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:7 #: ../qml/pages/settings/ImportKeyFile.qml:9
msgid "GPG Key Import" msgid "GPG Key Import"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:8 #: ../qml/pages/settings/ImportKeyFile.qml:10
msgid "Key successfully imported !" msgid "Key successfully imported !"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:9 #: ../qml/pages/settings/ImportKeyFile.qml:11
msgid "Key import failed !" msgid "Key import failed !"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportKeyFile.qml:33 #: ../qml/pages/settings/ImportKeyFile.qml:35
msgid "The file is not in a valid key format" msgid "The file is not in a valid key format"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportSSHkey.qml:10 #: ../qml/pages/settings/ImportSSHkey.qml:12
msgid "SSH Key Import" msgid "SSH Key Import"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportSSHkey.qml:11 #: ../qml/pages/settings/ImportSSHkey.qml:13
msgid "SSH Key successfully imported !" msgid "SSH Key successfully imported !"
msgstr "" msgstr ""
#: ../qml/pages/settings/ImportSSHkey.qml:12 #: ../qml/pages/settings/ImportSSHkey.qml:14
msgid "SSH Key import failed !" msgid "SSH Key import failed !"
msgstr "" msgstr ""

View File

@@ -1,18 +1,16 @@
import Git 1.0 import Git 1.0
import Lomiri.Components 1.3 import Lomiri.Components 1.3
import Pass 1.0 import Pass 1.0
import Utils 1.0
import QtQuick 2.4 import QtQuick 2.4
import Utils 1.0
Column { Column {
property alias importSshPrivKeyButton: repoImportPrivKeyButton
property alias importSshPubKeyButton: repoImportPubKeyButton
property alias importSshPrivKeyButton : repoImportPrivKeyButton property alias deleteSshPrivKeyButton: repoDeletePrivKeyButton
property alias importSshPubKeyButton : repoImportPubKeyButton property alias deleteSshPubKeyButton: repoDeletePubKeyButton
property alias deleteSshPrivKeyButton : repoDeletePrivKeyButton property bool __sshPrivKeyAvailable: false
property alias deleteSshPubKeyButton : repoDeletePubKeyButton property bool __sshPubKeyAvailable: false
property bool __sshPrivKeyAvailable : false
property bool __sshPubKeyAvailable : false
signal repoUrlChanged(string url) signal repoUrlChanged(string url)
@@ -28,7 +26,6 @@ Column {
Component.onCompleted: { Component.onCompleted: {
update(); update();
} }
anchors.top: parent.fill anchors.top: parent.fill
spacing: units.gu(1) spacing: units.gu(1)
@@ -108,7 +105,6 @@ Column {
visible: !__sshPrivKeyAvailable visible: !__sshPrivKeyAvailable
} }
Text { Text {
id: repoPassphraseLabel id: repoPassphraseLabel

View File

@@ -1,4 +1,3 @@
import "../dialogs"
import "../pages/headers" import "../pages/headers"
import Lomiri.Components 1.3 import Lomiri.Components 1.3
import Lomiri.Components.Popups 1.3 import Lomiri.Components.Popups 1.3
@@ -11,17 +10,15 @@ Page {
id: importKeyFilePage id: importKeyFilePage
property var activeTransfer property var activeTransfer
property alias contentPicker : contentPicker property alias contentPicker: contentPicker
property alias dialogImportKeyPageError : dialogImportKeyPageError property string headerTitle: i18n.tr("Import succeeded !")
property alias dialogImportKeyPageSucess : dialogImportKeyPageSucess property string dialogErrorTxt: i18n.tr("Import failed !")
property string dialogErrorDescriptionTxt: null
property string headerTitle : i18n.tr("Import succeeded !") property string dialogSuccessTxt: i18n.tr("File Imported")
property string dialogErrorTxt : i18n.tr("Import failed !")
property string dialogErrorDescriptionTxt : null
property string dialogSuccessTxt : i18n.tr("File Imported")
ContentPeerPicker { ContentPeerPicker {
id: contentPicker id: contentPicker
anchors.top: importKeyHeader.bottom anchors.top: importKeyHeader.bottom
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.topMargin: importKeyFilePage.header.height anchors.topMargin: importKeyFilePage.header.height
@@ -42,28 +39,6 @@ Page {
activeTransfer: importKeyFilePage.activeTransfer activeTransfer: importKeyFilePage.activeTransfer
} }
Component {
id: dialogImportKeyPageError
ErrorDialog {
textError: importKeyFilePage.dialogErrorTxt
textErrorDescription: importKeyFilePage.dialogErrorDescriptionTxt
}
}
Component {
id: dialogImportKeyPageSucess
SuccessDialog {
textSuccess: importKeyFilePage.dialogSuccessTxt
onDialogClosed: {
pageStack.pop();
}
}
}
header: StackHeader { header: StackHeader {
id: importKeyHeader id: importKeyHeader

View File

@@ -6,12 +6,12 @@ Dialog {
id: dialog id: dialog
property string textError property string textError
property string textErrorDescription : null property string textErrorDescription: null
signal dialogClosed() signal dialogClosed()
title: i18n.tr("Error !") title: i18n.tr("Error !")
text: textErrorDescription ? (textError + "<br>" + textErrorDescription) : textError text: textErrorDescription ? (textError + "<br>" + textErrorDescription) : textError
Button { Button {
text: i18n.tr("Close") text: i18n.tr("Close")

View File

@@ -51,22 +51,26 @@ Page {
}); });
Pass.onShowFailed.connect(function(code, message) { Pass.onShowFailed.connect(function(code, message) {
switch (code) { switch (code) {
case 1: // UnexceptedError -> use the default (not translate) rnp error case 1:
__text_error_description = message; // UnexceptedError -> use the default (not translate) rnp error
break; __text_error_description = message;
case 2: // BadPassphrase break;
__text_error_description = i18n.tr("Bad passphrase"); case 2:
break; // BadPassphrase
case 3: // NoKeyFound __text_error_description = i18n.tr("Bad passphrase");
__text_error_description = i18n.tr("No valid key found"); break;
break; case 3:
case 3: // DecryptFailed // NoKeyFound
__text_error_description = i18n.tr("Decryption failed"); __text_error_description = i18n.tr("No valid key found");
break; break;
default: case 3:
console.warn("Unhandled error code"); // DecryptFailed
__text_error_description = message; __text_error_description = i18n.tr("Decryption failed");
break; break;
default:
console.warn("Unhandled error code");
__text_error_description = message;
break;
} }
PopupUtils.open(passwordPageDecryptError); PopupUtils.open(passwordPageDecryptError);
}); });
@@ -195,10 +199,9 @@ Page {
textError: i18n.tr("Decryption failed !") textError: i18n.tr("Decryption failed !")
textErrorDescription: __text_error_description textErrorDescription: __text_error_description
} }
} }
Timer { Timer {
id: searchTimer id: searchTimer

View File

@@ -9,27 +9,35 @@ 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 import Utils 1.0
Page { Page {
// property int __gitModeSSH_KEY : 2
id: importGitClonePage id: importGitClonePage
property int __gitModeHTTP : 0 property int __gitModeHTTP: 0
property int __gitModeHTTP_AUTH : 1 property int __gitModeHTTP_AUTH: 1
property int __gitModeSSH_KEY : 2 property int __gitCloneErrorCodeSuccessful: 0
property int __gitCloneErrorCodeUnexpectedError: 1
property int __gitCloneErrorCodeInvalidUrl: 2
property int __gitCloneErrorCodeNoUsername: 3
property int __gitCloneErrorCodeAuthentificationError: 4
property int __gitCloneErrorCodeUrlTypeDoNotMatchCreds: 5
property string __repoUrl property string __repoUrl
property string __err_message: null
function __loadForm() { function __loadForm() {
// case __gitModeSSH_KEY:
// importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneSshKey.qml");
// break;
switch (combo.selectedIndex) { switch (combo.selectedIndex) {
case __gitModeHTTP: case __gitModeHTTP:
importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttp.qml"); importGitCloneForm.source = Qt.resolvedUrl("../../components/GitCloneHttp.qml");
break; break;
case __gitModeHTTP_AUTH: 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;
} }
} }
@@ -37,14 +45,35 @@ 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 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.privKey);
Utils.rmFile(Git.pubKey); Utils.rmFile(Git.pubKey);
} }
PopupUtils.open(dialogGitCloneSuccess); PopupUtils.open(dialogGitCloneSuccess, importGitClonePage);
}); });
Git.cloneFailed.connect(function() { Git.cloneFailed.connect(function(err_code, msg) {
PopupUtils.open(dialogGitCloneError); switch (err_code) {
case __gitCloneErrorCodeUnexpectedError:
__err_message = i18n.tr("An error occurred during the clone operation.");
break;
case __gitCloneErrorCodeInvalidUrl:
__err_message = i18n.tr("Invalid URL for the current clone operation.");
break;
case __gitCloneErrorCodeNoUsername:
__err_message = i18n.tr("Username is missing in the URL.");
break;
case __gitCloneErrorCodeAuthentificationError:
__err_message = i18n.tr("Authentication error, credentials may be invalid.");
break;
case __gitCloneErrorCodeUrlTypeDoNotMatchCreds:
__err_message = i18n.tr("Credentials type does not match the URL for the current clone operation.");
break;
default:
__err_message = msg;
break;
}
PopupUtils.open(dialogGitCloneError, importGitClonePage);
}); });
if (GitSettings.repoUrl) if (GitSettings.repoUrl)
__repoUrl = GitSettings.repoUrl; __repoUrl = GitSettings.repoUrl;
@@ -76,7 +105,7 @@ Page {
id: combo id: combo
width: parent.width width: parent.width
model: ["HTTP", "HTTP AUTH", "SSH KEY"] model: ["HTTP", "HTTP AUTH"] //, "SSH KEY"]
onDelegateClicked: function(i) { onDelegateClicked: function(i) {
timer.setTimeout(function() { timer.setTimeout(function() {
__loadForm(); __loadForm();
@@ -106,29 +135,29 @@ Page {
width: parent.width width: parent.width
onLoaded: { onLoaded: {
// 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;
importGitCloneForm.item.repoUrlChanged.connect(function(url) { importGitCloneForm.item.repoUrlChanged.connect(function(url) {
importGitClonePage.__repoUrl = url; importGitClonePage.__repoUrl = url;
}); });
importGitCloneForm.item.setRepoUrl(importGitClonePage.__repoUrl); importGitCloneForm.item.setRepoUrl(importGitClonePage.__repoUrl);
switch (combo.selectedIndex) { switch (combo.selectedIndex) {
case __gitModeHTTP: case __gitModeHTTP:
break; break;
case __gitModeHTTP_AUTH: case __gitModeHTTP_AUTH:
break; 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;
}
} }
} }
@@ -153,6 +182,7 @@ Page {
ErrorDialog { ErrorDialog {
textError: i18n.tr("An error occured during git clone !") textError: i18n.tr("An error occured during git clone !")
textErrorDescription: __err_message
} }
} }

View File

@@ -1,46 +1,105 @@
import "../../components" import "../../dialogs"
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
ImportFile { Page {
id: importKeyFilePage id: importKeyFilePage
headerTitle: i18n.tr("GPG Key Import") property var activeTransfer
dialogSuccessTxt : i18n.tr("Key successfully imported !") property alias contentPicker: contentPicker
dialogErrorTxt : i18n.tr("Key import failed !") property string __text_error_description: null
contentPicker.onPeerSelected: { ContentPeerPicker {
{ id: contentPicker
peer.selectionType = ContentTransfer.Single;
importKeyFilePage.activeTransfer = peer.request(); anchors.top: importKeyHeader.bottom
importKeyFilePage.activeTransfer.stateChanged.connect(function() { anchors.bottom: parent.bottom
if (importKeyFilePage.activeTransfer.state === ContentTransfer.Charged) { anchors.topMargin: importKeyFilePage.header.height
console.log("Charged"); width: parent.width
console.log(importKeyFilePage.activeTransfer.items[0].url); visible: parent.visible
Pass.importGPGKey(importKeyFilePage.activeTransfer.items[0].url); showTitle: false
Pass.importGPGKeySucceed.connect(function() { contentType: ContentType.Text
Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); handler: ContentHandler.Source
importKeyFilePage.activeTransfer = null; onPeerSelected: {
PopupUtils.open(importKeyFilePage.dialogImportKeyPageSucess); {
}); importKeyFilePage.contentPicker.peer.selectionType = ContentTransfer.Single;
Pass.importGPGKeyFailed.connect(function(err, message) { importKeyFilePage.activeTransfer = importKeyFilePage.contentPicker.peer.request();
Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url); importKeyFilePage.activeTransfer.stateChanged.connect(function() {
importKeyFilePage.activeTransfer = null; if (importKeyFilePage.activeTransfer.state === ContentTransfer.Charged) {
switch (code) { console.log("Charged");
case 1: // UnexceptedError -> use the default (not translate) rnp error console.log(importKeyFilePage.activeTransfer.items[0].url);
__text_error_description = message; Pass.importGPGKey(importKeyFilePage.activeTransfer.items[0].url);
break; Pass.importGPGKeySucceed.connect(function() {
case 2: // BadFormat Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url);
importKeyFilePage.activeTransfer = null;
PopupUtils.open(dialogImportKeyPageSucess);
});
Pass.importGPGKeyFailed.connect(function(err, message) {
Utils.rmFile(importKeyFilePage.activeTransfer.items[0].url);
importKeyFilePage.activeTransfer = null;
switch (code) {
case 1:
// UnexceptedError -> use the default (not translate) rnp error
__text_error_description = message;
break;
case 2:
// BadFormat
__text_error_description = i18n.tr("The file is not in a valid key format"); __text_error_description = i18n.tr("The file is not in a valid key format");
break; break;
default: default:
console.warn("Unhandled error code"); console.warn("Unhandled error code");
__text_error_description = message; __text_error_description = message;
break; break;
} }
PopupUtils.open(importKeyFilePage.dialogImportKeyPageError); PopupUtils.open(dialogImportKeyPageError);
}); });
} }
}); });
} };
}
onCancelPressed: {
pageStack.pop();
}
} }
ContentTransferHint {
id: transferHint
anchors.fill: parent
activeTransfer: importKeyFilePage.activeTransfer
}
Component {
id: dialogImportKeyPageError
ErrorDialog {
textError: i18n.tr("Key import failed !")
textErrorDescription: __text_error_description
}
}
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

@@ -1,6 +1,9 @@
import "../../components" import "../../components"
import Utils 1.0
import Git 1.0 import Git 1.0
import Lomiri.Components 1.3
import Lomiri.Components.Popups 1.3
import Lomiri.Content 1.3
import Utils 1.0
ImportFile { ImportFile {
id: importSSHKeyPage id: importSSHKeyPage
@@ -8,27 +11,25 @@ ImportFile {
property bool isPrivateKey property bool isPrivateKey
headerTitle: i18n.tr("SSH Key Import") headerTitle: i18n.tr("SSH Key Import")
dialogSuccessTxt : i18n.tr("SSH Key successfully imported !") dialogSuccessTxt: i18n.tr("SSH Key successfully imported !")
dialogErrorTxt : i18n.tr("SSH Key import failed !") dialogErrorTxt: i18n.tr("SSH Key import failed !")
contentPicker.onPeerSelected: { contentPicker.onPeerSelected: {
{ {
peer.selectionType = ContentTransfer.Single; importSSHKeyPage.contentPicker.peer.selectionType = ContentTransfer.Single;
importSSHKeyPage.activeTransfer = peer.request(); importSSHKeyPage.activeTransfer = importSSHKeyPage.contentPicker.peer.request();
importSSHKeyPage.activeTransfer.stateChanged.connect(function() { importSSHKeyPage.activeTransfer.stateChanged.connect(function() {
if (importSSHKeyPage.activeTransfer.state === ContentTransfer.Charged) { if (importSSHKeyPage.activeTransfer.state === ContentTransfer.Charged) {
console.log("Charged"); console.log("Charged");
console.log(importSSHKeyPage.activeTransfer.items[0].url); console.log(importSSHKeyPage.activeTransfer.items[0].url);
var ret = Git.importSshKey(importSSHKeyPage.activeTransfer.items[0].url, isPrivateKey); var ret = Git.importSshKey(importSSHKeyPage.activeTransfer.items[0].url, isPrivateKey);
Utils.rmFile(importSSHKeyPage.activeTransfer.items[0].url); Utils.rmFile(importSSHKeyPage.activeTransfer.items[0].url);
importSSHKeyPage.activeTransfer = null; importSSHKeyPage.activeTransfer = null;
if(ret) { if (ret)
PopupUtils.open(importSSHKeyPage.dialogImportKeyPageSucess); PopupUtils.open(importSSHKeyPage.dialogImportKeyPageSucess);
} else { else
PopupUtils.open(importSSHKeyPage.dialogImportKeyPageError); PopupUtils.open(importSSHKeyPage.dialogImportKeyPageError);
} }
} });
}); };
}
} }
} }

View File

@@ -195,7 +195,7 @@ Page {
SuccessDialog { SuccessDialog {
textSuccess: i18n.tr("Key successfully deleted !") textSuccess: i18n.tr("Key successfully deleted !")
onDialogClosed: { onDialogClosed: {
infoKeysListView.model = Pass.getAllGPGKeys(); Pass.getAllGPGKeys();
} }
} }