Merged remote-management

FossilOrigin-Name: 4c246e5290d2cb3b1a90127fe953bffbbf77ea53
This commit is contained in:
kostas 2015-05-29 19:26:31 +00:00
commit 440563b1ea
41 changed files with 3718 additions and 174 deletions

View File

@ -0,0 +1,195 @@
cmake_minimum_required(VERSION 2.8)
project(qtkeychain)
###
set(QTKEYCHAIN_VERSION 0.5.90)
set(QTKEYCHAIN_SOVERSION 0)
###
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake/Modules")
include(GNUInstallDirs)
option(BUILD_WITH_QT4 "Build qtkeychain with Qt4 no matter if Qt5 was found" OFF)
if( NOT BUILD_WITH_QT4 )
# try Qt5 first, and prefer that if found
find_package(Qt5Core QUIET)
endif()
if (Qt5Core_FOUND)
set(QTKEYCHAIN_VERSION_INFIX 5)
if(UNIX AND NOT APPLE)
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()
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)
macro(qt_wrap_cpp)
qt5_wrap_cpp(${ARGN})
endmacro()
set(QTCORE_LIBRARIES ${Qt5Core_LIBRARIES})
include_directories(${Qt5Core_INCLUDE_DIRS})
if (Qt5_POSITION_INDEPENDENT_CODE)
if (CMAKE_VERSION VERSION_LESS 2.8.9) # TODO remove once we increase the cmake requirement
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
else()
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
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
)
ADD_DEFINITIONS( -Wall )
if(WIN32)
list(APPEND qtkeychain_SOURCES keychain_win.cpp)
list(APPEND qtkeychain_LIBRARIES crypt32)
#FIXME: mingw bug; otherwise getting undefined refs to RtlSecureZeroMemory there
if(MINGW)
add_definitions( -O2 )
endif()
endif()
if(APPLE)
list(APPEND qtkeychain_SOURCES keychain_mac.cpp)
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
list(APPEND qtkeychain_LIBRARIES ${COREFOUNDATION_LIBRARY})
find_library(SECURITY_LIBRARY Security)
list(APPEND qtkeychain_LIBRARIES ${SECURITY_LIBRARY})
endif()
if(UNIX AND NOT APPLE)
list(APPEND qtkeychain_SOURCES keychain_unix.cpp gnomekeyring.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)
set(qtkeychain_TR_FILES
translations/qtkeychain_de.ts
translations/qtkeychain_ro.ts
)
file(GLOB qtkeychain_TR_SOURCES *.cpp *.h *.ui)
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})
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})
set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_BUILD_QKEYCHAIN_LIB)
target_link_libraries(${QTKEYCHAIN_TARGET_NAME} ${qtkeychain_LIBRARIES})
else()
add_library(${QTKEYCHAIN_TARGET_NAME} STATIC ${qtkeychain_SOURCES} ${qtkeychain_MOC_OUTFILES} ${qtkeychain_QM_FILES})
set_target_properties(${QTKEYCHAIN_TARGET_NAME} PROPERTIES COMPILE_DEFINITIONS QKEYCHAIN_STATICLIB)
endif()
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(FILES keychain.h 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}
)
add_executable( testclient testclient.cpp )
target_link_libraries( testclient ${QTKEYCHAIN_TARGET_NAME})
###
### CMake config file
###
export(TARGETS ${QTKEYCHAIN_TARGET_NAME} FILE "${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainLibraryDepends.cmake")
export(PACKAGE Qt${QTKEYCHAIN_VERSION_INFIX}Keychain)
configure_file(QtKeychainBuildTreeSettings.cmake.in
"${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainBuildTreeSettings.cmake" @ONLY)
configure_file(QtKeychainConfig.cmake.in
"${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfig.cmake" @ONLY)
configure_file(QtKeychainConfigVersion.cmake.in
"${PROJECT_BINARY_DIR}/Qt${QTKEYCHAIN_VERSION_INFIX}KeychainConfigVersion.cmake" @ONLY)
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
ext/qtkeychain/COPYING Normal file
View 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.

23
ext/qtkeychain/ChangeLog Normal file
View File

@ -0,0 +1,23 @@
ChangeLog
=========
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

View File

@ -0,0 +1,4 @@
set(QTKEYCHAIN_INCLUDE_DIRS
"@PROJECT_SOURCE_DIR@"
"@PROJECT_BINARY_DIR@"
)

View File

@ -0,0 +1,21 @@
# - Config file for the QtKeychain package
# It defines the following variables
# QTKEYCHAIN_INCLUDE_DIRS - include directories for QtKeychain
# QTKEYCHAIN_LIBRARIES - libraries to link against
# Compute paths
get_filename_component(QTKEYCHAIN_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
if(EXISTS "${QTKEYCHAIN_CMAKE_DIR}/CMakeCache.txt")
# In build tree
include("${QTKEYCHAIN_CMAKE_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainBuildTreeSettings.cmake")
else()
set(QTKEYCHAIN_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
endif()
# Our library dependencies (contains definitions for IMPORTED targets)
include("${QTKEYCHAIN_CMAKE_DIR}/Qt@QTKEYCHAIN_VERSION_INFIX@KeychainLibraryDepends.cmake")
# These are IMPORTED targets created by FooBarLibraryDepends.cmake
set(QTKEYCHAIN_LIBRARIES "@QTKEYCHAIN_TARGET_NAME@")
set(QTKEYCHAIN_FOUND TRUE)

View File

@ -0,0 +1,11 @@
set(PACKAGE_VERSION "@QTKEYCHAIN_VERSION@")
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()

View File

@ -0,0 +1,15 @@
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:** Windows does not provide a service for secure storage. 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.

15
ext/qtkeychain/ReadMe.txt Normal file
View File

@ -0,0 +1,15 @@
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:** Windows does not provide a service for secure storage. 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.

View 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()

View File

@ -0,0 +1,71 @@
#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* 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, static_cast<char*>(0) );
}
GnomeKeyring::gpointer GnomeKeyring::find_network_password( const gchar* user, const gchar* server,
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, 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("gnome-keyring", 0)
{
static const PasswordSchema schema = {
ITEM_NETWORK_PASSWORD,
{{ "user", ATTRIBUTE_TYPE_STRING },
{ "server", 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;
}

View File

@ -0,0 +1,88 @@
#ifndef QTKEYCHAIN_GNOME_P_H
#define QTKEYCHAIN_GNOME_P_H
#include <QLibrary>
class GnomeKeyring : private QLibrary {
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, 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* password,
OperationDoneCallback callback, gpointer data, GDestroyNotify destroy_data );
static gpointer find_network_password( const gchar* user, const gchar* server,
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

229
ext/qtkeychain/keychain.cpp Normal file
View File

@ -0,0 +1,229 @@
/******************************************************************************
* 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( const QString& service, QObject *parent )
: QObject( parent )
, d ( new JobPrivate( service ) ) {
}
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::emitFinished() {
emit finished( this );
if ( d->autoDelete )
deleteLater();
}
void Job::emitFinishedWithError( Error error, const QString& errorString ) {
d->error = error;
d->errorString = errorString;
emitFinished();
}
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( service, parent )
, d( new ReadPasswordJobPrivate( this ) )
{}
ReadPasswordJob::~ReadPasswordJob() {
delete d;
}
QString ReadPasswordJob::textData() const {
return QString::fromUtf8( d->data );
}
QByteArray ReadPasswordJob::binaryData() const {
return d->data;
}
QString ReadPasswordJob::key() const {
return d->key;
}
void ReadPasswordJob::setKey( const QString& key ) {
d->key = key;
}
void ReadPasswordJob::doStart() {
JobExecutor::instance()->enqueue( this );
}
WritePasswordJob::WritePasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new WritePasswordJobPrivate( this ) ) {
}
WritePasswordJob::~WritePasswordJob() {
delete d;
}
QString WritePasswordJob::key() const {
return d->key;
}
void WritePasswordJob::setKey( const QString& key ) {
d->key = key;
}
void WritePasswordJob::setBinaryData( const QByteArray& data ) {
d->binaryData = data;
d->mode = WritePasswordJobPrivate::Binary;
}
void WritePasswordJob::setTextData( const QString& data ) {
d->textData = data;
d->mode = WritePasswordJobPrivate::Text;
}
void WritePasswordJob::doStart() {
JobExecutor::instance()->enqueue( this );
}
DeletePasswordJob::DeletePasswordJob( const QString& service, QObject* parent )
: Job( service, parent )
, d( new DeletePasswordJobPrivate( this ) ) {
}
DeletePasswordJob::~DeletePasswordJob() {
delete d;
}
void DeletePasswordJob::doStart() {
//Internally, to delete a password we just execute a write job with no data set (null byte array).
//In all current implementations, this deletes the entry so this is sufficient
WritePasswordJob* job = new WritePasswordJob( service(), this );
connect( job, SIGNAL(finished(QKeychain::Job*)), d, SLOT(jobFinished(QKeychain::Job*)) );
job->setInsecureFallback(true);
job->setSettings(settings());
job->setKey( d->key );
job->doStart();
}
QString DeletePasswordJob::key() const {
return d->key;
}
void DeletePasswordJob::setKey( const QString& key ) {
d->key = key;
}
void DeletePasswordJobPrivate::jobFinished( Job* job ) {
q->setError( job->error() );
q->setErrorString( job->errorString() );
q->emitFinished();
}
JobExecutor::JobExecutor()
: QObject( 0 )
, m_runningJob( 0 )
{
}
void JobExecutor::enqueue( Job* job ) {
m_queue.append( job );
startNextIfNoneRunning();
}
void JobExecutor::startNextIfNoneRunning() {
if ( m_queue.isEmpty() || m_runningJob )
return;
QPointer<Job> next;
while ( !next && !m_queue.isEmpty() ) {
next = m_queue.first();
m_queue.pop_front();
}
if ( next ) {
connect( next, SIGNAL(finished(QKeychain::Job*)), this, SLOT(jobFinished(QKeychain::Job*)) );
connect( next, SIGNAL(destroyed(QObject*)), this, SLOT(jobDestroyed(QObject*)) );
m_runningJob = next;
if ( ReadPasswordJob* rpj = qobject_cast<ReadPasswordJob*>( m_runningJob ) )
rpj->d->scheduledStart();
else if ( WritePasswordJob* wpj = qobject_cast<WritePasswordJob*>( m_runningJob) )
wpj->d->scheduledStart();
}
}
void JobExecutor::jobDestroyed( QObject* object ) {
Q_UNUSED( object ) // for release mode
Q_ASSERT( object == m_runningJob );
m_runningJob->disconnect( this );
m_runningJob = 0;
startNextIfNoneRunning();
}
void JobExecutor::jobFinished( Job* job ) {
Q_UNUSED( job ) // for release mode
Q_ASSERT( job == m_runningJob );
m_runningJob->disconnect( this );
m_runningJob = 0;
startNextIfNoneRunning();
}
JobExecutor* JobExecutor::s_instance = 0;
JobExecutor* JobExecutor::instance() {
if ( !s_instance )
s_instance = new JobExecutor;
return s_instance;
}

145
ext/qtkeychain/keychain.h Normal file
View File

@ -0,0 +1,145 @@
/******************************************************************************
* 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
#include "qkeychain_export.h"
#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;
class QKEYCHAIN_EXPORT Job : public QObject {
Q_OBJECT
public:
explicit Job( const QString& service, QObject* parent=0 );
~Job();
QSettings* settings() const;
void setSettings( QSettings* settings );
void start();
QString service() const;
Error error() const;
QString errorString() const;
bool autoDelete() const;
void setAutoDelete( bool autoDelete );
bool insecureFallback() const;
void setInsecureFallback( bool insecureFallback );
Q_SIGNALS:
void finished( QKeychain::Job* );
protected:
Q_INVOKABLE virtual void doStart() = 0;
void setError( Error error );
void setErrorString( const QString& errorString );
void emitFinished();
void emitFinishedWithError(Error, const QString& errorString);
private:
JobPrivate* const d;
};
class ReadPasswordJobPrivate;
class QKEYCHAIN_EXPORT ReadPasswordJob : public Job {
Q_OBJECT
public:
explicit ReadPasswordJob( const QString& service, QObject* parent=0 );
~ReadPasswordJob();
QString key() const;
void setKey( const QString& key );
QByteArray binaryData() const;
QString textData() const;
protected:
void doStart();
private:
friend class QKeychain::ReadPasswordJobPrivate;
friend class QKeychain::JobExecutor;
ReadPasswordJobPrivate* const d;
};
class WritePasswordJobPrivate;
class QKEYCHAIN_EXPORT WritePasswordJob : public Job {
Q_OBJECT
public:
explicit WritePasswordJob( const QString& service, QObject* parent=0 );
~WritePasswordJob();
QString key() const;
void setKey( const QString& key );
void setBinaryData( const QByteArray& data );
void setTextData( const QString& data );
protected:
void doStart();
private:
friend class QKeychain::JobExecutor;
friend class QKeychain::WritePasswordJobPrivate;
friend class DeletePasswordJob;
WritePasswordJobPrivate* const d;
};
class DeletePasswordJobPrivate;
class QKEYCHAIN_EXPORT DeletePasswordJob : public Job {
Q_OBJECT
public:
explicit DeletePasswordJob( const QString& service, QObject* parent=0 );
~DeletePasswordJob();
QString key() const;
void setKey( const QString& key );
protected:
void doStart();
private:
friend class QKeychain::DeletePasswordJobPrivate;
DeletePasswordJobPrivate* const d;
};
} // namespace QtKeychain
#endif

View File

@ -0,0 +1,161 @@
/******************************************************************************
* 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( "%1 (OSStatus %2)" )
.arg( "OSX Keychain Error" ).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;
if ( mode == Delete ) {
const Error derr = deleteEntryImpl( q->service(), key, &errorString );
if ( derr != NoError )
error = CouldNotDeleteEntry;
q->emitFinishedWithError( error, errorString );
return;
}
const QByteArray data = mode == Text ? textData.toUtf8() : binaryData;
error = writeEntryImpl( q->service(), key, data, &errorString );
q->emitFinishedWithError( error, errorString );
}

163
ext/qtkeychain/keychain_p.h Normal file
View 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'. *
*****************************************************************************/
#ifndef KEYCHAIN_P_H
#define KEYCHAIN_P_H
#include <QCoreApplication>
#include <QObject>
#include <QPointer>
#include <QSettings>
#include <QVector>
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
#include <QDBusPendingCallWatcher>
#include "kwallet_interface.h"
#else
class QDBusPendingCallWatcher;
#endif
#include "keychain.h"
namespace QKeychain {
class JobExecutor;
class JobPrivate : public QObject {
Q_OBJECT
public:
JobPrivate( const QString& service_ )
: error( NoError )
, service( service_ )
, autoDelete( true )
, insecureFallback( false ) {}
QKeychain::Error error;
QString errorString;
QString service;
bool autoDelete;
bool insecureFallback;
QPointer<QSettings> settings;
};
class ReadPasswordJobPrivate : public QObject {
Q_OBJECT
public:
explicit ReadPasswordJobPrivate( ReadPasswordJob* qq ) : q( qq ), walletHandle( 0 ), dataType( Text ) {}
void scheduledStart();
ReadPasswordJob* const q;
QByteArray data;
QString key;
int walletHandle;
enum DataType {
Binary,
Text
};
DataType dataType;
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
org::kde::KWallet* iface;
static void gnomeKeyring_cb( int result, const char* string, ReadPasswordJobPrivate* data );
friend class QKeychain::JobExecutor;
void fallbackOnError(const QDBusError& err);
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* watcher );
void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* watcher );
void kwalletReadFinished( QDBusPendingCallWatcher* watcher );
#else //moc's too dumb to respect above macros, so just define empty slot implementations
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* ) {}
void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
void kwalletEntryTypeFinished( QDBusPendingCallWatcher* ) {}
void kwalletReadFinished( QDBusPendingCallWatcher* ) {}
#endif
};
class WritePasswordJobPrivate : public QObject {
Q_OBJECT
public:
explicit WritePasswordJobPrivate( WritePasswordJob* qq ) : q( qq ), mode( Delete ) {}
void scheduledStart();
enum Mode {
Delete,
Text,
Binary
};
static QString modeToString(Mode m);
static Mode stringToMode(const QString& s);
WritePasswordJob* const q;
Mode mode;
QString key;
QByteArray binaryData;
QString textData;
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
org::kde::KWallet* iface;
static void gnomeKeyring_cb( int result, WritePasswordJobPrivate* self );
friend class QKeychain::JobExecutor;
void fallbackOnError(const QDBusError& err);
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* watcher );
void kwalletOpenFinished( QDBusPendingCallWatcher* watcher );
void kwalletWriteFinished( QDBusPendingCallWatcher* watcher );
#else
private Q_SLOTS:
void kwalletWalletFound( QDBusPendingCallWatcher* ) {}
void kwalletOpenFinished( QDBusPendingCallWatcher* ) {}
void kwalletWriteFinished( QDBusPendingCallWatcher* ) {}
#endif
};
class DeletePasswordJobPrivate : public QObject {
Q_OBJECT
public:
explicit DeletePasswordJobPrivate( DeletePasswordJob* qq ) : q( qq ) {}
void doStart();
DeletePasswordJob* const q;
QString key;
private Q_SLOTS:
void jobFinished( QKeychain::Job* );
};
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;
Job* m_runningJob;
QVector<QPointer<Job> > m_queue;
};
}
#endif // KEYCHAIN_P_H

View File

@ -0,0 +1,510 @@
/******************************************************************************
* 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 <QSettings>
#include <QScopedPointer>
using namespace QKeychain;
static QString typeKey( const QString& key )
{
return QString::fromLatin1( "%1/type" ).arg( key );
}
static QString dataKey( const QString& key )
{
return QString::fromLatin1( "%1/data" ).arg( key );
}
enum KeyringBackend {
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() {
QString 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();
}
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 KeyringBackend detectKeyringBackend()
{
switch (detectDesktopEnvironment()) {
case DesktopEnv_Kde4:
return Backend_Kwallet4;
break;
case DesktopEnv_Plasma5:
return Backend_Kwallet5;
break;
// fall through
case DesktopEnv_Gnome:
case DesktopEnv_Unity:
case DesktopEnv_Xfce:
case DesktopEnv_Other:
default:
if ( GnomeKeyring::isAvailable() ) {
return Backend_GnomeKeyring;
} else {
return Backend_Kwallet4;
}
}
}
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_GnomeKeyring:
if ( !GnomeKeyring::find_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(),
reinterpret_cast<GnomeKeyring::OperationGetStringCallback>( &ReadPasswordJobPrivate::gnomeKeyring_cb ),
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 ReadPasswordJobPrivate::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 ReadPasswordJobPrivate::gnomeKeyring_cb( int result, const char* string, ReadPasswordJobPrivate* self )
{
if ( result == GnomeKeyring::RESULT_OK ) {
if ( self->dataType == ReadPasswordJobPrivate::Text )
self->data = string;
else
self->data = QByteArray::fromBase64( string );
self->q->emitFinished();
} else {
const QPair<Error, QString> errorResult = mapGnomeKeyringError( result );
self->q->emitFinishedWithError( errorResult.first, errorResult.second );
}
}
void ReadPasswordJobPrivate::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() && actual->contains( dataKey( key ) ) ) {
const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() );
if (mode == WritePasswordJobPrivate::Binary)
dataType = Binary;
else
dataType = Text;
data = actual->value( dataKey( key ) ).toByteArray();
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;
QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.data();
if ( reply.isError() ) {
fallbackOnError( reply.error() );
return;
}
if ( actual->contains( dataKey( key ) ) ) {
// We previously stored data in the insecure QSettings, but now have KWallet available.
// Do the migration
data = actual->value( dataKey( key ) ).toByteArray();
const WritePasswordJobPrivate::Mode mode = WritePasswordJobPrivate::stringToMode( actual->value( typeKey( key ) ).toString() );
actual->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:
dataType = Text;
break;
case Stream:
dataType = 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 = dataType == 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(kwalletReadFinished(QDBusPendingCallWatcher*)) );
}
void ReadPasswordJobPrivate::kwalletReadFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
if ( watcher->isError() ) {
const QDBusError err = watcher->error();
q->emitFinishedWithError( OtherError, tr("Could not read password: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
if ( dataType == Binary ) {
QDBusPendingReply<QByteArray> reply = *watcher;
data = reply.value();
} else {
QDBusPendingReply<QString> reply = *watcher;
data = reply.value().toUtf8();
}
q->emitFinished();
}
static void kwalletWritePasswordScheduledStart( const char * service, const char * path, WritePasswordJobPrivate * 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_GnomeKeyring:
if ( mode == WritePasswordJobPrivate::Delete ) {
if ( !GnomeKeyring::delete_network_password( key.toUtf8().constData(), q->service().toUtf8().constData(),
reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &WritePasswordJobPrivate::gnomeKeyring_cb ),
this, 0 ) )
q->emitFinishedWithError( OtherError, tr("Unknown error") );
} else {
QByteArray password = mode == WritePasswordJobPrivate::Text ? textData.toUtf8() : binaryData.toBase64();
QByteArray service = q->service().toUtf8();
if ( !GnomeKeyring::store_network_password( GnomeKeyring::GNOME_KEYRING_DEFAULT, service.constData(),
key.toUtf8().constData(), service.constData(), password.constData(),
reinterpret_cast<GnomeKeyring::OperationDoneCallback>( &WritePasswordJobPrivate::gnomeKeyring_cb ),
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;
}
}
QString WritePasswordJobPrivate::modeToString(Mode m)
{
switch (m) {
case Delete:
return QLatin1String("Delete");
case Text:
return QLatin1String("Text");
case Binary:
return QLatin1String("Binary");
}
Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled Mode value");
return QString();
}
WritePasswordJobPrivate::Mode WritePasswordJobPrivate::stringToMode(const QString& s)
{
if (s == QLatin1String("Delete") || s == QLatin1String("0"))
return Delete;
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;
}
void WritePasswordJobPrivate::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;
}
if ( mode == Delete ) {
actual->remove( key );
actual->sync();
q->emitFinished();
return;
}
actual->setValue( QString::fromLatin1( "%1/type" ).arg( key ), mode );
if ( mode == Text )
actual->setValue( QString::fromLatin1( "%1/data" ).arg( key ), textData.toUtf8() );
else if ( mode == Binary )
actual->setValue( QString::fromLatin1( "%1/data" ).arg( key ), binaryData );
actual->sync();
q->emitFinished();
}
void WritePasswordJobPrivate::gnomeKeyring_cb( int result, WritePasswordJobPrivate* 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 WritePasswordJobPrivate::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*)) );
}
void WritePasswordJobPrivate::kwalletOpenFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
QDBusPendingReply<int> reply = *watcher;
QScopedPointer<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.data();
if ( reply.isError() ) {
fallbackOnError( reply.error() );
return;
}
if ( actual->contains( key ) )
{
// If we had previously written to QSettings, but we now have a kwallet available, migrate and delete old insecure data
actual->remove( key );
actual->sync();
}
const int handle = reply.value();
if ( handle < 0 ) {
q->emitFinishedWithError( AccessDenied, tr("Access to keychain denied") );
return;
}
QDBusPendingReply<int> nextReply;
if ( !textData.isEmpty() )
nextReply = iface->writePassword( handle, q->service(), key, textData, q->service() );
else if ( !binaryData.isEmpty() )
nextReply = iface->writeEntry( handle, q->service(), key, binaryData, 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(kwalletWriteFinished(QDBusPendingCallWatcher*)) );
}
void WritePasswordJobPrivate::kwalletWriteFinished( QDBusPendingCallWatcher* watcher ) {
watcher->deleteLater();
QDBusPendingReply<int> reply = *watcher;
if ( reply.isError() ) {
const QDBusError err = reply.error();
q->emitFinishedWithError( OtherError, tr("Could not open wallet: %1; %2").arg( QDBusError::errorString( err.type() ), err.message() ) );
return;
}
q->emitFinished();
}

View File

@ -0,0 +1,107 @@
/******************************************************************************
* 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 <QSettings>
#include <windows.h>
#include <wincrypt.h>
#include <memory>
using namespace QKeychain;
void ReadPasswordJobPrivate::scheduledStart() {
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
QByteArray encrypted = actual->value( key ).toByteArray();
if ( encrypted.isNull() ) {
q->emitFinishedWithError( EntryNotFound, tr("Entry not found") );
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() {
if ( mode == Delete ) {
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
actual->remove( key );
actual->sync();
if ( actual->status() != QSettings::NoError ) {
const QString err = actual->status() == QSettings::AccessError
? tr("Could not delete encrypted data from settings: access error")
: tr("Could not delete encrypted data from settings: format error");
q->emitFinishedWithError( OtherError, err );
} else {
q->emitFinished();
}
return;
}
QByteArray data = mode == Binary ? binaryData : textData.toUtf8();
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 );
//Use settings member if there, create local settings object if not
std::auto_ptr<QSettings> local( !q->settings() ? new QSettings( q->service() ) : 0 );
QSettings* actual = q->settings() ? q->settings() : local.get();
actual->setValue( key, encrypted );
actual->sync();
if ( actual->status() != QSettings::NoError ) {
const QString errorString = actual->status() == QSettings::AccessError
? tr("Could not store encrypted data in settings: access error")
: tr("Could not store encrypted data in settings: format error");
q->emitFinishedWithError( OtherError, errorString );
return;
}
q->emitFinished();
}

View 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>

View File

@ -0,0 +1,17 @@
#ifndef QKEYCHAIN_EXPORT_H
#define QKEYCHAIN_EXPORT_H
#include <qglobal.h>
# ifdef QKEYCHAIN_STATICLIB
# undef QKEYCHAIN_SHAREDLIB
# define QKEYCHAIN_EXPORT
# else
# ifdef QKEYCHAIN_BUILD_QKEYCHAIN_LIB
# define QKEYCHAIN_EXPORT Q_DECL_EXPORT
# else
# define QKEYCHAIN_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif

View File

@ -0,0 +1,98 @@
/******************************************************************************
* 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("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();
}
}

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="de_DE">
<context>
<name>QKeychain::ReadPasswordJobPrivate</name>
<message>
<location filename="../keychain_unix.cpp" line="140"/>
<source>Unknown error</source>
<translation>Unbekannter Fehler</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="129"/>
<source>D-Bus is not running</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="222"/>
<source>No keychain service available</source>
<translation>Kein Schlüsselbund-Dienst verfügbar</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="224"/>
<source>Could not open wallet: %1; %2</source>
<translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="270"/>
<source>Access to keychain denied</source>
<translation>Zugriff auf Schlüsselbund verweigert</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="291"/>
<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="309"/>
<source>Unsupported entry type &apos;Map&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="312"/>
<source>Unknown kwallet entry type &apos;%1&apos;</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="327"/>
<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="300"/>
<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="364"/>
<location filename="../keychain_unix.cpp" line="372"/>
<source>Unknown error</source>
<translation>Unbekannter Fehler</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="352"/>
<source>D-Bus is not running</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="420"/>
<location filename="../keychain_unix.cpp" line="505"/>
<source>Could not open wallet: %1; %2</source>
<translation>Konnte Brieftasche nicht öffnen: %1; %2</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="483"/>
<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="167"/>
<source>Access to keychain denied</source>
<translation>Zugriff auf Schlüsselbund verweigert</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="169"/>
<source>No keyring daemon</source>
<translation>Kein Schlüsselbund-Dienst </translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="171"/>
<source>Already unlocked</source>
<translation>Bereits entsperrt</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="173"/>
<source>No such keyring</source>
<translation>Kein solcher Schlüsselbund</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="175"/>
<source>Bad arguments</source>
<translation>Ungültige Argumente</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="177"/>
<source>I/O error</source>
<translation>Ein-/Ausgabe-Fehler</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="179"/>
<source>Cancelled</source>
<translation>Abgebrochen</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="181"/>
<source>Keyring already exists</source>
<translation>Schlüsselbund existiert bereits</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="183"/>
<source>No match</source>
<translation>Kein Treffer</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="188"/>
<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>

View File

@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ro_RO">
<context>
<name>QKeychain::ReadPasswordJobPrivate</name>
<message>
<location filename="../keychain_unix.cpp" line="140"/>
<source>Unknown error</source>
<translation>Eroare necunoscută</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="129"/>
<source>D-Bus is not running</source>
<translation>D-Bus nu rulează</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="222"/>
<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="224"/>
<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="270"/>
<source>Access to keychain denied</source>
<translation>Acces interzis la serviciul de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="291"/>
<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="309"/>
<source>Unsupported entry type &apos;Map&apos;</source>
<translation>Tip de înregistrare nesuportat &apos;Map&apos;</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="312"/>
<source>Unknown kwallet entry type &apos;%1&apos;</source>
<translation>Tip de înregistrare kwallet necunoscut &apos;%1&apos;</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="327"/>
<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="300"/>
<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="364"/>
<location filename="../keychain_unix.cpp" line="372"/>
<source>Unknown error</source>
<translation>Eroare necunoscută</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="352"/>
<source>D-Bus is not running</source>
<translation>D-Bus nu rulează</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="420"/>
<location filename="../keychain_unix.cpp" line="505"/>
<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="483"/>
<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="167"/>
<source>Access to keychain denied</source>
<translation>Acces interzis la serviciul de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="169"/>
<source>No keyring daemon</source>
<translation>Niciun demon pentru inelul de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="171"/>
<source>Already unlocked</source>
<translation>Deja deblocat</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="173"/>
<source>No such keyring</source>
<translation>Nu există astfel de inel de chei</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="175"/>
<source>Bad arguments</source>
<translation>Argumente greșite</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="177"/>
<source>I/O error</source>
<translation>Eroare de I/E</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="179"/>
<source>Cancelled</source>
<translation>Anulat</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="181"/>
<source>Keyring already exists</source>
<translation>Inelul de chei deja există</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="183"/>
<source>No match</source>
<translation>Nicio potrivire</translation>
</message>
<message>
<location filename="../keychain_unix.cpp" line="188"/>
<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>

View File

@ -58,7 +58,8 @@ SOURCES += src/main.cpp\
src/Fossil.cpp \
src/Workspace.cpp \
src/SearchBox.cpp \
src/Settings.cpp
src/Settings.cpp \
src/RemoteDialog.cpp
HEADERS += src/MainWindow.h \
src/CommitDialog.h \
@ -75,7 +76,8 @@ HEADERS += src/MainWindow.h \
src/Fossil.h \
src/Workspace.h \
src/SearchBox.h \
src/Settings.h
src/Settings.h \
src/RemoteDialog.h
FORMS += ui/MainWindow.ui \
ui/CommitDialog.ui \
@ -84,11 +86,40 @@ FORMS += ui/MainWindow.ui \
ui/FslSettingsDialog.ui \
ui/CloneDialog.ui \
ui/BrowserWidget.ui \
ui/RevisionDialog.ui
ui/RevisionDialog.ui \
ui/RemoteDialog.ui
RESOURCES += \
rsrc/resources.qrc
# QtKeychain
SOURCES += ext/qtkeychain/keychain.cpp
HEADERS += ext/qtkeychain/keychain.h \
ext/qtkeychain/keychain_p.h \
ext/qtkeychain/qkeychain_export.h
unix:!macx {
QT += dbus
SOURCES += ext/qtkeychain/keychain_unix.cpp \
ext/qtkeychain/gnomekeyring.cpp
HEADERS += ext/qtkeychain/gnomekeyring_p.h
DBUS_INTERFACES += ext/qtkeychain/org.kde.KWallet.xml
}
macx {
SOURCES += ext/qtkeychain/keychain_mac.cpp
}
win32 {
SOURCES += ext/qtkeychain/keychain_win.cpp
}
CODECFORTR = UTF-8
TRANSLATIONS += \

View File

@ -1,5 +1,5 @@
C Merged\snew\sworkspace
D 2015-05-26T18:03:05.443
C Merged\sremote-management
D 2015-05-29T19:26:31.501
F .travis.yml 77966888a81c4ceee1fcc79bce842c9667ad8a35
F debian/changelog eb4304dfcb6bb66850ec740838090eb50ce1249b
F debian/compat b6abd567fa79cbe0196d093a067271361dc6ca8b
@ -15,7 +15,29 @@ F dist/win/fuel.iss ef3558dbba409eb194938b930377fc9ee27d319e
F doc/Building.txt 17b43fa23da764b5d1b828cc48c5a95e612bbd8f
F doc/Changes.txt b03302545e4a6c0b16a30d623a7627f8aef65ef6
F doc/License.txt 4cc77b90af91e615a64ae04893fdffa7939db84c
F fuel.pro b010c4ee3093112003a9d27045927efce5985dab
F ext/qtkeychain/CMakeLists.txt fc1afa05034f2765ba243ce758a7e9d6b6efe2d6
F ext/qtkeychain/COPYING d0f83c8198fdd5464d2373015b7b64ce7cae607e
F ext/qtkeychain/ChangeLog 1703279e17036995806ba1719033d14840a8a7e2
F ext/qtkeychain/QtKeychainBuildTreeSettings.cmake.in a50c3b646181124f15b946c3297f13012e959341
F ext/qtkeychain/QtKeychainConfig.cmake.in ac7c87e54854a06c51e00f833f21f8323d1e6884
F ext/qtkeychain/QtKeychainConfigVersion.cmake.in 3b650037d5775f28802c0471afe2cf6dbe51084e
F ext/qtkeychain/ReadMe.markdown 65fe7f400600aa98a9a7fa5c3fc842ad8699cc43
F ext/qtkeychain/ReadMe.txt 65fe7f400600aa98a9a7fa5c3fc842ad8699cc43
F ext/qtkeychain/cmake/Modules/GNUInstallDirs.cmake 7a2ccf81f25546e93a6f48c792cdebaae51857e9
F ext/qtkeychain/gnomekeyring.cpp 7fa97bd4ffb7c9df00d25e56cd9173d34109afe6
F ext/qtkeychain/gnomekeyring_p.h 7f6acaf6d00b36bd08f5f31ba9136efa969e9875
F ext/qtkeychain/keychain.cpp 427cbda7c6a76de995b1f1b4caa700cd06a9d19a
F ext/qtkeychain/keychain.h f084c671b481af6ac7ce00bf641055a3cfc9cf9b
F ext/qtkeychain/keychain_mac.cpp a028f6fc5e40f9ab88c94ebe30b8b0ae417f2f34
F ext/qtkeychain/keychain_p.h 36f4caee2cbdbde971a1105ab388681ad2924665
F ext/qtkeychain/keychain_unix.cpp 8e657da9acd9e86b2fdec19dc40f1afa4a1c5191
F ext/qtkeychain/keychain_win.cpp e52877828703650219c1c674e618c7211f588d0d
F ext/qtkeychain/org.kde.KWallet.xml f3729fda9f8fa8031a6f69415dcc29455c3c9ae6
F ext/qtkeychain/qkeychain_export.h d756528188ef9bf3c4461ecc80048f06c362b54b
F ext/qtkeychain/testclient.cpp cb1290a9584b627306a7bfdf1c02a8bbae503f5f
F ext/qtkeychain/translations/qtkeychain_de.ts 0a70c8205c066c30ed8172f0670942de1fc5eede
F ext/qtkeychain/translations/qtkeychain_ro.ts f16939382fd1a047b0692426bc82847347f14b32
F fuel.pro d67b78b5257d5d39bf4d0c2a4b9fbba1dbdd7005
F intl/convert.bat 4222ae403418381452b843929d15259ea9850ab1 x
F intl/convert.sh 2ca2179ff53e727f241925b75e19182607883c45 x
F intl/de_DE.ts e2faceab920ac60c97bbc6fba038e261d51fc741
@ -196,26 +218,28 @@ F src/FileActionDialog.cpp fcaebf9986f789b3440d5390b3458ad5f86fe0c8
F src/FileActionDialog.h 15db1650b3a13d70bc338371e4c033c66e3b79ce
F src/FileTableView.cpp 5ddf8c391c9a3ac449ec61fb1db837b577afeec2
F src/FileTableView.h 03e56d87c2d46411b9762b87f4d301619aaf18df
F src/Fossil.cpp 0d4c50327a61c48506d2d45e28cd6f71f1697ea2
F src/Fossil.h 31765ef57e20a860914372d56c024033b30aa765
F src/FslSettingsDialog.cpp f5a34a70ecb0560d2b6eea6bf27e42048548aedd
F src/Fossil.cpp 2b03aeec20149a1b89f10118058fac8ca33425fc
F src/Fossil.h e706992b331385660d57df6a27e5418342c14e19
F src/FslSettingsDialog.cpp 2531d3709f0eab66651671e3edead2ca720d07d5
F src/FslSettingsDialog.h dfe2a61884a55a74cbb9206b6f6b482b979725e7
F src/LoggedProcess.cpp 2a1e5c94bc1e57c8984563e66c210e43a14dc60c
F src/LoggedProcess.h 85df7c635c807a5a0e8c4763f17a0752aaff7261
F src/MainWindow.cpp 4cbfa1fdf3092b97649711388576230e4808d50e
F src/MainWindow.h a848462f21423b5c1e0c218cab1805c308299607
F src/MainWindow.cpp bba716022fd683843abc07be47fdca3e15391e1f
F src/MainWindow.h 1ecfb255c8e013cf4e2d07bfd485ed41d26c97c7
F src/RemoteDialog.cpp 7f4272117080260c31c748e2ada3e33adc024826
F src/RemoteDialog.h 5e0438c2bd7c79b1bb44bfbd58c2181b544a9e5d
F src/RevisionDialog.cpp 51065c65a07c118dd1a7363da4a55a135d1c6c9c
F src/RevisionDialog.h b718c3009342eaabad39c8a11a253a4e4fef7a73
F src/SearchBox.cpp d4209c575baa9933e1ce5ed376e785b289a145ba
F src/SearchBox.h 0c78d3a68136dab3e0e71b83ae36f22bd2688ab2
F src/Settings.cpp 6ab826273b9693bfcd65f0f59b550ae2aa3577f1
F src/Settings.h 1ff8bb71e19949150e8caa4f5e5f13f8810e496b
F src/Settings.cpp 7a674604caa9d9f5ffb6b73d95745bde09525389
F src/Settings.h 883ac5c0f38a6ac93b400b7b96447b017ee50c06
F src/SettingsDialog.cpp 25be4c351dd21ea9132321944f42dc0bc22fb128
F src/SettingsDialog.h b324dfd77ca3ad24fd83588aaf79a7e4c291e716
F src/Utils.cpp c48e3316f3a0a5d735e8d4953710500358985e32
F src/Utils.h c2a28611bd77fb35ea3dcf65fb60ed585f68aa3c
F src/Workspace.cpp f68a4ca05d1b7c5c345fbd89527691813593c663
F src/Workspace.h d6649a3ae1cd0fbad55237030313e85530417271
F src/Utils.cpp 09ba0fc6d6d293ebbd2f7c7283286b68d3cb04fb
F src/Utils.h e22c5d86722e3987894fa06bdee3d57597ff425e
F src/Workspace.cpp dd2dfb259fd917bd8434b5f00d438fdd9643242f
F src/Workspace.h 7ae2e63196433ae34864d182e49e3a2f0726fb78
F src/main.cpp d8c65ea5e54102e4989fef9fd8cfd4f13ef8a8f0
F tools/git-push.sh 62cc58434cae5b7bcd6bd9d4cce8b08739f31cd7 x
F tools/pack.sh d7f38a498c4e9327fecd6a6e5ac27be270d43008 x
@ -223,12 +247,13 @@ F ui/BrowserWidget.ui 994ad9ea0e9f5815d6b1a27acc2f6f39164c507f
F ui/CloneDialog.ui 4886e7d4f258ea8b852b5eefc860396e35145712
F ui/CommitDialog.ui aea77347eef82b6b591f31fb058a1bb96193c728
F ui/FileActionDialog.ui 89bb4dc2d0b8adcd41adcb11ec65f2028a09a12d
F ui/FslSettingsDialog.ui 042717833d8efea905b4fc380bad580be809717d
F ui/MainWindow.ui 2d36b1ba9886356802f2cddf12c4cabb5ee1acde
F ui/FslSettingsDialog.ui eb3d4cb764cab90b01e82922237d8c42d6ce1749
F ui/MainWindow.ui 5857b45ed96fb027b6159e44742e9afaa3e89cfb
F ui/RemoteDialog.ui 95a4750d972ed8c49bb10b95db91ff16cfe2dd0b
F ui/RevisionDialog.ui 27c3b98c665fec014a50cbf3352c0627f75e68cd
F ui/SettingsDialog.ui 4c480cd595a32664d01c85bf74845c4282fc0068
P e2ba8f0aba679a1c52d36daf3041cd139bd49579 7ebac37b3255e6721889c070b7327a96c63b6c97
R 8f806c4029c039de40ed9c8c7d5c5dca
T +closed 7ebac37b3255e6721889c070b7327a96c63b6c97
P 322729110f48bfb8c007dd611d47e411b389044a 1f10dd85e5d7e95539baaea140623673b19f12f2
R ddb33050a72347b140cbfa284e3483f6
T +closed 1f10dd85e5d7e95539baaea140623673b19f12f2
U kostas
Z 0150f2a9d78e86830f0a9f5b26438204
Z a7be8a80a6ffb699e0de683c517f0113

View File

@ -1 +1 @@
322729110f48bfb8c007dd611d47e411b389044a
4c246e5290d2cb3b1a90127fe953bffbbf77ea53

View File

@ -70,6 +70,10 @@ RepoStatus Fossil::getRepoStatus()
}
}
defaultRemoteUrl.clear();
if(run_ok)
getRemoteUrl(defaultRemoteUrl);
return run_ok ? REPO_OK : REPO_NOT_FOUND;
}
@ -128,15 +132,51 @@ bool Fossil::status(QStringList &result)
}
//------------------------------------------------------------------------------
bool Fossil::pushRepository()
bool Fossil::pushRepository(const QUrl &url)
{
return runFossil(QStringList() << "push");
QStringList params;
params << "push";
int runFlags=RUNFLAGS_NONE;
if(!url.isEmpty())
{
params << url.toString();
params << "--once";
QStringList log_params = params;
log_params[1] = url.toDisplayString();
log_params.push_front("fossil");
runFlags = RUNFLAGS_SILENT_INPUT;
log("<b>&gt;"+log_params.join(" ")+"</b><br>", true);
}
return runFossil(params, 0, runFlags);
}
//------------------------------------------------------------------------------
bool Fossil::pullRepository()
bool Fossil::pullRepository(const QUrl &url)
{
return runFossil(QStringList() << "pull");
QStringList params;
params << "pull";
int runFlags=RUNFLAGS_NONE;
if(!url.isEmpty())
{
params << url.toString();
params << "--once";
QStringList log_params = params;
log_params[1] = url.toDisplayString();
log_params.push_front("fossil");
runFlags = RUNFLAGS_SILENT_INPUT;
log("<b>&gt;"+log_params.join(" ")+"</b><br>", true);
}
return runFossil(params, 0, runFlags);
}
//------------------------------------------------------------------------------
@ -361,20 +401,25 @@ bool Fossil::setFossilSetting(const QString& name, const QString& value, bool gl
}
//------------------------------------------------------------------------------
bool Fossil::setRemoteUrl(const QString& url)
bool Fossil::setRemoteUrl(const QUrl& url)
{
QString u = url;
QString u = url.toString(QUrl::FullyEncoded);
if(url.isEmpty())
u = "off";
// Run as silent to avoid displaying credentials in the log
// FIXME: maybe use a QUrl instead
return runFossil(QStringList() << "remote-url" << u, 0, RUNFLAGS_SILENT_INPUT);
bool ok = runFossil(QStringList() << "remote-url" << u, 0, RUNFLAGS_SILENT_INPUT);
// Retrieve default url
if(ok)
getRemoteUrl(defaultRemoteUrl);
return ok;
}
//------------------------------------------------------------------------------
bool Fossil::getRemoteUrl(QString& url)
bool Fossil::getRemoteUrl(QUrl& url)
{
url.clear();
@ -382,8 +427,15 @@ bool Fossil::getRemoteUrl(QString& url)
if(!runFossil(QStringList() << "remote-url", &out, RUNFLAGS_SILENT_ALL))
return false;
QString url_str;
if(out.length()>0)
url = out[0].trimmed();
url_str = out[0].trimmed();
if(url_str == "off")
url.clear();
else
url.setUrl(url_str);
return true;
}

View File

@ -4,7 +4,7 @@
class QStringList;
#include <QString>
#include <QStringList>
#include <QObject>
#include <QUrl>
#include "LoggedProcess.h"
#include "Utils.h"
@ -81,8 +81,8 @@ public:
bool openRepository(const QString &repositoryPath, const QString& workspacePath);
bool newRepository(const QString &repositoryPath);
bool closeRepository();
bool pushRepository();
bool pullRepository();
bool pushRepository(const QUrl& url);
bool pullRepository(const QUrl& url);
bool cloneRepository(const QString &repository, const QUrl &url, const QUrl &proxyUrl);
bool undoRepository(QStringList& result, bool explainOnly);
bool updateRepository(QStringList& result, const QString& revision, bool explainOnly);
@ -103,8 +103,8 @@ public:
bool renameFile(const QString& beforePath, const QString& afterPath, bool renameLocal);
bool getFossilSettings(QStringList& result);
bool setFossilSetting(const QString &name, const QString &value, bool global);
bool setRemoteUrl(const QString &url);
bool getRemoteUrl(QString &url);
bool setRemoteUrl(const QUrl& url);
bool getRemoteUrl(QUrl &url);
bool stashNew(const QStringList& fileList, const QString& name, bool revert);
bool stashList(stashmap_t &stashes);
@ -128,6 +128,8 @@ public:
const QString &getUIHttpPort() const { return fossilUIPort; }
QString getUIHttpAddress() const;
const QUrl &getDefaultRemoteUrl() const { return defaultRemoteUrl; }
private:
void log(const QString &text, bool isHTML=false)
{
@ -144,6 +146,7 @@ private:
QString repositoryFile;
QString projectName;
QString currentRevision;
QUrl defaultRemoteUrl;
QStringList currentTags;
LoggedProcess fossilUI;
QString fossilUIPort;

View File

@ -20,7 +20,6 @@ FslSettingsDialog::FslSettingsDialog(QWidget *parent, Settings &_settings) :
ui->lineProxy->setText(settings->GetFossilValue(FOSSIL_SETTING_PROXY_URL).toString());
// Repository Settings
ui->lineRemoteURL->setText(settings->GetFossilValue(FOSSIL_SETTING_REMOTE_URL).toString());
ui->lineIgnore->setText(settings->GetFossilValue(FOSSIL_SETTING_IGNORE_GLOB).toString());
ui->lineIgnoreCRNL->setText(settings->GetFossilValue(FOSSIL_SETTING_CRNL_GLOB).toString());
}
@ -47,7 +46,6 @@ void FslSettingsDialog::on_buttonBox_accepted()
settings->SetFossilValue(FOSSIL_SETTING_GMERGE_CMD, ui->lineGMergeCommand->text());
settings->SetFossilValue(FOSSIL_SETTING_PROXY_URL, ui->lineProxy->text());
settings->SetFossilValue(FOSSIL_SETTING_REMOTE_URL, ui->lineRemoteURL->text());
settings->SetFossilValue(FOSSIL_SETTING_IGNORE_GLOB, ui->lineIgnore->text());
settings->SetFossilValue(FOSSIL_SETTING_CRNL_GLOB, ui->lineIgnoreCRNL->text());

View File

@ -19,6 +19,7 @@
#include "FileActionDialog.h"
#include "CloneDialog.h"
#include "RevisionDialog.h"
#include "RemoteDialog.h"
#include "Utils.h"
#define REVISION_LATEST "Latest revision"
@ -58,7 +59,7 @@ struct WorkspaceItem
TYPE_TAGS,
TYPE_TAG,
TYPE_REMOTES,
TYPE_SETTINGS
TYPE_REMOTE,
};
WorkspaceItem()
@ -174,6 +175,16 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
menuBranches->addAction(ui->actionMergeBranch);
menuBranches->addAction(ui->actionUpdate);
// RemotesMenu
menuRemotes = new QMenu(this);
menuRemotes->addAction(ui->actionPushRemote);
menuRemotes->addAction(ui->actionPullRemote);
menuRemotes->addAction(separator);
menuRemotes->addAction(ui->actionAddRemote);
menuRemotes->addAction(ui->actionDeleteRemote);
menuRemotes->addAction(ui->actionEditRemote);
menuRemotes->addAction(ui->actionSetDefaultRemote);
// Recent Workspaces
// Locate a sequence of two separator actions in file menu
QList<QAction*> file_actions = ui->menuFile->actions();
@ -207,7 +218,6 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
ui->statusBar->insertPermanentWidget(1, lblTags);
lblTags->setVisible(true);
// Construct ProgressBar
progressBar = new QProgressBar();
progressBar->setMinimum(0);
@ -244,7 +254,6 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
searchShortcut->setEnabled(true);
connect(searchShortcut, SIGNAL(activated()), this, SLOT(onSearch()));
// Create SearchBox
searchBox = new SearchBox(this);
searchBox->setPlaceholderText(tr("Find (%0)").arg(searchShortcut->key().toString()));
@ -277,6 +286,7 @@ MainWindow::MainWindow(Settings &_settings, QWidget *parent, QString *workspaceP
MainWindow::~MainWindow()
{
stopUI();
getWorkspace().storeWorkspace(*settings.GetStore());
updateSettings();
delete ui;
@ -291,24 +301,14 @@ const QString &MainWindow::getCurrentWorkspace()
//-----------------------------------------------------------------------------
void MainWindow::setCurrentWorkspace(const QString &workspace)
{
if(workspace.isEmpty())
{
fossil().setCurrentWorkspace("");
return;
}
QString new_workspace = QFileInfo(workspace).absoluteFilePath();
fossil().setCurrentWorkspace(new_workspace);
addWorkspace(new_workspace);
if(!QDir::setCurrent(new_workspace))
QMessageBox::critical(this, tr("Error"), tr("Could not change current directory to '%0'").arg(new_workspace), QMessageBox::Ok );
if(!getWorkspace().switchWorkspace(workspace, *settings.GetStore()))
QMessageBox::critical(this, tr("Error"), tr("Could not change current directory to '%0'").arg(workspace), QMessageBox::Ok );
else
addWorkspaceHistory(fossil().getCurrentWorkspace());
}
//------------------------------------------------------------------------------
void MainWindow::addWorkspace(const QString &dir)
void MainWindow::addWorkspaceHistory(const QString &dir)
{
if(dir.isEmpty())
return;
@ -773,7 +773,6 @@ void MainWindow::updateWorkspaceView()
{
const QString &tag_name = it.key();
QStandardItem *tag = new QStandardItem(getInternalIcon(":icons/icon-item-tag"), tag_name);
tag->setData(WorkspaceItem(WorkspaceItem::TYPE_TAG, tag_name), ROLE_WORKSPACE_ITEM);
@ -787,7 +786,6 @@ void MainWindow::updateWorkspaceView()
tags->appendRow(tag);
}
// FIXME: Unique Icon name
// Stashes
QStandardItem *stashes = new QStandardItem(getInternalIcon(":icons/icon-action-repo-open"), tr("Stashes"));
stashes->setData(WorkspaceItem(WorkspaceItem::TYPE_STASHES, ""), ROLE_WORKSPACE_ITEM);
@ -800,21 +798,26 @@ void MainWindow::updateWorkspaceView()
stashes->appendRow(stash);
}
#if 0
// Remotes
QStandardItem *remotes = new QStandardItem(getInternalIcon(":icons/icon-item-remote"), tr("Remotes"));
remotes->setData(WorkspaceItem(WorkspaceItem::TYPE_REMOTES, ""), ROLE_WORKSPACE_ITEM);
remotes->setEditable(false);
getWorkspace().getTreeModel().appendRow(remotes);
#endif
for(remote_map_t::const_iterator it=getWorkspace().getRemotes().begin(); it!=getWorkspace().getRemotes().end(); ++it)
{
QStandardItem *remote_item = new QStandardItem(getInternalIcon(":icons/icon-item-remote"), it->name);
remote_item->setData(WorkspaceItem(WorkspaceItem::TYPE_REMOTE, it->url.toString()), ROLE_WORKSPACE_ITEM);
remote_item->setToolTip(it->url.toDisplayString());
#if 0 // Unimplemented for now
// Settings
QStandardItem *settings = new QStandardItem(getInternalIcon(":icons/icon-action-settings"), tr("Settings"));
settings->setData(WorkspaceItem(WorkspaceItem::TYPE_SETTINGS, ""), ROLE_WORKSPACE_ITEM);
settings->setEditable(false);
getWorkspace().getTreeModel().appendRow(settings);
#endif
// Mark the default url as bold
if(it->isDefault)
{
QFont font = remote_item->font();
font.setBold(true);
remote_item->setFont(font);
}
remotes->appendRow(remote_item);
}
// Expand previously selected nodes
name_map.clear();
@ -956,6 +959,7 @@ void MainWindow::on_actionClearLog_triggered()
void MainWindow::applySettings()
{
QSettings *store = settings.GetStore();
QString active_workspace;
int num_wks = store->beginReadArray("Workspaces");
for(int i=0; i<num_wks; ++i)
@ -967,10 +971,10 @@ void MainWindow::applySettings()
if(wk.isEmpty() || !QDir(wk).exists())
continue;
addWorkspace(wk);
addWorkspaceHistory(wk);
if(store->contains("Active") && store->value("Active").toBool())
setCurrentWorkspace(wk);
active_workspace = wk;
}
store->endArray();
@ -1025,7 +1029,10 @@ void MainWindow::applySettings()
ui->actionViewAsFolders->setChecked(!store->value("ViewAsList").toBool());
viewMode = store->value("ViewAsList").toBool()? VIEWMODE_LIST : VIEWMODE_TREE;
}
//ui->workspaceTreeView->setVisible(viewMode == VIEWMODE_TREE);
// Set the workspace after loading the settings, since it may trigger a remote info storage
if(!active_workspace.isEmpty())
setCurrentWorkspace(active_workspace);
}
//------------------------------------------------------------------------------
@ -1233,9 +1240,26 @@ void MainWindow::getSelectionStashes(QStringList &stashNames)
QString name = mi.model()->data(mi, Qt::DisplayRole).toString();
stashNames.append(name);
}
}
//------------------------------------------------------------------------------
void MainWindow::getSelectionRemotes(QStringList &remoteUrls)
{
QModelIndexList selection = ui->workspaceTreeView->selectionModel()->selectedIndexes();
foreach(const QModelIndex &mi, selection)
{
QVariant data = mi.model()->data(mi, ROLE_WORKSPACE_ITEM);
Q_ASSERT(data.isValid());
WorkspaceItem tv = data.value<WorkspaceItem>();
if(tv.Type != WorkspaceItem::TYPE_REMOTE)
continue;
QString url = tv.Value;
remoteUrls.append(url);
}
}
//------------------------------------------------------------------------------
bool MainWindow::diffFile(const QString &repoFile)
{
@ -1332,34 +1356,6 @@ void MainWindow::on_actionOpenFile_triggered()
}
}
//------------------------------------------------------------------------------
void MainWindow::on_actionPush_triggered()
{
QString remote_url = settings.GetFossilValue(FOSSIL_SETTING_REMOTE_URL).toString();
if(remote_url.isEmpty() || remote_url == "off")
{
QMessageBox::critical(this, tr("Error"), tr("A remote repository has not been specified.\nUse the preferences window to set the remote repostory location"), QMessageBox::Ok );
return;
}
fossil().pushRepository();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionPull_triggered()
{
QString remote_url = settings.GetFossilValue(FOSSIL_SETTING_REMOTE_URL).toString();
if(remote_url.isEmpty() || remote_url == "off")
{
QMessageBox::critical(this, tr("Error"), tr("A remote repository has not been specified.\nUse the preferences window to set the remote repostory location"), QMessageBox::Ok );
return;
}
fossil().pullRepository();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionCommit_triggered()
{
@ -1608,17 +1604,6 @@ void MainWindow::loadFossilSettings()
const QString &name = it.key();
Settings::Setting::SettingType type = it->Type;
// Command types we issue directly on fossil
if(name == FOSSIL_SETTING_REMOTE_URL)
{
// Retrieve existing url
QString url;
if(fossil().getRemoteUrl(url))
it.value().Value = url;
continue;
}
Q_ASSERT(type == Settings::Setting::TYPE_FOSSIL_GLOBAL || type == Settings::Setting::TYPE_FOSSIL_LOCAL);
// Otherwise it must be a fossil setting
@ -1664,15 +1649,6 @@ void MainWindow::on_actionFossilSettings_triggered()
const QString &name = it.key();
Settings::Setting::SettingType type = it.value().Type;
// Command types we issue directly on fossil
// FIXME: major uglyness with settings management
if(name == FOSSIL_SETTING_REMOTE_URL)
{
// Run as silent to avoid displaying credentials in the log
fossil().setRemoteUrl(it.value().Value.toString());
continue;
}
Q_ASSERT(type == Settings::Setting::TYPE_FOSSIL_GLOBAL || type == Settings::Setting::TYPE_FOSSIL_LOCAL);
QString value = it.value().Value.toString();
@ -1788,13 +1764,16 @@ void MainWindow::on_workspaceTreeView_doubleClicked(const QModelIndex &index)
Q_ASSERT(data.isValid());
WorkspaceItem tv = data.value<WorkspaceItem>();
if(tv.Type!=WorkspaceItem::TYPE_FOLDER && tv.Type!=WorkspaceItem::TYPE_WORKSPACE)
return;
QString target = getCurrentWorkspace() + PATH_SEPARATOR + tv.Value;
QUrl url = QUrl::fromLocalFile(target);
QDesktopServices::openUrl(url);
if(tv.Type==WorkspaceItem::TYPE_FOLDER || tv.Type==WorkspaceItem::TYPE_WORKSPACE)
{
QString target = getCurrentWorkspace() + PATH_SEPARATOR + tv.Value;
QUrl url = QUrl::fromLocalFile(target);
QDesktopServices::openUrl(url);
}
else if(tv.Type==WorkspaceItem::TYPE_REMOTE)
{
on_actionEditRemote_triggered();
}
}
//------------------------------------------------------------------------------
@ -2187,6 +2166,8 @@ void MainWindow::on_workspaceTreeView_customContextMenuRequested(const QPoint &)
menu = menuTags;
else if (tv.Type == WorkspaceItem::TYPE_BRANCH || tv.Type == WorkspaceItem::TYPE_BRANCHES)
menu = menuBranches;
else if (tv.Type == WorkspaceItem::TYPE_REMOTE || tv.Type == WorkspaceItem::TYPE_REMOTES)
menu = menuRemotes;
if(menu)
{
@ -2477,3 +2458,173 @@ void MainWindow::onSearch()
searchBox->setFocus();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionEditRemote_triggered()
{
QStringList remotes;
getSelectionRemotes(remotes);
if(remotes.empty())
return;
QUrl old_url(remotes.first());
QString name;
Remote *remote = getWorkspace().findRemote(old_url);
if(remote)
name = remote->name;
bool exists = KeychainGet(this, old_url);
QUrl new_url = old_url;
if(!RemoteDialog::run(this, new_url, name))
return;
if(!new_url.isLocalFile())
{
if(exists)
KeychainDelete(this, new_url);
if(!KeychainSet(this, new_url))
QMessageBox::critical(this, tr("Error"), tr("Could not store information to keychain."), QMessageBox::Ok );
}
// Remove password
new_url.setPassword("");
old_url.setPassword("");
// Url changed?
if(new_url != old_url)
{
getWorkspace().removeRemote(old_url);
getWorkspace().addRemote(new_url, name);
}
else // Just data changed
remote->name = name;
updateWorkspaceView();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionPushRemote_triggered()
{
QStringList remotes;
getSelectionRemotes(remotes);
if(remotes.empty())
return;
QUrl url(remotes.first());
// Retrieve password from keychain
if(!url.isLocalFile())
KeychainGet(this, url);
fossil().pushRepository(url);
}
//------------------------------------------------------------------------------
void MainWindow::on_actionPullRemote_triggered()
{
QStringList remotes;
getSelectionRemotes(remotes);
if(remotes.empty())
return;
QUrl url(remotes.first());
// Retrieve password from keychain
if(!url.isLocalFile())
KeychainGet(this, url);
fossil().pullRepository(url);
}
//------------------------------------------------------------------------------
void MainWindow::on_actionPush_triggered()
{
QUrl url = getWorkspace().getRemoteDefault();
if(url.isEmpty())
{
QMessageBox::critical(this, tr("Error"), tr("A default remote repository has not been specified."), QMessageBox::Ok );
return;
}
// Retrieve password from keychain
if(!url.isLocalFile())
KeychainGet(this, url);
fossil().pushRepository(url);
}
//------------------------------------------------------------------------------
void MainWindow::on_actionPull_triggered()
{
QUrl url = getWorkspace().getRemoteDefault();
if(url.isEmpty())
{
QMessageBox::critical(this, tr("Error"), tr("A default remote repository has not been specified."), QMessageBox::Ok );
return;
}
// Retrieve password from keychain
if(!url.isLocalFile())
KeychainGet(this, url);
fossil().pullRepository(url);
}
//------------------------------------------------------------------------------
void MainWindow::on_actionSetDefaultRemote_triggered()
{
QStringList remotes;
getSelectionRemotes(remotes);
if(remotes.empty())
return;
QUrl url(remotes.first());
getWorkspace().setRemoteDefault(url);
updateWorkspaceView();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionAddRemote_triggered()
{
QUrl url;
QString name;
if(!RemoteDialog::run(this, url, name))
return;
if(!url.isLocalFile())
{
KeychainDelete(this, url);
if(!KeychainSet(this, url))
QMessageBox::critical(this, tr("Error"), tr("Could not store information to keychain."), QMessageBox::Ok );
}
url.setPassword("");
getWorkspace().addRemote(url, name);
updateWorkspaceView();
}
//------------------------------------------------------------------------------
void MainWindow::on_actionDeleteRemote_triggered()
{
QStringList remotes;
getSelectionRemotes(remotes);
if(remotes.empty())
return;
QUrl url(remotes.first());
Remote *remote = getWorkspace().findRemote(url);
Q_ASSERT(remote);
if(QMessageBox::Yes != DialogQuery(this, tr("Delete Remote"), tr("Are you sure want to delete the remote '%0' ?").arg(remote->name)))
return;
getWorkspace().removeRemote(url);
updateWorkspaceView();
}

View File

@ -40,11 +40,12 @@ private:
void getDirViewSelection(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL, bool allIfEmpty=false);
void getSelectionStashes(QStringList &stashNames);
void getSelectionPaths(stringset_t &paths);
void getSelectionRemotes(QStringList& remoteUrls);
void getAllFilenames(QStringList &filenames, int includeMask=WorkspaceFile::TYPE_ALL);
bool startUI();
void stopUI();
void enableActions(bool on);
void addWorkspace(const QString &dir);
void addWorkspaceHistory(const QString &dir);
void rebuildRecent();
bool openWorkspace(const QString &path);
void loadFossilSettings();
@ -89,6 +90,8 @@ private slots:
void on_actionOpenFile_triggered();
void on_actionPush_triggered();
void on_actionPull_triggered();
void on_actionPushRemote_triggered();
void on_actionPullRemote_triggered();
void on_actionCommit_triggered();
void on_actionAdd_triggered();
void on_actionDelete_triggered();
@ -124,6 +127,10 @@ private slots:
void on_actionDeleteTag_triggered();
void on_actionCreateBranch_triggered();
void on_actionMergeBranch_triggered();
void on_actionEditRemote_triggered();
void on_actionSetDefaultRemote_triggered();
void on_actionAddRemote_triggered();
void on_actionDeleteRemote_triggered();
private:
class MainWinUICallback : public UICallback
@ -171,6 +178,7 @@ private:
QMenu *menuStashes;
QMenu *menuTags;
QMenu *menuBranches;
QMenu *menuRemotes;
bool operationAborted;
stringset_t selectedDirs; // The directory selected in the tree

95
src/RemoteDialog.cpp Normal file
View File

@ -0,0 +1,95 @@
#include "RemoteDialog.h"
#include "ui_RemoteDialog.h"
#include <QFileDialog>
#include <QDir>
#include <QMessageBox>
#include <QClipboard>
#include <QUrl>
#include "Utils.h"
//-----------------------------------------------------------------------------
RemoteDialog::RemoteDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::RemoteDialog)
{
ui->setupUi(this);
}
//-----------------------------------------------------------------------------
RemoteDialog::~RemoteDialog()
{
delete ui;
}
//-----------------------------------------------------------------------------
bool RemoteDialog::run(QWidget *parent, QUrl &url, QString &name)
{
RemoteDialog dlg(parent);
// Set URL components
if(!url.isEmpty())
{
QString url_no_credentials = url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo);
dlg.ui->lineURL->setText(url_no_credentials);
dlg.ui->lineUserName->setText(url.userName());
dlg.ui->linePassword->setText(url.password());
dlg.ui->lineName->setText(name);
}
if(dlg.exec() != QDialog::Accepted)
return false;
QString urltext = dlg.ui->lineURL->text();
url = QUrl::fromUserInput(urltext);
if(url.isEmpty() || !url.isValid())
{
QMessageBox::critical(parent, tr("Error"), tr("Invalid URL."), QMessageBox::Ok );
return false;
}
if(!dlg.ui->lineUserName->text().trimmed().isEmpty())
url.setUserName(dlg.ui->lineUserName->text());
if(!dlg.ui->linePassword->text().trimmed().isEmpty())
url.setPassword(dlg.ui->linePassword->text());
name =dlg.ui->lineName->text().trimmed();
if(name.isEmpty())
name = url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo);
return true;
}
//-----------------------------------------------------------------------------
void RemoteDialog::GetRepositoryPath(QString &pathResult)
{
QString filter(tr("Fossil Repository") + QString(" (*." FOSSIL_EXT ")"));
pathResult = QFileDialog::getSaveFileName(
this,
tr("Select Fossil Repository"),
QDir::toNativeSeparators(pathResult),
filter,
&filter,
QFileDialog::DontConfirmOverwrite);
}
//-----------------------------------------------------------------------------
void RemoteDialog::on_btnSelectSourceRepo_clicked()
{
QString path = ui->lineURL->text();
GetRepositoryPath(path);
if(path.trimmed().isEmpty())
return;
if(!QFile::exists(path))
{
QMessageBox::critical(this, tr("Error"), tr("Invalid Repository File."), QMessageBox::Ok);
return;
}
ui->lineURL->setText(QDir::toNativeSeparators(path));
}

29
src/RemoteDialog.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef REMOTEDIALOG_H
#define REMOTEDIALOG_H
#include <QDialog>
namespace Ui {
class RemoteDialog;
}
class RemoteDialog : public QDialog
{
Q_OBJECT
public:
explicit RemoteDialog(QWidget *parent = 0);
~RemoteDialog();
static bool run(QWidget *parent, class QUrl &url, QString &name);
private slots:
void on_btnSelectSourceRepo_clicked();
private:
void GetRepositoryPath(QString &pathResult);
Ui::RemoteDialog *ui;
};
#endif // REMOTEDIALOG_H

View File

@ -17,7 +17,6 @@ Settings::Settings(bool portableMode) : store(0)
Mappings.insert(FOSSIL_SETTING_IGNORE_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL));
Mappings.insert(FOSSIL_SETTING_CRNL_GLOB, Setting("", Setting::TYPE_FOSSIL_LOCAL));
Mappings.insert(FOSSIL_SETTING_REMOTE_URL, Setting("off", Setting::TYPE_FOSSIL_COMMAND));
// Go into portable mode when explicitly requested or if a config file exists next to the executable
QString ini_path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + QDir::separator() + QCoreApplication::applicationName() + ".ini");

View File

@ -16,7 +16,6 @@
#define FOSSIL_SETTING_PROXY_URL "proxy"
#define FOSSIL_SETTING_IGNORE_GLOB "ignore-glob"
#define FOSSIL_SETTING_CRNL_GLOB "crnl-glob"
#define FOSSIL_SETTING_REMOTE_URL "remote-url"
#define FOSSIL_SETTING_HTTP_PORT "http-port"
@ -35,8 +34,7 @@ struct Settings
enum SettingType
{
TYPE_FOSSIL_GLOBAL,
TYPE_FOSSIL_LOCAL,
TYPE_FOSSIL_COMMAND
TYPE_FOSSIL_LOCAL
};
Setting(QVariant value, SettingType type) : Value(value), Type(type)

View File

@ -2,6 +2,9 @@
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QEventLoop>
#include "ext/qtkeychain/keychain.h"
#include <QCryptographicHash>
///////////////////////////////////////////////////////////////////////////////
QMessageBox::StandardButton DialogQuery(QWidget *parent, const QString &title, const QString &query, QMessageBox::StandardButtons buttons)
@ -388,4 +391,66 @@ void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItemModel
Q_ASSERT(item);
BuildNameToModelIndex(map, *item);
}
}
}
//------------------------------------------------------------------------------
bool KeychainSet(QObject *parent, const QUrl &url)
{
QEventLoop loop(parent);
QKeychain::WritePasswordJob job(url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo));
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.setAutoDelete( false );
job.setInsecureFallback(false);
job.setKey(url.userName());
job.setTextData(url.password());
job.start();
loop.exec();
return job.error() == QKeychain::NoError;
}
//------------------------------------------------------------------------------
bool KeychainGet(QObject *parent, QUrl &url)
{
QEventLoop loop(parent);
QKeychain::ReadPasswordJob job(url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo));
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
job.setAutoDelete( false );
job.setInsecureFallback(false);
job.setAutoDelete( false );
job.setKey(url.userName());
job.start();
loop.exec();
if(job.error() != QKeychain::NoError)
return false;
url.setUserName(job.key());
url.setPassword(job.textData());
return true;
}
//------------------------------------------------------------------------------
bool KeychainDelete(QObject* parent, const QUrl& url)
{
QEventLoop loop(parent);
QKeychain::DeletePasswordJob job(url.toString(QUrl::PrettyDecoded|QUrl::RemoveUserInfo));
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()));
job.setAutoDelete( false );
job.setInsecureFallback(false);
job.setAutoDelete( false );
job.setKey(url.userName());
job.start();
loop.exec();
return job.error() == QKeychain::NoError;
}
//------------------------------------------------------------------------------
QString HashString(const QString& str)
{
QCryptographicHash hash(QCryptographicHash::Sha1);
const QByteArray ba(str.toUtf8());
hash.addData(ba.data(), ba.size());
QString str_out(hash.result().toHex());
return str_out;
}

View File

@ -22,6 +22,10 @@ typedef QMap<QString, QModelIndex> name_modelindex_map_t;
void GetStandardItemTextRecursive(QString &name, const QStandardItem &item, const QChar &separator='/');
void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItem &item);
void BuildNameToModelIndex(name_modelindex_map_t &map, const QStandardItemModel &model);
bool KeychainSet(QObject* parent, const QUrl& url);
bool KeychainGet(QObject* parent, QUrl& url);
bool KeychainDelete(QObject* parent, const QUrl& url);
QString HashString(const QString &str);
typedef QMap<QString, QString> QStringMap;

View File

@ -2,10 +2,15 @@
#include <QCoreApplication>
#include "Utils.h"
//-----------------------------------------------------------------------------
Workspace::Workspace()
{
}
//-----------------------------------------------------------------------------
Workspace::~Workspace()
{
clearState();
remotes.clear();
}
//------------------------------------------------------------------------------
@ -23,6 +28,93 @@ void Workspace::clearState()
isIntegrated = false;
}
//------------------------------------------------------------------------------
void Workspace::storeWorkspace(QSettings &store)
{
QString workspace = fossil().getCurrentWorkspace();
if(workspace.isEmpty())
return;
store.beginGroup("Remotes");
QString workspace_hash = HashString(QDir::toNativeSeparators(workspace));
store.beginWriteArray(workspace_hash);
int index = 0;
for(remote_map_t::iterator it=remotes.begin(); it!=remotes.end(); ++it, ++index)
{
store.setArrayIndex(index);
store.setValue("Name", it->name);
QUrl url = it->url;
url.setPassword("");
store.setValue("Url", url);
if(it->isDefault)
store.setValue("Default", it->isDefault);
else
store.remove("Default");
}
store.endArray();
store.endGroup();
}
//------------------------------------------------------------------------------
bool Workspace::switchWorkspace(const QString& workspace, QSettings &store)
{
// Save Remotes
storeWorkspace(store);
clearState();
remotes.clear();
fossil().setCurrentWorkspace("");
if(workspace.isEmpty())
return true;
QString new_workspace = QFileInfo(workspace).absoluteFilePath();
if(!QDir::setCurrent(new_workspace))
return false;
fossil().setCurrentWorkspace(new_workspace);
// Load Remotes
QString workspace_hash = HashString(QDir::toNativeSeparators(new_workspace));
QString gr = store.group();
store.beginGroup("Remotes");
gr = store.group();
int num_remotes = store.beginReadArray(workspace_hash);
for(int i=0; i<num_remotes; ++i)
{
store.setArrayIndex(i);
QString name = store.value("Name").toString();
QUrl url = store.value("Url").toUrl();
bool def = store.value("Default", false).toBool();
addRemote(url, name);
if(def)
setRemoteDefault(url);
}
store.endArray();
store.endGroup();
// Add the default url from fossil
QUrl default_remote;
if(fossil().getRemoteUrl(default_remote) && default_remote.isValid() && !default_remote.isEmpty())
{
default_remote.setPassword("");
// Add Default remote if not available already
if(findRemote(default_remote)==NULL)
{
addRemote(default_remote, default_remote.toDisplayString());
setRemoteDefault(default_remote);
}
}
return true;
}
//------------------------------------------------------------------------------
bool Workspace::scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool &abort, UICallback &uiCallback)
{
@ -216,3 +308,56 @@ _done:
uiCallback.endProcess();
}
//------------------------------------------------------------------------------
bool Workspace::addRemote(const QUrl& url, const QString& name)
{
if(remotes.contains(url))
return false;
Q_ASSERT(url.password().isEmpty());
Remote r(name, url);
remotes.insert(url, r);
return true;
}
//------------------------------------------------------------------------------
bool Workspace::removeRemote(const QUrl& url)
{
return remotes.remove(url) > 0;
}
//------------------------------------------------------------------------------
bool Workspace::setRemoteDefault(const QUrl& url)
{
Q_ASSERT(url.password().isEmpty());
bool found = false;
for(remote_map_t::iterator it=remotes.begin(); it!=remotes.end(); ++it)
{
if(it->url == url)
{
it->isDefault = true;
found = true;
}
else
it->isDefault = false;
}
return found;
}
//------------------------------------------------------------------------------
const QUrl & Workspace::getRemoteDefault() const
{
return fossil().getDefaultRemoteUrl();
}
//------------------------------------------------------------------------------
Remote * Workspace::findRemote(const QUrl& url)
{
remote_map_t::iterator it = remotes.find(url);
if(it!=remotes.end())
return &(*it);
return NULL;
}

View File

@ -6,6 +6,7 @@
#include <QDir>
#include <QSet>
#include <QMap>
#include <QSettings>
#include "Utils.h"
#include "Fossil.h"
@ -99,13 +100,31 @@ private:
typedef QSet<QString> stringset_t;
class Remote
{
public:
Remote(const QString &_name, const QUrl &_url, bool _isDefault=false)
: name(_name), url(_url), isDefault(_isDefault)
{
}
QString name;
QUrl url;
bool isDefault;
};
typedef QMap<QUrl, Remote> remote_map_t;
//////////////////////////////////////////////////////////////////////////
// Workspace
//////////////////////////////////////////////////////////////////////////
class Workspace
{
public:
Workspace();
~Workspace();
typedef QList<WorkspaceFile*> filelist_t;
@ -116,7 +135,7 @@ public:
Fossil & fossil() { return bridge; }
const Fossil & fossil() const { return bridge; }
static bool scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool& abort, UICallback &uiCallback);
bool switchWorkspace(const QString &workspace, QSettings &store);
void scanWorkspace(bool scanLocal, bool scanIgnored, bool scanModified, bool scanUnchanged, const QString &ignoreGlob, UICallback &uiCallback, bool &operationAborted);
QStandardItemModel &getFileModel() { return repoFileModel; }
@ -129,6 +148,19 @@ public:
QStringList &getBranches() { return branchList; }
bool otherChanges() const { return isIntegrated; }
// Remotes
const remote_map_t &getRemotes() const { return remotes; }
bool addRemote(const QUrl &url, const QString &name);
bool removeRemote(const QUrl &url);
bool setRemoteDefault(const QUrl& url);
const QUrl &getRemoteDefault() const;
Remote * findRemote(const QUrl& url);
void storeWorkspace(QSettings &store);
private:
static bool scanDirectory(QFileInfoList &entries, const QString& dirPath, const QString &baseDir, const QString ignoreSpec, const bool& abort, UICallback &uiCallback);
private:
Fossil bridge;
filemap_t workspaceFiles;
@ -136,6 +168,7 @@ private:
stashmap_t stashMap;
QStringList branchList;
QStringMap tags;
remote_map_t remotes;
bool isIntegrated;
QStandardItemModel repoFileModel;

View File

@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>457</width>
<height>266</height>
<height>235</height>
</rect>
</property>
<property name="windowTitle">
@ -165,19 +165,6 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineIgnoreCRNL">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>A comma separated list of glob-style file patterns to exclude from Fossil's CR/NL consistency checking</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_7">
<property name="minimumSize">
@ -191,8 +178,8 @@
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="lineIgnore">
<item row="4" column="1">
<widget class="QLineEdit" name="lineIgnoreCRNL">
<property name="minimumSize">
<size>
<width>0</width>
@ -200,7 +187,7 @@
</size>
</property>
<property name="toolTip">
<string>A comma separated list of glob-style file/path patterns ignored in Fossil file operations</string>
<string>A comma separated list of glob-style file patterns to exclude from Fossil's CR/NL consistency checking</string>
</property>
</widget>
</item>
@ -217,8 +204,8 @@
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="lineRemoteURL">
<item row="5" column="1">
<widget class="QLineEdit" name="lineIgnore">
<property name="minimumSize">
<size>
<width>0</width>
@ -226,22 +213,7 @@
</size>
</property>
<property name="toolTip">
<string>The remote url used to push/pull changes.
URL style user names and passwords are also supported.
For example http://username:password@server.com/fossil</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_5">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Remote Url</string>
<string>A comma separated list of glob-style file/path patterns ignored in Fossil file operations</string>
</property>
</widget>
</item>

View File

@ -493,7 +493,7 @@
<string>&amp;Push</string>
</property>
<property name="toolTip">
<string>Push changes to the remote repository</string>
<string>Push changes to the default remote repository</string>
</property>
<property name="statusTip">
<string>Push changes to the remote repository</string>
@ -511,7 +511,7 @@
<string>Pu&amp;ll</string>
</property>
<property name="toolTip">
<string>Pull changes from the remote repository</string>
<string>Pull changes from the default remote repository</string>
</property>
<property name="statusTip">
<string>Pull changes from the remote repository</string>
@ -520,6 +520,36 @@
<string>Ctrl+L</string>
</property>
</action>
<action name="actionPushRemote">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-push</normaloff>:/icons/icon-action-push</iconset>
</property>
<property name="text">
<string>&amp;Push to Remote</string>
</property>
<property name="toolTip">
<string>Push changes to a remote repository</string>
</property>
<property name="statusTip">
<string>Push changes to a remote repository</string>
</property>
</action>
<action name="actionPullRemote">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
<normaloff>:/icons/icon-action-pull</normaloff>:/icons/icon-action-pull</iconset>
</property>
<property name="text">
<string>Pu&amp;ll from Remote</string>
</property>
<property name="toolTip">
<string>Pull changes from a remote repository</string>
</property>
<property name="statusTip">
<string>Pull changes from a remote repository</string>
</property>
</action>
<action name="actionRename">
<property name="icon">
<iconset resource="../rsrc/resources.qrc">
@ -978,6 +1008,35 @@
<string>F&amp;ossil Settings</string>
</property>
</action>
<action name="actionEditRemote">
<property name="text">
<string>Edit Remote</string>
</property>
<property name="toolTip">
<string>Edit Remote URL</string>
</property>
</action>
<action name="actionSetDefaultRemote">
<property name="text">
<string>Set Remote as Default</string>
</property>
<property name="statusTip">
<string>Makes the selected remote </string>
</property>
</action>
<action name="actionAddRemote">
<property name="text">
<string>Add Remote</string>
</property>
<property name="statusTip">
<string>Adds a Remote Url</string>
</property>
</action>
<action name="actionDeleteRemote">
<property name="text">
<string>Delete Remote</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

165
ui/RemoteDialog.ui Normal file
View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>RemoteDialog</class>
<widget class="QDialog" name="RemoteDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>478</width>
<height>189</height>
</rect>
</property>
<property name="windowTitle">
<string>Remote Repository</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_1">
<property name="text">
<string>URL</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QLineEdit" name="lineURL">
<property name="toolTip">
<string>The URL of the source repository</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectSourceRepo">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>User Name</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineUserName">
<property name="toolTip">
<string>The user name used to access the remote repository. Leave blank if not required</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="linePassword">
<property name="toolTip">
<string>The password used to access the remote repository. Leave blank if not required</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="lineName">
<property name="toolTip">
<string>The password used to access the remote repository. Leave blank if not required</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="Name">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>lineURL</tabstop>
<tabstop>btnSelectSourceRepo</tabstop>
<tabstop>lineUserName</tabstop>
<tabstop>linePassword</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>RemoteDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>RemoteDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>