- support for keychain password storage
This commit is contained in:
parent
7cc4ce2b94
commit
90ddb7535a
@ -1,23 +1,27 @@
|
|||||||
#include "appevents.h"
|
#include "appevents.h"
|
||||||
|
|
||||||
ClientInitEvent::ClientInitEvent()
|
ClientInitEvent::ClientInitEvent()
|
||||||
:QEvent((Type)ClientInitId)
|
:QEvent(static_cast<Type>(ClientInitId))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientCloseEvent::ClientCloseEvent()
|
ClientCloseEvent::ClientCloseEvent()
|
||||||
:QEvent((Type)ClientCloseId)
|
:QEvent(static_cast<Type>(ClientCloseId))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
AttachDatabaseEvent::AttachDatabaseEvent()
|
AttachDatabaseEvent::AttachDatabaseEvent()
|
||||||
:QEvent((Type)AttachDatabaseId)
|
:QEvent(static_cast<Type>(AttachDatabaseId))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
SelectTaskEvent::SelectTaskEvent(PTask task)
|
SelectTaskEvent::SelectTaskEvent(PTask task)
|
||||||
:QEvent((Type)SelectTaskId), mTask(task)
|
:QEvent(static_cast<Type>(SelectTaskId)), mTask(task)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
PTask SelectTaskEvent::task()
|
PTask SelectTaskEvent::task()
|
||||||
{
|
{
|
||||||
return mTask;
|
return mTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UiInitEvent::UiInitEvent()
|
||||||
|
:QEvent(static_cast<Type>(UiInitId))
|
||||||
|
{}
|
||||||
|
|||||||
@ -8,7 +8,8 @@ enum
|
|||||||
ClientInitId = 62000,
|
ClientInitId = 62000,
|
||||||
ClientCloseId = 62001,
|
ClientCloseId = 62001,
|
||||||
AttachDatabaseId,
|
AttachDatabaseId,
|
||||||
SelectTaskId
|
SelectTaskId,
|
||||||
|
UiInitId
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClientInitEvent: public QEvent
|
class ClientInitEvent: public QEvent
|
||||||
@ -39,4 +40,11 @@ protected:
|
|||||||
PTask mTask;
|
PTask mTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UiInitEvent: public QEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UiInitEvent();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // APPEVENTS_H
|
#endif // APPEVENTS_H
|
||||||
|
|||||||
@ -146,3 +146,7 @@ OBJECTIVE_SOURCES += \
|
|||||||
!include("fervor/Fervor.pri") {
|
!include("fervor/Fervor.pri") {
|
||||||
error("Unable to include Fervor autoupdater.")
|
error("Unable to include Fervor autoupdater.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
!include("qtkeychain/qt5keychain.pri") {
|
||||||
|
error("Unable to include QTKeyChain library.")
|
||||||
|
}
|
||||||
|
|||||||
@ -300,3 +300,35 @@ bool EscapeKeyEventFilter::eventFilter(QObject *obj, QEvent * event)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "qtkeychain/keychain.h"
|
||||||
|
|
||||||
|
QString password::load()
|
||||||
|
{
|
||||||
|
QKeychain::ReadPasswordJob job(APPNAME);
|
||||||
|
job.setKey(KEY_PASSWORD);
|
||||||
|
job.setAutoDelete(false);
|
||||||
|
QEventLoop loop;
|
||||||
|
job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
|
||||||
|
job.start();
|
||||||
|
loop.exec();
|
||||||
|
if (job.error())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
return job.textData();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool password::save(const QString& password)
|
||||||
|
{
|
||||||
|
QKeychain::WritePasswordJob job(APPNAME);
|
||||||
|
job.setKey(KEY_PASSWORD);
|
||||||
|
job.setAutoDelete(false);
|
||||||
|
job.setTextData(password);
|
||||||
|
QEventLoop loop;
|
||||||
|
job.connect(&job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
|
||||||
|
job.start();
|
||||||
|
loop.exec();
|
||||||
|
if (job.error())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@ -92,6 +92,13 @@ namespace helper
|
|||||||
void escapePressed(QObject* obj);
|
void escapePressed(QObject* obj);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class password
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QString load();
|
||||||
|
static bool save(const QString& password);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
#include "stopworkdialog.h"
|
#include "stopworkdialog.h"
|
||||||
#include "connectdb_widget.h"
|
#include "connectdb_widget.h"
|
||||||
#include "openorcreatedb_widget.h"
|
#include "openorcreatedb_widget.h"
|
||||||
|
#include "qtkeychain/keychain.h"
|
||||||
|
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -71,9 +72,27 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
|
|
||||||
if (!QFile::exists(path))
|
if (!QFile::exists(path))
|
||||||
askNewDbPassword();
|
askNewDbPassword();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
|
||||||
|
{
|
||||||
|
QString password = helper::password::load();
|
||||||
|
if (password.isEmpty())
|
||||||
|
askDbPassword();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Storage::instance().setKey(password);
|
||||||
|
if (!Storage::instance().open())
|
||||||
|
{
|
||||||
|
askDbPassword(tr("Invalid password, please try again."));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
QApplication::postEvent(this, new UiInitEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
askDbPassword(QString());
|
askDbPassword(QString());
|
||||||
|
}
|
||||||
this->setUpdatesEnabled(true);
|
this->setUpdatesEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,28 +114,6 @@ void MainWindow::attachDatabase()
|
|||||||
QString folder = QFileInfo(path).absoluteDir().path();
|
QString folder = QFileInfo(path).absoluteDir().path();
|
||||||
Storage::instance().setPath(path);
|
Storage::instance().setPath(path);
|
||||||
|
|
||||||
/*
|
|
||||||
if (!QFile::exists(path))
|
|
||||||
{
|
|
||||||
QDir().mkpath(folder);
|
|
||||||
|
|
||||||
// Ask about new password
|
|
||||||
mNewPasswordDlg = new NewPasswordDlg(this);
|
|
||||||
connect(mNewPasswordDlg, SIGNAL(finished(int)), this, SLOT(newPasswordDialogFinished(int)));
|
|
||||||
mNewPasswordDlg->show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// See if there is stored password
|
|
||||||
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool() && mSettings->data()[KEY_PASSWORD].toString() != NOPASSWORDSTRING)
|
|
||||||
password = mSettings->data()[KEY_PASSWORD].toString();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mPasswordDlg = new PasswordDlg(this);
|
|
||||||
connect(mPasswordDlg, SIGNAL(finished(int)), this, SLOT(passwordDialogFinished(int)));
|
|
||||||
mPasswordDlg->show();
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
this->setUpdatesEnabled(true);
|
this->setUpdatesEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,20 +396,20 @@ void MainWindow::customEvent(QEvent *ev)
|
|||||||
{
|
{
|
||||||
switch (ev->type())
|
switch (ev->type())
|
||||||
{
|
{
|
||||||
case (QEvent::Type)ClientInitId:
|
case static_cast<QEvent::Type>(ClientInitId):
|
||||||
// Process client initialization here
|
// Process client initialization here
|
||||||
initClient();
|
initClient();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (QEvent::Type)ClientCloseId:
|
case static_cast<QEvent::Type>(ClientCloseId):
|
||||||
close();
|
close();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (QEvent::Type)AttachDatabaseId:
|
case static_cast<QEvent::Type>(AttachDatabaseId):
|
||||||
attachDatabase();
|
attachDatabase();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case (QEvent::Type)SelectTaskId:
|
case static_cast<QEvent::Type>(SelectTaskId):
|
||||||
{
|
{
|
||||||
QModelIndex index = mTaskTreeModel->getIndex(dynamic_cast<SelectTaskEvent*>(ev)->task());
|
QModelIndex index = mTaskTreeModel->getIndex(dynamic_cast<SelectTaskEvent*>(ev)->task());
|
||||||
if (index.isValid())
|
if (index.isValid())
|
||||||
@ -421,6 +418,13 @@ void MainWindow::customEvent(QEvent *ev)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case static_cast<QEvent::Type>(UiInitId):
|
||||||
|
setupMainUi();
|
||||||
|
connectUiToDatabase();
|
||||||
|
loadGeometry();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -443,7 +447,10 @@ void MainWindow::preferences()
|
|||||||
// Delete autosaved password if needed
|
// Delete autosaved password if needed
|
||||||
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool() == false)
|
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool() == false)
|
||||||
{
|
{
|
||||||
mSettings->data()[KEY_PASSWORD] = NOPASSWORDSTRING;
|
// Reset password in keychain
|
||||||
|
helper::password::save(QString(""));
|
||||||
|
|
||||||
|
//mSettings->data()[KEY_PASSWORD] = NOPASSWORDSTRING;
|
||||||
mSettings->save();
|
mSettings->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1618,23 +1625,19 @@ void MainWindow::trayWindowDestroyed(QObject *object)
|
|||||||
|
|
||||||
void MainWindow::onDbPasswordEntered(const QString& password)
|
void MainWindow::onDbPasswordEntered(const QString& password)
|
||||||
{
|
{
|
||||||
|
// Save password to keychain if needed
|
||||||
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
|
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
|
||||||
{
|
helper::password::save(password);
|
||||||
mSettings->data()[KEY_PASSWORD] = password;
|
|
||||||
mSettings->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Try to open database
|
||||||
Storage::instance().setKey(password);
|
Storage::instance().setKey(password);
|
||||||
if (!Storage::instance().open())
|
if (!Storage::instance().open())
|
||||||
{
|
{
|
||||||
|
// Ask password again if failed
|
||||||
askDbPassword(tr("Invalid password, please try again."));
|
askDbPassword(tr("Invalid password, please try again."));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
QApplication::postEvent(this, new UiInitEvent());
|
||||||
setupMainUi();
|
|
||||||
connectUiToDatabase();
|
|
||||||
loadGeometry();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onDbPasswordCancelled()
|
void MainWindow::onDbPasswordCancelled()
|
||||||
@ -1644,12 +1647,11 @@ void MainWindow::onDbPasswordCancelled()
|
|||||||
|
|
||||||
void MainWindow::onNewDbPasswordEntered(const QString& password)
|
void MainWindow::onNewDbPasswordEntered(const QString& password)
|
||||||
{
|
{
|
||||||
|
// Save password to keychain if needed
|
||||||
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
|
if (mSettings->data()[KEY_AUTOSAVE_PASSWORD].toBool())
|
||||||
{
|
helper::password::save(password);
|
||||||
mSettings->data()[KEY_PASSWORD] = password;
|
|
||||||
mSettings->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Configure storage
|
||||||
Storage::instance().setKey(password);
|
Storage::instance().setKey(password);
|
||||||
|
|
||||||
// Remove old database
|
// Remove old database
|
||||||
@ -1661,11 +1663,7 @@ void MainWindow::onNewDbPasswordEntered(const QString& password)
|
|||||||
showFatal(tr("Failed to create new database. Exiting."));
|
showFatal(tr("Failed to create new database. Exiting."));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
QApplication::postEvent(this, new UiInitEvent());
|
||||||
setupMainUi();
|
|
||||||
connectUiToDatabase();
|
|
||||||
loadGeometry();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onDatabaseChanged(const QString& path)
|
void MainWindow::onDatabaseChanged(const QString& path)
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#include "ui_preferencesdlg.h"
|
#include "ui_preferencesdlg.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
|
#include "storage.h"
|
||||||
#include "platforms/hidtracker.h"
|
#include "platforms/hidtracker.h"
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@ -73,7 +74,10 @@ void PreferencesDlg::selectDatabase()
|
|||||||
|
|
||||||
void PreferencesDlg::accepted()
|
void PreferencesDlg::accepted()
|
||||||
{
|
{
|
||||||
mSettings.data()[KEY_AUTOSAVE_PASSWORD] = ui->mAutosavePasswordCheckbox->isChecked();
|
bool savePassword = ui->mAutosavePasswordCheckbox->isChecked();
|
||||||
|
mSettings.data()[KEY_AUTOSAVE_PASSWORD] = savePassword;
|
||||||
|
helper::password::save(savePassword ? Storage::instance().key() : QString(""));
|
||||||
|
|
||||||
mSettings.data()[KEY_SHOW_SECONDS] = ui->mShowSecondsCheckbox->isChecked();
|
mSettings.data()[KEY_SHOW_SECONDS] = ui->mShowSecondsCheckbox->isChecked();
|
||||||
mSettings.data()[KEY_DB_FILENAME_SPECIFIED] = ui->mCustomDatabaseFileCheckbox->isChecked();
|
mSettings.data()[KEY_DB_FILENAME_SPECIFIED] = ui->mCustomDatabaseFileCheckbox->isChecked();
|
||||||
mSettings.data()[KEY_DB_FILENAME] = ui->mDatabaseLocation->text();
|
mSettings.data()[KEY_DB_FILENAME] = ui->mDatabaseLocation->text();
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>444</width>
|
<width>444</width>
|
||||||
<height>353</height>
|
<height>374</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -30,7 +30,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="mAutosavePasswordCheckbox">
|
<widget class="QCheckBox" name="mAutosavePasswordCheckbox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Autosave password (requires app restart)</string>
|
<string>Save password in keychain</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
50
client/qtkeychain/.gitignore
vendored
Normal file
50
client/qtkeychain/.gitignore
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#CMake files
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
cmake_install.cmake
|
||||||
|
|
||||||
|
#Keychain temporary files
|
||||||
|
Qt5KeychainBuildTreeSettings.cmake
|
||||||
|
Qt5KeychainConfig.cmake
|
||||||
|
Qt5KeychainConfigVersion.cmake
|
||||||
|
QtKeychainBuildTreeSettings.cmake
|
||||||
|
QtKeychainConfig.cmake
|
||||||
|
QtKeychainConfigVersion.cmake
|
||||||
|
kwallet_interface.cpp
|
||||||
|
kwallet_interface.h
|
||||||
|
kwallet_interface.moc
|
||||||
|
moc_keychain.*
|
||||||
|
moc_keychain_p.*
|
||||||
|
moc_gnomekeyring_p.*
|
||||||
|
qkeychain_export.h
|
||||||
|
qt_Qt5Keychain.pri
|
||||||
|
|
||||||
|
#Qt files
|
||||||
|
*_parameters
|
||||||
|
*.qm
|
||||||
|
|
||||||
|
#General build files
|
||||||
|
Debug
|
||||||
|
Release
|
||||||
|
Makefile
|
||||||
|
|
||||||
|
#Linux build files
|
||||||
|
libqt5keychain.*
|
||||||
|
testclient
|
||||||
|
|
||||||
|
#Windows build files
|
||||||
|
install_manifest.txt
|
||||||
|
*.manifest
|
||||||
|
*.lib
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
#Mac build files
|
||||||
|
qtkeychain.xcodeproj
|
||||||
|
qtkeychain.build
|
||||||
|
|
||||||
|
#Temporary files
|
||||||
|
*.sw?
|
||||||
|
*~
|
||||||
|
|
||||||
|
|
||||||
271
client/qtkeychain/CMakeLists.txt
Normal file
271
client/qtkeychain/CMakeLists.txt
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8.11)
|
||||||
|
project(qtkeychain)
|
||||||
|
|
||||||
|
include(FindPkgConfig)
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
set(QTKEYCHAIN_VERSION 0.9.90)
|
||||||
|
set(QTKEYCHAIN_SOVERSION 1)
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules")
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
include(GenerateExportHeader)
|
||||||
|
include(ECMPackageConfigHelpers)
|
||||||
|
include(ECMSetupVersion)
|
||||||
|
include(ECMGeneratePriFile)
|
||||||
|
|
||||||
|
option(BUILD_WITH_QT4 "Build qtkeychain with Qt4 no matter if Qt5 was found" OFF)
|
||||||
|
option(BUILD_TEST_APPLICATION "Build test application" ON)
|
||||||
|
option(BUILD_TRANSLATIONS "Build translations" ON)
|
||||||
|
option(QTKEYCHAIN_STATIC "Build static library" OFF)
|
||||||
|
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL Android)
|
||||||
|
set(ANDROID 1)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
option(USE_CREDENTIAL_STORE "Build with windows CredentialStore support" ON)
|
||||||
|
|
||||||
|
if (USE_CREDENTIAL_STORE)
|
||||||
|
add_definitions(-DUSE_CREDENTIAL_STORE=1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if( NOT BUILD_WITH_QT4 )
|
||||||
|
# try Qt5 first, and prefer that if found
|
||||||
|
find_package(Qt5Core QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (Qt5Core_FOUND AND NOT BUILD_WITH_QT4)
|
||||||
|
set(QTKEYCHAIN_VERSION_INFIX 5)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||||
|
find_package(Qt5DBus REQUIRED)
|
||||||
|
include_directories(${Qt5DBus_INCLUDE_DIRS})
|
||||||
|
set(QTDBUS_LIBRARIES ${Qt5DBus_LIBRARIES})
|
||||||
|
macro(qt_add_dbus_interface)
|
||||||
|
qt5_add_dbus_interface(${ARGN})
|
||||||
|
endmacro()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_TRANSLATIONS)
|
||||||
|
find_package(Qt5LinguistTools REQUIRED)
|
||||||
|
macro(qt_add_translation)
|
||||||
|
qt5_add_translation(${ARGN})
|
||||||
|
endmacro(qt_add_translation)
|
||||||
|
macro(qt_create_translation)
|
||||||
|
qt5_create_translation(${ARGN})
|
||||||
|
endmacro(qt_create_translation)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
macro(qt_wrap_cpp)
|
||||||
|
qt5_wrap_cpp(${ARGN})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES})
|
||||||
|
include_directories(${Qt5Core_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
if (NOT Qt5Core_VERSION VERSION_LESS "5.7.0")
|
||||||
|
if (CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
if ((NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "6.1.0"))
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||||
|
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0")
|
||||||
|
message(FATAL_ERROR "Can't build QtKeychain using g++-${CMAKE_CXX_COMPILER_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required")
|
||||||
|
endif()
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
if (NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 3.3)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Can't build QtKeychain using clang++-${CMAKE_CXX_COMPILER_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required")
|
||||||
|
endif()
|
||||||
|
elseif ((CMAKE_CXX_COMPILER_ID MATCHES "MSVC") AND (MSVC_VERSION LESS 1700))
|
||||||
|
message(FATAL_ERROR "Can't build QtKeychain using VC++-${MSVC_VERSION} and Qt ${Qt5Core_VERSION}: compiler supporting C++11 is required")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(QTKEYCHAIN_VERSION_INFIX "")
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
find_package(Qt4 COMPONENTS QtCore QtDBus REQUIRED)
|
||||||
|
set(QTDBUS_LIBRARIES ${QT_QTDBUS_LIBRARY})
|
||||||
|
macro(qt_add_dbus_interface)
|
||||||
|
qt4_add_dbus_interface(${ARGN})
|
||||||
|
endmacro()
|
||||||
|
else()
|
||||||
|
find_package(Qt4 COMPONENTS QtCore REQUIRED)
|
||||||
|
endif()
|
||||||
|
include_directories(${QT_INCLUDES})
|
||||||
|
set(QTCORE_LIBRARIES ${QT_QTCORE_LIBRARY})
|
||||||
|
macro(qt_add_translation)
|
||||||
|
qt4_add_translation(${ARGN})
|
||||||
|
endmacro(qt_add_translation)
|
||||||
|
macro(qt_create_translation)
|
||||||
|
qt4_create_translation(${ARGN})
|
||||||
|
endmacro(qt_create_translation)
|
||||||
|
macro(qt_wrap_cpp)
|
||||||
|
qt4_wrap_cpp(${ARGN})
|
||||||
|
endmacro()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
list(APPEND qtkeychain_LIBRARIES ${QTCORE_LIBRARIES})
|
||||||
|
set(qtkeychain_SOURCES
|
||||||
|
keychain.cpp
|
||||||
|
qkeychain_export.h
|
||||||
|
keychain.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_definitions( -Wall )
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
list(APPEND qtkeychain_SOURCES keychain_win.cpp)
|
||||||
|
if (NOT USE_CREDENTIAL_STORE)
|
||||||
|
list(APPEND qtkeychain_LIBRARIES crypt32)
|
||||||
|
list(APPEND qtkeychain_SOURCES plaintextstore.cpp)
|
||||||
|
endif()
|
||||||
|
#FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there
|
||||||
|
if(MINGW)
|
||||||
|
add_definitions( -O2 )
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
if(IOS)
|
||||||
|
list(APPEND qtkeychain_SOURCES keychain_ios.cpp)
|
||||||
|
else()
|
||||||
|
list(APPEND qtkeychain_SOURCES keychain_mac.cpp)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_library(COREFOUNDATION_LIBRARY CoreFoundation REQUIRED)
|
||||||
|
list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY})
|
||||||
|
|
||||||
|
find_library(SECURITY_LIBRARY Security REQUIRED)
|
||||||
|
list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||||
|
option(LIBSECRET_SUPPORT "Build with libsecret support" ON)
|
||||||
|
|
||||||
|
if(LIBSECRET_SUPPORT)
|
||||||
|
pkg_check_modules(LIBSECRET libsecret-1)
|
||||||
|
|
||||||
|
if (LIBSECRET_FOUND)
|
||||||
|
add_definitions(-DHAVE_LIBSECRET=1)
|
||||||
|
endif()
|
||||||
|
INCLUDE_DIRECTORIES(${LIBSECRET_INCLUDE_DIRS})
|
||||||
|
list(APPEND qtkeychain_LIBRARIES ${LIBSECRET_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.cpp libsecret.cpp plaintextstore.cpp)
|
||||||
|
qt_add_dbus_interface(qtkeychain_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.KWallet.xml kwallet_interface KWalletInterface)
|
||||||
|
list(APPEND qtkeychain_LIBRARIES ${QTDBUS_LIBRARIES} )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
QT_WRAP_CPP(qtkeychain_MOC_OUTFILES keychain.h keychain_p.h gnomekeyring_p.h)
|
||||||
|
|
||||||
|
set(qtkeychain_TR_FILES
|
||||||
|
translations/qtkeychain_de.ts
|
||||||
|
translations/qtkeychain_ro.ts
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui)
|
||||||
|
if ( BUILD_TRANSLATIONS )
|
||||||
|
qt_create_translation(qtkeychain_MESSAGES ${qtkeychain_TR_SOURCES} ${qtkeychain_TR_FILES})
|
||||||
|
qt_add_translation(qtkeychain_QM_FILES ${qtkeychain_TR_FILES})
|
||||||
|
add_custom_target(messages DEPENDS ${qtkeychain_MESSAGES})
|
||||||
|
add_custom_target(translations DEPENDS ${qtkeychain_QM_FILES})
|
||||||
|
|
||||||
|
if(NOT QT_TRANSLATIONS_DIR)
|
||||||
|
# If this directory is missing, we are in a Qt5 environment.
|
||||||
|
# Extract the qmake executable location
|
||||||
|
get_target_property(QT5_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
|
||||||
|
# Ask Qt5 where to put the translations
|
||||||
|
execute_process( COMMAND ${QT5_QMAKE_EXECUTABLE} -query QT_INSTALL_TRANSLATIONS
|
||||||
|
OUTPUT_VARIABLE qt_translations_dir OUTPUT_STRIP_TRAILING_WHITESPACE )
|
||||||
|
# make sure we have / and not \ as qmake gives on windows
|
||||||
|
file( TO_CMAKE_PATH "${qt_translations_dir}" qt_translations_dir)
|
||||||
|
set( QT_TRANSLATIONS_DIR ${qt_translations_dir} CACHE PATH
|
||||||
|
"The location of the Qt translations" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
install(FILES ${qtkeychain_QM_FILES}
|
||||||
|
DESTINATION ${QT_TRANSLATIONS_DIR})
|
||||||
|
endif( BUILD_TRANSLATIONS )
|
||||||
|
|
||||||
|
set(QTKEYCHAIN_TARGET_NAME qt${QTKEYCHAIN_VERSION_INFIX}keychain)
|
||||||
|
if(NOT QTKEYCHAIN_STATIC)
|
||||||
|
add_library(${QTKEYCHAIN_TARGET_NAME} SHARED ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES})
|
||||||
|
else()
|
||||||
|
add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(${QTKEYCHAIN_TARGET_NAME} PUBLIC ${qtkeychain_LIBRARIES})
|
||||||
|
target_include_directories(${QTKEYCHAIN_TARGET_NAME} PUBLIC $<INSTALL_INTERFACE:include/>)
|
||||||
|
|
||||||
|
generate_export_header(${QTKEYCHAIN_TARGET_NAME}
|
||||||
|
EXPORT_FILE_NAME qkeychain_export.h
|
||||||
|
EXPORT_MACRO_NAME QKEYCHAIN_EXPORT
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES
|
||||||
|
VERSION ${QTKEYCHAIN_VERSION}
|
||||||
|
SOVERSION ${QTKEYCHAIN_SOVERSION}
|
||||||
|
MACOSX_RPATH 1
|
||||||
|
INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
|
||||||
|
INSTALL_RPATH_USE_LINK_PATH TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES keychain.h ${CMAKE_CURRENT_BINARY_DIR}/qkeychain_export.h
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/qt${QTKEYCHAIN_VERSION_INFIX}keychain/
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS ${QTKEYCHAIN_TARGET_NAME}
|
||||||
|
EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_TEST_APPLICATION)
|
||||||
|
add_executable( testclient testclient.cpp )
|
||||||
|
target_link_libraries( testclient ${QTKEYCHAIN_TARGET_NAME})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
### CMake config file
|
||||||
|
###
|
||||||
|
|
||||||
|
ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/QtKeychainConfig.cmake.in"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake"
|
||||||
|
INSTALL_DESTINATION Qt${QTKEYCHAIN_VERSION_INFIX}Keychain)
|
||||||
|
|
||||||
|
ecm_setup_version("${QTKEYCHAIN_VERSION}" VARIABLE_PREFIX SNORE
|
||||||
|
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake"
|
||||||
|
SOVERSION ${QTKEYCHAIN_VERSION})
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||||
|
set(PRI_EXTRA_DEPS "dbus")
|
||||||
|
endif()
|
||||||
|
ecm_generate_pri_file(BASE_NAME Qt${QTKEYCHAIN_VERSION_INFIX}Keychain
|
||||||
|
LIB_NAME ${QTKEYCHAIN_TARGET_NAME}
|
||||||
|
DEPS "core ${PRI_EXTRA_DEPS}"
|
||||||
|
INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
FILENAME_VAR pri_filename)
|
||||||
|
|
||||||
|
install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
|
||||||
|
|
||||||
|
|
||||||
|
install(EXPORT Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends
|
||||||
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain"
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Qt${QTKEYCHAIN_VERSION_INFIX}Keychain
|
||||||
|
)
|
||||||
|
|
||||||
20
client/qtkeychain/COPYING
Normal file
20
client/qtkeychain/COPYING
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
54
client/qtkeychain/ChangeLog
Normal file
54
client/qtkeychain/ChangeLog
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
ChangeLog
|
||||||
|
=========
|
||||||
|
|
||||||
|
version 0.9.1 (release 2018-08-20)
|
||||||
|
* Windows Credential Store: Use CRED_PERSIST_ENTERPRISE (Olivier Goffart <ogoffart@woboq.com>)
|
||||||
|
* Secret: Don't match the schema name #114 (Christian Kamm <mail@ckamm.de>)
|
||||||
|
* Fix qmake build on Windows (Alexander Gorishnyak <kefir500@gmail.com>)
|
||||||
|
|
||||||
|
version 0.9.0 (release 2018-07-13)
|
||||||
|
* Fall back on libsecret if kwallet is not available (Christian Kamm <mail@ckamm.de>)
|
||||||
|
* Only require QtLinguist if building translations (Victor Kropp <victor.kropp@jetbrains.com>)
|
||||||
|
* Fix building on Windows without credential store (Dmitry Ivanov <dm.vl.ivanov@gmail.com>)
|
||||||
|
* Fix Qt 4 build (Sandro Knauß <hefee@debian.org>)
|
||||||
|
* Make build of test application optional (Boris Pek <tehnick-8@yandex.ru>)
|
||||||
|
|
||||||
|
version 0.8.0 (release 2017-04-19)
|
||||||
|
* Buildsystem improvements (Kristofer Tingdahl <kristofer.tingdahl@dgbes.com>, Hannah von Reth <hannah.vonreth@kdab.com>, Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>)
|
||||||
|
* Enable C++11 support for Qt >= 5.7 (Dmitry Ivanov <dm.vl.ivanov@gmail.com>)
|
||||||
|
* Doxygen documentation ( Elvis Angelaccio <elvis.angelaccio@kdemail.net>)
|
||||||
|
* Libsecret support (Armin Novak <armin.novak@thincast.com>)
|
||||||
|
* iOS support (Mathias Hasselmann <mathias.hasselmann@kdab.com>)
|
||||||
|
|
||||||
|
version 0.7.0 (release 2016-05-23)
|
||||||
|
* Bump SO version due to 0.6 being binary-incompatible to previous releases
|
||||||
|
|
||||||
|
version 0.6.2 (release 2016-04-04)
|
||||||
|
* KWallet: Fixes a crash when storing passwords, seen on Debian/KDE4
|
||||||
|
|
||||||
|
version 0.6.1 (release 2016-03-31)
|
||||||
|
* Fix KWallet not working (regressions in 0.6.0)
|
||||||
|
|
||||||
|
version 0.6.0 (release 2016-03-18)
|
||||||
|
* Added support for the Windows Credential Store
|
||||||
|
|
||||||
|
version 0.5.0 (release 2015-05-04)
|
||||||
|
* Added support for KWallet5 (KDE5/KF)
|
||||||
|
|
||||||
|
version 0.4.0 (release 2014-09-01)
|
||||||
|
* KWallet: Handle case where no wallet exists yet (Liviu Cristian Mirea Ghiban <contact@liviucmg.com>)
|
||||||
|
* Improved desktop environment detection at runtime (Daniel Molkentin <daniel@molkentin.de>)
|
||||||
|
|
||||||
|
version 0.3.0 (release 2014-03-13)
|
||||||
|
* Gnome Keyring supported added (Francois Ferrand <thetypz@gmail.com>)
|
||||||
|
* Improved Qt 5 support
|
||||||
|
* KWallet: Distinguish empty passwords from non-existing entries
|
||||||
|
* KWallet: Do not use hardcoded wallet name
|
||||||
|
* German translation (Daniel Molkentin <daniel@molkentin.de>)
|
||||||
|
* Romanian translation (Arthur Țițeică <arthur@psw.ro>)
|
||||||
|
|
||||||
|
version 0.2.0: no official release
|
||||||
|
|
||||||
|
version 0.1.0 (release 2013-01-16)
|
||||||
|
* Initial release
|
||||||
|
|
||||||
22
client/qtkeychain/QtKeychainConfig.cmake.in
Normal file
22
client/qtkeychain/QtKeychainConfig.cmake.in
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# - Config file for the QtKeychain package
|
||||||
|
# It defines the following variables
|
||||||
|
# QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain
|
||||||
|
# QTKEYCHAIN_LIBRARIES - libraries to link against
|
||||||
|
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake")
|
||||||
|
|
||||||
|
|
||||||
|
if("@QTKEYCHAIN_VERSION_INFIX@" STREQUAL "5")
|
||||||
|
find_dependency(Qt5Core)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
find_dependency(Qt5DBus)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
find_dependency(Qt4 COMPONENTS QtCore)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@")
|
||||||
|
get_target_property(QTKEYCHAIN_INCLUDE_DIRS "@QTKEYCHAIN_TARGET_NAME@" INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
1
client/qtkeychain/ReadMe.markdown
Symbolic link
1
client/qtkeychain/ReadMe.markdown
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
ReadMe.txt
|
||||||
17
client/qtkeychain/ReadMe.txt
Normal file
17
client/qtkeychain/ReadMe.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
QtKeychain
|
||||||
|
==========
|
||||||
|
|
||||||
|
QtKeychain is a Qt API to store passwords and other secret data securely. How the data is stored depends on the platform:
|
||||||
|
|
||||||
|
* **Mac OS X:** Passwords are stored in the OS X Keychain.
|
||||||
|
|
||||||
|
* **Linux/Unix:** If running, GNOME Keyring is used, otherwise qtkeychain tries to use KWallet (via D-Bus), if available.
|
||||||
|
|
||||||
|
* **Windows:** By default, the Windows Credential Store is used (requires Windows 7 or newer).
|
||||||
|
Pass -DUSE_CREDENTIAL_STORE=OFF to cmake use disable it. If disabled, QtKeychain uses the Windows API function
|
||||||
|
[CryptProtectData](http://msdn.microsoft.com/en-us/library/windows/desktop/aa380261%28v=vs.85%29.aspx "CryptProtectData function")
|
||||||
|
to encrypt the password with the user's logon credentials. The encrypted data is then persisted via QSettings.
|
||||||
|
|
||||||
|
In unsupported environments QtKeychain will report an error. It will not store any data unencrypted unless explicitly requested (setInsecureFallback( true )).
|
||||||
|
|
||||||
|
**License:** QtKeychain is available under the [Modified BSD License](http://www.gnu.org/licenses/license-list.html#ModifiedBSD). See the file COPYING for details.
|
||||||
31
client/qtkeychain/appveyor.yml
Normal file
31
client/qtkeychain/appveyor.yml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
version: '{build}'
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- ps: |
|
||||||
|
$ErrorActionPreference="Stop"
|
||||||
|
Import-Module $env:APPVEYOR_BUILD_FOLDER\appveyorHelp.psm1
|
||||||
|
|
||||||
|
Init @("ninja")
|
||||||
|
mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\build\$env:APPVEYOR_PROJECT_NAME
|
||||||
|
cd $env:APPVEYOR_BUILD_FOLDER\work\build\$env:APPVEYOR_PROJECT_NAME
|
||||||
|
LogExec cmake -G"Ninja" $env:APPVEYOR_BUILD_FOLDER -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$CMAKE_INSTALL_ROOT"
|
||||||
|
|
||||||
|
CmakeImageInstall
|
||||||
|
|
||||||
|
test: off
|
||||||
|
|
||||||
|
cache:
|
||||||
|
- work\install -> appveyor.yml
|
||||||
|
- C:\ProgramData\chocolatey\bin -> appveyor.yml
|
||||||
|
- C:\ProgramData\chocolatey\lib -> appveyor.yml
|
||||||
|
|
||||||
|
environment:
|
||||||
|
QT_VER: 5.7
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
- COMPILER: msvc2015
|
||||||
|
- COMPILER: msvc2015_64
|
||||||
|
- COMPILER: mingw53_32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
362
client/qtkeychain/appveyorHelp.psm1
Normal file
362
client/qtkeychain/appveyorHelp.psm1
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
Write-Host "Appveyor Helper scrips https://github.com/TheOneRing/appVeyorHelp"
|
||||||
|
|
||||||
|
$ErrorActionPreference="Stop"
|
||||||
|
|
||||||
|
$script:INSTALL_DIR="$env:APPVEYOR_BUILD_FOLDER\work\install"
|
||||||
|
$CMAKE_INSTALL_ROOT="`"$INSTALL_DIR`"" -replace "\\", "/"
|
||||||
|
$env:PATH="$env:PATH;$script:INSTALL_DIR"
|
||||||
|
|
||||||
|
if(!$env:CI -eq "true")
|
||||||
|
{
|
||||||
|
function Push-AppveyorArtifact()
|
||||||
|
{
|
||||||
|
Write-Host "Push-AppveyorArtifact $ARGS"
|
||||||
|
}
|
||||||
|
function Start-FileDownload([string] $url, [string] $out)
|
||||||
|
{
|
||||||
|
if(!$out)
|
||||||
|
{
|
||||||
|
$out = $url.SubString($url.LastIndexOf("/"))
|
||||||
|
}
|
||||||
|
wget $url -Outfile $out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function LogExec()
|
||||||
|
{
|
||||||
|
$OldErrorActionPreference=$ErrorActionPreference
|
||||||
|
$ErrorActionPreference="Continue"
|
||||||
|
$LastExitCode = 0
|
||||||
|
Write-Host $Args[0], $Args[1..(($Args.Count)-1)]
|
||||||
|
& $Args[0] $Args[1..(($Args.Count)-1)]
|
||||||
|
if(!$LastExitCode -eq 0)
|
||||||
|
{
|
||||||
|
exit $LastExitCode
|
||||||
|
}
|
||||||
|
$ErrorActionPreference=$OldErrorActionPreference
|
||||||
|
}
|
||||||
|
|
||||||
|
#Set environment variables for Visual Studio Command Prompt
|
||||||
|
#http://stackoverflow.com/questions/2124753/how-i-can-use-powershell-with-the-visual-studio-command-prompt
|
||||||
|
function BAT-CALL([string] $path, [string] $arg)
|
||||||
|
{
|
||||||
|
Write-Host "Calling `"$path`" `"$arg`""
|
||||||
|
cmd /c "$path" "$arg" `&`& set `|`| exit 1|
|
||||||
|
foreach {
|
||||||
|
if ($_ -match "=") {
|
||||||
|
$v = $_.split("=")
|
||||||
|
#Write-Host "ENV:\$($v[0])=$($v[1])"
|
||||||
|
set-item -force -path "ENV:\$($v[0])" -value "$($v[1])"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($LastExitCode -eq 1) {
|
||||||
|
Write-Error "$path not found."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-QtDir()
|
||||||
|
{
|
||||||
|
$ver = 5.5
|
||||||
|
if($env:QT_VER)
|
||||||
|
{
|
||||||
|
$ver = $env:QT_VER
|
||||||
|
}
|
||||||
|
return "C:\Qt\$ver\$env:COMPILER\"
|
||||||
|
}
|
||||||
|
|
||||||
|
function SETUP-QT()
|
||||||
|
{
|
||||||
|
[string] $compiler=$env:COMPILER
|
||||||
|
$qtDir = Get-QtDir
|
||||||
|
$script:QT_BINARY_DIRS = @($qtDir)
|
||||||
|
|
||||||
|
BAT-CALL "$qtDir\bin\qtenv2.bat"
|
||||||
|
if ($compiler.StartsWith("mingw49"))
|
||||||
|
{
|
||||||
|
#remove sh.exe from path
|
||||||
|
$env:PATH=$env:PATH -replace "C:\\Program Files \(x86\)\\Git\\bin", ""
|
||||||
|
$script:MAKE="mingw32-make"
|
||||||
|
$script:CMAKE_GENERATOR="MinGW Makefiles"
|
||||||
|
$script:STRIP=@("strip", "-s")
|
||||||
|
$script:QT_BINARY_DIRS += (Resolve-Path "$qtDir\..\..\Tools\mingw492_32\opt\")
|
||||||
|
}
|
||||||
|
elseif ($compiler.StartsWith("msvc"))
|
||||||
|
{
|
||||||
|
$arch = "x86"
|
||||||
|
if($compiler.EndsWith("64"))
|
||||||
|
{
|
||||||
|
$arch = "amd64"
|
||||||
|
}
|
||||||
|
$compilerDirs = @{
|
||||||
|
"msvc2010" = "VS100COMNTOOLS";
|
||||||
|
"msvc2012" = "VS110COMNTOOLS";
|
||||||
|
"msvc2013" = "VS120COMNTOOLS";
|
||||||
|
"msvc2015" = "VS140COMNTOOLS"
|
||||||
|
}
|
||||||
|
|
||||||
|
$compilerVar = $compilerDirs[$compiler.Split("_")[0]]
|
||||||
|
$compilerDir = (get-item -path "env:\$($compilerVar)").Value
|
||||||
|
BAT-CALL "$compilerDir\..\..\VC\vcvarsall.bat" $arch
|
||||||
|
$script:MAKE="nmake"
|
||||||
|
$script:CMAKE_GENERATOR="NMake Makefiles"
|
||||||
|
if($arch -eq "x86")
|
||||||
|
{
|
||||||
|
$script:QT_BINARY_DIRS += ("C:\OpenSSL-Win32")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$script:QT_BINARY_DIRS += ("C:\OpenSSL-Win64")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-ChocolatelyModule([string] $module, [string[]] $myargs)
|
||||||
|
{
|
||||||
|
Write-Host "Install chocolately package $module"
|
||||||
|
LogExec cinst $module @myargs -y
|
||||||
|
}
|
||||||
|
|
||||||
|
function Install-CmakeGitModule([string] $url, [hashtable] $arguments)
|
||||||
|
{
|
||||||
|
$module = $url.SubString($url.LastIndexOf("/")+1)
|
||||||
|
$module = $module.Substring(0,$module.Length - 4)
|
||||||
|
if(!$arguments.Contains("branch"))
|
||||||
|
{
|
||||||
|
$arguments["branch"] = "master"
|
||||||
|
}
|
||||||
|
if(!$arguments.Contains("buildType"))
|
||||||
|
{
|
||||||
|
$arguments["buildType"] = "Release"
|
||||||
|
}
|
||||||
|
mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\build\$module
|
||||||
|
pushd $env:APPVEYOR_BUILD_FOLDER\work\git
|
||||||
|
LogExec git clone -q --depth 1 --branch ([string]$arguments["branch"]) $url $module
|
||||||
|
popd
|
||||||
|
pushd $env:APPVEYOR_BUILD_FOLDER\work\build\$module
|
||||||
|
LogExec cmake -G $script:CMAKE_GENERATOR ("-DCMAKE_BUILD_TYPE=`"{0}`"" -f [string]$arguments["buildType"]) $env:APPVEYOR_BUILD_FOLDER\work\git\$module -DCMAKE_INSTALL_PREFIX="$CMAKE_INSTALL_ROOT" $arguments["options"]
|
||||||
|
LogExec $script:MAKE install
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
function Init([string[]] $chocoDeps, [System.Collections.Specialized.OrderedDictionary] $cmakeModules)
|
||||||
|
{
|
||||||
|
$script:MAKE=""
|
||||||
|
$script:CMAKE_GENERATOR=""
|
||||||
|
$script:STRIP=$null
|
||||||
|
|
||||||
|
mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\image | Out-Null
|
||||||
|
mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\build | Out-Null
|
||||||
|
|
||||||
|
SETUP-QT
|
||||||
|
|
||||||
|
if($chocoDeps -contains "ninja") {
|
||||||
|
$script:CMAKE_GENERATOR="Ninja"
|
||||||
|
$script:MAKE="ninja"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !(Test-Path "$env:APPVEYOR_BUILD_FOLDER\work\install" ) )
|
||||||
|
{
|
||||||
|
mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\install | Out-Null
|
||||||
|
mkdir -Force $env:APPVEYOR_BUILD_FOLDER\work\git | Out-Null
|
||||||
|
|
||||||
|
foreach($module in $chocoDeps) {
|
||||||
|
if($module -eq "nsis")
|
||||||
|
{
|
||||||
|
Install-ChocolatelyModule "nsis.portable" @("-pre")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
Install-ChocolatelyModule $module
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($key in $cmakeModules.Keys) {
|
||||||
|
Install-CmakeGitModule $key $cmakeModules[$key]
|
||||||
|
}
|
||||||
|
|
||||||
|
[string] $compiler=$env:COMPILER
|
||||||
|
if($compiler.StartsWith("msvc"))
|
||||||
|
{
|
||||||
|
Write-Host "Downloading vcredist.exe"
|
||||||
|
if ($compiler.StartsWith("msvc2015"))
|
||||||
|
{
|
||||||
|
if($compiler.EndsWith("64"))
|
||||||
|
{
|
||||||
|
Start-FileDownload https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Start-FileDownload https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x86.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if($compiler.EndsWith("64"))
|
||||||
|
{
|
||||||
|
Start-FileDownload http://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x64.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Start-FileDownload http://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3/vcredist_x86.exe $env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function relativePath([string] $root, [string] $path)
|
||||||
|
{
|
||||||
|
pushd $root
|
||||||
|
$out = Resolve-Path -Relative $path
|
||||||
|
popd
|
||||||
|
return $out
|
||||||
|
}
|
||||||
|
|
||||||
|
function StripFile([string] $name)
|
||||||
|
{
|
||||||
|
if($script:STRIP) {
|
||||||
|
if( $name.EndsWith(".dll") -or $name.EndsWith(".exe"))
|
||||||
|
{
|
||||||
|
Write-Host "strip file $name"
|
||||||
|
LogExec @script:STRIP $name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-DeployImageName()
|
||||||
|
{
|
||||||
|
$version = Get-Version
|
||||||
|
if($env:APPVEYOR_REPO_TAG -eq "true") {
|
||||||
|
return "$env:APPVEYOR_PROJECT_NAME`_$version`_Qt$env:QT_VER`_$env:COMPILER"
|
||||||
|
}else{
|
||||||
|
return "$env:APPVEYOR_PROJECT_NAME`_$env:APPVEYOR_REPO_BRANCH`_$version`_Qt$env:QT_VER`_$env:COMPILER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-Version()
|
||||||
|
{
|
||||||
|
if($env:APPVEYOR_REPO_TAG -eq "true") {
|
||||||
|
return $env:APPVEYOR_REPO_TAG_NAME
|
||||||
|
}else{
|
||||||
|
$commit = ([string]$env:APPVEYOR_REPO_COMMIT).SubString(0,6)
|
||||||
|
return $commit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function CmakeImageInstall()
|
||||||
|
{
|
||||||
|
$imageName = Get-DeployImageName
|
||||||
|
$destDir = "$env:APPVEYOR_BUILD_FOLDER\work\cmakeDeployImage\$imageName"
|
||||||
|
$env:DESTDIR = $destDir
|
||||||
|
LogExec $script:MAKE install
|
||||||
|
$env:DESTDIR = $null
|
||||||
|
if(!$LastExitCode -eq 0)
|
||||||
|
{
|
||||||
|
Write-Error "Build Failed"
|
||||||
|
}
|
||||||
|
$env:DESTDIR=$null
|
||||||
|
$prefix=$script:INSTALL_DIR
|
||||||
|
if( $prefix.substring(1,1) -eq ":")
|
||||||
|
{
|
||||||
|
$prefix=$prefix.substring(3)
|
||||||
|
}
|
||||||
|
Write-Host "move $destDir\$prefix to $destDir"
|
||||||
|
mv -Force "$destDir\$prefix\*" "$destDir"
|
||||||
|
$rootLeftOver = $prefix.substring(0, $prefix.indexOf("\"))
|
||||||
|
rm -Recurse "$destDir\$rootLeftOver"
|
||||||
|
}
|
||||||
|
|
||||||
|
function CreateDeployImage([string[]] $whiteList, [string[]] $blackList)
|
||||||
|
{
|
||||||
|
$imageName = Get-DeployImageName
|
||||||
|
$deployPath = "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName"
|
||||||
|
|
||||||
|
function copyWithWhitelist([string] $root)
|
||||||
|
{
|
||||||
|
$files = ls $root -Recurse
|
||||||
|
foreach($fileName in $files.FullName)
|
||||||
|
{
|
||||||
|
$relPath = (relativePath $root $fileName).SubString(2)
|
||||||
|
if($whiteList | Where {$relPath -match $_})
|
||||||
|
{
|
||||||
|
if($blackList | Where {$relPath -match $_})
|
||||||
|
{
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if(!(Test-Path $deployPath\$relPath))
|
||||||
|
{
|
||||||
|
Write-Host "copy $fileName to $deployPath\$relPath"
|
||||||
|
mkdir -Force (Split-Path -Parent $deployPath\$relPath) | Out-Null
|
||||||
|
cp -Force $fileName $deployPath\$relPath
|
||||||
|
StripFile $deployPath\$relPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "CreateDeployImage $imageName"
|
||||||
|
mkdir $deployPath | Out-Null
|
||||||
|
|
||||||
|
copyWithWhitelist "$env:APPVEYOR_BUILD_FOLDER\work\cmakeDeployImage\$imageName"
|
||||||
|
copyWithWhitelist "$env:APPVEYOR_BUILD_FOLDER\work\install\"
|
||||||
|
foreach($folder in $script:QT_BINARY_DIRS)
|
||||||
|
{
|
||||||
|
copyWithWhitelist $folder
|
||||||
|
}
|
||||||
|
Write-Host "Deploy path $deployPath"
|
||||||
|
return $deployPath
|
||||||
|
}
|
||||||
|
|
||||||
|
function 7ZipDeployImage()
|
||||||
|
{
|
||||||
|
$imageName = Get-DeployImageName
|
||||||
|
LogExec 7za a "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName.7z" "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName"
|
||||||
|
Push-AppveyorArtifact "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName.7z"
|
||||||
|
}
|
||||||
|
|
||||||
|
function NsisDeployImage([string] $scriptName)
|
||||||
|
{
|
||||||
|
$imageName = Get-DeployImageName
|
||||||
|
$installerName = "$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName.exe"
|
||||||
|
$version = Get-Version
|
||||||
|
if(([string]$env:COMPILER).StartsWith("msvc"))
|
||||||
|
{
|
||||||
|
$redist = "$env:APPVEYOR_BUILD_FOLDER\work\install\vcredist.exe"
|
||||||
|
}else{
|
||||||
|
$redist = "none"
|
||||||
|
}
|
||||||
|
if($env:COMPILER.EndsWith("64"))
|
||||||
|
{
|
||||||
|
$defaultinstdir = "`$PROGRAMFILES64"
|
||||||
|
}else{
|
||||||
|
$defaultinstdir = "`$PROGRAMFILES"
|
||||||
|
}
|
||||||
|
LogExec makensis.exe /DgitDir=$env:APPVEYOR_BUILD_FOLDER /Dsetupname=$installerName /Dcaption=$imageName /Dversion=$version /Dcompiler=$env:COMPILER /Dvcredist=$redist /Ddefaultinstdir=$defaultinstdir /Dsrcdir=$env:APPVEYOR_BUILD_FOLDER\work\deployImage\$imageName $scriptName
|
||||||
|
Push-AppveyorArtifact $installerName
|
||||||
|
}
|
||||||
|
|
||||||
|
# based on http://thesurlyadmin.com/2013/01/07/remove-empty-directories-recursively/
|
||||||
|
function DeleteEmptyFodlers([string] $root)
|
||||||
|
{
|
||||||
|
$Folders = @()
|
||||||
|
foreach($Folder in (Get-ChildItem -Path $root -Recurse -Directory))
|
||||||
|
{
|
||||||
|
$Folders += New-Object PSObject -Property @{
|
||||||
|
Object = $Folder
|
||||||
|
Depth = ($Folder.FullName.Split("\")).Count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$Folders = $Folders | Sort Depth -Descending
|
||||||
|
|
||||||
|
foreach($Folder in $Folders)
|
||||||
|
{
|
||||||
|
If ($Folder.Object.GetFileSystemInfos().Count -eq 0)
|
||||||
|
{
|
||||||
|
Write-Host "Delete empty dir:" $Folder.Object.FullName
|
||||||
|
Remove-Item -Path $Folder.Object.FullName -Force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "CMAKE_INSTALL_ROOT: $CMAKE_INSTALL_ROOT"
|
||||||
|
Write-Host "Image-Name: ", (Get-DeployImageName)
|
||||||
|
|
||||||
|
Export-ModuleMember -Function @("Init","CmakeImageInstall", "CreateDeployImage", "LogExec", "7ZipDeployImage", "NsisDeployImage", "DeleteEmptyFodlers") -Variable @("CMAKE_INSTALL_ROOT")
|
||||||
198
client/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake
Normal file
198
client/qtkeychain/cmake/Modules/ECMGeneratePriFile.cmake
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
#.rst:
|
||||||
|
# ECMGeneratePriFile
|
||||||
|
# ------------------
|
||||||
|
#
|
||||||
|
# Generate a ``.pri`` file for the benefit of qmake-based projects.
|
||||||
|
#
|
||||||
|
# As well as the function below, this module creates the cache variable
|
||||||
|
# ``ECM_MKSPECS_INSTALL_DIR`` and sets the default value to ``mkspecs/modules``.
|
||||||
|
# This assumes Qt and the current project are both installed to the same
|
||||||
|
# non-system prefix. Packagers who use ``-DCMAKE_INSTALL_PREFIX=/usr`` will
|
||||||
|
# certainly want to set ``ECM_MKSPECS_INSTALL_DIR`` to something like
|
||||||
|
# ``share/qt5/mkspecs/modules``.
|
||||||
|
#
|
||||||
|
# The main thing is that this should be the ``modules`` subdirectory of either
|
||||||
|
# the default qmake ``mkspecs`` directory or of a directory that will be in the
|
||||||
|
# ``$QMAKEPATH`` environment variable when qmake is run.
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# ecm_generate_pri_file(BASE_NAME <baseName>
|
||||||
|
# LIB_NAME <libName>
|
||||||
|
# [DEPS "<dep> [<dep> [...]]"]
|
||||||
|
# [FILENAME_VAR <filename_variable>]
|
||||||
|
# [INCLUDE_INSTALL_DIR <dir>]
|
||||||
|
# [LIB_INSTALL_DIR <dir>])
|
||||||
|
#
|
||||||
|
# If your CMake project produces a Qt-based library, you may expect there to be
|
||||||
|
# applications that wish to use it that use a qmake-based build system, rather
|
||||||
|
# than a CMake-based one. Creating a ``.pri`` file will make use of your
|
||||||
|
# library convenient for them, in much the same way that CMake config files make
|
||||||
|
# things convenient for CMake-based applications.
|
||||||
|
#
|
||||||
|
# ecm_generate_pri_file() generates just such a file. It requires the
|
||||||
|
# ``PROJECT_VERSION_STRING`` variable to be set. This is typically set by
|
||||||
|
# :module:`ECMSetupVersion`, although the project() command in CMake 3.0.0 and
|
||||||
|
# later can also set this.
|
||||||
|
#
|
||||||
|
# BASE_NAME specifies the name qmake project (.pro) files should use to refer to
|
||||||
|
# the library (eg: KArchive). LIB_NAME is the name of the actual library to
|
||||||
|
# link to (ie: the first argument to add_library()). DEPS is a space-separated
|
||||||
|
# list of the base names of other libraries (for Qt libraries, use the same
|
||||||
|
# names you use with the ``QT`` variable in a qmake project file, such as "core"
|
||||||
|
# for QtCore). FILENAME_VAR specifies the name of a variable to store the path
|
||||||
|
# to the generated file in.
|
||||||
|
#
|
||||||
|
# INCLUDE_INSTALL_DIR is the path (relative to ``CMAKE_INSTALL_PREFIX``) that
|
||||||
|
# include files will be installed to. It defaults to
|
||||||
|
# ``${INCLUDE_INSTALL_DIR}/<baseName>`` if the ``INCLUDE_INSTALL_DIR`` variable
|
||||||
|
# is set. If that variable is not set, the ``CMAKE_INSTALL_INCLUDEDIR`` variable
|
||||||
|
# is used instead, and if neither are set ``include`` is used. LIB_INSTALL_DIR
|
||||||
|
# operates similarly for the installation location for libraries; it defaults to
|
||||||
|
# ``${LIB_INSTALL_DIR}``, ``${CMAKE_INSTALL_LIBDIR}`` or ``lib``, in that order.
|
||||||
|
#
|
||||||
|
# Example usage:
|
||||||
|
#
|
||||||
|
# .. code-block:: cmake
|
||||||
|
#
|
||||||
|
# ecm_generate_pri_file(
|
||||||
|
# BASE_NAME KArchive
|
||||||
|
# LIB_NAME KF5KArchive
|
||||||
|
# DEPS "core"
|
||||||
|
# FILENAME_VAR pri_filename
|
||||||
|
# )
|
||||||
|
# install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
|
||||||
|
#
|
||||||
|
# A qmake-based project that wished to use this would then do::
|
||||||
|
#
|
||||||
|
# QT += KArchive
|
||||||
|
#
|
||||||
|
# in their ``.pro`` file.
|
||||||
|
#
|
||||||
|
# Since pre-1.0.0.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2014 David Faure <faure@kde.org>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. The name of the author may not be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
# Replicate the logic from KDEInstallDirs.cmake as we can't depend on it
|
||||||
|
# Ask qmake if we're using the same prefix as Qt
|
||||||
|
set(_askqmake OFF)
|
||||||
|
if(NOT DEFINED KDE_INSTALL_USE_QT_SYS_PATHS)
|
||||||
|
include(ECMQueryQmake)
|
||||||
|
query_qmake(qt_install_prefix_dir QT_INSTALL_PREFIX)
|
||||||
|
if(qt_install_prefix_dir STREQUAL "${CMAKE_INSTALL_PREFIX}")
|
||||||
|
set(_askqmake ON)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(KDE_INSTALL_USE_QT_SYS_PATHS OR _askqmake)
|
||||||
|
include(ECMQueryQmake)
|
||||||
|
query_qmake(qt_host_data_dir QT_HOST_DATA)
|
||||||
|
set(ECM_MKSPECS_INSTALL_DIR ${qt_host_data_dir}/mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.")
|
||||||
|
else()
|
||||||
|
set(ECM_MKSPECS_INSTALL_DIR mkspecs/modules CACHE PATH "The directory where mkspecs will be installed to.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(ECM_GENERATE_PRI_FILE)
|
||||||
|
set(options )
|
||||||
|
set(oneValueArgs BASE_NAME LIB_NAME DEPS FILENAME_VAR INCLUDE_INSTALL_DIR LIB_INSTALL_DIR)
|
||||||
|
set(multiValueArgs )
|
||||||
|
|
||||||
|
cmake_parse_arguments(EGPF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(EGPF_UNPARSED_ARGUMENTS)
|
||||||
|
message(FATAL_ERROR "Unknown keywords given to ECM_GENERATE_PRI_FILE(): \"${EGPF_UNPARSED_ARGUMENTS}\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT EGPF_BASE_NAME)
|
||||||
|
message(FATAL_ERROR "Required argument BASE_NAME missing in ECM_GENERATE_PRI_FILE() call")
|
||||||
|
endif()
|
||||||
|
if(NOT EGPF_LIB_NAME)
|
||||||
|
message(FATAL_ERROR "Required argument LIB_NAME missing in ECM_GENERATE_PRI_FILE() call")
|
||||||
|
endif()
|
||||||
|
if(NOT PROJECT_VERSION_STRING)
|
||||||
|
message(FATAL_ERROR "Required variable PROJECT_VERSION_STRING not set before ECM_GENERATE_PRI_FILE() call. Did you call ecm_setup_version?")
|
||||||
|
endif()
|
||||||
|
if(NOT EGPF_INCLUDE_INSTALL_DIR)
|
||||||
|
if(INCLUDE_INSTALL_DIR)
|
||||||
|
set(EGPF_INCLUDE_INSTALL_DIR "${INCLUDE_INSTALL_DIR}/${EGPF_BASE_NAME}")
|
||||||
|
elseif(CMAKE_INSTALL_INCLUDEDIR)
|
||||||
|
set(EGPF_INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}/${EGPF_BASE_NAME}")
|
||||||
|
else()
|
||||||
|
set(EGPF_INCLUDE_INSTALL_DIR "include/${EGPF_BASE_NAME}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT EGPF_LIB_INSTALL_DIR)
|
||||||
|
if(LIB_INSTALL_DIR)
|
||||||
|
set(EGPF_LIB_INSTALL_DIR "${LIB_INSTALL_DIR}")
|
||||||
|
elseif(CMAKE_INSTALL_LIBDIR)
|
||||||
|
set(EGPF_LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
else()
|
||||||
|
set(EGPF_LIB_INSTALL_DIR "lib")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" PROJECT_VERSION_MAJOR "${PROJECT_VERSION_STRING}")
|
||||||
|
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" PROJECT_VERSION_MINOR "${PROJECT_VERSION_STRING}")
|
||||||
|
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" PROJECT_VERSION_PATCH "${PROJECT_VERSION_STRING}")
|
||||||
|
|
||||||
|
set(PRI_TARGET_BASENAME ${EGPF_BASE_NAME})
|
||||||
|
set(PRI_TARGET_LIBNAME ${EGPF_LIB_NAME})
|
||||||
|
set(PRI_TARGET_QTDEPS ${EGPF_DEPS})
|
||||||
|
if(IS_ABSOLUTE "${EGPF_INCLUDE_INSTALL_DIR}")
|
||||||
|
set(PRI_TARGET_INCLUDES "${EGPF_INCLUDE_INSTALL_DIR}")
|
||||||
|
else()
|
||||||
|
set(PRI_TARGET_INCLUDES "${CMAKE_INSTALL_PREFIX}/${EGPF_INCLUDE_INSTALL_DIR}")
|
||||||
|
endif()
|
||||||
|
if(IS_ABSOLUTE "${EGPF_LIB_INSTALL_DIR}")
|
||||||
|
set(PRI_TARGET_LIBS "${EGPF_LIB_INSTALL_DIR}")
|
||||||
|
else()
|
||||||
|
set(PRI_TARGET_LIBS "${CMAKE_INSTALL_PREFIX}/${EGPF_LIB_INSTALL_DIR}")
|
||||||
|
endif()
|
||||||
|
set(PRI_TARGET_DEFINES "")
|
||||||
|
|
||||||
|
set(PRI_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/qt_${PRI_TARGET_BASENAME}.pri)
|
||||||
|
if (EGPF_FILENAME_VAR)
|
||||||
|
set(${EGPF_FILENAME_VAR} ${PRI_FILENAME} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(GENERATE
|
||||||
|
OUTPUT ${PRI_FILENAME}
|
||||||
|
CONTENT
|
||||||
|
"QT.${PRI_TARGET_BASENAME}.VERSION = ${PROJECT_VERSION_STRING}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.MAJOR_VERSION = ${PROJECT_VERSION_MAJOR}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.MINOR_VERSION = ${PROJECT_VERSION_MINOR}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.PATCH_VERSION = ${PROJECT_VERSION_PATCH}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.name = ${PRI_TARGET_LIBNAME}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.module = ${PRI_TARGET_LIBNAME}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.defines = ${PRI_TARGET_DEFINES}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.includes = ${PRI_TARGET_INCLUDES}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.private_includes =
|
||||||
|
QT.${PRI_TARGET_BASENAME}.libs = ${PRI_TARGET_LIBS}
|
||||||
|
QT.${PRI_TARGET_BASENAME}.depends = ${PRI_TARGET_QTDEPS}
|
||||||
|
"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
223
client/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake
Normal file
223
client/qtkeychain/cmake/Modules/ECMPackageConfigHelpers.cmake
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#.rst:
|
||||||
|
# ECMPackageConfigHelpers
|
||||||
|
# -----------------------
|
||||||
|
#
|
||||||
|
# Helper macros for generating CMake package config files.
|
||||||
|
#
|
||||||
|
# ``write_basic_package_version_file()`` is the same as the one provided by the
|
||||||
|
# `CMakePackageConfigHelpers
|
||||||
|
# <http://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers>`_
|
||||||
|
# module in CMake; see that module's documentation for
|
||||||
|
# more information.
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# ecm_configure_package_config_file(<input> <output>
|
||||||
|
# INSTALL_DESTINATION <path>
|
||||||
|
# [PATH_VARS <var1> [<var2> [...]]
|
||||||
|
# [NO_SET_AND_CHECK_MACRO]
|
||||||
|
# [NO_CHECK_REQUIRED_COMPONENTS_MACRO])
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# This behaves in the same way as configure_package_config_file() from CMake
|
||||||
|
# 2.8.12, except that it adds an extra helper macro: find_dependency(). It is
|
||||||
|
# highly recommended that you read the `documentation for
|
||||||
|
# CMakePackageConfigHelpers
|
||||||
|
# <http://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers>`_
|
||||||
|
# for more information, particularly with regard to the PATH_VARS argument.
|
||||||
|
#
|
||||||
|
# Note that there is no argument that will disable the find_dependency() macro;
|
||||||
|
# if you do not require this macro, you should use
|
||||||
|
# ``configure_package_config_file`` from the CMakePackageConfigHelpers module.
|
||||||
|
#
|
||||||
|
# CMake 3.0 includes a CMakeFindDependencyMacro module that provides the
|
||||||
|
# find_dependency() macro (which you can ``include()`` in your package config
|
||||||
|
# file), so this file is only useful for projects wishing to provide config
|
||||||
|
# files that will work with CMake 2.8.12.
|
||||||
|
#
|
||||||
|
# Additional Config File Macros
|
||||||
|
# =============================
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# find_dependency(<dep> [<version> [EXACT]])
|
||||||
|
#
|
||||||
|
# find_dependency() should be used instead of find_package() to find package
|
||||||
|
# dependencies. It forwards the correct parameters for EXACT, QUIET and
|
||||||
|
# REQUIRED which were passed to the original find_package() call. It also sets
|
||||||
|
# an informative diagnostic message if the dependency could not be found.
|
||||||
|
#
|
||||||
|
# Since pre-1.0.0.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2014 Alex Merry <alex.merry@kdemail.net>
|
||||||
|
# Copyright 2013 Stephen Kelly <steveire@gmail.com>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. The name of the author may not be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
include(${CMAKE_ROOT}/Modules/CMakePackageConfigHelpers.cmake)
|
||||||
|
|
||||||
|
set(_ecm_package_config_helpers_included TRUE)
|
||||||
|
|
||||||
|
if(NOT CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.13)
|
||||||
|
message(AUTHOR_WARNING "Your project already requires a version of CMake that includes the find_dependency macro via the CMakeFindDependencyMacro module. You should use CMakePackageConfigHelpers instead of ECMPackageConfigHelpers.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(ECM_CONFIGURE_PACKAGE_CONFIG_FILE _inputFile _outputFile)
|
||||||
|
set(options NO_SET_AND_CHECK_MACRO NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||||
|
set(oneValueArgs INSTALL_DESTINATION )
|
||||||
|
set(multiValueArgs PATH_VARS )
|
||||||
|
|
||||||
|
cmake_parse_arguments(CCF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(CCF_UNPARSED_ARGUMENTS)
|
||||||
|
message(FATAL_ERROR "Unknown keywords given to CONFIGURE_PACKAGE_CONFIG_FILE(): \"${CCF_UNPARSED_ARGUMENTS}\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CCF_INSTALL_DESTINATION)
|
||||||
|
message(FATAL_ERROR "No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(IS_ABSOLUTE "${CCF_INSTALL_DESTINATION}")
|
||||||
|
set(absInstallDir "${CCF_INSTALL_DESTINATION}")
|
||||||
|
else()
|
||||||
|
set(absInstallDir "${CMAKE_INSTALL_PREFIX}/${CCF_INSTALL_DESTINATION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(RELATIVE_PATH PACKAGE_RELATIVE_PATH "${absInstallDir}" "${CMAKE_INSTALL_PREFIX}" )
|
||||||
|
|
||||||
|
foreach(var ${CCF_PATH_VARS})
|
||||||
|
if(NOT DEFINED ${var})
|
||||||
|
message(FATAL_ERROR "Variable ${var} does not exist")
|
||||||
|
else()
|
||||||
|
if(IS_ABSOLUTE "${${var}}")
|
||||||
|
string(REPLACE "${CMAKE_INSTALL_PREFIX}" "\${PACKAGE_PREFIX_DIR}"
|
||||||
|
PACKAGE_${var} "${${var}}")
|
||||||
|
else()
|
||||||
|
set(PACKAGE_${var} "\${PACKAGE_PREFIX_DIR}/${${var}}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
get_filename_component(inputFileName "${_inputFile}" NAME)
|
||||||
|
|
||||||
|
set(PACKAGE_INIT "
|
||||||
|
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() (ECM variant) #######
|
||||||
|
####### Any changes to this file will be overwritten by the next CMake run #######
|
||||||
|
####### The input file was ${inputFileName} #######
|
||||||
|
|
||||||
|
get_filename_component(PACKAGE_PREFIX_DIR \"\${CMAKE_CURRENT_LIST_DIR}/${PACKAGE_RELATIVE_PATH}\" ABSOLUTE)
|
||||||
|
")
|
||||||
|
|
||||||
|
if("${absInstallDir}" MATCHES "^(/usr)?/lib(64)?/.+")
|
||||||
|
# Handle "/usr move" symlinks created by some Linux distros.
|
||||||
|
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||||
|
# Use original install prefix when loaded through a \"/usr move\"
|
||||||
|
# cross-prefix symbolic link such as /lib -> /usr/lib.
|
||||||
|
get_filename_component(_realCurr \"\${CMAKE_CURRENT_LIST_DIR}\" REALPATH)
|
||||||
|
get_filename_component(_realOrig \"${absInstallDir}\" REALPATH)
|
||||||
|
if(_realCurr STREQUAL _realOrig)
|
||||||
|
set(PACKAGE_PREFIX_DIR \"${CMAKE_INSTALL_PREFIX}\")
|
||||||
|
endif()
|
||||||
|
unset(_realOrig)
|
||||||
|
unset(_realCurr)
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CCF_NO_SET_AND_CHECK_MACRO)
|
||||||
|
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||||
|
macro(set_and_check _var _file)
|
||||||
|
set(\${_var} \"\${_file}\")
|
||||||
|
if(NOT EXISTS \"\${_file}\")
|
||||||
|
message(FATAL_ERROR \"File or directory \${_file} referenced by variable \${_var} does not exist !\")
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
include(CMakeFindDependencyMacro OPTIONAL RESULT_VARIABLE _CMakeFindDependencyMacro_FOUND)
|
||||||
|
|
||||||
|
if (NOT _CMakeFindDependencyMacro_FOUND)
|
||||||
|
macro(find_dependency dep)
|
||||||
|
if (NOT \${dep}_FOUND)
|
||||||
|
|
||||||
|
set(ecm_fd_version)
|
||||||
|
if (\${ARGC} GREATER 1)
|
||||||
|
set(ecm_fd_version \${ARGV1})
|
||||||
|
endif()
|
||||||
|
set(ecm_fd_exact_arg)
|
||||||
|
if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION_EXACT)
|
||||||
|
set(ecm_fd_exact_arg EXACT)
|
||||||
|
endif()
|
||||||
|
set(ecm_fd_quiet_arg)
|
||||||
|
if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
|
||||||
|
set(ecm_fd_quiet_arg QUIET)
|
||||||
|
endif()
|
||||||
|
set(ecm_fd_required_arg)
|
||||||
|
if(\${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED)
|
||||||
|
set(ecm_fd_required_arg REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(\${dep} \${ecm_fd_version}
|
||||||
|
\${ecm_fd_exact_arg}
|
||||||
|
\${ecm_fd_quiet_arg}
|
||||||
|
\${ecm_fd_required_arg}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT \${dep}_FOUND)
|
||||||
|
set(\${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE \"\${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency \${dep} could not be found.\")
|
||||||
|
set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND False)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ecm_fd_version)
|
||||||
|
set(ecm_fd_required_arg)
|
||||||
|
set(ecm_fd_quiet_arg)
|
||||||
|
set(ecm_fd_exact_arg)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(NOT CCF_NO_CHECK_REQUIRED_COMPONENTS_MACRO)
|
||||||
|
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||||
|
macro(check_required_components _NAME)
|
||||||
|
foreach(comp \${\${_NAME}_FIND_COMPONENTS})
|
||||||
|
if(NOT \${_NAME}_\${comp}_FOUND)
|
||||||
|
if(\${_NAME}_FIND_REQUIRED_\${comp})
|
||||||
|
set(\${_NAME}_FOUND FALSE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endmacro()
|
||||||
|
")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(PACKAGE_INIT "${PACKAGE_INIT}
|
||||||
|
####################################################################################")
|
||||||
|
|
||||||
|
configure_file("${_inputFile}" "${_outputFile}" @ONLY)
|
||||||
|
|
||||||
|
endfunction()
|
||||||
32
client/qtkeychain/cmake/Modules/ECMQueryQmake.cmake
Normal file
32
client/qtkeychain/cmake/Modules/ECMQueryQmake.cmake
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
find_package(Qt5Core QUIET)
|
||||||
|
|
||||||
|
if (Qt5Core_FOUND)
|
||||||
|
set(_qmake_executable_default "qmake-qt5")
|
||||||
|
endif ()
|
||||||
|
if (TARGET Qt5::qmake)
|
||||||
|
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
|
||||||
|
endif()
|
||||||
|
set(QMAKE_EXECUTABLE ${_qmake_executable_default}
|
||||||
|
CACHE FILEPATH "Location of the Qt5 qmake executable")
|
||||||
|
|
||||||
|
# This is not public API (yet)!
|
||||||
|
function(query_qmake result_variable qt_variable)
|
||||||
|
if(NOT QMAKE_EXECUTABLE)
|
||||||
|
set(${result_variable} "" PARENT_SCOPE)
|
||||||
|
message(WARNING "Should specify a qmake Qt5 binary. Can't check ${qt_variable}")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${QMAKE_EXECUTABLE} -query "${qt_variable}"
|
||||||
|
RESULT_VARIABLE return_code
|
||||||
|
OUTPUT_VARIABLE output
|
||||||
|
)
|
||||||
|
if(return_code EQUAL 0)
|
||||||
|
string(STRIP "${output}" output)
|
||||||
|
file(TO_CMAKE_PATH "${output}" output_path)
|
||||||
|
set(${result_variable} "${output_path}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(WARNING "Failed call: ${QMAKE_EXECUTABLE} -query \"${qt_variable}\"")
|
||||||
|
message(FATAL_ERROR "QMake call failed: ${return_code}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
223
client/qtkeychain/cmake/Modules/ECMSetupVersion.cmake
Normal file
223
client/qtkeychain/cmake/Modules/ECMSetupVersion.cmake
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#.rst:
|
||||||
|
# ECMSetupVersion
|
||||||
|
# ---------------
|
||||||
|
#
|
||||||
|
# Handle library version information.
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# ecm_setup_version(<version>
|
||||||
|
# VARIABLE_PREFIX <prefix>
|
||||||
|
# [SOVERSION <soversion>]
|
||||||
|
# [VERSION_HEADER <filename>]
|
||||||
|
# [PACKAGE_VERSION_FILE <filename> [COMPATIBILITY <compat>]] )
|
||||||
|
#
|
||||||
|
# This parses a version string and sets up a standard set of version variables.
|
||||||
|
# It can optionally also create a C version header file and a CMake package
|
||||||
|
# version file to install along with the library.
|
||||||
|
#
|
||||||
|
# If the ``<version>`` argument is of the form ``<major>.<minor>.<patch>``
|
||||||
|
# (or ``<major>.<minor>.<patch>.<tweak>``), The following CMake variables are
|
||||||
|
# set::
|
||||||
|
#
|
||||||
|
# <prefix>_VERSION_MAJOR - <major>
|
||||||
|
# <prefix>_VERSION_MINOR - <minor>
|
||||||
|
# <prefix>_VERSION_PATCH - <patch>
|
||||||
|
# <prefix>_VERSION - <version>
|
||||||
|
# <prefix>_VERSION_STRING - <version> (for compatibility: use <prefix>_VERSION instead)
|
||||||
|
# <prefix>_SOVERSION - <soversion>, or <major> if SOVERSION was not given
|
||||||
|
#
|
||||||
|
# If CMake policy CMP0048 is not NEW, the following CMake variables will also
|
||||||
|
# be set:
|
||||||
|
#
|
||||||
|
# PROJECT_VERSION_MAJOR - <major>
|
||||||
|
# PROJECT_VERSION_MINOR - <minor>
|
||||||
|
# PROJECT_VERSION_PATCH - <patch>
|
||||||
|
# PROJECT_VERSION - <version>
|
||||||
|
# PROJECT_VERSION_STRING - <version> (for compatibility: use PROJECT_VERSION instead)
|
||||||
|
#
|
||||||
|
# If the VERSION_HEADER option is used, a simple C header is generated with the
|
||||||
|
# given filename. If filename is a relative path, it is interpreted as relative
|
||||||
|
# to CMAKE_CURRENT_BINARY_DIR. The generated header contains the following
|
||||||
|
# macros::
|
||||||
|
#
|
||||||
|
# <prefix>_VERSION_MAJOR - <major> as an integer
|
||||||
|
# <prefix>_VERSION_MINOR - <minor> as an integer
|
||||||
|
# <prefix>_VERSION_PATCH - <patch> as an integer
|
||||||
|
# <prefix>_VERSION_STRING - <version> as a C string
|
||||||
|
# <prefix>_VERSION - the version as an integer
|
||||||
|
#
|
||||||
|
# ``<prefix>_VERSION`` has ``<patch>`` in the bottom 8 bits, ``<minor>`` in the
|
||||||
|
# next 8 bits and ``<major>`` in the remaining bits. Note that ``<patch>`` and
|
||||||
|
# ``<minor>`` must be less than 256.
|
||||||
|
#
|
||||||
|
# If the PACKAGE_VERSION_FILE option is used, a simple CMake package version
|
||||||
|
# file is created using the write_basic_package_version_file() macro provided by
|
||||||
|
# CMake. It should be installed in the same location as the Config.cmake file of
|
||||||
|
# the library so that it can be found by find_package(). If the filename is a
|
||||||
|
# relative path, it is interpreted as relative to CMAKE_CURRENT_BINARY_DIR. The
|
||||||
|
# optional COMPATIBILITY option is forwarded to
|
||||||
|
# write_basic_package_version_file(), and defaults to AnyNewerVersion.
|
||||||
|
#
|
||||||
|
# If CMake policy CMP0048 is NEW, an alternative form of the command is
|
||||||
|
# available::
|
||||||
|
#
|
||||||
|
# ecm_setup_version(PROJECT
|
||||||
|
# [VARIABLE_PREFIX <prefix>]
|
||||||
|
# [SOVERSION <soversion>]
|
||||||
|
# [VERSION_HEADER <filename>]
|
||||||
|
# [PACKAGE_VERSION_FILE <filename>] )
|
||||||
|
#
|
||||||
|
# This will use the version information set by the project() command.
|
||||||
|
# VARIABLE_PREFIX defaults to the project name. Note that PROJECT must be the
|
||||||
|
# first argument. In all other respects, it behaves like the other form of the
|
||||||
|
# command.
|
||||||
|
#
|
||||||
|
# Since pre-1.0.0.
|
||||||
|
#
|
||||||
|
# COMPATIBLITY option available since 1.6.0.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||||
|
# Copyright 2012 Alexander Neundorf <neundorf@kde.org>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# 1. Redistributions of source code must retain the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
# 3. The name of the author may not be used to endorse or promote products
|
||||||
|
# derived from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
# save the location of the header template while CMAKE_CURRENT_LIST_DIR
|
||||||
|
# has the value we want
|
||||||
|
set(_ECM_SETUP_VERSION_HEADER_TEMPLATE "${CMAKE_CURRENT_LIST_DIR}/ECMVersionHeader.h.in")
|
||||||
|
|
||||||
|
function(ecm_setup_version _version)
|
||||||
|
set(options )
|
||||||
|
set(oneValueArgs VARIABLE_PREFIX SOVERSION VERSION_HEADER PACKAGE_VERSION_FILE COMPATIBILITY)
|
||||||
|
set(multiValueArgs )
|
||||||
|
|
||||||
|
cmake_parse_arguments(ESV "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(ESV_UNPARSED_ARGUMENTS)
|
||||||
|
message(FATAL_ERROR "Unknown keywords given to ECM_SETUP_VERSION(): \"${ESV_UNPARSED_ARGUMENTS}\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(project_manages_version FALSE)
|
||||||
|
set(use_project_version FALSE)
|
||||||
|
# CMP0048 only exists in CMake 3.0.0 and later
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 3.0.0)
|
||||||
|
set(project_version_policy "OLD")
|
||||||
|
else()
|
||||||
|
cmake_policy(GET CMP0048 project_version_policy)
|
||||||
|
endif()
|
||||||
|
if(project_version_policy STREQUAL "NEW")
|
||||||
|
set(project_manages_version TRUE)
|
||||||
|
if(_version STREQUAL "PROJECT")
|
||||||
|
set(use_project_version TRUE)
|
||||||
|
endif()
|
||||||
|
elseif(_version STREQUAL "PROJECT")
|
||||||
|
message(FATAL_ERROR "ecm_setup_version given PROJECT argument, but CMP0048 is not NEW")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(should_set_prefixed_vars TRUE)
|
||||||
|
if(NOT ESV_VARIABLE_PREFIX)
|
||||||
|
if(use_project_version)
|
||||||
|
set(ESV_VARIABLE_PREFIX "${PROJECT_NAME}")
|
||||||
|
set(should_set_prefixed_vars FALSE)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Required argument PREFIX missing in ECM_SETUP_VERSION() call")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(use_project_version)
|
||||||
|
set(_version "${PROJECT_VERSION}")
|
||||||
|
set(_major "${PROJECT_VERSION_MAJOR}")
|
||||||
|
set(_minor "${PROJECT_VERSION_MINOR}")
|
||||||
|
set(_patch "${PROJECT_VERSION_PATCH}")
|
||||||
|
else()
|
||||||
|
string(REGEX REPLACE "^([0-9]+)\\.[0-9]+\\.[0-9]+.*" "\\1" _major "${_version}")
|
||||||
|
string(REGEX REPLACE "^[0-9]+\\.([0-9]+)\\.[0-9]+.*" "\\1" _minor "${_version}")
|
||||||
|
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" _patch "${_version}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ESV_SOVERSION)
|
||||||
|
set(ESV_SOVERSION ${_major})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(should_set_prefixed_vars)
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION "${_version}")
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR ${_major})
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR ${_minor})
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH ${_patch})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_SOVERSION ${ESV_SOVERSION})
|
||||||
|
|
||||||
|
if(NOT project_manages_version)
|
||||||
|
set(PROJECT_VERSION "${_version}")
|
||||||
|
set(PROJECT_VERSION_MAJOR "${_major}")
|
||||||
|
set(PROJECT_VERSION_MINOR "${_minor}")
|
||||||
|
set(PROJECT_VERSION_PATCH "${_patch}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# compat
|
||||||
|
set(PROJECT_VERSION_STRING "${PROJECT_VERSION}")
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}")
|
||||||
|
|
||||||
|
if(ESV_VERSION_HEADER)
|
||||||
|
set(HEADER_PREFIX "${ESV_VARIABLE_PREFIX}")
|
||||||
|
set(HEADER_VERSION "${_version}")
|
||||||
|
set(HEADER_VERSION_MAJOR "${_major}")
|
||||||
|
set(HEADER_VERSION_MINOR "${_minor}")
|
||||||
|
set(HEADER_VERSION_PATCH "${_patch}")
|
||||||
|
configure_file("${_ECM_SETUP_VERSION_HEADER_TEMPLATE}" "${ESV_VERSION_HEADER}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ESV_PACKAGE_VERSION_FILE)
|
||||||
|
if(NOT ESV_COMPATIBILITY)
|
||||||
|
set(ESV_COMPATIBILITY AnyNewerVersion)
|
||||||
|
endif()
|
||||||
|
write_basic_package_version_file("${ESV_PACKAGE_VERSION_FILE}" VERSION ${_version} COMPATIBILITY ${ESV_COMPATIBILITY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(should_set_prefixed_vars)
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_MAJOR "${${ESV_VARIABLE_PREFIX}_VERSION_MAJOR}" PARENT_SCOPE)
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_MINOR "${${ESV_VARIABLE_PREFIX}_VERSION_MINOR}" PARENT_SCOPE)
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_PATCH "${${ESV_VARIABLE_PREFIX}_VERSION_PATCH}" PARENT_SCOPE)
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# always set the soversion
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_SOVERSION "${${ESV_VARIABLE_PREFIX}_SOVERSION}" PARENT_SCOPE)
|
||||||
|
|
||||||
|
if(NOT project_manages_version)
|
||||||
|
set(PROJECT_VERSION "${PROJECT_VERSION}" PARENT_SCOPE)
|
||||||
|
set(PROJECT_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}" PARENT_SCOPE)
|
||||||
|
set(PROJECT_VERSION_MINOR "${PROJECT_VERSION_MINOR}" PARENT_SCOPE)
|
||||||
|
set(PROJECT_VERSION_PATCH "${PROJECT_VERSION_PATCH}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# always set the compatibility variables
|
||||||
|
set(PROJECT_VERSION_STRING "${PROJECT_VERSION_STRING}" PARENT_SCOPE)
|
||||||
|
set(${ESV_VARIABLE_PREFIX}_VERSION_STRING "${${ESV_VARIABLE_PREFIX}_VERSION}" PARENT_SCOPE)
|
||||||
|
|
||||||
|
endfunction()
|
||||||
188
client/qtkeychain/cmake/Modules/GNUInstallDirs.cmake
Normal file
188
client/qtkeychain/cmake/Modules/GNUInstallDirs.cmake
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
# - Define GNU standard installation directories
|
||||||
|
# Provides install directory variables as defined for GNU software:
|
||||||
|
# http://www.gnu.org/prep/standards/html_node/Directory-Variables.html
|
||||||
|
# Inclusion of this module defines the following variables:
|
||||||
|
# CMAKE_INSTALL_<dir> - destination for files of a given type
|
||||||
|
# CMAKE_INSTALL_FULL_<dir> - corresponding absolute path
|
||||||
|
# where <dir> is one of:
|
||||||
|
# BINDIR - user executables (bin)
|
||||||
|
# SBINDIR - system admin executables (sbin)
|
||||||
|
# LIBEXECDIR - program executables (libexec)
|
||||||
|
# SYSCONFDIR - read-only single-machine data (etc)
|
||||||
|
# SHAREDSTATEDIR - modifiable architecture-independent data (com)
|
||||||
|
# LOCALSTATEDIR - modifiable single-machine data (var)
|
||||||
|
# LIBDIR - object code libraries (lib or lib64 or lib/<multiarch-tuple> on Debian)
|
||||||
|
# INCLUDEDIR - C header files (include)
|
||||||
|
# OLDINCLUDEDIR - C header files for non-gcc (/usr/include)
|
||||||
|
# DATAROOTDIR - read-only architecture-independent data root (share)
|
||||||
|
# DATADIR - read-only architecture-independent data (DATAROOTDIR)
|
||||||
|
# INFODIR - info documentation (DATAROOTDIR/info)
|
||||||
|
# LOCALEDIR - locale-dependent data (DATAROOTDIR/locale)
|
||||||
|
# MANDIR - man documentation (DATAROOTDIR/man)
|
||||||
|
# DOCDIR - documentation root (DATAROOTDIR/doc/PROJECT_NAME)
|
||||||
|
# Each CMAKE_INSTALL_<dir> value may be passed to the DESTINATION options of
|
||||||
|
# install() commands for the corresponding file type. If the includer does
|
||||||
|
# not define a value the above-shown default will be used and the value will
|
||||||
|
# appear in the cache for editing by the user.
|
||||||
|
# Each CMAKE_INSTALL_FULL_<dir> value contains an absolute path constructed
|
||||||
|
# from the corresponding destination by prepending (if necessary) the value
|
||||||
|
# of CMAKE_INSTALL_PREFIX.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# Copyright 2011 Nikita Krupen'ko <krnekit@gmail.com>
|
||||||
|
# Copyright 2011 Kitware, Inc.
|
||||||
|
#
|
||||||
|
# Distributed under the OSI-approved BSD License (the "License");
|
||||||
|
# see accompanying file Copyright.txt for details.
|
||||||
|
#
|
||||||
|
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||||
|
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the License for more information.
|
||||||
|
#=============================================================================
|
||||||
|
# (To distribute this file outside of CMake, substitute the full
|
||||||
|
# License text for the above reference.)
|
||||||
|
|
||||||
|
# Installation directories
|
||||||
|
#
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_BINDIR)
|
||||||
|
set(CMAKE_INSTALL_BINDIR "bin" CACHE PATH "user executables (bin)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_SBINDIR)
|
||||||
|
set(CMAKE_INSTALL_SBINDIR "sbin" CACHE PATH "system admin executables (sbin)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_LIBEXECDIR)
|
||||||
|
set(CMAKE_INSTALL_LIBEXECDIR "libexec" CACHE PATH "program executables (libexec)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_SYSCONFDIR)
|
||||||
|
set(CMAKE_INSTALL_SYSCONFDIR "etc" CACHE PATH "read-only single-machine data (etc)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_SHAREDSTATEDIR)
|
||||||
|
set(CMAKE_INSTALL_SHAREDSTATEDIR "com" CACHE PATH "modifiable architecture-independent data (com)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_LOCALSTATEDIR)
|
||||||
|
set(CMAKE_INSTALL_LOCALSTATEDIR "var" CACHE PATH "modifiable single-machine data (var)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||||
|
set(_LIBDIR_DEFAULT "lib")
|
||||||
|
# Override this default 'lib' with 'lib64' iff:
|
||||||
|
# - we are on Linux system but NOT cross-compiling
|
||||||
|
# - we are NOT on debian
|
||||||
|
# - we are on a 64 bits system
|
||||||
|
# reason is: amd64 ABI: http://www.x86-64.org/documentation/abi.pdf
|
||||||
|
# For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if
|
||||||
|
# CMAKE_LIBRARY_ARCHITECTURE is set (which contains e.g. "i386-linux-gnu"
|
||||||
|
# See http://wiki.debian.org/Multiarch
|
||||||
|
if(CMAKE_SYSTEM_NAME MATCHES "Linux"
|
||||||
|
AND NOT CMAKE_CROSSCOMPILING)
|
||||||
|
if (EXISTS "/etc/debian_version") # is this a debian system ?
|
||||||
|
if(CMAKE_LIBRARY_ARCHITECTURE)
|
||||||
|
set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
|
||||||
|
endif()
|
||||||
|
else() # not debian, rely on CMAKE_SIZEOF_VOID_P:
|
||||||
|
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
|
||||||
|
message(AUTHOR_WARNING
|
||||||
|
"Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
|
||||||
|
"Please enable at least one language before including GNUInstallDirs.")
|
||||||
|
else()
|
||||||
|
if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
|
||||||
|
set(_LIBDIR_DEFAULT "lib64")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "object code libraries (${_LIBDIR_DEFAULT})")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
|
||||||
|
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE PATH "C header files (include)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_OLDINCLUDEDIR)
|
||||||
|
set(CMAKE_INSTALL_OLDINCLUDEDIR "/usr/include" CACHE PATH "C header files for non-gcc (/usr/include)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED CMAKE_INSTALL_DATAROOTDIR)
|
||||||
|
set(CMAKE_INSTALL_DATAROOTDIR "share" CACHE PATH "read-only architecture-independent data root (share)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
# Values whose defaults are relative to DATAROOTDIR. Store empty values in
|
||||||
|
# the cache and store the defaults in local variables if the cache values are
|
||||||
|
# not set explicitly. This auto-updates the defaults as DATAROOTDIR changes.
|
||||||
|
|
||||||
|
if(NOT CMAKE_INSTALL_DATADIR)
|
||||||
|
set(CMAKE_INSTALL_DATADIR "" CACHE PATH "read-only architecture-independent data (DATAROOTDIR)")
|
||||||
|
set(CMAKE_INSTALL_DATADIR "${CMAKE_INSTALL_DATAROOTDIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_INSTALL_INFODIR)
|
||||||
|
set(CMAKE_INSTALL_INFODIR "" CACHE PATH "info documentation (DATAROOTDIR/info)")
|
||||||
|
set(CMAKE_INSTALL_INFODIR "${CMAKE_INSTALL_DATAROOTDIR}/info")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_INSTALL_LOCALEDIR)
|
||||||
|
set(CMAKE_INSTALL_LOCALEDIR "" CACHE PATH "locale-dependent data (DATAROOTDIR/locale)")
|
||||||
|
set(CMAKE_INSTALL_LOCALEDIR "${CMAKE_INSTALL_DATAROOTDIR}/locale")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_INSTALL_MANDIR)
|
||||||
|
set(CMAKE_INSTALL_MANDIR "" CACHE PATH "man documentation (DATAROOTDIR/man)")
|
||||||
|
set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_DATAROOTDIR}/man")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_INSTALL_DOCDIR)
|
||||||
|
set(CMAKE_INSTALL_DOCDIR "" CACHE PATH "documentation root (DATAROOTDIR/doc/PROJECT_NAME)")
|
||||||
|
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATAROOTDIR}/doc/${PROJECT_NAME}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
CMAKE_INSTALL_BINDIR
|
||||||
|
CMAKE_INSTALL_SBINDIR
|
||||||
|
CMAKE_INSTALL_LIBEXECDIR
|
||||||
|
CMAKE_INSTALL_SYSCONFDIR
|
||||||
|
CMAKE_INSTALL_SHAREDSTATEDIR
|
||||||
|
CMAKE_INSTALL_LOCALSTATEDIR
|
||||||
|
CMAKE_INSTALL_LIBDIR
|
||||||
|
CMAKE_INSTALL_INCLUDEDIR
|
||||||
|
CMAKE_INSTALL_OLDINCLUDEDIR
|
||||||
|
CMAKE_INSTALL_DATAROOTDIR
|
||||||
|
CMAKE_INSTALL_DATADIR
|
||||||
|
CMAKE_INSTALL_INFODIR
|
||||||
|
CMAKE_INSTALL_LOCALEDIR
|
||||||
|
CMAKE_INSTALL_MANDIR
|
||||||
|
CMAKE_INSTALL_DOCDIR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Result directories
|
||||||
|
#
|
||||||
|
foreach(dir
|
||||||
|
BINDIR
|
||||||
|
SBINDIR
|
||||||
|
LIBEXECDIR
|
||||||
|
SYSCONFDIR
|
||||||
|
SHAREDSTATEDIR
|
||||||
|
LOCALSTATEDIR
|
||||||
|
LIBDIR
|
||||||
|
INCLUDEDIR
|
||||||
|
OLDINCLUDEDIR
|
||||||
|
DATAROOTDIR
|
||||||
|
DATADIR
|
||||||
|
INFODIR
|
||||||
|
LOCALEDIR
|
||||||
|
MANDIR
|
||||||
|
DOCDIR
|
||||||
|
)
|
||||||
|
if(NOT IS_ABSOLUTE ${CMAKE_INSTALL_${dir}})
|
||||||
|
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_${dir}}")
|
||||||
|
else()
|
||||||
|
set(CMAKE_INSTALL_FULL_${dir} "${CMAKE_INSTALL_${dir}}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
86
client/qtkeychain/gnomekeyring.cpp
Normal file
86
client/qtkeychain/gnomekeyring.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "gnomekeyring_p.h"
|
||||||
|
|
||||||
|
const char* GnomeKeyring::GNOME_KEYRING_DEFAULT = NULL;
|
||||||
|
|
||||||
|
bool GnomeKeyring::isAvailable()
|
||||||
|
{
|
||||||
|
const GnomeKeyring& keyring = instance();
|
||||||
|
return keyring.isLoaded() &&
|
||||||
|
keyring.NETWORK_PASSWORD &&
|
||||||
|
keyring.is_available &&
|
||||||
|
keyring.find_password &&
|
||||||
|
keyring.store_password &&
|
||||||
|
keyring.delete_password &&
|
||||||
|
keyring.is_available();
|
||||||
|
}
|
||||||
|
|
||||||
|
GnomeKeyring::gpointer GnomeKeyring::store_network_password(
|
||||||
|
const gchar* keyring,
|
||||||
|
const gchar* display_name,
|
||||||
|
const gchar* user,
|
||||||
|
const gchar* server,
|
||||||
|
const gchar* type,
|
||||||
|
const gchar* password,
|
||||||
|
OperationDoneCallback callback,
|
||||||
|
gpointer data,
|
||||||
|
GDestroyNotify destroy_data )
|
||||||
|
{
|
||||||
|
if ( !isAvailable() )
|
||||||
|
return 0;
|
||||||
|
return instance().store_password( instance().NETWORK_PASSWORD,
|
||||||
|
keyring, display_name, password, callback,
|
||||||
|
data, destroy_data,
|
||||||
|
"user", user,
|
||||||
|
"server", server,
|
||||||
|
"type", type,
|
||||||
|
static_cast<char*>(0) );
|
||||||
|
}
|
||||||
|
|
||||||
|
GnomeKeyring::gpointer GnomeKeyring::find_network_password(
|
||||||
|
const gchar* user, const gchar* server, const gchar* type,
|
||||||
|
OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data )
|
||||||
|
{
|
||||||
|
if ( !isAvailable() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return instance().find_password( instance().NETWORK_PASSWORD,
|
||||||
|
callback, data, destroy_data,
|
||||||
|
"user", user, "server", server, "type", type,
|
||||||
|
static_cast<char*>(0) );
|
||||||
|
}
|
||||||
|
|
||||||
|
GnomeKeyring::gpointer GnomeKeyring::delete_network_password( const gchar* user,
|
||||||
|
const gchar* server,
|
||||||
|
OperationDoneCallback callback,
|
||||||
|
gpointer data,
|
||||||
|
GDestroyNotify destroy_data )
|
||||||
|
{
|
||||||
|
if ( !isAvailable() )
|
||||||
|
return 0;
|
||||||
|
return instance().delete_password( instance().NETWORK_PASSWORD,
|
||||||
|
callback, data, destroy_data,
|
||||||
|
"user", user, "server", server, static_cast<char*>(0) );
|
||||||
|
}
|
||||||
|
|
||||||
|
GnomeKeyring::GnomeKeyring()
|
||||||
|
: QLibrary(QLatin1String("gnome-keyring"), 0)
|
||||||
|
{
|
||||||
|
static const PasswordSchema schema = {
|
||||||
|
ITEM_NETWORK_PASSWORD,
|
||||||
|
{{ "user", ATTRIBUTE_TYPE_STRING },
|
||||||
|
{ "server", ATTRIBUTE_TYPE_STRING },
|
||||||
|
{ "type", ATTRIBUTE_TYPE_STRING },
|
||||||
|
{ 0, static_cast<AttributeType>( 0 ) }}
|
||||||
|
};
|
||||||
|
|
||||||
|
NETWORK_PASSWORD = &schema;
|
||||||
|
is_available = reinterpret_cast<is_available_fn*>( resolve( "gnome_keyring_is_available" ) );
|
||||||
|
find_password = reinterpret_cast<find_password_fn*>( resolve( "gnome_keyring_find_password" ) );
|
||||||
|
store_password = reinterpret_cast<store_password_fn*>( resolve( "gnome_keyring_store_password" ) );
|
||||||
|
delete_password = reinterpret_cast<delete_password_fn*>( resolve( "gnome_keyring_delete_password" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
GnomeKeyring& GnomeKeyring::instance() {
|
||||||
|
static GnomeKeyring keyring;
|
||||||
|
return keyring;
|
||||||
|
}
|
||||||
94
client/qtkeychain/gnomekeyring_p.h
Normal file
94
client/qtkeychain/gnomekeyring_p.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#ifndef QTKEYCHAIN_GNOME_P_H
|
||||||
|
#define QTKEYCHAIN_GNOME_P_H
|
||||||
|
|
||||||
|
#include <QLibrary>
|
||||||
|
|
||||||
|
class GnomeKeyring : private QLibrary {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Result {
|
||||||
|
RESULT_OK,
|
||||||
|
RESULT_DENIED,
|
||||||
|
RESULT_NO_KEYRING_DAEMON,
|
||||||
|
RESULT_ALREADY_UNLOCKED,
|
||||||
|
RESULT_NO_SUCH_KEYRING,
|
||||||
|
RESULT_BAD_ARGUMENTS,
|
||||||
|
RESULT_IO_ERROR,
|
||||||
|
RESULT_CANCELLED,
|
||||||
|
RESULT_KEYRING_ALREADY_EXISTS,
|
||||||
|
RESULT_NO_MATCH
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ItemType {
|
||||||
|
ITEM_GENERIC_SECRET = 0,
|
||||||
|
ITEM_NETWORK_PASSWORD,
|
||||||
|
ITEM_NOTE,
|
||||||
|
ITEM_CHAINED_KEYRING_PASSWORD,
|
||||||
|
ITEM_ENCRYPTION_KEY_PASSWORD,
|
||||||
|
ITEM_PK_STORAGE = 0x100
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AttributeType {
|
||||||
|
ATTRIBUTE_TYPE_STRING,
|
||||||
|
ATTRIBUTE_TYPE_UINT32
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef char gchar;
|
||||||
|
typedef void* gpointer;
|
||||||
|
typedef bool gboolean;
|
||||||
|
typedef struct {
|
||||||
|
ItemType item_type;
|
||||||
|
struct {
|
||||||
|
const gchar* name;
|
||||||
|
AttributeType type;
|
||||||
|
} attributes[32];
|
||||||
|
} PasswordSchema;
|
||||||
|
|
||||||
|
typedef void ( *OperationGetStringCallback )( Result result, bool binary,
|
||||||
|
const char* string, gpointer data );
|
||||||
|
typedef void ( *OperationDoneCallback )( Result result, gpointer data );
|
||||||
|
typedef void ( *GDestroyNotify )( gpointer data );
|
||||||
|
|
||||||
|
static const char* GNOME_KEYRING_DEFAULT;
|
||||||
|
|
||||||
|
static bool isAvailable();
|
||||||
|
|
||||||
|
static gpointer store_network_password( const gchar* keyring, const gchar* display_name,
|
||||||
|
const gchar* user, const gchar* server,
|
||||||
|
const gchar* type, const gchar* password,
|
||||||
|
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data );
|
||||||
|
|
||||||
|
static gpointer find_network_password( const gchar* user, const gchar* server,
|
||||||
|
const gchar* type,
|
||||||
|
OperationGetStringCallback callback,
|
||||||
|
gpointer data, GDestroyNotify destroy_data );
|
||||||
|
|
||||||
|
static gpointer delete_network_password( const gchar* user, const gchar* server,
|
||||||
|
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data );
|
||||||
|
private:
|
||||||
|
GnomeKeyring();
|
||||||
|
|
||||||
|
static GnomeKeyring& instance();
|
||||||
|
|
||||||
|
const PasswordSchema* NETWORK_PASSWORD;
|
||||||
|
typedef gboolean ( is_available_fn )( void );
|
||||||
|
typedef gpointer ( store_password_fn )( const PasswordSchema* schema, const gchar* keyring,
|
||||||
|
const gchar* display_name, const gchar* password,
|
||||||
|
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data,
|
||||||
|
... );
|
||||||
|
typedef gpointer ( find_password_fn )( const PasswordSchema* schema,
|
||||||
|
OperationGetStringCallback callback, gpointer data, GDestroyNotify destroy_data,
|
||||||
|
... );
|
||||||
|
typedef gpointer ( delete_password_fn )( const PasswordSchema* schema,
|
||||||
|
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data,
|
||||||
|
... );
|
||||||
|
|
||||||
|
is_available_fn* is_available;
|
||||||
|
find_password_fn* find_password;
|
||||||
|
store_password_fn* store_password;
|
||||||
|
delete_password_fn* delete_password;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
235
client/qtkeychain/keychain.cpp
Normal file
235
client/qtkeychain/keychain.cpp
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
#include "keychain.h"
|
||||||
|
#include "keychain_p.h"
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
Job::Job( JobPrivate *q, QObject *parent )
|
||||||
|
: QObject( parent )
|
||||||
|
, d ( q ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Job::~Job() {
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Job::service() const {
|
||||||
|
return d->service;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSettings* Job::settings() const {
|
||||||
|
return d->settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::setSettings( QSettings* settings ) {
|
||||||
|
d->settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::start() {
|
||||||
|
QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Job::autoDelete() const {
|
||||||
|
return d->autoDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::setAutoDelete( bool autoDelete ) {
|
||||||
|
d->autoDelete = autoDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Job::insecureFallback() const {
|
||||||
|
return d->insecureFallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::setInsecureFallback( bool insecureFallback ) {
|
||||||
|
d->insecureFallback = insecureFallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::doStart() {
|
||||||
|
JobExecutor::instance()->enqueue( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::emitFinished() {
|
||||||
|
emit finished( this );
|
||||||
|
if ( d->autoDelete )
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::emitFinishedWithError( Error error, const QString& errorString ) {
|
||||||
|
d->error = error;
|
||||||
|
d->errorString = errorString;
|
||||||
|
emitFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::scheduledStart() {
|
||||||
|
d->scheduledStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Error Job::error() const {
|
||||||
|
return d->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Job::errorString() const {
|
||||||
|
return d->errorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::setError( Error error ) {
|
||||||
|
d->error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::setErrorString( const QString& errorString ) {
|
||||||
|
d->errorString = errorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadPasswordJob::ReadPasswordJob( const QString& service, QObject* parent )
|
||||||
|
: Job( new ReadPasswordJobPrivate( service, this ), parent ) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadPasswordJob::~ReadPasswordJob() {
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ReadPasswordJob::textData() const {
|
||||||
|
return QString::fromUtf8( d->data );
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray ReadPasswordJob::binaryData() const {
|
||||||
|
return d->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Job::key() const {
|
||||||
|
return d->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Job::setKey( const QString& key_ ) {
|
||||||
|
d->key = key_;
|
||||||
|
}
|
||||||
|
|
||||||
|
WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent )
|
||||||
|
: Job( new WritePasswordJobPrivate( service, this ), parent ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
WritePasswordJob::~WritePasswordJob() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJob::setBinaryData( const QByteArray& data ) {
|
||||||
|
d->data = data;
|
||||||
|
d->mode = JobPrivate::Binary;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJob::setTextData( const QString& data ) {
|
||||||
|
d->data = data.toUtf8();
|
||||||
|
d->mode = JobPrivate::Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent )
|
||||||
|
: Job( new DeletePasswordJobPrivate( service, this ), parent ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DeletePasswordJob::~DeletePasswordJob() {
|
||||||
|
}
|
||||||
|
|
||||||
|
DeletePasswordJobPrivate::DeletePasswordJobPrivate(const QString &service_, DeletePasswordJob *qq) :
|
||||||
|
JobPrivate(service_, qq) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
JobExecutor::JobExecutor()
|
||||||
|
: QObject( 0 )
|
||||||
|
, m_jobRunning( false ) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobExecutor::enqueue( Job* job ) {
|
||||||
|
m_queue.enqueue( job );
|
||||||
|
startNextIfNoneRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobExecutor::startNextIfNoneRunning() {
|
||||||
|
if ( m_queue.isEmpty() || m_jobRunning )
|
||||||
|
return;
|
||||||
|
QPointer<Job> next;
|
||||||
|
while ( !next && !m_queue.isEmpty() ) {
|
||||||
|
next = m_queue.dequeue();
|
||||||
|
}
|
||||||
|
if ( next ) {
|
||||||
|
connect( next, SIGNAL(finished(QKeychain::Job*)), this, SLOT(jobFinished(QKeychain::Job*)) );
|
||||||
|
connect( next, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) );
|
||||||
|
m_jobRunning = true;
|
||||||
|
next->scheduledStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobExecutor::jobDestroyed( QObject* object ) {
|
||||||
|
Job* job = static_cast<Job*>(object);
|
||||||
|
Q_UNUSED( object ) // for release mode
|
||||||
|
job->disconnect( this );
|
||||||
|
m_jobRunning = false;
|
||||||
|
startNextIfNoneRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobExecutor::jobFinished( Job* job ) {
|
||||||
|
Q_UNUSED( job ) // for release mode
|
||||||
|
job->disconnect( this );
|
||||||
|
m_jobRunning = false;
|
||||||
|
startNextIfNoneRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
JobExecutor* JobExecutor::s_instance = 0;
|
||||||
|
|
||||||
|
JobExecutor* JobExecutor::instance() {
|
||||||
|
if ( !s_instance )
|
||||||
|
s_instance = new JobExecutor;
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadPasswordJobPrivate::ReadPasswordJobPrivate(const QString &service_, ReadPasswordJob *qq) :
|
||||||
|
JobPrivate(service_, qq) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
JobPrivate::JobPrivate(const QString &service_, Job *qq)
|
||||||
|
: q(qq)
|
||||||
|
, mode( Text )
|
||||||
|
, error( NoError )
|
||||||
|
, service( service_ )
|
||||||
|
, autoDelete( true )
|
||||||
|
, insecureFallback( false )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString JobPrivate::modeToString(Mode m)
|
||||||
|
{
|
||||||
|
switch (m) {
|
||||||
|
case Text:
|
||||||
|
return QLatin1String("Text");
|
||||||
|
case Binary:
|
||||||
|
return QLatin1String("Binary");
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled Mode value");
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
JobPrivate::Mode JobPrivate::stringToMode(const QString& s)
|
||||||
|
{
|
||||||
|
if (s == QLatin1String("Text") || s == QLatin1String("1"))
|
||||||
|
return Text;
|
||||||
|
if (s == QLatin1String("Binary") || s == QLatin1String("2"))
|
||||||
|
return Binary;
|
||||||
|
|
||||||
|
qCritical("Unexpected mode string '%s'", qPrintable(s));
|
||||||
|
|
||||||
|
return Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
WritePasswordJobPrivate::WritePasswordJobPrivate(const QString &service_, WritePasswordJob *qq) :
|
||||||
|
JobPrivate(service_, qq) {
|
||||||
|
|
||||||
|
}
|
||||||
271
client/qtkeychain/keychain.h
Normal file
271
client/qtkeychain/keychain.h
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
#ifndef KEYCHAIN_H
|
||||||
|
#define KEYCHAIN_H
|
||||||
|
|
||||||
|
#if !defined(QTKEYCHAIN_NO_EXPORT)
|
||||||
|
#include "qkeychain_export.h"
|
||||||
|
#else
|
||||||
|
#define QKEYCHAIN_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
|
#define QTKEYCHAIN_VERSION 0x000100
|
||||||
|
|
||||||
|
namespace QKeychain {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error codes
|
||||||
|
*/
|
||||||
|
enum Error {
|
||||||
|
NoError=0, /**< No error occurred, operation was successful */
|
||||||
|
EntryNotFound, /**< For the given key no data was found */
|
||||||
|
CouldNotDeleteEntry, /**< Could not delete existing secret data */
|
||||||
|
AccessDeniedByUser, /**< User denied access to keychain */
|
||||||
|
AccessDenied, /**< Access denied for other reasons */
|
||||||
|
NoBackendAvailable, /**< No platform-specific keychain service available */
|
||||||
|
NotImplemented, /**< Not implemented on platform */
|
||||||
|
OtherError /**< Something else went wrong (errorString() might provide details) */
|
||||||
|
};
|
||||||
|
|
||||||
|
class JobExecutor;
|
||||||
|
class JobPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Abstract base class for all QKeychain jobs.
|
||||||
|
*/
|
||||||
|
class QKEYCHAIN_EXPORT Job : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
~Job();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The QSettings instance used as plaintext storage if insecureFallback() is true.
|
||||||
|
* @see setSettings()
|
||||||
|
* @see insecureFallback()
|
||||||
|
*/
|
||||||
|
QSettings* settings() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Set the QSettings instance that will be used as plaintext storage if insecureFallback() is true.
|
||||||
|
* @see settings()
|
||||||
|
* @see insecureFallback()
|
||||||
|
*/
|
||||||
|
void setSettings( QSettings* settings );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this method to start the job.
|
||||||
|
* Typically you want to connect some slot to the finished() signal first:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* SomeClass::startJob()
|
||||||
|
* {
|
||||||
|
* connect(job, &Job::finished, this, &SomeClass::slotJobFinished);
|
||||||
|
* job->start();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* SomeClass::slotJobFinished(Job *job)
|
||||||
|
* {
|
||||||
|
* if (job->error()) {
|
||||||
|
* // handle error
|
||||||
|
* } else {
|
||||||
|
* // do job-specific stuff
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* @see finished()
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
QString service() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note Call this method only after finished() has been emitted.
|
||||||
|
* @return The error code of the job (0 if no error).
|
||||||
|
*/
|
||||||
|
Error error() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return An error message that might provide details if error() returns OtherError.
|
||||||
|
*/
|
||||||
|
QString errorString() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this job autodeletes itself once finished() has been emitted. Default is true.
|
||||||
|
* @see setAutoDelete()
|
||||||
|
*/
|
||||||
|
bool autoDelete() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this job should autodelete itself once finished() has been emitted.
|
||||||
|
* @see autoDelete()
|
||||||
|
*/
|
||||||
|
void setAutoDelete( bool autoDelete );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Whether this job will use plaintext storage on unsupported platforms. Default is false.
|
||||||
|
* @see setInsecureFallback()
|
||||||
|
*/
|
||||||
|
bool insecureFallback() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set whether this job should use plaintext storage on unsupported platforms.
|
||||||
|
* @see insecureFallback()
|
||||||
|
*/
|
||||||
|
void setInsecureFallback( bool insecureFallback );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The string used as key by this job.
|
||||||
|
* @see setKey()
|
||||||
|
*/
|
||||||
|
QString key() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the @p key that this job will use to read or write data from/to the keychain.
|
||||||
|
* The key can be an empty string.
|
||||||
|
* @see key()
|
||||||
|
*/
|
||||||
|
void setKey( const QString& key );
|
||||||
|
|
||||||
|
void emitFinished();
|
||||||
|
void emitFinishedWithError(Error, const QString& errorString);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/**
|
||||||
|
* Emitted when this job is finished.
|
||||||
|
* You can connect to this signal to be notified about the job's completion.
|
||||||
|
* @see start()
|
||||||
|
*/
|
||||||
|
void finished( QKeychain::Job* );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit Job( JobPrivate *q, QObject* parent=0 );
|
||||||
|
Q_INVOKABLE void doStart();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setError( Error error );
|
||||||
|
void setErrorString( const QString& errorString );
|
||||||
|
|
||||||
|
void scheduledStart();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
JobPrivate* const d;
|
||||||
|
|
||||||
|
friend class JobExecutor;
|
||||||
|
friend class JobPrivate;
|
||||||
|
friend class ReadPasswordJobPrivate;
|
||||||
|
friend class WritePasswordJobPrivate;
|
||||||
|
friend class DeletePasswordJobPrivate;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReadPasswordJobPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Job for reading secrets from the keychain.
|
||||||
|
* You can use a ReadPasswordJob to read passwords or binary data from the keychain.
|
||||||
|
* This job requires a "service" string, which is basically a namespace of keys within the keychain.
|
||||||
|
* This means that you can read all the pairs <key, secret> stored in the same service string.
|
||||||
|
*/
|
||||||
|
class QKEYCHAIN_EXPORT ReadPasswordJob : public Job {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new ReadPasswordJob.
|
||||||
|
* @param service The service string used by this job (can be empty).
|
||||||
|
* @param parent The parent of this job.
|
||||||
|
*/
|
||||||
|
explicit ReadPasswordJob( const QString& service, QObject* parent=0 );
|
||||||
|
~ReadPasswordJob();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The binary data stored as value of this job's key().
|
||||||
|
* @see Job::key()
|
||||||
|
*/
|
||||||
|
QByteArray binaryData() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The string stored as value of this job's key().
|
||||||
|
* @see Job::key()
|
||||||
|
* @warning Returns meaningless data if the data was stored as binary data.
|
||||||
|
* @see WritePasswordJob::setTextData()
|
||||||
|
*/
|
||||||
|
QString textData() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class QKeychain::ReadPasswordJobPrivate;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WritePasswordJobPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Job for writing secrets to the keychain.
|
||||||
|
* You can use a WritePasswordJob to store passwords or binary data in the keychain.
|
||||||
|
* This job requires a "service" string, which is basically a namespace of keys within the keychain.
|
||||||
|
* This means that you can store different pairs <key, secret> under the same service string.
|
||||||
|
*/
|
||||||
|
class QKEYCHAIN_EXPORT WritePasswordJob : public Job {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new WritePasswordJob.
|
||||||
|
* @param service The service string used by this job (can be empty).
|
||||||
|
* @param parent The parent of this job.
|
||||||
|
*/
|
||||||
|
explicit WritePasswordJob( const QString& service, QObject* parent=0 );
|
||||||
|
~WritePasswordJob();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the @p data that the job will store in the keychain as binary data.
|
||||||
|
* @warning setBinaryData() and setTextData() are mutually exclusive.
|
||||||
|
*/
|
||||||
|
void setBinaryData( const QByteArray& data );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the @p data that the job will store in the keychain as string.
|
||||||
|
* Typically @p data is a password.
|
||||||
|
* @warning setBinaryData() and setTextData() are mutually exclusive.
|
||||||
|
*/
|
||||||
|
void setTextData( const QString& data );
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class QKeychain::WritePasswordJobPrivate;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeletePasswordJobPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Job for deleting secrets from the keychain.
|
||||||
|
* You can use a DeletePasswordJob to delete passwords or binary data from the keychain.
|
||||||
|
* This job requires a "service" string, which is basically a namespace of keys within the keychain.
|
||||||
|
* This means that you can delete all the pairs <key, secret> stored in the same service string.
|
||||||
|
*/
|
||||||
|
class QKEYCHAIN_EXPORT DeletePasswordJob : public Job {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new DeletePasswordJob.
|
||||||
|
* @param service The service string used by this job (can be empty).
|
||||||
|
* @param parent The parent of this job.
|
||||||
|
*/
|
||||||
|
explicit DeletePasswordJob( const QString& service, QObject* parent=0 );
|
||||||
|
~DeletePasswordJob();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class QKeychain::DeletePasswordJobPrivate;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QtKeychain
|
||||||
|
|
||||||
|
#endif
|
||||||
146
client/qtkeychain/keychain_ios.mm
Normal file
146
client/qtkeychain/keychain_ios.mm
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "keychain_p.h"
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Security/Security.h>
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
struct ErrorDescription
|
||||||
|
{
|
||||||
|
QKeychain::Error code;
|
||||||
|
QString message;
|
||||||
|
|
||||||
|
ErrorDescription(QKeychain::Error code, const QString &message)
|
||||||
|
: code(code), message(message) {}
|
||||||
|
|
||||||
|
static ErrorDescription fromStatus(OSStatus status)
|
||||||
|
{
|
||||||
|
switch(status) {
|
||||||
|
case errSecSuccess:
|
||||||
|
return ErrorDescription(QKeychain::NoError, Job::tr("No error"));
|
||||||
|
case errSecItemNotFound:
|
||||||
|
return ErrorDescription(QKeychain::EntryNotFound, Job::tr("The specified item could not be found in the keychain"));
|
||||||
|
case errSecUserCanceled:
|
||||||
|
return ErrorDescription(QKeychain::AccessDeniedByUser, Job::tr("User canceled the operation"));
|
||||||
|
case errSecInteractionNotAllowed:
|
||||||
|
return ErrorDescription(QKeychain::AccessDenied, Job::tr("User interaction is not allowed"));
|
||||||
|
case errSecNotAvailable:
|
||||||
|
return ErrorDescription(QKeychain::AccessDenied, Job::tr("No keychain is available. You may need to restart your computer"));
|
||||||
|
case errSecAuthFailed:
|
||||||
|
return ErrorDescription(QKeychain::AccessDenied, Job::tr("The user name or passphrase you entered is not correct"));
|
||||||
|
case errSecVerifyFailed:
|
||||||
|
return ErrorDescription(QKeychain::AccessDenied, Job::tr("A cryptographic verification failure has occurred"));
|
||||||
|
case errSecUnimplemented:
|
||||||
|
return ErrorDescription(QKeychain::NotImplemented, Job::tr("Function or operation not implemented"));
|
||||||
|
case errSecIO:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("I/O error"));
|
||||||
|
case errSecOpWr:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("Already open with with write permission"));
|
||||||
|
case errSecParam:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("Invalid parameters passed to a function"));
|
||||||
|
case errSecAllocate:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("Failed to allocate memory"));
|
||||||
|
case errSecBadReq:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("Bad parameter or invalid state for operation"));
|
||||||
|
case errSecInternalComponent:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("An internal component failed"));
|
||||||
|
case errSecDuplicateItem:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("The specified item already exists in the keychain"));
|
||||||
|
case errSecDecode:
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("Unable to decode the provided data"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorDescription(QKeychain::OtherError, Job::tr("Unknown error"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::scheduledStart()
|
||||||
|
{
|
||||||
|
NSDictionary *const query = @{
|
||||||
|
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||||
|
(__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
|
||||||
|
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||||
|
(__bridge id) kSecReturnData: @YES,
|
||||||
|
};
|
||||||
|
|
||||||
|
CFTypeRef dataRef = nil;
|
||||||
|
const OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, &dataRef);
|
||||||
|
|
||||||
|
data.clear();
|
||||||
|
mode = Binary;
|
||||||
|
|
||||||
|
if (status == errSecSuccess) {
|
||||||
|
if (dataRef)
|
||||||
|
data = QByteArray::fromCFData((CFDataRef) dataRef);
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
} else {
|
||||||
|
const ErrorDescription error = ErrorDescription::fromStatus(status);
|
||||||
|
q->emitFinishedWithError(error.code, Job::tr("Could not retrieve private key from keystore: %1").arg(error.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataRef)
|
||||||
|
[dataRef release];
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJobPrivate::scheduledStart()
|
||||||
|
{
|
||||||
|
NSDictionary *const query = @{
|
||||||
|
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||||
|
(__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
|
||||||
|
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef) query, nil);
|
||||||
|
|
||||||
|
if (status == errSecSuccess) {
|
||||||
|
NSDictionary *const update = @{
|
||||||
|
(__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(),
|
||||||
|
};
|
||||||
|
|
||||||
|
status = SecItemUpdate((__bridge CFDictionaryRef) query, (__bridge CFDictionaryRef) update);
|
||||||
|
} else {
|
||||||
|
NSDictionary *const insert = @{
|
||||||
|
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||||
|
(__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
|
||||||
|
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||||
|
(__bridge id) kSecValueData: (__bridge NSData *) data.toCFData(),
|
||||||
|
};
|
||||||
|
|
||||||
|
status = SecItemAdd((__bridge CFDictionaryRef) insert, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == errSecSuccess) {
|
||||||
|
q->emitFinished();
|
||||||
|
} else {
|
||||||
|
const ErrorDescription error = ErrorDescription::fromStatus(status);
|
||||||
|
q->emitFinishedWithError(error.code, tr("Could not store data in settings: %1").arg(error.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePasswordJobPrivate::scheduledStart()
|
||||||
|
{
|
||||||
|
const NSDictionary *const query = @{
|
||||||
|
(__bridge id) kSecClass: (__bridge id) kSecClassGenericPassword,
|
||||||
|
(__bridge id) kSecAttrService: (__bridge NSString *) service.toCFString(),
|
||||||
|
(__bridge id) kSecAttrAccount: (__bridge NSString *) key.toCFString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const OSStatus status = SecItemDelete((__bridge CFDictionaryRef) query);
|
||||||
|
|
||||||
|
if (status == errSecSuccess) {
|
||||||
|
q->emitFinished();
|
||||||
|
} else {
|
||||||
|
const ErrorDescription error = ErrorDescription::fromStatus(status);
|
||||||
|
q->emitFinishedWithError(error.code, Job::tr("Could not remove private key from keystore: %1").arg(error.message));
|
||||||
|
}
|
||||||
|
}
|
||||||
163
client/qtkeychain/keychain_mac.cpp
Normal file
163
client/qtkeychain/keychain_mac.cpp
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
#include "keychain_p.h"
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <Security/Security.h>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Releaser {
|
||||||
|
explicit Releaser( const T& v ) : value( v ) {}
|
||||||
|
~Releaser() {
|
||||||
|
CFRelease( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
const T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static QString strForStatus( OSStatus os ) {
|
||||||
|
const Releaser<CFStringRef> str( SecCopyErrorMessageString( os, 0 ) );
|
||||||
|
const char * const buf = CFStringGetCStringPtr( str.value, kCFStringEncodingUTF8 );
|
||||||
|
if ( !buf )
|
||||||
|
return QObject::tr( "OS X Keychain error (OSStatus %1)" ).arg( os );
|
||||||
|
return QObject::tr( "%1 (OSStatus %2)" )
|
||||||
|
.arg( QString::fromUtf8( buf, strlen( buf ) ) ).arg( os );
|
||||||
|
}
|
||||||
|
|
||||||
|
static OSStatus readPw( QByteArray* pw,
|
||||||
|
const QString& service,
|
||||||
|
const QString& account,
|
||||||
|
SecKeychainItemRef* ref ) {
|
||||||
|
Q_ASSERT( pw );
|
||||||
|
pw->clear();
|
||||||
|
const QByteArray serviceData = service.toUtf8();
|
||||||
|
const QByteArray accountData = account.toUtf8();
|
||||||
|
|
||||||
|
void* data = 0;
|
||||||
|
UInt32 len = 0;
|
||||||
|
|
||||||
|
const OSStatus ret = SecKeychainFindGenericPassword( NULL, // default keychain
|
||||||
|
serviceData.size(),
|
||||||
|
serviceData.constData(),
|
||||||
|
accountData.size(),
|
||||||
|
accountData.constData(),
|
||||||
|
&len,
|
||||||
|
&data,
|
||||||
|
ref );
|
||||||
|
if ( ret == noErr ) {
|
||||||
|
*pw = QByteArray( reinterpret_cast<const char*>( data ), len );
|
||||||
|
const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data );
|
||||||
|
if ( ret2 != noErr )
|
||||||
|
qWarning() << "Could not free item content: " << strForStatus( ret2 );
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::scheduledStart()
|
||||||
|
{
|
||||||
|
QString errorString;
|
||||||
|
Error error = NoError;
|
||||||
|
const OSStatus ret = readPw( &data, q->service(), q->key(), 0 );
|
||||||
|
|
||||||
|
switch ( ret ) {
|
||||||
|
case noErr:
|
||||||
|
break;
|
||||||
|
case errSecItemNotFound:
|
||||||
|
errorString = tr("Password not found");
|
||||||
|
error = EntryNotFound;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorString = strForStatus( ret );
|
||||||
|
error = OtherError;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
q->emitFinishedWithError( error, errorString );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static QKeychain::Error deleteEntryImpl( const QString& service, const QString& account, QString* err ) {
|
||||||
|
SecKeychainItemRef ref;
|
||||||
|
QByteArray pw;
|
||||||
|
const OSStatus ret1 = readPw( &pw, service, account, &ref );
|
||||||
|
if ( ret1 == errSecItemNotFound )
|
||||||
|
return NoError; // No item stored, we're done
|
||||||
|
if ( ret1 != noErr ) {
|
||||||
|
*err = strForStatus( ret1 );
|
||||||
|
//TODO map error code, set errstr
|
||||||
|
return OtherError;
|
||||||
|
}
|
||||||
|
const Releaser<SecKeychainItemRef> releaser( ref );
|
||||||
|
|
||||||
|
const OSStatus ret2 = SecKeychainItemDelete( ref );
|
||||||
|
|
||||||
|
if ( ret2 == noErr )
|
||||||
|
return NoError;
|
||||||
|
//TODO map error code
|
||||||
|
*err = strForStatus( ret2 );
|
||||||
|
return CouldNotDeleteEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QKeychain::Error writeEntryImpl( const QString& service,
|
||||||
|
const QString& account,
|
||||||
|
const QByteArray& data,
|
||||||
|
QString* err ) {
|
||||||
|
Q_ASSERT( err );
|
||||||
|
err->clear();
|
||||||
|
const QByteArray serviceData = service.toUtf8();
|
||||||
|
const QByteArray accountData = account.toUtf8();
|
||||||
|
const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain
|
||||||
|
serviceData.size(),
|
||||||
|
serviceData.constData(),
|
||||||
|
accountData.size(),
|
||||||
|
accountData.constData(),
|
||||||
|
data.size(),
|
||||||
|
data.constData(),
|
||||||
|
NULL //item reference
|
||||||
|
);
|
||||||
|
if ( ret != noErr ) {
|
||||||
|
switch ( ret ) {
|
||||||
|
case errSecDuplicateItem:
|
||||||
|
{
|
||||||
|
Error derr = deleteEntryImpl( service, account, err );
|
||||||
|
if ( derr != NoError )
|
||||||
|
return CouldNotDeleteEntry;
|
||||||
|
else
|
||||||
|
return writeEntryImpl( service, account, data, err );
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
*err = strForStatus( ret );
|
||||||
|
return OtherError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJobPrivate::scheduledStart()
|
||||||
|
{
|
||||||
|
QString errorString;
|
||||||
|
Error error = NoError;
|
||||||
|
|
||||||
|
error = writeEntryImpl( q->service(), key, data, &errorString );
|
||||||
|
q->emitFinishedWithError( error, errorString );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePasswordJobPrivate::scheduledStart()
|
||||||
|
{
|
||||||
|
QString errorString;
|
||||||
|
Error error = NoError;
|
||||||
|
|
||||||
|
const Error derr = deleteEntryImpl( q->service(), key, &errorString );
|
||||||
|
if ( derr != NoError )
|
||||||
|
error = CouldNotDeleteEntry;
|
||||||
|
q->emitFinishedWithError( error, errorString );
|
||||||
|
}
|
||||||
167
client/qtkeychain/keychain_p.h
Normal file
167
client/qtkeychain/keychain_p.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
#ifndef KEYCHAIN_P_H
|
||||||
|
#define KEYCHAIN_P_H
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
|
||||||
|
|
||||||
|
#include <QDBusPendingCallWatcher>
|
||||||
|
|
||||||
|
#include "kwallet_interface.h"
|
||||||
|
#else
|
||||||
|
|
||||||
|
class QDBusPendingCallWatcher;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "keychain.h"
|
||||||
|
|
||||||
|
namespace QKeychain {
|
||||||
|
|
||||||
|
class JobExecutor;
|
||||||
|
|
||||||
|
class JobPrivate : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Mode {
|
||||||
|
Text,
|
||||||
|
Binary
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual void scheduledStart() = 0;
|
||||||
|
|
||||||
|
static QString modeToString(Mode m);
|
||||||
|
static Mode stringToMode(const QString& s);
|
||||||
|
|
||||||
|
Job* const q;
|
||||||
|
Mode mode;
|
||||||
|
QByteArray data;
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
|
||||||
|
org::kde::KWallet* iface;
|
||||||
|
int walletHandle;
|
||||||
|
|
||||||
|
static void gnomeKeyring_readCb( int result, const char* string, JobPrivate* data );
|
||||||
|
static void gnomeKeyring_writeCb( int result, JobPrivate* self );
|
||||||
|
|
||||||
|
virtual void fallbackOnError(const QDBusError& err) = 0;
|
||||||
|
|
||||||
|
protected Q_SLOTS:
|
||||||
|
void kwalletWalletFound( QDBusPendingCallWatcher* watcher );
|
||||||
|
virtual void kwalletFinished( QDBusPendingCallWatcher* watcher );
|
||||||
|
virtual void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
|
||||||
|
#else
|
||||||
|
void kwalletWalletFound( QDBusPendingCallWatcher* ) {}
|
||||||
|
virtual void kwalletFinished( QDBusPendingCallWatcher* ) {}
|
||||||
|
virtual void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
JobPrivate( const QString& service_, Job *q );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QKeychain::Error error;
|
||||||
|
QString errorString;
|
||||||
|
QString service;
|
||||||
|
bool autoDelete;
|
||||||
|
bool insecureFallback;
|
||||||
|
QPointer<QSettings> settings;
|
||||||
|
QString key;
|
||||||
|
|
||||||
|
friend class Job;
|
||||||
|
friend class JobExecutor;
|
||||||
|
friend class ReadPasswordJob;
|
||||||
|
friend class WritePasswordJob;
|
||||||
|
friend class PlainTextStore;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReadPasswordJobPrivate : public JobPrivate {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ReadPasswordJobPrivate( const QString &service_, ReadPasswordJob* qq );
|
||||||
|
void scheduledStart();
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
|
||||||
|
void fallbackOnError(const QDBusError& err);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
|
||||||
|
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher );
|
||||||
|
void kwalletFinished( QDBusPendingCallWatcher* watcher );
|
||||||
|
#else //moc's too dumb to respect above macros, so just define empty slot implementations
|
||||||
|
private Q_SLOTS:
|
||||||
|
void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
|
||||||
|
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {}
|
||||||
|
void kwalletFinished( QDBusPendingCallWatcher* ) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
friend class ReadPasswordJob;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WritePasswordJobPrivate : public JobPrivate {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit WritePasswordJobPrivate( const QString &service_, WritePasswordJob* qq );
|
||||||
|
void scheduledStart();
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
|
||||||
|
void fallbackOnError(const QDBusError& err);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
friend class WritePasswordJob;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DeletePasswordJobPrivate : public JobPrivate {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DeletePasswordJobPrivate( const QString &service_, DeletePasswordJob* qq );
|
||||||
|
|
||||||
|
void scheduledStart();
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
|
||||||
|
void fallbackOnError(const QDBusError& err);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void doStart();
|
||||||
|
|
||||||
|
friend class DeletePasswordJob;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JobExecutor : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
static JobExecutor* instance();
|
||||||
|
|
||||||
|
void enqueue( Job* job );
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit JobExecutor();
|
||||||
|
void startNextIfNoneRunning();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void jobFinished( QKeychain::Job* );
|
||||||
|
void jobDestroyed( QObject* object );
|
||||||
|
|
||||||
|
private:
|
||||||
|
static JobExecutor* s_instance;
|
||||||
|
QQueue<QPointer<Job> > m_queue;
|
||||||
|
bool m_jobRunning;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KEYCHAIN_P_H
|
||||||
586
client/qtkeychain/keychain_unix.cpp
Normal file
586
client/qtkeychain/keychain_unix.cpp
Normal file
@ -0,0 +1,586 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
#include "keychain_p.h"
|
||||||
|
#include "gnomekeyring_p.h"
|
||||||
|
#include "libsecret_p.h"
|
||||||
|
#include "plaintextstore_p.h"
|
||||||
|
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
enum KeyringBackend {
|
||||||
|
Backend_LibSecretKeyring,
|
||||||
|
Backend_GnomeKeyring,
|
||||||
|
Backend_Kwallet4,
|
||||||
|
Backend_Kwallet5
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DesktopEnvironment {
|
||||||
|
DesktopEnv_Gnome,
|
||||||
|
DesktopEnv_Kde4,
|
||||||
|
DesktopEnv_Plasma5,
|
||||||
|
DesktopEnv_Unity,
|
||||||
|
DesktopEnv_Xfce,
|
||||||
|
DesktopEnv_Other
|
||||||
|
};
|
||||||
|
|
||||||
|
// the following detection algorithm is derived from chromium,
|
||||||
|
// licensed under BSD, see base/nix/xdg_util.cc
|
||||||
|
|
||||||
|
static DesktopEnvironment getKdeVersion() {
|
||||||
|
QByteArray value = qgetenv("KDE_SESSION_VERSION");
|
||||||
|
if ( value == "5" ) {
|
||||||
|
return DesktopEnv_Plasma5;
|
||||||
|
} else if (value == "4" ) {
|
||||||
|
return DesktopEnv_Kde4;
|
||||||
|
} else {
|
||||||
|
// most likely KDE3
|
||||||
|
return DesktopEnv_Other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DesktopEnvironment detectDesktopEnvironment() {
|
||||||
|
QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
|
||||||
|
if ( xdgCurrentDesktop == "GNOME" ) {
|
||||||
|
return DesktopEnv_Gnome;
|
||||||
|
} else if ( xdgCurrentDesktop == "Unity" ) {
|
||||||
|
return DesktopEnv_Unity;
|
||||||
|
} else if ( xdgCurrentDesktop == "KDE" ) {
|
||||||
|
return getKdeVersion();
|
||||||
|
} else if ( xdgCurrentDesktop == "XFCE" ) {
|
||||||
|
return DesktopEnv_Xfce;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
|
||||||
|
if ( desktopSession == "gnome" ) {
|
||||||
|
return DesktopEnv_Gnome;
|
||||||
|
} else if ( desktopSession == "kde" ) {
|
||||||
|
return getKdeVersion();
|
||||||
|
} else if ( desktopSession == "kde4" ) {
|
||||||
|
return DesktopEnv_Kde4;
|
||||||
|
} else if ( desktopSession.contains("xfce") || desktopSession == "xubuntu" ) {
|
||||||
|
return DesktopEnv_Xfce;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty() ) {
|
||||||
|
return DesktopEnv_Gnome;
|
||||||
|
} else if ( !qgetenv("KDE_FULL_SESSION").isEmpty() ) {
|
||||||
|
return getKdeVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
return DesktopEnv_Other;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isKwallet5Available()
|
||||||
|
{
|
||||||
|
if (!QDBusConnection::sessionBus().isConnected())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
org::kde::KWallet iface(
|
||||||
|
QLatin1String("org.kde.kwalletd5"),
|
||||||
|
QLatin1String("/modules/kwalletd5"),
|
||||||
|
QDBusConnection::sessionBus());
|
||||||
|
|
||||||
|
// At this point iface.isValid() can return false even though the
|
||||||
|
// interface is activatable by making a call. Hence we check whether
|
||||||
|
// a wallet can be opened.
|
||||||
|
|
||||||
|
iface.setTimeout(500);
|
||||||
|
QDBusMessage reply = iface.call(QStringLiteral("networkWallet"));
|
||||||
|
return reply.type() == QDBusMessage::ReplyMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KeyringBackend detectKeyringBackend()
|
||||||
|
{
|
||||||
|
/* The secret service dbus api, accessible through libsecret, is supposed
|
||||||
|
* to unify password services.
|
||||||
|
*
|
||||||
|
* Unfortunately at the time of Kubuntu 18.04 the secret service backend
|
||||||
|
* in KDE is gnome-keyring-daemon - using it has several complications:
|
||||||
|
* - the default collection isn't opened on session start, so users need
|
||||||
|
* to manually unlock it when the first application uses it
|
||||||
|
* - it's separate from the kwallet5 keyring, so switching to it means the
|
||||||
|
* existing keyring data can't be accessed anymore
|
||||||
|
*
|
||||||
|
* Thus we still prefer kwallet backends on KDE even if libsecret is
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch (detectDesktopEnvironment()) {
|
||||||
|
case DesktopEnv_Kde4:
|
||||||
|
return Backend_Kwallet4;
|
||||||
|
|
||||||
|
case DesktopEnv_Plasma5:
|
||||||
|
if (isKwallet5Available()) {
|
||||||
|
return Backend_Kwallet5;
|
||||||
|
}
|
||||||
|
if (LibSecretKeyring::isAvailable()) {
|
||||||
|
return Backend_LibSecretKeyring;
|
||||||
|
}
|
||||||
|
if (GnomeKeyring::isAvailable()) {
|
||||||
|
return Backend_GnomeKeyring;
|
||||||
|
}
|
||||||
|
// During startup the keychain backend might just not have started yet
|
||||||
|
return Backend_Kwallet5;
|
||||||
|
|
||||||
|
case DesktopEnv_Gnome:
|
||||||
|
case DesktopEnv_Unity:
|
||||||
|
case DesktopEnv_Xfce:
|
||||||
|
case DesktopEnv_Other:
|
||||||
|
default:
|
||||||
|
if (LibSecretKeyring::isAvailable()) {
|
||||||
|
return Backend_LibSecretKeyring;
|
||||||
|
}
|
||||||
|
if (GnomeKeyring::isAvailable()) {
|
||||||
|
return Backend_GnomeKeyring;
|
||||||
|
}
|
||||||
|
if (isKwallet5Available()) {
|
||||||
|
return Backend_Kwallet5;
|
||||||
|
}
|
||||||
|
// During startup the keychain backend might just not have started yet
|
||||||
|
//
|
||||||
|
// This doesn't need to be libsecret because LibSecretKeyring::isAvailable()
|
||||||
|
// only fails if the libsecret shared library couldn't be loaded. In contrast
|
||||||
|
// to that GnomeKeyring::isAvailable() can return false if the shared library
|
||||||
|
// *was* loaded but its libgnome_keyring::is_available() returned false.
|
||||||
|
//
|
||||||
|
// In the future there should be a difference between "API available" and
|
||||||
|
// "keychain available".
|
||||||
|
return Backend_GnomeKeyring;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static KeyringBackend getKeyringBackend()
|
||||||
|
{
|
||||||
|
static KeyringBackend backend = detectKeyringBackend();
|
||||||
|
return backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kwalletReadPasswordScheduledStartImpl(const char * service, const char * path, ReadPasswordJobPrivate * priv) {
|
||||||
|
if ( QDBusConnection::sessionBus().isConnected() )
|
||||||
|
{
|
||||||
|
priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
|
||||||
|
const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
|
||||||
|
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
|
||||||
|
priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// D-Bus is not reachable so none can tell us something about KWalletd
|
||||||
|
QDBusError err( QDBusError::NoServer, ReadPasswordJobPrivate::tr("D-Bus is not running") );
|
||||||
|
priv->fallbackOnError( err );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::scheduledStart() {
|
||||||
|
switch ( getKeyringBackend() ) {
|
||||||
|
case Backend_LibSecretKeyring: {
|
||||||
|
if ( !LibSecretKeyring::findPassword(key, q->service(), this) ) {
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Unknown error") );
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Backend_GnomeKeyring:
|
||||||
|
this->mode = JobPrivate::Text;
|
||||||
|
if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(),
|
||||||
|
q->service().toUtf8().constData(),
|
||||||
|
"plaintext",
|
||||||
|
reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
|
||||||
|
this, 0 ) )
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Unknown error") );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Backend_Kwallet4:
|
||||||
|
kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd", "/modules/kwalletd", this);
|
||||||
|
break;
|
||||||
|
case Backend_Kwallet5:
|
||||||
|
kwalletReadPasswordScheduledStartImpl("org.kde.kwalletd5", "/modules/kwalletd5", this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobPrivate::kwalletWalletFound(QDBusPendingCallWatcher *watcher)
|
||||||
|
{
|
||||||
|
watcher->deleteLater();
|
||||||
|
const QDBusPendingReply<QString> reply = *watcher;
|
||||||
|
const QDBusPendingReply<int> pendingReply = iface->open( reply.value(), 0, q->service() );
|
||||||
|
QDBusPendingCallWatcher* pendingWatcher = new QDBusPendingCallWatcher( pendingReply, this );
|
||||||
|
connect( pendingWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
|
||||||
|
this, SLOT(kwalletOpenFinished(QDBusPendingCallWatcher*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static QPair<Error, QString> mapGnomeKeyringError( int result )
|
||||||
|
{
|
||||||
|
Q_ASSERT( result != GnomeKeyring::RESULT_OK );
|
||||||
|
|
||||||
|
switch ( result ) {
|
||||||
|
case GnomeKeyring::RESULT_DENIED:
|
||||||
|
return qMakePair( AccessDenied, QObject::tr("Access to keychain denied") );
|
||||||
|
case GnomeKeyring::RESULT_NO_KEYRING_DAEMON:
|
||||||
|
return qMakePair( NoBackendAvailable, QObject::tr("No keyring daemon") );
|
||||||
|
case GnomeKeyring::RESULT_ALREADY_UNLOCKED:
|
||||||
|
return qMakePair( OtherError, QObject::tr("Already unlocked") );
|
||||||
|
case GnomeKeyring::RESULT_NO_SUCH_KEYRING:
|
||||||
|
return qMakePair( OtherError, QObject::tr("No such keyring") );
|
||||||
|
case GnomeKeyring::RESULT_BAD_ARGUMENTS:
|
||||||
|
return qMakePair( OtherError, QObject::tr("Bad arguments") );
|
||||||
|
case GnomeKeyring::RESULT_IO_ERROR:
|
||||||
|
return qMakePair( OtherError, QObject::tr("I/O error") );
|
||||||
|
case GnomeKeyring::RESULT_CANCELLED:
|
||||||
|
return qMakePair( OtherError, QObject::tr("Cancelled") );
|
||||||
|
case GnomeKeyring::RESULT_KEYRING_ALREADY_EXISTS:
|
||||||
|
return qMakePair( OtherError, QObject::tr("Keyring already exists") );
|
||||||
|
case GnomeKeyring::RESULT_NO_MATCH:
|
||||||
|
return qMakePair( EntryNotFound, QObject::tr("No match") );
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qMakePair( OtherError, QObject::tr("Unknown error") );
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobPrivate::gnomeKeyring_readCb( int result, const char* string, JobPrivate* self )
|
||||||
|
{
|
||||||
|
if ( result == GnomeKeyring::RESULT_OK ) {
|
||||||
|
if (self->mode == JobPrivate::Text)
|
||||||
|
self->data = QByteArray(string);
|
||||||
|
else
|
||||||
|
self->data = QByteArray::fromBase64(string);
|
||||||
|
|
||||||
|
self->q->emitFinished();
|
||||||
|
} else if (self->mode == JobPrivate::Text) {
|
||||||
|
self->mode = JobPrivate::Binary;
|
||||||
|
if ( !GnomeKeyring::find_network_password( self->key.toUtf8().constData(),
|
||||||
|
self->q->service().toUtf8().constData(),
|
||||||
|
"base64",
|
||||||
|
reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &JobPrivate::gnomeKeyring_readCb ),
|
||||||
|
self, 0 ) )
|
||||||
|
self->q->emitFinishedWithError( OtherError, tr("Unknown error") );
|
||||||
|
} else {
|
||||||
|
const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
|
||||||
|
self->q->emitFinishedWithError( errorResult.first, errorResult.second );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::fallbackOnError(const QDBusError& err )
|
||||||
|
{
|
||||||
|
PlainTextStore plainTextStore( q->service(), q->settings() );
|
||||||
|
|
||||||
|
if ( q->insecureFallback() && plainTextStore.contains( key ) ) {
|
||||||
|
mode = plainTextStore.readMode( key );
|
||||||
|
data = plainTextStore.readData( key );
|
||||||
|
|
||||||
|
if ( plainTextStore.error() != NoError )
|
||||||
|
q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
|
||||||
|
else
|
||||||
|
q->emitFinished();
|
||||||
|
} else {
|
||||||
|
if ( err.type() == QDBusError::ServiceUnknown ) //KWalletd not running
|
||||||
|
q->emitFinishedWithError( NoBackendAvailable, tr("No keychain service available") );
|
||||||
|
else
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
const QDBusPendingReply<int> reply = *watcher;
|
||||||
|
|
||||||
|
if ( reply.isError() ) {
|
||||||
|
fallbackOnError( reply.error() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlainTextStore plainTextStore( q->service(), q->settings() );
|
||||||
|
|
||||||
|
if ( plainTextStore.contains( key ) ) {
|
||||||
|
// We previously stored data in the insecure QSettings, but now have KWallet available.
|
||||||
|
// Do the migration
|
||||||
|
|
||||||
|
data = plainTextStore.readData( key );
|
||||||
|
const WritePasswordJobPrivate::Mode mode = plainTextStore.readMode( key );
|
||||||
|
plainTextStore.remove( key );
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
|
||||||
|
|
||||||
|
WritePasswordJob* j = new WritePasswordJob( q->service(), 0 );
|
||||||
|
j->setSettings( q->settings() );
|
||||||
|
j->setKey( key );
|
||||||
|
j->setAutoDelete( true );
|
||||||
|
if ( mode == WritePasswordJobPrivate::Binary )
|
||||||
|
j->setBinaryData( data );
|
||||||
|
else if ( mode == WritePasswordJobPrivate::Text )
|
||||||
|
j->setTextData( QString::fromUtf8( data ) );
|
||||||
|
else
|
||||||
|
Q_ASSERT( false );
|
||||||
|
|
||||||
|
j->start();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
walletHandle = reply.value();
|
||||||
|
|
||||||
|
if ( walletHandle < 0 ) {
|
||||||
|
q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDBusPendingReply<int> nextReply = iface->entryType( walletHandle, q->service(), key, q->service() );
|
||||||
|
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
|
||||||
|
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletEntryTypeFinished(QDBusPendingCallWatcher*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//Must be in sync with KWallet::EntryType (kwallet.h)
|
||||||
|
enum KWalletEntryType {
|
||||||
|
Unknown=0,
|
||||||
|
Password,
|
||||||
|
Stream,
|
||||||
|
Map
|
||||||
|
};
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher ) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
if ( watcher->isError() ) {
|
||||||
|
const QDBusError err = watcher->error();
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Could not determine data type: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDBusPendingReply<int> reply = *watcher;
|
||||||
|
const int value = reply.value();
|
||||||
|
|
||||||
|
switch ( value ) {
|
||||||
|
case Unknown:
|
||||||
|
q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
|
||||||
|
return;
|
||||||
|
case Password:
|
||||||
|
mode = Text;
|
||||||
|
break;
|
||||||
|
case Stream:
|
||||||
|
mode = Binary;
|
||||||
|
break;
|
||||||
|
case Map:
|
||||||
|
q->emitFinishedWithError( EntryNotFound, tr("Unsupported entry type 'Map'") );
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Unknown kwallet entry type '%1'").arg( value ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDBusPendingCall nextReply = (mode == Text)
|
||||||
|
? QDBusPendingCall( iface->readPassword( walletHandle, q->service(), key, q->service() ) )
|
||||||
|
: QDBusPendingCall( iface->readEntry( walletHandle, q->service(), key, q->service() ) );
|
||||||
|
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
|
||||||
|
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
|
||||||
|
if ( !watcher->isError() ) {
|
||||||
|
if ( mode == Binary ) {
|
||||||
|
QDBusPendingReply<QByteArray> reply = *watcher;
|
||||||
|
if (reply.isValid()) {
|
||||||
|
data = reply.value();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QDBusPendingReply<QString> reply = *watcher;
|
||||||
|
if (reply.isValid()) {
|
||||||
|
data = reply.value().toUtf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JobPrivate::kwalletFinished(watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kwalletWritePasswordScheduledStart( const char * service, const char * path, JobPrivate * priv ) {
|
||||||
|
if ( QDBusConnection::sessionBus().isConnected() )
|
||||||
|
{
|
||||||
|
priv->iface = new org::kde::KWallet( QLatin1String(service), QLatin1String(path), QDBusConnection::sessionBus(), priv );
|
||||||
|
const QDBusPendingReply<QString> reply = priv->iface->networkWallet();
|
||||||
|
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher( reply, priv );
|
||||||
|
priv->connect( watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), priv, SLOT(kwalletWalletFound(QDBusPendingCallWatcher*)) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// D-Bus is not reachable so none can tell us something about KWalletd
|
||||||
|
QDBusError err( QDBusError::NoServer, WritePasswordJobPrivate::tr("D-Bus is not running") );
|
||||||
|
priv->fallbackOnError( err );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJobPrivate::scheduledStart() {
|
||||||
|
switch ( getKeyringBackend() ) {
|
||||||
|
case Backend_LibSecretKeyring: {
|
||||||
|
if ( !LibSecretKeyring::writePassword(service, key, service, mode,
|
||||||
|
data, this) ) {
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Unknown error") );
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Backend_GnomeKeyring: {
|
||||||
|
QString type;
|
||||||
|
QByteArray password;
|
||||||
|
|
||||||
|
switch(mode) {
|
||||||
|
case JobPrivate::Text:
|
||||||
|
type = QLatin1String("plaintext");
|
||||||
|
password = data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
type = QLatin1String("base64");
|
||||||
|
password = data.toBase64();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray service = q->service().toUtf8();
|
||||||
|
if ( !GnomeKeyring::store_network_password( GnomeKeyring::GNOME_KEYRING_DEFAULT,
|
||||||
|
service.constData(),
|
||||||
|
key.toUtf8().constData(),
|
||||||
|
service.constData(),
|
||||||
|
type.toUtf8().constData(),
|
||||||
|
password.constData(),
|
||||||
|
reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
|
||||||
|
this, 0 ) )
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Unknown error") );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Backend_Kwallet4:
|
||||||
|
kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this);
|
||||||
|
break;
|
||||||
|
case Backend_Kwallet5:
|
||||||
|
kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJobPrivate::fallbackOnError(const QDBusError &err)
|
||||||
|
{
|
||||||
|
if ( !q->insecureFallback() ) {
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlainTextStore plainTextStore( q->service(), q->settings() );
|
||||||
|
plainTextStore.write( key, data, mode );
|
||||||
|
|
||||||
|
if ( plainTextStore.error() != NoError )
|
||||||
|
q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
|
||||||
|
else
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobPrivate::gnomeKeyring_writeCb(int result, JobPrivate* self )
|
||||||
|
{
|
||||||
|
if ( result == GnomeKeyring::RESULT_OK ) {
|
||||||
|
self->q->emitFinished();
|
||||||
|
} else {
|
||||||
|
const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
|
||||||
|
self->q->emitFinishedWithError( errorResult.first, errorResult.second );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
QDBusPendingReply<int> reply = *watcher;
|
||||||
|
|
||||||
|
if ( reply.isError() ) {
|
||||||
|
fallbackOnError( reply.error() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlainTextStore plainTextStore( q->service(), q->settings() );
|
||||||
|
if ( plainTextStore.contains( key ) ) {
|
||||||
|
// If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data
|
||||||
|
plainTextStore.remove( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
const int handle = reply.value();
|
||||||
|
|
||||||
|
if ( handle < 0 ) {
|
||||||
|
q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDBusPendingReply<int> nextReply;
|
||||||
|
|
||||||
|
if ( mode == Text )
|
||||||
|
nextReply = iface->writePassword( handle, q->service(), key, QString::fromUtf8(data), q->service() );
|
||||||
|
else if ( mode == Binary )
|
||||||
|
nextReply = iface->writeEntry( handle, q->service(), key, data, q->service() );
|
||||||
|
else
|
||||||
|
nextReply = iface->removeEntry( handle, q->service(), key, q->service() );
|
||||||
|
|
||||||
|
QDBusPendingCallWatcher* nextWatcher = new QDBusPendingCallWatcher( nextReply, this );
|
||||||
|
connect( nextWatcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(kwalletFinished(QDBusPendingCallWatcher*)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void JobPrivate::kwalletFinished( QDBusPendingCallWatcher* watcher ) {
|
||||||
|
if ( !watcher->isError() ) {
|
||||||
|
if ( mode == Binary ) {
|
||||||
|
QDBusPendingReply<QByteArray> reply = *watcher;
|
||||||
|
if (reply.isValid()) {
|
||||||
|
data = reply.value();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QDBusPendingReply<QString> reply = *watcher;
|
||||||
|
if (reply.isValid()) {
|
||||||
|
data = reply.value().toUtf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePasswordJobPrivate::scheduledStart() {
|
||||||
|
switch ( getKeyringBackend() ) {
|
||||||
|
case Backend_LibSecretKeyring: {
|
||||||
|
if ( !LibSecretKeyring::deletePassword(key, q->service(), this) ) {
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Unknown error") );
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case Backend_GnomeKeyring: {
|
||||||
|
if ( !GnomeKeyring::delete_network_password(
|
||||||
|
key.toUtf8().constData(), q->service().toUtf8().constData(),
|
||||||
|
reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &JobPrivate::gnomeKeyring_writeCb ),
|
||||||
|
this, 0 ) )
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Unknown error") );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Backend_Kwallet4:
|
||||||
|
kwalletWritePasswordScheduledStart("org.kde.kwalletd", "/modules/kwalletd", this);
|
||||||
|
break;
|
||||||
|
case Backend_Kwallet5:
|
||||||
|
kwalletWritePasswordScheduledStart("org.kde.kwalletd5", "/modules/kwalletd5", this);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePasswordJobPrivate::fallbackOnError(const QDBusError &err) {
|
||||||
|
QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
|
||||||
|
QSettings* actual = q->settings() ? q->settings() : local.data();
|
||||||
|
|
||||||
|
if ( !q->insecureFallback() ) {
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2")
|
||||||
|
.arg( QDBusError::errorString( err.type() ), err.message() ) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
actual->remove( key );
|
||||||
|
actual->sync();
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
188
client/qtkeychain/keychain_win.cpp
Normal file
188
client/qtkeychain/keychain_win.cpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
#include "keychain_p.h"
|
||||||
|
#include "plaintextstore_p.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <wincrypt.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
#if defined(USE_CREDENTIAL_STORE)
|
||||||
|
#include <wincred.h>
|
||||||
|
|
||||||
|
void ReadPasswordJobPrivate::scheduledStart() {
|
||||||
|
LPCWSTR name = (LPCWSTR)key.utf16();
|
||||||
|
PCREDENTIALW cred;
|
||||||
|
|
||||||
|
if (!CredReadW(name, CRED_TYPE_GENERIC, 0, &cred)) {
|
||||||
|
Error error;
|
||||||
|
QString msg;
|
||||||
|
switch(GetLastError()) {
|
||||||
|
case ERROR_NOT_FOUND:
|
||||||
|
error = EntryNotFound;
|
||||||
|
msg = tr("Password entry not found");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = OtherError;
|
||||||
|
msg = tr("Could not decrypt data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->emitFinishedWithError( error, msg );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = QByteArray((char*)cred->CredentialBlob, cred->CredentialBlobSize);
|
||||||
|
CredFree(cred);
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJobPrivate::scheduledStart() {
|
||||||
|
CREDENTIALW cred;
|
||||||
|
char *pwd = data.data();
|
||||||
|
LPWSTR name = (LPWSTR)key.utf16();
|
||||||
|
|
||||||
|
memset(&cred, 0, sizeof(cred));
|
||||||
|
cred.Comment = const_cast<wchar_t*>(L"QtKeychain");
|
||||||
|
cred.Type = CRED_TYPE_GENERIC;
|
||||||
|
cred.TargetName = name;
|
||||||
|
cred.CredentialBlobSize = data.size();
|
||||||
|
cred.CredentialBlob = (LPBYTE)pwd;
|
||||||
|
cred.Persist = CRED_PERSIST_ENTERPRISE;
|
||||||
|
|
||||||
|
if (CredWriteW(&cred, 0)) {
|
||||||
|
q->emitFinished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
|
||||||
|
// Detect size-exceeded errors and provide nicer messages.
|
||||||
|
// Unfortunately these error codes aren't documented.
|
||||||
|
// Found empirically on Win10 1803 build 17134.523.
|
||||||
|
if (err == RPC_X_BAD_STUB_DATA) {
|
||||||
|
const size_t maxBlob = CRED_MAX_CREDENTIAL_BLOB_SIZE;
|
||||||
|
if (cred.CredentialBlobSize > maxBlob) {
|
||||||
|
q->emitFinishedWithError(
|
||||||
|
OtherError,
|
||||||
|
tr("Credential size exceeds maximum size of %1").arg(maxBlob));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (err == RPC_S_INVALID_BOUND) {
|
||||||
|
const size_t maxTargetName = CRED_MAX_GENERIC_TARGET_NAME_LENGTH;
|
||||||
|
if (key.size() > maxTargetName) {
|
||||||
|
q->emitFinishedWithError(
|
||||||
|
OtherError,
|
||||||
|
tr("Credential key exceeds maximum size of %1").arg(maxTargetName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Writing credentials failed: Win32 error code %1").arg(err) );
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePasswordJobPrivate::scheduledStart() {
|
||||||
|
LPCWSTR name = (LPCWSTR)key.utf16();
|
||||||
|
|
||||||
|
if (!CredDeleteW(name, CRED_TYPE_GENERIC, 0)) {
|
||||||
|
Error error;
|
||||||
|
QString msg;
|
||||||
|
switch(GetLastError()) {
|
||||||
|
case ERROR_NOT_FOUND:
|
||||||
|
error = EntryNotFound;
|
||||||
|
msg = tr("Password entry not found");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error = OtherError;
|
||||||
|
msg = tr("Could not decrypt data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->emitFinishedWithError( error, msg );
|
||||||
|
} else {
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void ReadPasswordJobPrivate::scheduledStart() {
|
||||||
|
PlainTextStore plainTextStore( q->service(), q->settings() );
|
||||||
|
QByteArray encrypted = plainTextStore.readData( key );
|
||||||
|
if ( plainTextStore.error() != NoError ) {
|
||||||
|
q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DATA_BLOB blob_in, blob_out;
|
||||||
|
|
||||||
|
blob_in.pbData = reinterpret_cast<BYTE*>( encrypted.data() );
|
||||||
|
blob_in.cbData = encrypted.size();
|
||||||
|
|
||||||
|
const BOOL ret = CryptUnprotectData( &blob_in,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
&blob_out );
|
||||||
|
if ( !ret ) {
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Could not decrypt data") );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = QByteArray( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
|
||||||
|
SecureZeroMemory( blob_out.pbData, blob_out.cbData );
|
||||||
|
LocalFree( blob_out.pbData );
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WritePasswordJobPrivate::scheduledStart() {
|
||||||
|
DATA_BLOB blob_in, blob_out;
|
||||||
|
blob_in.pbData = reinterpret_cast<BYTE*>( data.data() );
|
||||||
|
blob_in.cbData = data.size();
|
||||||
|
const BOOL res = CryptProtectData( &blob_in,
|
||||||
|
L"QKeychain-encrypted data",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
&blob_out );
|
||||||
|
if ( !res ) {
|
||||||
|
q->emitFinishedWithError( OtherError, tr("Encryption failed") ); //TODO more details available?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray encrypted( reinterpret_cast<char*>( blob_out.pbData ), blob_out.cbData );
|
||||||
|
LocalFree( blob_out.pbData );
|
||||||
|
|
||||||
|
PlainTextStore plainTextStore( q->service(), q->settings() );
|
||||||
|
plainTextStore.write( key, encrypted, Binary );
|
||||||
|
if ( plainTextStore.error() != NoError ) {
|
||||||
|
q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePasswordJobPrivate::scheduledStart() {
|
||||||
|
PlainTextStore plainTextStore( q->service(), q->settings() );
|
||||||
|
plainTextStore.remove( key );
|
||||||
|
if ( plainTextStore.error() != NoError ) {
|
||||||
|
q->emitFinishedWithError( plainTextStore.error(), plainTextStore.errorString() );
|
||||||
|
} else {
|
||||||
|
q->emitFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
336
client/qtkeychain/libsecret.cpp
Normal file
336
client/qtkeychain/libsecret.cpp
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
#if defined(HAVE_LIBSECRET)
|
||||||
|
#include <libsecret/secret.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libsecret_p.h"
|
||||||
|
|
||||||
|
#include <QLibrary>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#if defined(HAVE_LIBSECRET)
|
||||||
|
const SecretSchema* qtkeychainSchema(void) {
|
||||||
|
static const SecretSchema schema = {
|
||||||
|
"org.qt.keychain", SECRET_SCHEMA_DONT_MATCH_NAME,
|
||||||
|
{
|
||||||
|
{ "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
|
||||||
|
{ "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
|
||||||
|
{ "type", SECRET_SCHEMA_ATTRIBUTE_STRING }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return &schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
QKeychain::JobPrivate *self;
|
||||||
|
QString user;
|
||||||
|
QString server;
|
||||||
|
} callbackArg;
|
||||||
|
|
||||||
|
typedef void (*secret_password_lookup_t) (const SecretSchema *schema,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
...) G_GNUC_NULL_TERMINATED;
|
||||||
|
typedef gchar *(*secret_password_lookup_finish_t) (GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
typedef void (*secret_password_store_t) (const SecretSchema *schema,
|
||||||
|
const gchar *collection,
|
||||||
|
const gchar *label,
|
||||||
|
const gchar *password,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
...) G_GNUC_NULL_TERMINATED;
|
||||||
|
typedef gboolean (*secret_password_store_finish_t) (GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
typedef void (*secret_password_clear_t) (const SecretSchema *schema,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data,
|
||||||
|
...) G_GNUC_NULL_TERMINATED;
|
||||||
|
typedef gboolean (*secret_password_clear_finish_t) (GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
typedef void (*secret_password_free_t) (gchar *password);
|
||||||
|
typedef GQuark (*secret_error_get_quark_t) (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
static secret_password_lookup_t secret_password_lookup_fn = NULL;
|
||||||
|
static secret_password_lookup_finish_t secret_password_lookup_finish_fn = NULL;
|
||||||
|
static secret_password_store_t secret_password_store_fn = NULL;
|
||||||
|
static secret_password_store_finish_t secret_password_store_finish_fn = NULL;
|
||||||
|
static secret_password_clear_t secret_password_clear_fn = NULL;
|
||||||
|
static secret_password_clear_finish_t secret_password_clear_finish_fn = NULL;
|
||||||
|
static secret_password_free_t secret_password_free_fn = NULL;
|
||||||
|
static secret_error_get_quark_t secret_error_get_quark_fn = NULL;
|
||||||
|
|
||||||
|
static QKeychain::Error gerrorToCode(const GError *error) {
|
||||||
|
if (error->domain != secret_error_get_quark_fn()) {
|
||||||
|
return QKeychain::OtherError;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(error->code) {
|
||||||
|
case SECRET_ERROR_NO_SUCH_OBJECT:
|
||||||
|
return QKeychain::EntryNotFound;
|
||||||
|
case SECRET_ERROR_IS_LOCKED:
|
||||||
|
return QKeychain::AccessDenied;
|
||||||
|
default:
|
||||||
|
return QKeychain::OtherError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_password_lookup (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer inst)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
callbackArg *arg = (callbackArg*)inst;
|
||||||
|
gchar *password = secret_password_lookup_finish_fn (result, &error);
|
||||||
|
|
||||||
|
Q_UNUSED(source);
|
||||||
|
|
||||||
|
if (arg) {
|
||||||
|
if (error) {
|
||||||
|
QKeychain::Error code = gerrorToCode(error);
|
||||||
|
|
||||||
|
arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) );
|
||||||
|
} else {
|
||||||
|
if (password != NULL) {
|
||||||
|
QByteArray raw = QByteArray(password);
|
||||||
|
switch(arg->self->mode) {
|
||||||
|
case QKeychain::JobPrivate::Binary:
|
||||||
|
arg->self->data = QByteArray::fromBase64(raw);
|
||||||
|
break;
|
||||||
|
case QKeychain::JobPrivate::Text:
|
||||||
|
default:
|
||||||
|
arg->self->data = raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg->self->q->emitFinished();
|
||||||
|
} else if (arg->self->mode == QKeychain::JobPrivate::Text) {
|
||||||
|
arg->self->mode = QKeychain::JobPrivate::Binary;
|
||||||
|
secret_password_lookup_fn (qtkeychainSchema(), NULL,
|
||||||
|
on_password_lookup, arg,
|
||||||
|
"user", arg->user.toUtf8().constData(),
|
||||||
|
"server", arg->server.toUtf8().constData(),
|
||||||
|
"type", "base64",
|
||||||
|
NULL);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error) {
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
secret_password_free_fn (password);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg) {
|
||||||
|
delete arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_password_stored (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer inst)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
|
||||||
|
|
||||||
|
Q_UNUSED(source);
|
||||||
|
|
||||||
|
secret_password_store_finish_fn (result, &error);
|
||||||
|
|
||||||
|
if (self) {
|
||||||
|
if (error != NULL) {
|
||||||
|
self->q->emitFinishedWithError( gerrorToCode(error),
|
||||||
|
QString::fromUtf8(error->message) );
|
||||||
|
} else {
|
||||||
|
self->q->emitFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != NULL) {
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_password_cleared (GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer inst)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
|
||||||
|
gboolean removed = secret_password_clear_finish_fn (result, &error);
|
||||||
|
|
||||||
|
Q_UNUSED(source);
|
||||||
|
if (self) {
|
||||||
|
if ( error ) {
|
||||||
|
self->q->emitFinishedWithError( gerrorToCode(error),
|
||||||
|
QString::fromUtf8(error->message) );
|
||||||
|
} else {
|
||||||
|
Q_UNUSED(removed);
|
||||||
|
self->q->emitFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != NULL) {
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString modeToString(QKeychain::JobPrivate::Mode mode) {
|
||||||
|
switch(mode) {
|
||||||
|
case QKeychain::JobPrivate::Binary:
|
||||||
|
return "base64";
|
||||||
|
default:
|
||||||
|
return "plaintext";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool LibSecretKeyring::isAvailable() {
|
||||||
|
#if defined(HAVE_LIBSECRET)
|
||||||
|
const LibSecretKeyring& keyring = instance();
|
||||||
|
if (!keyring.isLoaded())
|
||||||
|
return false;
|
||||||
|
if (secret_password_lookup_fn == NULL)
|
||||||
|
return false;
|
||||||
|
if (secret_password_lookup_finish_fn == NULL)
|
||||||
|
return false;
|
||||||
|
if (secret_password_store_fn == NULL)
|
||||||
|
return false;
|
||||||
|
if (secret_password_store_finish_fn == NULL)
|
||||||
|
return false;
|
||||||
|
if (secret_password_clear_fn == NULL)
|
||||||
|
return false;
|
||||||
|
if (secret_password_clear_finish_fn == NULL)
|
||||||
|
return false;
|
||||||
|
if (secret_password_free_fn == NULL)
|
||||||
|
return false;
|
||||||
|
if (secret_error_get_quark_fn == NULL)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LibSecretKeyring::findPassword(const QString &user, const QString &server,
|
||||||
|
QKeychain::JobPrivate *self)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_LIBSECRET)
|
||||||
|
if (!isAvailable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->mode = QKeychain::JobPrivate::Text;
|
||||||
|
self->data = QByteArray();
|
||||||
|
|
||||||
|
callbackArg *arg = new callbackArg;
|
||||||
|
arg->self = self;
|
||||||
|
arg->user = user;
|
||||||
|
arg->server = server;
|
||||||
|
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
secret_password_lookup_fn (qtkeychainSchema(), NULL, on_password_lookup, arg,
|
||||||
|
"user", user.toUtf8().constData(),
|
||||||
|
"server", server.toUtf8().constData(),
|
||||||
|
"type", "plaintext",
|
||||||
|
NULL);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LibSecretKeyring::writePassword(const QString &display_name,
|
||||||
|
const QString &user,
|
||||||
|
const QString &server,
|
||||||
|
const QKeychain::JobPrivate::Mode mode,
|
||||||
|
const QByteArray &password,
|
||||||
|
QKeychain::JobPrivate *self)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_LIBSECRET)
|
||||||
|
if (!isAvailable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString type = modeToString(mode);
|
||||||
|
QByteArray pwd;
|
||||||
|
switch(mode) {
|
||||||
|
case QKeychain::JobPrivate::Binary:
|
||||||
|
pwd = password.toBase64();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pwd = password;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT,
|
||||||
|
display_name.toUtf8().constData(),
|
||||||
|
pwd.constData(), NULL, on_password_stored, self,
|
||||||
|
"user", user.toUtf8().constData(),
|
||||||
|
"server", server.toUtf8().constData(),
|
||||||
|
"type", type.toUtf8().constData(),
|
||||||
|
NULL);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LibSecretKeyring::deletePassword(const QString &key, const QString &service,
|
||||||
|
QKeychain::JobPrivate* self)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_LIBSECRET)
|
||||||
|
if (!isAvailable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
secret_password_clear_fn (qtkeychainSchema(), NULL, on_password_cleared, self,
|
||||||
|
"user", key.toUtf8().constData(),
|
||||||
|
"server", service.toUtf8().constData(),
|
||||||
|
NULL);
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LibSecretKeyring::LibSecretKeyring()
|
||||||
|
: QLibrary(QStringLiteral("secret-1"), 0)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LIBSECRET
|
||||||
|
if (load()) {
|
||||||
|
secret_password_lookup_fn =
|
||||||
|
(secret_password_lookup_t)resolve("secret_password_lookup");
|
||||||
|
secret_password_lookup_finish_fn =
|
||||||
|
(secret_password_lookup_finish_t)resolve("secret_password_lookup_finish");
|
||||||
|
secret_password_store_fn =
|
||||||
|
(secret_password_store_t)resolve("secret_password_store");
|
||||||
|
secret_password_store_finish_fn =
|
||||||
|
(secret_password_store_finish_t)resolve("secret_password_store_finish");
|
||||||
|
secret_password_clear_fn =
|
||||||
|
(secret_password_clear_t)resolve("secret_password_clear");
|
||||||
|
secret_password_clear_finish_fn =
|
||||||
|
(secret_password_clear_finish_t)resolve("secret_password_clear_finish");
|
||||||
|
secret_password_free_fn =
|
||||||
|
(secret_password_free_t)resolve("secret_password_free");
|
||||||
|
secret_error_get_quark_fn =
|
||||||
|
(secret_error_get_quark_t)resolve("secret_error_get_quark");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
LibSecretKeyring &LibSecretKeyring::instance() {
|
||||||
|
static LibSecretKeyring instance;
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
33
client/qtkeychain/libsecret_p.h
Normal file
33
client/qtkeychain/libsecret_p.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef QTKEYCHAIN_LIBSECRET_P_H
|
||||||
|
#define QTKEYCHAIN_LIBSECRET_P_H
|
||||||
|
|
||||||
|
#include <QLibrary>
|
||||||
|
|
||||||
|
#include "keychain_p.h"
|
||||||
|
|
||||||
|
class LibSecretKeyring : public QLibrary {
|
||||||
|
public:
|
||||||
|
static bool isAvailable();
|
||||||
|
|
||||||
|
static bool findPassword(const QString& user,
|
||||||
|
const QString& server,
|
||||||
|
QKeychain::JobPrivate* self);
|
||||||
|
|
||||||
|
static bool writePassword(const QString& display_name,
|
||||||
|
const QString& user,
|
||||||
|
const QString& server,
|
||||||
|
const QKeychain::JobPrivate::Mode type,
|
||||||
|
const QByteArray& password,
|
||||||
|
QKeychain::JobPrivate* self);
|
||||||
|
|
||||||
|
static bool deletePassword(const QString &key, const QString &service,
|
||||||
|
QKeychain::JobPrivate* self);
|
||||||
|
|
||||||
|
private:
|
||||||
|
LibSecretKeyring();
|
||||||
|
|
||||||
|
static LibSecretKeyring &instance();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
276
client/qtkeychain/org.kde.KWallet.xml
Normal file
276
client/qtkeychain/org.kde.KWallet.xml
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="org.kde.KWallet">
|
||||||
|
<signal name="walletListDirty">
|
||||||
|
</signal>
|
||||||
|
<signal name="walletCreated">
|
||||||
|
<arg name="wallet" type="s" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="walletOpened">
|
||||||
|
<arg name="wallet" type="s" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="walletAsyncOpened">
|
||||||
|
<arg name="tId" type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="walletDeleted">
|
||||||
|
<arg name="wallet" type="s" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="walletClosed">
|
||||||
|
<arg name="wallet" type="s" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="walletClosed">
|
||||||
|
<arg name="handle" type="i" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="allWalletsClosed">
|
||||||
|
</signal>
|
||||||
|
<signal name="folderListUpdated">
|
||||||
|
<arg name="wallet" type="s" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="folderUpdated">
|
||||||
|
<arg type="s" direction="out"/>
|
||||||
|
<arg type="s" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<signal name="applicationDisconnected">
|
||||||
|
<arg name="wallet" type="s" direction="out"/>
|
||||||
|
<arg name="application" type="s" direction="out"/>
|
||||||
|
</signal>
|
||||||
|
<method name="isEnabled">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="open">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="wId" type="x" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="openPath">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="path" type="s" direction="in"/>
|
||||||
|
<arg name="wId" type="x" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="openAsync">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="wId" type="x" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
<arg name="handleSession" type="b" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="openPathAsync">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="path" type="s" direction="in"/>
|
||||||
|
<arg name="wId" type="x" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
<arg name="handleSession" type="b" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="close">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="force" type="b" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="close">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="force" type="b" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="sync">
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||||
|
</method>
|
||||||
|
<method name="deleteWallet">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="isOpen">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="isOpen">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="users">
|
||||||
|
<arg type="as" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="changePassword">
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="wId" type="x" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="wallets">
|
||||||
|
<arg type="as" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="folderList">
|
||||||
|
<arg type="as" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="hasFolder">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="createFolder">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="removeFolder">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="entryList">
|
||||||
|
<arg type="as" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="readEntry">
|
||||||
|
<arg type="ay" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="readMap">
|
||||||
|
<arg type="ay" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="readPassword">
|
||||||
|
<arg type="s" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="readEntryList">
|
||||||
|
<arg type="a{sv}" direction="out"/>
|
||||||
|
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="readMapList">
|
||||||
|
<arg type="a{sv}" direction="out"/>
|
||||||
|
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="readPasswordList">
|
||||||
|
<arg type="a{sv}" direction="out"/>
|
||||||
|
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="renameEntry">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="oldName" type="s" direction="in"/>
|
||||||
|
<arg name="newName" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="writeEntry">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="value" type="ay" direction="in"/>
|
||||||
|
<arg name="entryType" type="i" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="writeEntry">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="value" type="ay" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="writeMap">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="value" type="ay" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="writePassword">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="value" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="hasEntry">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="entryType">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="removeEntry">
|
||||||
|
<arg type="i" direction="out"/>
|
||||||
|
<arg name="handle" type="i" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
<arg name="appid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="disconnectApplication">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="application" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="reconfigure">
|
||||||
|
</method>
|
||||||
|
<method name="folderDoesNotExist">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="keyDoesNotExist">
|
||||||
|
<arg type="b" direction="out"/>
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="folder" type="s" direction="in"/>
|
||||||
|
<arg name="key" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
<method name="closeAllWallets">
|
||||||
|
</method>
|
||||||
|
<method name="networkWallet">
|
||||||
|
<arg type="s" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="localWallet">
|
||||||
|
<arg type="s" direction="out"/>
|
||||||
|
</method>
|
||||||
|
<method name="pamOpen">
|
||||||
|
<arg name="wallet" type="s" direction="in"/>
|
||||||
|
<arg name="passwordHash" type="ay" direction="in"/>
|
||||||
|
<arg name="sessionTimeout" type="i" direction="in"/>
|
||||||
|
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
110
client/qtkeychain/plaintextstore.cpp
Normal file
110
client/qtkeychain/plaintextstore.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "plaintextstore_p.h"
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
inline QString dataKey(const QString &key) { return key; }
|
||||||
|
#else // Q_OS_WIN
|
||||||
|
inline QString dataKey(const QString &key) { return key + QLatin1String("/data"); }
|
||||||
|
inline QString typeKey(const QString &key) { return key + QLatin1String("/type"); }
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PlainTextStore::PlainTextStore(const QString &service, QSettings *settings)
|
||||||
|
: m_localSettings(settings ? 0 : new QSettings(service))
|
||||||
|
, m_actualSettings(settings ? settings : m_localSettings.data())
|
||||||
|
, m_error(NoError)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlainTextStore::contains(const QString &key) const
|
||||||
|
{
|
||||||
|
return m_actualSettings->contains(dataKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray PlainTextStore::readData(const QString &key)
|
||||||
|
{
|
||||||
|
return read(dataKey(key)).toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
|
||||||
|
JobPrivate::Mode PlainTextStore::readMode(const QString &key)
|
||||||
|
{
|
||||||
|
return JobPrivate::stringToMode(read(typeKey(key)).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
|
void PlainTextStore::write(const QString &key, const QByteArray &data, JobPrivate::Mode mode)
|
||||||
|
{
|
||||||
|
if (m_actualSettings->status() != QSettings::NoError)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
m_actualSettings->setValue(typeKey(key), JobPrivate::modeToString(mode));
|
||||||
|
#else // Q_OS_WIN
|
||||||
|
Q_UNUSED(mode);
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
m_actualSettings->setValue(dataKey(key), data);
|
||||||
|
m_actualSettings->sync();
|
||||||
|
|
||||||
|
if (m_actualSettings->status() == QSettings::AccessError) {
|
||||||
|
setError(AccessDenied, tr("Could not store data in settings: access error"));
|
||||||
|
} else if (m_actualSettings->status() != QSettings::NoError) {
|
||||||
|
setError(OtherError, tr("Could not store data in settings: format error"));
|
||||||
|
} else {
|
||||||
|
setError(NoError, QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlainTextStore::remove(const QString &key)
|
||||||
|
{
|
||||||
|
if (m_actualSettings->status() != QSettings::NoError)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
m_actualSettings->remove(typeKey(key));
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
m_actualSettings->remove(dataKey(key));
|
||||||
|
m_actualSettings->sync();
|
||||||
|
|
||||||
|
if (m_actualSettings->status() == QSettings::AccessError) {
|
||||||
|
setError(AccessDenied, tr("Could not delete data from settings: access error"));
|
||||||
|
} else if (m_actualSettings->status() != QSettings::NoError) {
|
||||||
|
setError(OtherError, tr("Could not delete data from settings: format error"));
|
||||||
|
} else {
|
||||||
|
setError(NoError, QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlainTextStore::setError(Error error, const QString &errorString)
|
||||||
|
{
|
||||||
|
m_error = error;
|
||||||
|
m_errorString = errorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PlainTextStore::read(const QString &key)
|
||||||
|
{
|
||||||
|
const QVariant value = m_actualSettings->value(key);
|
||||||
|
|
||||||
|
if (value.isNull()) {
|
||||||
|
setError(EntryNotFound, tr("Entry not found"));
|
||||||
|
} else {
|
||||||
|
setError(NoError, QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
47
client/qtkeychain/plaintextstore_p.h
Normal file
47
client/qtkeychain/plaintextstore_p.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* Copyright (C) 2016 Mathias Hasselmann <mathias.hasselmann@kdab.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef QTKEYCHAIN_PLAINTEXTSTORE_P_H
|
||||||
|
#define QTKEYCHAIN_PLAINTEXTSTORE_P_H
|
||||||
|
|
||||||
|
#include "keychain_p.h"
|
||||||
|
|
||||||
|
namespace QKeychain {
|
||||||
|
|
||||||
|
class PlainTextStore {
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(QKeychain::PlainTextStore)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PlainTextStore(const QString &service, QSettings *settings);
|
||||||
|
|
||||||
|
Error error() const { return m_error; }
|
||||||
|
QString errorString() const { return m_errorString; }
|
||||||
|
|
||||||
|
bool contains(const QString &key) const;
|
||||||
|
|
||||||
|
QByteArray readData(const QString &key);
|
||||||
|
JobPrivate::Mode readMode(const QString &key);
|
||||||
|
|
||||||
|
void write(const QString &key, const QByteArray &data, JobPrivate::Mode mode);
|
||||||
|
void remove(const QString &key);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setError(Error error, const QString &errorString);
|
||||||
|
QVariant read(const QString &key);
|
||||||
|
|
||||||
|
const QScopedPointer<QSettings> m_localSettings;
|
||||||
|
QSettings *const m_actualSettings;
|
||||||
|
QString m_errorString;
|
||||||
|
Error m_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QTKEYCHAIN_PLAINTEXTSTORE_P_H
|
||||||
83
client/qtkeychain/qt5keychain.pri
Normal file
83
client/qtkeychain/qt5keychain.pri
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Minimal qmake support.
|
||||||
|
# This file is provided as is without any warranty.
|
||||||
|
# It can break at anytime or be removed without notice.
|
||||||
|
|
||||||
|
QT5KEYCHAIN_PWD = $$PWD
|
||||||
|
|
||||||
|
CONFIG += depend_includepath
|
||||||
|
DEFINES += QTKEYCHAIN_NO_EXPORT
|
||||||
|
|
||||||
|
INCLUDEPATH += \
|
||||||
|
$$PWD/.. \
|
||||||
|
$$QT5KEYCHAIN_PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$QT5KEYCHAIN_PWD/keychain_p.h \
|
||||||
|
$$QT5KEYCHAIN_PWD/keychain.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$QT5KEYCHAIN_PWD/keychain.cpp
|
||||||
|
|
||||||
|
unix:!macx:!ios {
|
||||||
|
# Remove the following LIBSECRET_SUPPORT line
|
||||||
|
# to build without libsecret support.
|
||||||
|
DEFINES += LIBSECRET_SUPPORT
|
||||||
|
contains(DEFINES, LIBSECRET_SUPPORT) {
|
||||||
|
packagesExist(libsecret-1) {
|
||||||
|
!build_pass:message("Libsecret support: on")
|
||||||
|
CONFIG += link_pkgconfig
|
||||||
|
PKGCONFIG += libsecret-1
|
||||||
|
DEFINES += HAVE_LIBSECRET
|
||||||
|
} else {
|
||||||
|
!build_pass:warning("Libsecret not found.")
|
||||||
|
!build_pass:message("Libsecret support: off")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
!build_pass:message("Libsecret support: off")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate D-Bus interface:
|
||||||
|
QT += dbus
|
||||||
|
kwallet_interface.files = $$PWD/org.kde.KWallet.xml
|
||||||
|
DBUS_INTERFACES += kwallet_interface
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$QT5KEYCHAIN_PWD/gnomekeyring_p.h \
|
||||||
|
$$QT5KEYCHAIN_PWD/plaintextstore_p.h \
|
||||||
|
$$QT5KEYCHAIN_PWD/libsecret_p.h
|
||||||
|
SOURCES += \
|
||||||
|
$$QT5KEYCHAIN_PWD/keychain_unix.cpp \
|
||||||
|
$$QT5KEYCHAIN_PWD/plaintextstore.cpp \
|
||||||
|
$$QT5KEYCHAIN_PWD/gnomekeyring.cpp \
|
||||||
|
$$QT5KEYCHAIN_PWD/libsecret.cpp
|
||||||
|
}
|
||||||
|
|
||||||
|
win32 {
|
||||||
|
# Remove the following USE_CREDENTIAL_STORE line
|
||||||
|
# to use the CryptProtectData Windows API function
|
||||||
|
# instead of the Windows Credential Store.
|
||||||
|
DEFINES += USE_CREDENTIAL_STORE
|
||||||
|
contains(DEFINES, USE_CREDENTIAL_STORE) {
|
||||||
|
!build_pass:message("Windows Credential Store support: on")
|
||||||
|
LIBS += -lAdvapi32
|
||||||
|
} else {
|
||||||
|
!build_pass:message("Windows Credential Store support: off")
|
||||||
|
LIBS += -lCrypt32
|
||||||
|
HEADERS += $$QT5KEYCHAIN_PWD/plaintextstore_p.h
|
||||||
|
SOURCES += $$QT5KEYCHAIN_PWD/plaintextstore.cpp
|
||||||
|
}
|
||||||
|
HEADERS += $$QT5KEYCHAIN_PWD/libsecret_p.h
|
||||||
|
SOURCES += \
|
||||||
|
$$QT5KEYCHAIN_PWD/keychain_win.cpp \
|
||||||
|
$$QT5KEYCHAIN_PWD/libsecret.cpp
|
||||||
|
}
|
||||||
|
|
||||||
|
macx:!ios {
|
||||||
|
LIBS += "-framework Security" "-framework Foundation"
|
||||||
|
SOURCES += $$QT5KEYCHAIN_PWD/keychain_mac.cpp
|
||||||
|
}
|
||||||
|
|
||||||
|
ios {
|
||||||
|
LIBS += "-framework Security" "-framework Foundation"
|
||||||
|
OBJECTIVE_SOURCES += $$QT5KEYCHAIN_PWD/keychain_ios.mm
|
||||||
|
}
|
||||||
121
client/qtkeychain/testclient.cpp
Normal file
121
client/qtkeychain/testclient.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/******************************************************************************
|
||||||
|
* Copyright (C) 2011-2015 Frank Osterfeld <frank.osterfeld@gmail.com> *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, but *
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. For licensing and distribution *
|
||||||
|
* details, check the accompanying file 'COPYING'. *
|
||||||
|
*****************************************************************************/
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include "keychain.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace QKeychain;
|
||||||
|
|
||||||
|
static int printUsage() {
|
||||||
|
std::cerr << "testclient store <account> <password>" << std::endl;
|
||||||
|
std::cerr << "testclient restore <account>" << std::endl;
|
||||||
|
std::cerr << "testclient delete <account>" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( int argc, char** argv ) {
|
||||||
|
QCoreApplication app( argc, argv );
|
||||||
|
const QStringList args = app.arguments();
|
||||||
|
if ( args.count() < 2 )
|
||||||
|
return printUsage();
|
||||||
|
|
||||||
|
QStringList::ConstIterator it = args.constBegin();
|
||||||
|
++it;
|
||||||
|
|
||||||
|
if ( *it == QLatin1String("store") ) {
|
||||||
|
if ( ++it == args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
const QString acc = *it;
|
||||||
|
if ( ++it == args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
const QString pass = *it;
|
||||||
|
if ( ++it != args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
WritePasswordJob job( QLatin1String("qtkeychain-testclient") );
|
||||||
|
job.setAutoDelete( false );
|
||||||
|
job.setKey( acc );
|
||||||
|
job.setTextData( pass );
|
||||||
|
QEventLoop loop;
|
||||||
|
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
|
||||||
|
job.start();
|
||||||
|
loop.exec();
|
||||||
|
if ( job.error() ) {
|
||||||
|
std::cerr << "Storing password failed: " << qPrintable(job.errorString()) << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "Password stored successfully" << std::endl;
|
||||||
|
} else if ( *it == QLatin1String("bstore") ) {
|
||||||
|
if ( ++it == args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
const QString acc = *it;
|
||||||
|
if ( ++it == args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
const QString pass = *it;
|
||||||
|
if ( ++it != args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
WritePasswordJob job( QLatin1String("qtkeychain-testclient") );
|
||||||
|
job.setAutoDelete( false );
|
||||||
|
job.setKey( acc );
|
||||||
|
job.setBinaryData( pass.toUtf8() );
|
||||||
|
QEventLoop loop;
|
||||||
|
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
|
||||||
|
job.start();
|
||||||
|
loop.exec();
|
||||||
|
if ( job.error() ) {
|
||||||
|
std::cerr << "Storing binary password failed: "
|
||||||
|
<< qPrintable(job.errorString()) << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "Password stored successfully" << std::endl;
|
||||||
|
} else if ( *it == QLatin1String("restore") ) {
|
||||||
|
if ( ++it == args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
const QString acc = *it;
|
||||||
|
if ( ++it != args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
ReadPasswordJob job( QLatin1String("qtkeychain-testclient") );
|
||||||
|
job.setAutoDelete( false );
|
||||||
|
job.setKey( acc );
|
||||||
|
QEventLoop loop;
|
||||||
|
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
|
||||||
|
job.start();
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
const QString pw = job.textData();
|
||||||
|
if ( job.error() ) {
|
||||||
|
std::cerr << "Restoring password failed: " << qPrintable(job.errorString()) << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << qPrintable(pw) << std::endl;
|
||||||
|
} else if ( *it == QLatin1String("delete") ) {
|
||||||
|
if ( ++it == args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
const QString acc = *it;
|
||||||
|
if ( ++it != args.constEnd() )
|
||||||
|
return printUsage();
|
||||||
|
DeletePasswordJob job( QLatin1String("qtkeychain-testclient") );
|
||||||
|
job.setAutoDelete( false );
|
||||||
|
job.setKey( acc );
|
||||||
|
QEventLoop loop;
|
||||||
|
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
|
||||||
|
job.start();
|
||||||
|
loop.exec();
|
||||||
|
|
||||||
|
if ( job.error() ) {
|
||||||
|
std::cerr << "Deleting password failed: " << qPrintable(job.errorString()) << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::cout << "Password deleted successfully" << std::endl;
|
||||||
|
} else {
|
||||||
|
return printUsage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
177
client/qtkeychain/translations/qtkeychain_de.ts
Normal file
177
client/qtkeychain/translations/qtkeychain_de.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.0" language="de_DE">
|
||||||
|
<context>
|
||||||
|
<name>QKeychain::ReadPasswordJobPrivate</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="119"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>Unbekannter Fehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="133"/>
|
||||||
|
<source>D-Bus is not running</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="210"/>
|
||||||
|
<source>No keychain service available</source>
|
||||||
|
<translation>Kein Schlüsselbund-Dienst verfügbar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="212"/>
|
||||||
|
<source>Could not open wallet: %1; %2</source>
|
||||||
|
<translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="258"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>Zugriff auf Schlüsselbund verweigert</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="279"/>
|
||||||
|
<source>Could not determine data type: %1; %2</source>
|
||||||
|
<translation>Datentyp kann nicht ermittelt werden: %1: %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="297"/>
|
||||||
|
<source>Unsupported entry type 'Map'</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="300"/>
|
||||||
|
<source>Unknown kwallet entry type '%1'</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="315"/>
|
||||||
|
<source>Could not read password: %1; %2</source>
|
||||||
|
<translation>Passwort konnte nicht ausgelesen werden: %1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_mac.cpp" line="76"/>
|
||||||
|
<source>Password not found</source>
|
||||||
|
<translation>Passwort nicht gefunden</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="288"/>
|
||||||
|
<location filename="../keychain_win.cpp" line="27"/>
|
||||||
|
<source>Entry not found</source>
|
||||||
|
<translation>Eintrag nicht gefunden</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="44"/>
|
||||||
|
<source>Could not decrypt data</source>
|
||||||
|
<translation>Kann Daten nicht entschlüsseln</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QKeychain::WritePasswordJobPrivate</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="336"/>
|
||||||
|
<location filename="../keychain_unix.cpp" line="344"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>Unbekannter Fehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="359"/>
|
||||||
|
<source>D-Bus is not running</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="400"/>
|
||||||
|
<location filename="../keychain_unix.cpp" line="485"/>
|
||||||
|
<source>Could not open wallet: %1; %2</source>
|
||||||
|
<translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="463"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>Zugriff auf Schlüsselbund verweigert</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="64"/>
|
||||||
|
<source>Could not delete encrypted data from settings: access error</source>
|
||||||
|
<translation>Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Zugriffsfehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="65"/>
|
||||||
|
<source>Could not delete encrypted data from settings: format error</source>
|
||||||
|
<translation>Kann verschlüsselte Daten nicht aus den Einstellungen entfernen: Formatfehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="85"/>
|
||||||
|
<source>Encryption failed</source>
|
||||||
|
<translation>Verschlüsselung fehlgeschlagen</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="100"/>
|
||||||
|
<source>Could not store encrypted data in settings: access error</source>
|
||||||
|
<translation>Kann verschlüsselte Daten nicht in den Einstellungen speichern: Zugriffsfehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="101"/>
|
||||||
|
<source>Could not store encrypted data in settings: format error</source>
|
||||||
|
<translation>Kann verschlüsselte Daten nicht in den Einstellungen speichern: Formatfehler</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="155"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>Zugriff auf Schlüsselbund verweigert</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="157"/>
|
||||||
|
<source>No keyring daemon</source>
|
||||||
|
<translation>Kein Schlüsselbund-Dienst </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="159"/>
|
||||||
|
<source>Already unlocked</source>
|
||||||
|
<translation>Bereits entsperrt</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="161"/>
|
||||||
|
<source>No such keyring</source>
|
||||||
|
<translation>Kein solcher Schlüsselbund</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="163"/>
|
||||||
|
<source>Bad arguments</source>
|
||||||
|
<translation>Ungültige Argumente</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="165"/>
|
||||||
|
<source>I/O error</source>
|
||||||
|
<translation>Ein-/Ausgabe-Fehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="167"/>
|
||||||
|
<source>Cancelled</source>
|
||||||
|
<translation>Abgebrochen</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="169"/>
|
||||||
|
<source>Keyring already exists</source>
|
||||||
|
<translation>Schlüsselbund existiert bereits</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="171"/>
|
||||||
|
<source>No match</source>
|
||||||
|
<translation>Kein Treffer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="176"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>Unbekannter Fehler</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_mac.cpp" line="31"/>
|
||||||
|
<location filename="../keychain_mac.cpp" line="33"/>
|
||||||
|
<source>%1 (OSStatus %2)</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
178
client/qtkeychain/translations/qtkeychain_ro.ts
Normal file
178
client/qtkeychain/translations/qtkeychain_ro.ts
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.0" language="ro_RO">
|
||||||
|
<context>
|
||||||
|
<name>QKeychain::ReadPasswordJobPrivate</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="119"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>Eroare necunoscută</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="133"/>
|
||||||
|
<source>D-Bus is not running</source>
|
||||||
|
<translation>D-Bus nu rulează</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="210"/>
|
||||||
|
<source>No keychain service available</source>
|
||||||
|
<translatorcomment>Nu există niciun serviciu de chei disponibil</translatorcomment>
|
||||||
|
<translation>Kein Schlüsselbund-Dienst verfügbar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="212"/>
|
||||||
|
<source>Could not open wallet: %1; %2</source>
|
||||||
|
<translation>Nu se poate deschide portofelul: %1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="258"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>Acces interzis la serviciul de chei</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="279"/>
|
||||||
|
<source>Could not determine data type: %1; %2</source>
|
||||||
|
<translation>Nu se poate stabili tipul de date: %1: %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="297"/>
|
||||||
|
<source>Unsupported entry type 'Map'</source>
|
||||||
|
<translation>Tip de înregistrare nesuportat 'Map'</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="300"/>
|
||||||
|
<source>Unknown kwallet entry type '%1'</source>
|
||||||
|
<translation>Tip de înregistrare kwallet necunoscut '%1'</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="315"/>
|
||||||
|
<source>Could not read password: %1; %2</source>
|
||||||
|
<translation>Nu se poate citi parola: %1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_mac.cpp" line="76"/>
|
||||||
|
<source>Password not found</source>
|
||||||
|
<translation>Parola nu a fost găsită</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="288"/>
|
||||||
|
<location filename="../keychain_win.cpp" line="27"/>
|
||||||
|
<source>Entry not found</source>
|
||||||
|
<translation>Înregistrarea nu a fost găsită</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="44"/>
|
||||||
|
<source>Could not decrypt data</source>
|
||||||
|
<translation>Nu se poate decripta data</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QKeychain::WritePasswordJobPrivate</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="336"/>
|
||||||
|
<location filename="../keychain_unix.cpp" line="344"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>Eroare necunoscută</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="359"/>
|
||||||
|
<source>D-Bus is not running</source>
|
||||||
|
<translation>D-Bus nu rulează</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="400"/>
|
||||||
|
<location filename="../keychain_unix.cpp" line="485"/>
|
||||||
|
<source>Could not open wallet: %1; %2</source>
|
||||||
|
<translation>Nu se poate deschide portofelul: %1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="463"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>Acces interzis la serviciul de chei</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="64"/>
|
||||||
|
<source>Could not delete encrypted data from settings: access error</source>
|
||||||
|
<translation>Nu se pot șterge datele criptate din setări: eroare de acces</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="65"/>
|
||||||
|
<source>Could not delete encrypted data from settings: format error</source>
|
||||||
|
<translation>Nu se pot șterge datele criptate din setări: eroare de format</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="85"/>
|
||||||
|
<source>Encryption failed</source>
|
||||||
|
<translation>Criptarea a eșuat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="100"/>
|
||||||
|
<source>Could not store encrypted data in settings: access error</source>
|
||||||
|
<translation>Nu se pot stoca datele criptate în setări: eroare de acces</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="101"/>
|
||||||
|
<source>Could not store encrypted data in settings: format error</source>
|
||||||
|
<translation>Nu se pot stoca datele criptate în setări: eroare de format</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="155"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>Acces interzis la serviciul de chei</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="157"/>
|
||||||
|
<source>No keyring daemon</source>
|
||||||
|
<translation>Niciun demon pentru inelul de chei</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="159"/>
|
||||||
|
<source>Already unlocked</source>
|
||||||
|
<translation>Deja deblocat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="161"/>
|
||||||
|
<source>No such keyring</source>
|
||||||
|
<translation>Nu există astfel de inel de chei</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="163"/>
|
||||||
|
<source>Bad arguments</source>
|
||||||
|
<translation>Argumente greșite</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="165"/>
|
||||||
|
<source>I/O error</source>
|
||||||
|
<translation>Eroare de I/E</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="167"/>
|
||||||
|
<source>Cancelled</source>
|
||||||
|
<translation>Anulat</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="169"/>
|
||||||
|
<source>Keyring already exists</source>
|
||||||
|
<translation>Inelul de chei deja există</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="171"/>
|
||||||
|
<source>No match</source>
|
||||||
|
<translation>Nicio potrivire</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="176"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>Eroare necunoscută</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_mac.cpp" line="31"/>
|
||||||
|
<location filename="../keychain_mac.cpp" line="33"/>
|
||||||
|
<source>%1 (OSStatus %2)</source>
|
||||||
|
<translation>%1 (OSStatus %2)</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
177
client/qtkeychain/translations/qtkeychain_zh.ts
Normal file
177
client/qtkeychain/translations/qtkeychain_zh.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.0" language="zh_TW">
|
||||||
|
<context>
|
||||||
|
<name>QKeychain::ReadPasswordJobPrivate</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="119"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>未知的錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="133"/>
|
||||||
|
<source>D-Bus is not running</source>
|
||||||
|
<translation>D-Bus 不在執行中</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="210"/>
|
||||||
|
<source>No keychain service available</source>
|
||||||
|
<translation>沒有可用的鑰匙圈服務</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="212"/>
|
||||||
|
<source>Could not open wallet: %1; %2</source>
|
||||||
|
<translation>無法開啟錢包:%1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="258"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>鑰匙圈存取被拒絕</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="279"/>
|
||||||
|
<source>Could not determine data type: %1; %2</source>
|
||||||
|
<translation>無法判斷資料型別:%1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="297"/>
|
||||||
|
<source>Unsupported entry type 'Map'</source>
|
||||||
|
<translation>不支援的項目類型 'Map'</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="300"/>
|
||||||
|
<source>Unknown kwallet entry type '%1'</source>
|
||||||
|
<translation>未知的 kwallet 項目類型 '%1'</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="315"/>
|
||||||
|
<source>Could not read password: %1; %2</source>
|
||||||
|
<translation>無法讀取密碼:%1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_mac.cpp" line="76"/>
|
||||||
|
<source>Password not found</source>
|
||||||
|
<translation>找不到密碼</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="288"/>
|
||||||
|
<location filename="../keychain_win.cpp" line="27"/>
|
||||||
|
<source>Entry not found</source>
|
||||||
|
<translation>找不到項目</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="44"/>
|
||||||
|
<source>Could not decrypt data</source>
|
||||||
|
<translation>無法解密資料</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QKeychain::WritePasswordJobPrivate</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="336"/>
|
||||||
|
<location filename="../keychain_unix.cpp" line="344"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>未知的錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="359"/>
|
||||||
|
<source>D-Bus is not running</source>
|
||||||
|
<translation>D-Bus 不在執行中</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="400"/>
|
||||||
|
<location filename="../keychain_unix.cpp" line="485"/>
|
||||||
|
<source>Could not open wallet: %1; %2</source>
|
||||||
|
<translation>無法開啟錢包:%1; %2</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="463"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>鑰匙圈存取被拒絕</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="64"/>
|
||||||
|
<source>Could not delete encrypted data from settings: access error</source>
|
||||||
|
<translation>無法從設定刪除加密資料:存取錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="65"/>
|
||||||
|
<source>Could not delete encrypted data from settings: format error</source>
|
||||||
|
<translation>無法從設定刪除加密資料:格式錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="85"/>
|
||||||
|
<source>Encryption failed</source>
|
||||||
|
<translation>加密失敗</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="100"/>
|
||||||
|
<source>Could not store encrypted data in settings: access error</source>
|
||||||
|
<translation>無法將加密資料儲存至設定:存取錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_win.cpp" line="101"/>
|
||||||
|
<source>Could not store encrypted data in settings: format error</source>
|
||||||
|
<translation>無法將加密資料儲存至設定:格式錯誤</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>QObject</name>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="155"/>
|
||||||
|
<source>Access to keychain denied</source>
|
||||||
|
<translation>鑰匙圈存取被拒絕</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="157"/>
|
||||||
|
<source>No keyring daemon</source>
|
||||||
|
<translation>沒有可用的鑰匙圈背景程式</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="159"/>
|
||||||
|
<source>Already unlocked</source>
|
||||||
|
<translation>已解鎖</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="161"/>
|
||||||
|
<source>No such keyring</source>
|
||||||
|
<translation>鑰匙圈不存在</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="163"/>
|
||||||
|
<source>Bad arguments</source>
|
||||||
|
<translation>引數錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="165"/>
|
||||||
|
<source>I/O error</source>
|
||||||
|
<translation>I/O 錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="167"/>
|
||||||
|
<source>Cancelled</source>
|
||||||
|
<translation>已取消</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="169"/>
|
||||||
|
<source>Keyring already exists</source>
|
||||||
|
<translation>鑰匙圈已存在</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="171"/>
|
||||||
|
<source>No match</source>
|
||||||
|
<translation>無相符項目</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_unix.cpp" line="176"/>
|
||||||
|
<source>Unknown error</source>
|
||||||
|
<translation>未知的錯誤</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../keychain_mac.cpp" line="31"/>
|
||||||
|
<location filename="../keychain_mac.cpp" line="33"/>
|
||||||
|
<source>%1 (OSStatus %2)</source>
|
||||||
|
<translation>%1 (OSStatus %2)</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
Loading…
x
Reference in New Issue
Block a user