Merge branch 'master' into keyring1

This commit is contained in:
csoler 2023-09-12 09:43:13 +02:00 committed by GitHub
commit c1bf4fb27f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2772 changed files with 62382 additions and 338334 deletions

View file

@ -0,0 +1,492 @@
################################################################################
# retroshare-gui/CMakeLists.txt #
# Copyright (C) 2022, Retroshare team <retroshare.team@gmailcom> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as #
# published by the Free Software Foundation, either version 3 of the #
# License, or (at your option) any later version. #
# #
# 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. See the #
# GNU Affero General Public License for more details. #
# #
# You should have received a copy of the GNU Affero General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
################################################################################
cmake_minimum_required (VERSION 3.18.0)
project(retroshare-gui VERSION 0.6.6 LANGUAGES CXX)
include(CMakeDependentOption)
set(
RS_BIN_INSTALL_DIR
"${CMAKE_INSTALL_PREFIX}/bin"
CACHE PATH
"Path where to install retroshare-service compiled binary" )
option(
RS_DEVELOPMENT_BUILD
"Disable optimization to speed up build, enable verbose build log. \
just for development purposes, not suitable for library usage"
ON )
option(
RS_JSON_API
"Use restbed to expose libretroshare as JSON API via HTTP"
OFF )
option(
RS_SERVICE_DESKTOP
"Install icons and shortcuts for desktop environements"
OFF )
option(
RS_SERVICE_TERMINAL_LOGIN
"Enable RetroShare login via terminal"
ON )
option( RS_GXSGUI "Enable GXS services in GUI" ON )
option( RS_GXSCHANNELS "Enable GXS channels in GUI" ON )
option( RS_GXSFORUMS "Enable GXS forums in GUI" ON )
option( RS_GXSPOSTED "Enable GXS posted in GUI" ON )
option( RS_GXSCIRCLES "Enable GXS circles in GUI" ON )
cmake_dependent_option(
RS_SERVICE_TERMINAL_WEBUI_PASSWORD
"Enable settin Web UI password via terminal in retroshare-service"
OFF
"RS_WEBUI"
ON )
cmake_dependent_option(
RS_WEBUI
"Install RetroShare Web UI"
OFF
"RS_JSON_API"
ON )
################################ QT FILES #######################################
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package( Qt5 COMPONENTS Core REQUIRED)
find_package( Qt5 COMPONENTS Widgets REQUIRED)
find_package( Qt5 COMPONENTS Xml REQUIRED)
find_package( Qt5 COMPONENTS Network REQUIRED)
find_package( Qt5 COMPONENTS Multimedia REQUIRED)
find_package( Qt5 COMPONENTS PrintSupport REQUIRED)
list( APPEND RS_LINK_LIBRARIES Qt5::Multimedia Qt5::Widgets Qt5::Xml Qt5::Network Qt5::PrintSupport)
################################################################################
set(FETCHCONTENT_QUIET OFF)
include(FetchContent)
find_package(Git REQUIRED)
################################################################################
if(RS_DEVELOPMENT_BUILD)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
endif(RS_DEVELOPMENT_BUILD)
################################################################################
include(src/CMakeLists.txt)
qt5_wrap_ui(RS_UI_HEADERS ${RS_GUI_FORMS})
add_executable(${PROJECT_NAME} ${RS_GUI_SOURCES} ${RS_UI_HEADERS} ${RS_GUI_QTRESOURCES})
install(TARGETS ${PROJECT_NAME} DESTINATION ${RS_BIN_INSTALL_DIR})
include_directories( ${CMAKE_BINARY_DIR} )
################################################################################
if(RS_DEVELOPMENT_BUILD)
target_compile_options(${PROJECT_NAME} PRIVATE "-O0")
endif(RS_DEVELOPMENT_BUILD)
################################################################################
set(LIBRETROSHARE_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../libretroshare/")
if(EXISTS "${LIBRETROSHARE_DEVEL_DIR}/.git" )
message(
STATUS
"libretroshare submodule found at ${LIBRETROSHARE_DEVEL_DIR} using it" )
add_subdirectory(
"${LIBRETROSHARE_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/libretroshare" )
else()
FetchContent_Declare(
libretroshare
GIT_REPOSITORY "https://gitlab.com/RetroShare/libretroshare.git"
GIT_TAG "origin/master"
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
TIMEOUT 10
)
FetchContent_MakeAvailable(libretroshare)
endif()
target_include_directories(${PROJECT_NAME} PRIVATE ${LIBRETROSHARE_DEVEL_DIR}/src/)
################################################################################
if(RS_SERVICE_DESKTOP)
if(UNIX AND NOT APPLE)
install(
FILES data/retroshare.svg
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/ )
install(
FILES data/retroshare.xpm
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps/ )
install(
FILES data/24x24/apps/retroshare.png
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/24x24/apps/retroshare.png )
install(
FILES data/48x48/apps/retroshare.png
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps/retroshare.png )
install(
FILES data/64x64/apps/retroshare.png
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps/retroshare.png )
install(
FILES data/128x128/apps/retroshare.png
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps/retroshare.png )
install(
FILES data/retroshare.desktop
DESTINATION ${CMAKE_INSTALL_PREFIX}/data/retroshare.desktop )
install(
FILES gui/qss/chat/Bubble gui/qss/chat/Bubble_Compact
DESTINATION ${CMAKE_INSTALL_PREFIX}/data/stylesheets/ )
install(
FILES src/sounds/ src/qss/
DESTINATION ${CMAKE_INSTALL_PREFIX}/ )
endif(UNIX AND NOT APPLE)
endif(RS_SERVICE_DESKTOP)
################################################################################
if(RS_JSON_API)
target_compile_definitions(${PROJECT_NAME} PUBLIC RS_JSONAPI)
endif(RS_JSON_API)
################################# CMark ########################################
if(RS_GUI_CMARK)
set(CMARK_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../supportlibs/cmark/")
if(EXISTS "${LIBRETROSHARE_DEVEL_DIR}/.git" )
message( STATUS "cmark submodule found at ${LIBRETROSHARE_DEVEL_DIR} using it" )
add_subdirectory( "${LIBRETROSHARE_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/cmark" )
else()
FetchContent_Declare(
cmark
GIT_REPOSITORY "https://github.com/commonmark/cmark.git"
GIT_TAG "origin/master"
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
TIMEOUT 10
)
FetchContent_MakeAvailable(cmark)
endif()
endif(RS_GUI_CMARK)
################################# LibSam v3 ####################################
set(SAM3_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../supportlibs/libsam3/")
if(EXISTS "${SAM3_DEVEL_DIR}/.git" )
message( STATUS "libsam3 submodule found at ${SAM3_DEVEL_DIR} using it" )
add_subdirectory( "${SAM3_DEVEL_DIR}" "${CMAKE_BINARY_DIR}/libsam3" )
else()
FetchContent_Declare(
libsam3
GIT_REPOSITORY "https://github.com/i2p/libsam3.git"
GIT_TAG "origin/master"
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
TIMEOUT 10
)
FetchContent_MakeAvailable(libsam3)
endif()
################################################################################
# TODO #
################################################################################
# # Auto detect installed version of cmark
# rs_gui_cmark {
# DEFINES *= USE_CMARK
# no_rs_cross_compiling {
# message("Using compiled cmark")
# CMARK_SRC_PATH=$$clean_path($${RS_SRC_PATH}/supportlibs/cmark)
# CMARK_BUILD_PATH=$$clean_path($${RS_BUILD_PATH}/supportlibs/cmark/build)
# INCLUDEPATH *= $$clean_path($${CMARK_SRC_PATH}/src/)
# DEPENDPATH *= $$clean_path($${CMARK_SRC_PATH}/src/)
# QMAKE_LIBDIR *= $$clean_path($${CMARK_BUILD_PATH}/)
# # Using sLibs would fail as libcmark.a is generated at compile-time
# LIBS *= -L$$clean_path($${CMARK_BUILD_PATH}/src/) -lcmark
#
# DUMMYCMARKINPUT = FORCE
# CMAKE_GENERATOR_OVERRIDE=""
# win32-g++|win32-clang-g++:CMAKE_GENERATOR_OVERRIDE="-G \"MSYS Makefiles\""
# gencmarklib.name = Generating libcmark.
# gencmarklib.input = DUMMYCMARKINPUT
# gencmarklib.output = $$clean_path($${CMARK_BUILD_PATH}/src/libcmark.a)
# gencmarklib.CONFIG += target_predeps combine
# gencmarklib.variable_out = PRE_TARGETDEPS
# gencmarklib.commands = \
# cd $${RS_SRC_PATH} && ( \
# git submodule update --init supportlibs/cmark ; \
# cd $${CMARK_SRC_PATH} ; \
# true ) && \
# mkdir -p $${CMARK_BUILD_PATH} && cd $${CMARK_BUILD_PATH} && \
# cmake \
# -DCMAKE_CXX_COMPILER=$$QMAKE_CXX \
# \"-DCMAKE_CXX_FLAGS=$${QMAKE_CXXFLAGS}\" \
# $${CMAKE_GENERATOR_OVERRIDE} \
# -DCMAKE_INSTALL_PREFIX=. \
# -B. \
# -H$$shell_path($${CMARK_SRC_PATH}) && \
# $(MAKE)
# QMAKE_EXTRA_COMPILERS += gencmarklib
# } else {
# message("Using systems cmark")
# sLibs *= libcmark
# }
# }
################################# Linux ##########################################
# Put lib dir in QMAKE_LFLAGS so it appears before -L/usr/lib
if(UNIX)
find_package(PkgConfig REQUIRED)
pkg_check_modules(X11 REQUIRED x11)
pkg_check_modules(XSCRNSAVER REQUIRED xscrnsaver)
list( APPEND RS_LINK_LIBRARIES ${X11_LIBRARIES} )
list( APPEND RS_LINK_LIBRARIES ${XSCRNSAVER_LIBRARIES} )
target_include_directories(${PROJECT_NAME} PRIVATE ${X11_INCLUDE_DIRS})
target_compile_options(${PROJECT_NAME} PRIVATE ${X11_CFLAGS_OTHER})
target_include_directories(retroshare-gui PRIVATE ${XSCRNSAVER_INCLUDE_DIRS})
target_compile_options(retroshare-gui PRIVATE ${XSCRNSAVER_CFLAGS_OTHER})
target_compile_definitions(${PROJECT_NAME} PRIVATE HAVE_XSS)
target_compile_definitions(${PROJECT_NAME} PRIVATE _FILE_OFFSET_BITS=64)
target_link_options(${PROJECT_NAME} PRIVATE LINKER:-rdynamic)
endif(UNIX)
if(RS_SANITIZE)
list( APPEND RS_LINK_LIBRARIES asan )
list( APPEND RS_LINK_LIBRARIES ubsan )
endif(RS_SANITIZE)
# #################### Cross compilation for windows under Linux ###################
#
# win32-x-g++ {
# OBJECTS_DIR = temp/win32-x-g++/obj
#
# LIBS += ../../../../lib/win32-x-g++-v0.5/libssl.a
# LIBS += ../../../../lib/win32-x-g++-v0.5/libcrypto.a
# LIBS += ../../../../lib/win32-x-g++-v0.5/libgpgme.dll.a
# LIBS += ../../../../lib/win32-x-g++-v0.5/libminiupnpc.a
# LIBS += ../../../../lib/win32-x-g++-v0.5/libz.a
# LIBS += -L${HOME}/.wine/drive_c/pthreads/lib -lpthreadGCE2
# LIBS += -lQtUiTools
# LIBS += -lws2_32 -luuid -lole32 -liphlpapi -lcrypt32 -gdi32
# LIBS += -lole32 -lwinmm
#
# DEFINES *= WINDOWS_SYS WIN32 WIN32_CROSS_UBUNTU
#
# INCLUDEPATH += ../../../../gpgme-1.1.8/src/
# INCLUDEPATH += ../../../../libgpg-error-1.7/src/
#
# RC_FILE = gui/images/retroshare_win.rc
# }
#
# #################################### Windows #####################################
#
# win32-g++|win32-clang-g++ {
# CONFIG(debug, debug|release) {
# # show console output
# CONFIG += console
# } else {
# CONFIG -= console
# }
#
# CONFIG(debug, debug|release) {
# } else {
# # Tell linker to use ASLR protection
# QMAKE_LFLAGS += -Wl,-dynamicbase
# # Tell linker to use DEP protection
# QMAKE_LFLAGS += -Wl,-nxcompat
# }
#
# # Fix linking error (ld.exe: Error: export ordinal too large) due to too
# # many exported symbols.
# !libretroshare_shared:QMAKE_LFLAGS+=-Wl,--exclude-libs,ALL
#
# # Switch off optimization for release version
# QMAKE_CXXFLAGS_RELEASE -= -O2
# QMAKE_CXXFLAGS_RELEASE += -O0
# QMAKE_CFLAGS_RELEASE -= -O2
# QMAKE_CFLAGS_RELEASE += -O0
#
# # Switch on optimization for debug version
# #QMAKE_CXXFLAGS_DEBUG += -O2
# #QMAKE_CFLAGS_DEBUG += -O2
#
# OBJECTS_DIR = temp/obj
#
# dLib = ws2_32 gdi32 uuid ole32 iphlpapi crypt32 winmm
# LIBS *= $$linkDynamicLibs(dLib)
#
# RC_FILE = gui/images/retroshare_win.rc
#
# # export symbols for the plugins
# LIBS += -Wl,--export-all-symbols,--out-implib,lib/libretroshare-gui.a
#
# # create lib directory
# isEmpty(QMAKE_SH) {
# QMAKE_PRE_LINK = $(CHK_DIR_EXISTS) lib $(MKDIR) lib
# } else {
# QMAKE_PRE_LINK = $(CHK_DIR_EXISTS) lib || $(MKDIR) lib
# }
#
# greaterThan(QT_MAJOR_VERSION, 4) {
# # Qt 5
# RC_INCLUDEPATH += $$_PRO_FILE_PWD_/../../libretroshare/src
# } else {
# # Qt 4
# QMAKE_RC += --include-dir=$$_PRO_FILE_PWD_/../../libretroshare/src
# }
# }
#
# ##################################### MacOS ######################################
#
# macx {
# # ENABLE THIS OPTION FOR Univeral Binary BUILD.
# #CONFIG += ppc x86
# #QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.4
# QMAKE_INFO_PLIST = Info.plist
# mac_icon.files = $$files($$PWD/rsMacIcon.icns)
# mac_icon.path = Contents/Resources
# QMAKE_BUNDLE_DATA += mac_icon
# dplQSS.files = $$PWD/qss
# dplQSS.path = Contents/Resources
# QMAKE_BUNDLE_DATA += dplQSS
# dplChatStyles.files = \
# $$PWD/gui/qss/chat/Bubble \
# $$PWD/gui/qss/chat/Bubble_Compact
# dplChatStyles.path = Contents/Resources/stylesheets
# QMAKE_BUNDLE_DATA += dplChatStyles
# # mac_webui.files = $$files($$PWD/../../libresapi/src/webui)
# # mac_webui.path = Contents/Resources
# # QMAKE_BUNDLE_DATA += mac_webui
#
# OBJECTS_DIR = temp/obj
#
# CONFIG += version_detail_bash_script
# LIBS += -lssl -lcrypto -lz
# for(lib, LIB_DIR):exists($$lib/libminiupnpc.a){ LIBS += $$lib/libminiupnpc.a}
# LIBS += -framework CoreFoundation
# LIBS += -framework Security
# LIBS += -framework Carbon
#
# for(lib, LIB_DIR):LIBS += -L"$$lib"
# for(bin, BIN_DIR):LIBS += -L"$$bin"
#
# DEPENDPATH += . $$INC_DIR
# INCLUDEPATH += . $$INC_DIR
#
# #DEFINES *= MAC_IDLE # for idle feature
# CONFIG -= uitools
# }
#
# ##################################### FreeBSD ######################################
#
# freebsd-* {
# INCLUDEPATH *= /usr/local/include/gpgme
# LIBS *= -lssl
# LIBS *= -lgpgme
# LIBS *= -lupnp
# LIBS *= -lgnome-keyring
#
# LIBS += -lsqlite3
# }
#
# ##################################### Haiku ######################################
#
# haiku-* {
# PRE_TARGETDEPS *= ../../libretroshare/src/lib/libretroshare.a
# PRE_TARGETDEPS *= ../../openpgpsdk/src/lib/libops.a
#
# LIBS *= ../../libretroshare/src/lib/libretroshare.a
# LIBS *= ../../openpgpsdk/src/lib/libops.a -lbz2 -lbsd
# LIBS *= -lssl -lcrypto -lnetwork
# LIBS *= -lgpgme
# LIBS *= -lupnp
# LIBS *= -lz
# LIBS *= -lixml
#
# LIBS += ../../supportlibs/pegmarkdown/lib/libpegmarkdown.a
# LIBS += -lsqlite3
# }
#
# ##################################### OpenBSD ######################################
#
# openbsd-* {
# INCLUDEPATH *= /usr/local/include
#
# LIBS *= -lssl -lcrypto
# LIBS *= -lgpgme
# LIBS *= -lupnp
# LIBS *= -lgnome-keyring
# LIBS += -lsqlite3
# LIBS *= -rdynamic
# }
#
# ################################### COMMON stuff ##################################
#
# wikipoos {
# PRE_TARGETDEPS *= $$OUT_PWD/../../supportlibs/pegmarkdown/lib/libpegmarkdown.a
# LIBS *= $$OUT_PWD/../../supportlibs/pegmarkdown/lib/libpegmarkdown.a
# LIBS *= -lglib-2.0
# }
################################ GENERAL #######################################
target_link_libraries(${PROJECT_NAME} PRIVATE ${RS_LINK_LIBRARIES})
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/)
set( CMAKE_CXX_FLAGS "-Wno-deprecated-declarations" )
target_compile_definitions(${PROJECT_NAME} PUBLIC RS_NO_WARN_DEPRECATED )
target_compile_definitions(${PROJECT_NAME} PRIVATE RS_RELEASE_VERSION )
target_compile_definitions(${PROJECT_NAME} PRIVATE TARGET=\"retroshare\")
if(RS_GXS_CIRCLES)
target_compile_definitions(${PROJECT_NAME} PRIVATE RS_USE_CIRCLES )
endif(RS_GXS_CIRCLES)
#add_dependencies(${PROJECT_NAME} libretroshare)

File diff suppressed because it is too large Load diff

View file

@ -1,116 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2016, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "AddOnionCommand.h"
#include "HiddenService.h"
#include "CryptoKey.h"
#include "StrUtil.h"
using namespace Tor;
AddOnionCommand::AddOnionCommand(HiddenService *service)
: m_service(service)
{
Q_ASSERT(m_service);
}
bool AddOnionCommand::isSuccessful() const
{
return statusCode() == 250 && m_errorMessage.isEmpty();
}
QByteArray AddOnionCommand::build()
{
QByteArray out("ADD_ONION");
if (m_service->privateKey().isLoaded()) {
out += " ";
out += m_service->privateKey().bytes();
} else {
//out += " NEW:RSA1024"; // this is v2. For v3, use NEW:BEST, or NEW:ED25519-V3
//out += " NEW:ED25519-V3"; // this is v3.
out += " NEW:BEST"; // this is v3, but without control of key type. Generates a RSA1024 key on older Tor versions.
}
foreach (const HiddenService::Target &target, m_service->targets()) {
out += " Port=";
out += QByteArray::number(target.servicePort);
out += ",";
out += target.targetAddress.toString().toLatin1();
out += ":";
out += QByteArray::number(target.targetPort);
}
out.append("\r\n");
return out;
}
void AddOnionCommand::onReply(int statusCode, const QByteArray &data)
{
TorControlCommand::onReply(statusCode, data);
if (statusCode != 250) {
m_errorMessage = QString::fromLatin1(data);
return;
}
const QByteArray keyPrefix("PrivateKey=");
const QByteArray sidPrefix("ServiceID=");
if(data.startsWith("ServiceID=")){
QByteArray service_id = data.mid(sidPrefix.size());
m_service->setServiceId(service_id);
}
if (data.startsWith(keyPrefix)) {
QByteArray keyData(data.mid(keyPrefix.size()));
CryptoKey key;
if (!key.loadFromTorMessage(keyData)) {
m_errorMessage = QStringLiteral("Key structure check failed");
return;
}
m_service->setPrivateKey(key);
}
}
void AddOnionCommand::onFinished(int statusCode)
{
TorControlCommand::onFinished(statusCode);
if (isSuccessful())
emit succeeded();
else
emit failed(statusCode);
}

View file

@ -1,77 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2016, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef ADDONIONCOMMAND_H
#define ADDONIONCOMMAND_H
#include "TorControlCommand.h"
#include <QList>
#include <QPair>
#include <QVariant>
namespace Tor
{
class HiddenService;
class AddOnionCommand : public TorControlCommand
{
Q_OBJECT
Q_DISABLE_COPY(AddOnionCommand)
Q_PROPERTY(QString errorMessage READ errorMessage CONSTANT)
Q_PROPERTY(bool successful READ isSuccessful CONSTANT)
public:
AddOnionCommand(HiddenService *service);
QByteArray build();
QString errorMessage() const { return m_errorMessage; }
bool isSuccessful() const;
signals:
void succeeded();
void failed(int code);
protected:
HiddenService *m_service;
QString m_errorMessage;
virtual void onReply(int statusCode, const QByteArray &data);
virtual void onFinished(int statusCode);
};
}
#endif // ADDONIONCOMMAND_H

View file

@ -1,64 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "AuthenticateCommand.h"
using namespace Tor;
AuthenticateCommand::AuthenticateCommand()
{
}
QByteArray AuthenticateCommand::build(const QByteArray &data)
{
if (data.isNull())
return QByteArray("AUTHENTICATE\r\n");
return QByteArray("AUTHENTICATE ") + data.toHex() + "\r\n";
}
void AuthenticateCommand::onReply(int statusCode, const QByteArray &data)
{
TorControlCommand::onReply(statusCode, data);
m_statusMessage = QString::fromLatin1(data);
}
void AuthenticateCommand::onFinished(int statusCode)
{
if (statusCode == 515) {
m_statusMessage = QStringLiteral("Authentication failed - incorrect password");
} else if (statusCode != 250) {
if (m_statusMessage.isEmpty())
m_statusMessage = QStringLiteral("Authentication failed (error %1").arg(statusCode);
}
TorControlCommand::onFinished(statusCode);
}

View file

@ -1,63 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef AUTHENTICATECOMMAND_H
#define AUTHENTICATECOMMAND_H
#include "TorControlCommand.h"
namespace Tor
{
class AuthenticateCommand : public TorControlCommand
{
Q_OBJECT
public:
AuthenticateCommand();
QByteArray build(const QByteArray &data = QByteArray());
bool isSuccessful() const { return statusCode() == 250; }
QString errorMessage() const { return m_statusMessage; }
protected:
virtual void onReply(int statusCode, const QByteArray &data);
virtual void onFinished(int statusCode);
private:
QString m_statusMessage;
};
}
#endif // AUTHENTICATECOMMAND_H

View file

@ -1,527 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <iostream>
#include "CryptoKey.h"
#include "SecureRNG.h"
#include "Useful.h"
#include <QtDebug>
#include <QFile>
#include <QByteArray>
#include <openssl/bn.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
{
*p = r->p;
*q = r->q;
}
#define RSA_bits(o) (BN_num_bits((o)->n))
#endif
#ifdef TO_REMOVE
void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen);
bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen);
#endif
CryptoKey::CryptoKey()
{
}
CryptoKey::~CryptoKey()
{
clear();
}
#ifdef TO_REMOVE
CryptoKey::Data::~Data()
{
if (key)
{
RSA_free(key);
key = 0;
}
}
#endif
void CryptoKey::clear()
{
key_data.clear();
}
#ifdef TO_REMOVE
bool CryptoKey::loadFromData(const QByteArray &data, KeyType type, KeyFormat format)
{
RSA *key = NULL;
clear();
if (data.isEmpty())
return false;
if (format == PEM) {
BIO *b = BIO_new_mem_buf((void*)data.constData(), -1);
if (type == PrivateKey)
key = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL);
else
key = PEM_read_bio_RSAPublicKey(b, NULL, NULL, NULL);
BIO_free(b);
} else if (format == DER) {
const uchar *dp = reinterpret_cast<const uchar*>(data.constData());
if (type == PrivateKey)
key = d2i_RSAPrivateKey(NULL, &dp, data.size());
else
key = d2i_RSAPublicKey(NULL, &dp, data.size());
} else {
Q_UNREACHABLE();
}
if (!key) {
qWarning() << "Failed to parse" << (type == PrivateKey ? "private" : "public") << "key from data";
return false;
}
d = new Data(key);
return true;
}
#endif
bool CryptoKey::loadFromFile(const QString& path)
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
{
qWarning() << "Failed to open Tor key file " << path << ": " << file.errorString();
return false;
}
QByteArray data = file.readAll();
file.close();
if(data.contains("-----BEGIN RSA PRIVATE KEY-----"))
{
std::cerr << "Note: Reading/converting Tor v2 key format." << std::endl;
// This to be compliant with old format. New format is oblivious to the type of key so we dont need a header
data = data.replace("-----BEGIN RSA PRIVATE KEY-----",nullptr);
data = data.replace("-----END RSA PRIVATE KEY-----",nullptr);
data = data.replace("\n",nullptr);
data = data.replace("\t",nullptr);
data = "RSA1024:"+data;
}
std::cerr << "Have read the following key: " << std::endl;
std::cerr << QString(data).toStdString() << std::endl;
key_data = data;
return true;
}
bool CryptoKey::loadFromTorMessage(const QByteArray& b)
{
// note: We should probably check the structure a bit more, for security.
std::cerr << "Loading new key:" << std::endl;
if(b.startsWith("RSA1024"))
std::cerr << " type: RSA-1024 (Tor v2)" << std::endl;
else if(b.startsWith("ED25519-V3"))
std::cerr << " type: ED25519-V3 (Tor v3)" << std::endl;
else if(b.indexOf(':'))
{
std::cerr << " unknown type, or bad syntax in key: \"" << b.left(b.indexOf(':')).toStdString() << "\". Not accepted." << std::endl;
return false;
}
key_data = b;
return true;
}
/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */
QByteArray torControlHashedPassword(const QByteArray &password)
{
QByteArray salt = SecureRNG::random(8);
if (salt.isNull())
return QByteArray();
int count = ((quint32)16 + (96 & 15)) << ((96 >> 4) + 6);
SHA_CTX hash;
SHA1_Init(&hash);
QByteArray tmp = salt + password;
while (count)
{
int c = qMin(count, tmp.size());
SHA1_Update(&hash, reinterpret_cast<const void*>(tmp.constData()), c);
count -= c;
}
unsigned char md[20];
SHA1_Final(md, &hash);
/* 60 is the hex-encoded value of 96, which is a constant used by Tor's algorithm. */
return QByteArray("16:") + salt.toHex().toUpper() + QByteArray("60") +
QByteArray::fromRawData(reinterpret_cast<const char*>(md), 20).toHex().toUpper();
}
#ifdef TO_REMOVE
bool CryptoKey::isPrivate() const
{
if (!isLoaded()) {
return false;
} else {
const BIGNUM *p, *q;
RSA_get0_factors(d->key, &p, &q);
return (p != 0);
}
}
int CryptoKey::bits() const
{
return isLoaded() ? RSA_bits(d->key) : 0;
}
QByteArray CryptoKey::publicKeyDigest() const
{
if (!isLoaded())
return QByteArray();
QByteArray buf = encodedPublicKey(DER);
QByteArray re(20, 0);
bool ok = SHA1(reinterpret_cast<const unsigned char*>(buf.constData()), buf.size(),
reinterpret_cast<unsigned char*>(re.data())) != NULL;
if (!ok)
{
qWarning() << "Failed to hash public key data for digest";
return QByteArray();
}
return re;
}
QByteArray CryptoKey::encodedPublicKey(KeyFormat format) const
{
if (!isLoaded())
return QByteArray();
if (format == PEM) {
BIO *b = BIO_new(BIO_s_mem());
if (!PEM_write_bio_RSAPublicKey(b, d->key)) {
BUG() << "Failed to encode public key in PEM format";
BIO_free(b);
return QByteArray();
}
BUF_MEM *buf;
BIO_get_mem_ptr(b, &buf);
/* Close BIO, but don't free buf. */
(void)BIO_set_close(b, BIO_NOCLOSE);
BIO_free(b);
QByteArray re((const char *)buf->data, (int)buf->length);
BUF_MEM_free(buf);
return re;
} else if (format == DER) {
uchar *buf = NULL;
int len = i2d_RSAPublicKey(d->key, &buf);
if (len <= 0 || !buf) {
BUG() << "Failed to encode public key in DER format";
return QByteArray();
}
QByteArray re((const char*)buf, len);
OPENSSL_free(buf);
return re;
} else {
Q_UNREACHABLE();
}
return QByteArray();
}
QByteArray CryptoKey::encodedPrivateKey(KeyFormat format) const
{
if (!isLoaded() || !isPrivate())
return QByteArray();
if (format == PEM) {
BIO *b = BIO_new(BIO_s_mem());
if (!PEM_write_bio_RSAPrivateKey(b, d->key, NULL, NULL, 0, NULL, NULL)) {
BUG() << "Failed to encode private key in PEM format";
BIO_free(b);
return QByteArray();
}
BUF_MEM *buf;
BIO_get_mem_ptr(b, &buf);
/* Close BIO, but don't free buf. */
(void)BIO_set_close(b, BIO_NOCLOSE);
BIO_free(b);
QByteArray re((const char *)buf->data, (int)buf->length);
BUF_MEM_free(buf);
return re;
} else if (format == DER) {
uchar *buf = NULL;
int len = i2d_RSAPrivateKey(d->key, &buf);
if (len <= 0 || !buf) {
BUG() << "Failed to encode private key in DER format";
return QByteArray();
}
QByteArray re((const char*)buf, len);
OPENSSL_free(buf);
return re;
} else {
Q_UNREACHABLE();
}
return QByteArray();
}
QString CryptoKey::torServiceID() const
{
if (!isLoaded())
return QString();
QByteArray digest = publicKeyDigest();
if (digest.isNull())
return QString();
static const int hostnameDigestSize = 10;
static const int hostnameEncodedSize = 16;
QByteArray re(hostnameEncodedSize+1, 0);
base32_encode(re.data(), re.size(), digest.constData(), hostnameDigestSize);
// Chop extra null byte
re.chop(1);
return QString::fromLatin1(re);
}
QByteArray CryptoKey::signData(const QByteArray &data) const
{
QByteArray digest(32, 0);
bool ok = SHA256(reinterpret_cast<const unsigned char*>(data.constData()), data.size(),
reinterpret_cast<unsigned char*>(digest.data())) != NULL;
if (!ok) {
qWarning() << "Digest for RSA signature failed";
return QByteArray();
}
return signSHA256(digest);
}
QByteArray CryptoKey::signSHA256(const QByteArray &digest) const
{
if (!isPrivate())
return QByteArray();
QByteArray re(RSA_size(d->key), 0);
unsigned sigsize = 0;
int r = RSA_sign(NID_sha256, reinterpret_cast<const unsigned char*>(digest.constData()), digest.size(),
reinterpret_cast<unsigned char*>(re.data()), &sigsize, d->key);
if (r != 1) {
qWarning() << "RSA encryption failed when generating signature";
return QByteArray();
}
re.truncate(sigsize);
return re;
}
bool CryptoKey::verifyData(const QByteArray &data, QByteArray signature) const
{
QByteArray digest(32, 0);
bool ok = SHA256(reinterpret_cast<const unsigned char*>(data.constData()), data.size(),
reinterpret_cast<unsigned char*>(digest.data())) != NULL;
if (!ok) {
qWarning() << "Digest for RSA verify failed";
return false;
}
return verifySHA256(digest, signature);
}
bool CryptoKey::verifySHA256(const QByteArray &digest, QByteArray signature) const
{
if (!isLoaded())
return false;
int r = RSA_verify(NID_sha256, reinterpret_cast<const uchar*>(digest.constData()), digest.size(),
reinterpret_cast<uchar*>(signature.data()), signature.size(), d->key);
if (r != 1)
return false;
return true;
}
/* Copyright (c) 2001-2004, Roger Dingledine
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
* Copyright (c) 2007-2010, The Tor Project, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 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.
*
* Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
/* Implements base32 encoding as in rfc3548. Requires that srclen*8 is a multiple of 5. */
void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen)
{
unsigned i, bit, v, u;
unsigned nbits = srclen * 8;
/* We need an even multiple of 5 bits, and enough space */
if ((nbits%5) != 0 || destlen > (nbits/5)+1) {
Q_ASSERT(false);
memset(dest, 0, destlen);
return;
}
for (i = 0, bit = 0; bit < nbits; ++i, bit += 5)
{
/* set v to the 16-bit value starting at src[bits/8], 0-padded. */
v = ((quint8) src[bit / 8]) << 8;
if (bit + 5 < nbits)
v += (quint8) src[(bit/8)+1];
/* set u to the 5-bit value at the bit'th bit of src. */
u = (v >> (11 - (bit % 8))) & 0x1F;
dest[i] = BASE32_CHARS[u];
}
dest[i] = '\0';
}
/* Implements base32 decoding as in rfc3548. Requires that srclen*5 is a multiple of 8. */
bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen)
{
unsigned int i, j, bit;
unsigned nbits = srclen * 5;
/* We need an even multiple of 8 bits, and enough space */
if ((nbits%8) != 0 || (nbits/8)+1 > destlen) {
Q_ASSERT(false);
return false;
}
char *tmp = new char[srclen];
/* Convert base32 encoded chars to the 5-bit values that they represent. */
for (j = 0; j < srclen; ++j)
{
if (src[j] > 0x60 && src[j] < 0x7B)
tmp[j] = src[j] - 0x61;
else if (src[j] > 0x31 && src[j] < 0x38)
tmp[j] = src[j] - 0x18;
else if (src[j] > 0x40 && src[j] < 0x5B)
tmp[j] = src[j] - 0x41;
else
{
delete[] tmp;
return false;
}
}
/* Assemble result byte-wise by applying five possible cases. */
for (i = 0, bit = 0; bit < nbits; ++i, bit += 8)
{
switch (bit % 40)
{
case 0:
dest[i] = (((quint8)tmp[(bit/5)]) << 3) + (((quint8)tmp[(bit/5)+1]) >> 2);
break;
case 8:
dest[i] = (((quint8)tmp[(bit/5)]) << 6) + (((quint8)tmp[(bit/5)+1]) << 1)
+ (((quint8)tmp[(bit/5)+2]) >> 4);
break;
case 16:
dest[i] = (((quint8)tmp[(bit/5)]) << 4) + (((quint8)tmp[(bit/5)+1]) >> 1);
break;
case 24:
dest[i] = (((quint8)tmp[(bit/5)]) << 7) + (((quint8)tmp[(bit/5)+1]) << 2)
+ (((quint8)tmp[(bit/5)+2]) >> 3);
break;
case 32:
dest[i] = (((quint8)tmp[(bit/5)]) << 5) + ((quint8)tmp[(bit/5)+1]);
break;
}
}
delete[] tmp;
return true;
}
#endif

View file

@ -1,106 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef CRYPTOKEY_H
#define CRYPTOKEY_H
#include <QString>
#include <QSharedData>
#include <QExplicitlySharedDataPointer>
class CryptoKey
{
public:
enum KeyType {
PrivateKey,
PublicKey
};
enum KeyFormat {
PEM,
DER
};
CryptoKey();
~CryptoKey();
#ifdef TO_REMOVE
bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM);
bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM);
#endif
bool loadFromFile(const QString &path);
void clear();
const QByteArray bytes() const { return key_data; }
bool loadFromTorMessage(const QByteArray& b);
bool isLoaded() const { return !key_data.isNull(); }
#ifdef TO_REMOVE
bool isPrivate() const;
QByteArray publicKeyDigest() const;
QByteArray encodedPublicKey(KeyFormat format) const;
QByteArray encodedPrivateKey(KeyFormat format) const;
QString torServiceID() const;
int bits() const;
// Calculate and sign SHA-256 digest of data using this key and PKCS #1 v2.0 padding
QByteArray signData(const QByteArray &data) const;
// Verify a signature as per signData
bool verifyData(const QByteArray &data, QByteArray signature) const;
// Sign the input SHA-256 digest using this key and PKCS #1 v2.0 padding
QByteArray signSHA256(const QByteArray &digest) const;
// Verify a signature as per signSHA256
bool verifySHA256(const QByteArray &digest, QByteArray signature) const;
#endif
private:
#ifdef TO_REMOVE
struct Data : public QSharedData
{
typedef struct rsa_st RSA;
RSA *key;
Data(RSA *k = 0) : key(k) { }
~Data();
};
#endif
QByteArray key_data;
#ifdef TO_REMOVE
QExplicitlySharedDataPointer<Data> d;
#endif
};
QByteArray torControlHashedPassword(const QByteArray &password);
#endif // CRYPTOKEY_H

View file

@ -1,124 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "GetConfCommand.h"
#include "StrUtil.h"
#include <QDebug>
using namespace Tor;
GetConfCommand::GetConfCommand(Type t)
: type(t)
{
}
QByteArray GetConfCommand::build(const QByteArray &key)
{
return build(QList<QByteArray>() << key);
}
QByteArray GetConfCommand::build(const QList<QByteArray> &keys)
{
QByteArray out;
if (type == GetConf) {
out = "GETCONF";
} else if (type == GetInfo) {
out = "GETINFO";
} else {
Q_ASSERT(false);
return out;
}
foreach (const QByteArray &key, keys) {
out.append(' ');
out.append(key);
}
out.append("\r\n");
return out;
}
void GetConfCommand::onReply(int statusCode, const QByteArray &data)
{
TorControlCommand::onReply(statusCode, data);
if (statusCode != 250)
return;
int kep = data.indexOf('=');
QString key = QString::fromLatin1(data.mid(0, kep));
QVariant value;
if (kep >= 0)
value = QString::fromLatin1(unquotedString(data.mid(kep + 1)));
m_lastKey = key;
QVariantMap::iterator it = m_results.find(key);
if (it != m_results.end()) {
// Make a list of values
QVariantList results = it->toList();
if (results.isEmpty())
results.append(*it);
results.append(value);
*it = QVariant(results);
} else {
m_results.insert(key, value);
}
}
void GetConfCommand::onDataLine(const QByteArray &data)
{
if (m_lastKey.isEmpty()) {
qWarning() << "torctrl: Unexpected data line in GetConf command";
return;
}
QVariantMap::iterator it = m_results.find(m_lastKey);
if (it != m_results.end()) {
QVariantList results = it->toList();
if (results.isEmpty() && !it->toByteArray().isEmpty())
results.append(*it);
results.append(data);
*it = QVariant(results);
} else {
m_results.insert(m_lastKey, QVariantList() << data);
}
}
void GetConfCommand::onDataFinished()
{
m_lastKey.clear();
}
QVariant GetConfCommand::get(const QByteArray &key) const
{
return m_results.value(QString::fromLatin1(key));
}

View file

@ -1,77 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef GETCONFCOMMAND_H
#define GETCONFCOMMAND_H
#include "TorControlCommand.h"
#include <QList>
#include <QVariantMap>
namespace Tor
{
class GetConfCommand : public TorControlCommand
{
Q_OBJECT
Q_DISABLE_COPY(GetConfCommand)
Q_PROPERTY(QVariantMap results READ results CONSTANT)
public:
enum Type {
GetConf,
GetInfo
};
const Type type;
GetConfCommand(Type type);
QByteArray build(const QByteArray &key);
QByteArray build(const QList<QByteArray> &keys);
const QVariantMap &results() const { return m_results; }
QVariant get(const QByteArray &key) const;
protected:
virtual void onReply(int statusCode, const QByteArray &data);
virtual void onDataLine(const QByteArray &data);
virtual void onDataFinished();
private:
QVariantMap m_results;
QString m_lastKey;
};
}
#endif // GETCONFCOMMAND_H

View file

@ -1,146 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "HiddenService.h"
#include "TorControl.h"
#include "TorSocket.h"
#include "CryptoKey.h"
#include "Useful.h"
#include <QDir>
#include <QFile>
#include <QTimer>
#include <QDebug>
using namespace Tor;
HiddenService::HiddenService(QObject *parent)
: QObject(parent), m_status(NotCreated)
{
}
HiddenService::HiddenService(const QString &path, QObject *parent)
: QObject(parent), m_dataPath(path), m_status(NotCreated)
{
/* Set the initial status and, if possible, load the hostname */
if (QDir(m_dataPath).exists(QLatin1String("private_key"))) {
loadPrivateKey();
if (!m_hostname.isEmpty())
m_status = Offline;
}
}
HiddenService::HiddenService(const CryptoKey &privateKey, const QString &path, QObject *parent)
: QObject(parent), m_dataPath(path), m_status(NotCreated)
{
setPrivateKey(privateKey);
m_status = Offline;
}
void HiddenService::setStatus(Status newStatus)
{
if (m_status == newStatus)
return;
Status old = m_status;
m_status = newStatus;
emit statusChanged(m_status, old);
if (m_status == Online)
emit serviceOnline();
}
void HiddenService::addTarget(const Target &target)
{
m_targets.append(target);
}
void HiddenService::addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort)
{
Target t = { targetAddress, servicePort, targetPort };
m_targets.append(t);
}
void HiddenService::setServiceId(const QByteArray& sid)
{
m_service_id = sid;
m_hostname = sid + ".onion";
emit hostnameChanged();
}
void HiddenService::setPrivateKey(const CryptoKey &key)
{
if (m_privateKey.isLoaded()) {
BUG() << "Cannot change the private key on an existing HiddenService";
return;
}
#ifdef TO_REMOVE
if (!key.isPrivate()) {
BUG() << "Cannot create a hidden service with a public key";
return;
}
#endif
m_privateKey = key;
emit privateKeyChanged();
}
void HiddenService::loadPrivateKey()
{
if (m_privateKey.isLoaded() || m_dataPath.isEmpty())
return;
bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"));
if (!ok) {
qWarning() << "Failed to load hidden service key";
return;
}
emit privateKeyChanged();
}
void HiddenService::servicePublished()
{
loadPrivateKey();
if (m_hostname.isEmpty()) {
std::cerr << "Failed to read hidden service hostname" << std::endl;
return;
}
std::cerr << "Hidden service published successfully" << std::endl;
setStatus(Online);
}

View file

@ -1,108 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef HIDDENSERVICE_H
#define HIDDENSERVICE_H
#include <QObject>
#include <QHostAddress>
#include <QList>
#include "CryptoKey.h"
namespace Tor
{
class TorSocket;
class HiddenService : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(HiddenService)
friend class TorControlPrivate;
public:
struct Target
{
QHostAddress targetAddress;
quint16 servicePort, targetPort;
};
enum Status
{
NotCreated = -1, /* Service has not been created yet */
Offline = 0, /* Data exists, but service is not published */
Online /* Published */
};
HiddenService(QObject *parent = 0);
HiddenService(const QString &dataPath, QObject *parent = 0);
HiddenService(const CryptoKey &privateKey, const QString &dataPath = QString(), QObject *parent = 0);
Status status() const { return m_status; }
const QString& hostname() const { return m_hostname; }
const QString serviceId() const { return QString(m_service_id); }
const QString& dataPath() const { return m_dataPath; }
CryptoKey privateKey() { return m_privateKey; }
void setPrivateKey(const CryptoKey &privateKey);
void setServiceId(const QByteArray& sid);
const QList<Target> &targets() const { return m_targets; }
void addTarget(const Target &target);
void addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort);
signals:
void statusChanged(int newStatus, int oldStatus);
void serviceOnline();
void privateKeyChanged();
void hostnameChanged();
private slots:
void servicePublished();
private:
QString m_dataPath;
QList<Target> m_targets;
QString m_hostname;
Status m_status;
CryptoKey m_privateKey;
QByteArray m_service_id;
void loadPrivateKey();
void setStatus(Status newStatus);
};
}
#endif // HIDDENSERVICE_H

View file

@ -1,84 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "PendingOperation.h"
PendingOperation::PendingOperation(QObject *parent)
: QObject(parent), m_finished(false)
{
}
bool PendingOperation::isFinished() const
{
return m_finished;
}
bool PendingOperation::isSuccess() const
{
return m_finished && m_errorMessage.isNull();
}
bool PendingOperation::isError() const
{
return m_finished && !m_errorMessage.isNull();
}
QString PendingOperation::errorMessage() const
{
return m_errorMessage;
}
void PendingOperation::finishWithError(const QString &message)
{
if (message.isEmpty())
m_errorMessage = QStringLiteral("Unknown Error");
m_errorMessage = message;
if (!m_finished) {
m_finished = true;
emit finished();
emit error(m_errorMessage);
}
}
void PendingOperation::finishWithSuccess()
{
Q_ASSERT(m_errorMessage.isNull());
if (!m_finished) {
m_finished = true;
emit finished();
if (isSuccess())
emit success();
}
}

View file

@ -1,87 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef PENDINGOPERATION_H
#define PENDINGOPERATION_H
#include <QObject>
/* Represents an asynchronous operation for reporting status
*
* This class is used for asynchronous operations that report a
* status and errors when finished, particularly for exposing them
* to QML.
*
* Subclass PendingOperation to implement your operation's logic.
* You also need to handle the object's lifetime, for example by
* calling deleteLater() when finished() is emitted.
*
* PendingOperation will emit finished() and one of success() or
* error() when completed.
*/
class PendingOperation : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isFinished READ isFinished NOTIFY finished FINAL)
Q_PROPERTY(bool isSuccess READ isSuccess NOTIFY success FINAL)
Q_PROPERTY(bool isError READ isError NOTIFY error FINAL)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY finished FINAL)
public:
PendingOperation(QObject *parent = 0);
bool isFinished() const;
bool isSuccess() const;
bool isError() const;
QString errorMessage() const;
signals:
// Always emitted once when finished, regardless of status
void finished();
// One of error() or success() is emitted once
void error(const QString &errorMessage);
void success();
protected slots:
void finishWithError(const QString &errorMessage);
void finishWithSuccess();
private:
bool m_finished;
QString m_errorMessage;
};
Q_DECLARE_METATYPE(PendingOperation*)
#endif

View file

@ -1,85 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "ProtocolInfoCommand.h"
#include "TorControl.h"
#include "StrUtil.h"
#include <QList>
using namespace Tor;
ProtocolInfoCommand::ProtocolInfoCommand(TorControl *m)
: manager(m)
{
}
QByteArray ProtocolInfoCommand::build()
{
return QByteArray("PROTOCOLINFO 1\r\n");
}
void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data)
{
TorControlCommand::onReply(statusCode, data);
if (statusCode != 250)
return;
if (data.startsWith("AUTH "))
{
QList<QByteArray> tokens = splitQuotedStrings(data.mid(5), ' ');
foreach (QByteArray token, tokens)
{
if (token.startsWith("METHODS="))
{
QList<QByteArray> textMethods = unquotedString(token.mid(8)).split(',');
for (QList<QByteArray>::Iterator it = textMethods.begin(); it != textMethods.end(); ++it)
{
if (*it == "NULL")
m_authMethods |= AuthNull;
else if (*it == "HASHEDPASSWORD")
m_authMethods |= AuthHashedPassword;
else if (*it == "COOKIE")
m_authMethods |= AuthCookie;
}
}
else if (token.startsWith("COOKIEFILE="))
{
m_cookieFile = QString::fromLatin1(unquotedString(token.mid(11)));
}
}
}
else if (data.startsWith("VERSION Tor="))
{
m_torVersion = QString::fromLatin1(unquotedString(data.mid(12, data.indexOf(' ', 12))));
}
}

View file

@ -1,78 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef PROTOCOLINFOCOMMAND_H
#define PROTOCOLINFOCOMMAND_H
#include "TorControlCommand.h"
#include <QFlags>
namespace Tor
{
class TorControl;
class ProtocolInfoCommand : public TorControlCommand
{
Q_OBJECT
Q_DISABLE_COPY(ProtocolInfoCommand)
public:
enum AuthMethod
{
AuthUnknown = 0,
AuthNull = 0x1,
AuthHashedPassword = 0x2,
AuthCookie = 0x4
};
Q_DECLARE_FLAGS(AuthMethods, AuthMethod)
ProtocolInfoCommand(TorControl *manager);
QByteArray build();
AuthMethods authMethods() const { return m_authMethods; }
QString torVersion() const { return m_torVersion; }
QString cookieFile() const { return m_cookieFile; }
protected:
virtual void onReply(int statusCode, const QByteArray &data);
private:
TorControl *manager;
AuthMethods m_authMethods;
QString m_torVersion;
QString m_cookieFile;
};
}
#endif // PROTOCOLINFOCOMMAND_H

View file

@ -1,147 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "SecureRNG.h"
#include <QtDebug>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <limits.h>
#ifdef Q_OS_WIN
#include <wtypes.h>
#include <wincrypt.h>
#endif
#if QT_VERSION >= 0x040700
#include <QElapsedTimer>
#endif
bool SecureRNG::seed()
{
#if QT_VERSION >= 0x040700
QElapsedTimer timer;
timer.start();
#endif
#ifdef Q_OS_WIN
/* RAND_poll is very unreliable on windows; with older versions of OpenSSL,
* it can take up to several minutes to run and has been known to crash.
* Even newer versions seem to take around 400ms, which is far too long for
* interactive startup. Random data from the windows CSP is used as a seed
* instead, as it should be very high quality random and fast. */
HCRYPTPROV provider = 0;
if (!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
{
qWarning() << "Failed to acquire CSP context for RNG seed:" << hex << GetLastError();
return false;
}
/* Same amount of entropy OpenSSL uses, apparently. */
char buf[32];
if (!CryptGenRandom(provider, sizeof(buf), reinterpret_cast<BYTE*>(buf)))
{
qWarning() << "Failed to get entropy from CSP for RNG seed: " << hex << GetLastError();
CryptReleaseContext(provider, 0);
return false;
}
CryptReleaseContext(provider, 0);
RAND_seed(buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
#else
if (!RAND_poll())
{
qWarning() << "OpenSSL RNG seed failed:" << ERR_get_error();
return false;
}
#endif
#if QT_VERSION >= 0x040700
qDebug() << "RNG seed took" << timer.elapsed() << "ms";
#endif
return true;
}
void SecureRNG::random(char *buf, int size)
{
int r = RAND_bytes(reinterpret_cast<unsigned char*>(buf), size);
if (r <= 0)
qFatal("RNG failed: %lu", ERR_get_error());
}
QByteArray SecureRNG::random(int size)
{
QByteArray re(size, 0);
random(re.data(), size);
return re;
}
QByteArray SecureRNG::randomPrintable(int length)
{
QByteArray re(length, 0);
for (int i = 0; i < re.size(); i++)
re[i] = randomInt(95) + 32;
return re;
}
unsigned SecureRNG::randomInt(unsigned max)
{
unsigned cutoff = UINT_MAX - (UINT_MAX % max);
unsigned value = 0;
for (;;)
{
random(reinterpret_cast<char*>(&value), sizeof(value));
if (value < cutoff)
return value % max;
}
}
#ifndef UINT64_MAX
#define UINT64_MAX ((quint64)-1)
#endif
quint64 SecureRNG::randomInt64(quint64 max)
{
quint64 cutoff = UINT64_MAX - (UINT64_MAX % max);
quint64 value = 0;
for (;;)
{
random(reinterpret_cast<char*>(value), sizeof(value));
if (value < cutoff)
return value % max;
}
}

View file

@ -1,51 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef SECURERNG_H
#define SECURERNG_H
#include <QByteArray>
class SecureRNG
{
public:
static bool seed();
static void random(char *buf, int size);
static QByteArray random(int size);
static QByteArray randomPrintable(int length);
static unsigned randomInt(unsigned max);
static quint64 randomInt64(quint64 max);
};
#endif // SECURERNG_H

View file

@ -1,106 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "SetConfCommand.h"
#include "StrUtil.h"
using namespace Tor;
SetConfCommand::SetConfCommand()
: m_resetMode(false)
{
}
void SetConfCommand::setResetMode(bool enabled)
{
m_resetMode = enabled;
}
bool SetConfCommand::isSuccessful() const
{
return statusCode() == 250;
}
QByteArray SetConfCommand::build(const QByteArray &key, const QByteArray &value)
{
return build(QList<QPair<QByteArray, QByteArray> >() << qMakePair(key, value));
}
QByteArray SetConfCommand::build(const QVariantMap &data)
{
QList<QPair<QByteArray, QByteArray> > out;
for (QVariantMap::ConstIterator it = data.begin(); it != data.end(); it++) {
QByteArray key = it.key().toLatin1();
if (static_cast<QMetaType::Type>(it.value().type()) == QMetaType::QVariantList) {
QVariantList values = it.value().value<QVariantList>();
foreach (const QVariant &value, values)
out.append(qMakePair(key, value.toString().toLatin1()));
} else {
out.append(qMakePair(key, it.value().toString().toLatin1()));
}
}
return build(out);
}
QByteArray SetConfCommand::build(const QList<QPair<QByteArray, QByteArray> > &data)
{
QByteArray out(m_resetMode ? "RESETCONF" : "SETCONF");
for (int i = 0; i < data.size(); i++) {
out += " " + data[i].first;
if (!data[i].second.isEmpty())
out += "=" + quotedString(data[i].second);
}
out.append("\r\n");
return out;
}
void SetConfCommand::onReply(int statusCode, const QByteArray &data)
{
TorControlCommand::onReply(statusCode, data);
if (statusCode != 250)
m_errorMessage = QString::fromLatin1(data);
}
void SetConfCommand::onFinished(int statusCode)
{
TorControlCommand::onFinished(statusCode);
if (isSuccessful())
emit setConfSucceeded();
else
emit setConfFailed(statusCode);
}

View file

@ -1,78 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef SETCONFCOMMAND_H
#define SETCONFCOMMAND_H
#include "TorControlCommand.h"
#include <QList>
#include <QPair>
#include <QVariant>
namespace Tor
{
class SetConfCommand : public TorControlCommand
{
Q_OBJECT
Q_DISABLE_COPY(SetConfCommand)
Q_PROPERTY(QString errorMessage READ errorMessage CONSTANT)
Q_PROPERTY(bool successful READ isSuccessful CONSTANT)
public:
SetConfCommand();
void setResetMode(bool resetMode);
QByteArray build(const QByteArray &key, const QByteArray &value);
QByteArray build(const QVariantMap &data);
QByteArray build(const QList<QPair<QByteArray, QByteArray> > &data);
QString errorMessage() const { return m_errorMessage; }
bool isSuccessful() const;
signals:
void setConfSucceeded();
void setConfFailed(int code);
protected:
QString m_errorMessage;
bool m_resetMode;
virtual void onReply(int statusCode, const QByteArray &data);
virtual void onFinished(int statusCode);
};
}
#endif // SETCONFCOMMAND_H

View file

@ -1,553 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Settings.h"
#include <QCoreApplication>
#include <QJsonDocument>
#include <QJsonParseError>
#include <QSaveFile>
#include <QFile>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QDebug>
#include <QPointer>
class SettingsFilePrivate : public QObject
{
Q_OBJECT
public:
SettingsFile *q;
QString filePath;
QString errorMessage;
QTimer syncTimer;
QJsonObject jsonRoot;
SettingsObject *rootObject;
SettingsFilePrivate(SettingsFile *qp);
virtual ~SettingsFilePrivate();
void reset();
void setError(const QString &message);
bool checkDirPermissions(const QString &path);
bool readFile();
bool writeFile();
static QStringList splitPath(const QString &input, bool &ok);
QJsonValue read(const QJsonObject &base, const QStringList &path);
bool write(const QStringList &path, const QJsonValue &value);
signals:
void modified(const QStringList &path, const QJsonValue &value);
private slots:
void sync();
};
SettingsFile::SettingsFile(QObject *parent)
: QObject(parent), d(new SettingsFilePrivate(this))
{
d->rootObject = new SettingsObject(this, QString());
}
SettingsFile::~SettingsFile()
{
}
SettingsFilePrivate::SettingsFilePrivate(SettingsFile *qp)
: QObject(qp)
, q(qp)
, rootObject(0)
{
syncTimer.setInterval(0);
syncTimer.setSingleShot(true);
connect(&syncTimer, &QTimer::timeout, this, &SettingsFilePrivate::sync);
}
SettingsFilePrivate::~SettingsFilePrivate()
{
if (syncTimer.isActive())
sync();
delete rootObject;
}
void SettingsFilePrivate::reset()
{
filePath.clear();
errorMessage.clear();
jsonRoot = QJsonObject();
emit modified(QStringList(), jsonRoot);
}
QString SettingsFile::filePath() const
{
return d->filePath;
}
bool SettingsFile::setFilePath(const QString &filePath)
{
if (d->filePath == filePath)
return hasError();
d->reset();
d->filePath = filePath;
QFileInfo fileInfo(filePath);
QDir dir(fileInfo.path());
if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
d->setError(QStringLiteral("Cannot create directory: %1").arg(dir.path()));
return false;
}
d->checkDirPermissions(fileInfo.path());
if (!d->readFile())
return false;
return true;
}
QString SettingsFile::errorMessage() const
{
return d->errorMessage;
}
bool SettingsFile::hasError() const
{
return !d->errorMessage.isEmpty();
}
void SettingsFilePrivate::setError(const QString &message)
{
errorMessage = message;
emit q->error();
}
bool SettingsFilePrivate::checkDirPermissions(const QString &path)
{
static QFile::Permissions desired = QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ExeUser;
static QFile::Permissions ignored = QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner;
QFile file(path);
if ((file.permissions() & ~ignored) != desired) {
qDebug() << "Correcting permissions on configuration directory";
if (!file.setPermissions(desired)) {
qWarning() << "Correcting permissions on configuration directory failed";
return false;
}
}
return true;
}
SettingsObject *SettingsFile::root()
{
return d->rootObject;
}
const SettingsObject *SettingsFile::root() const
{
return d->rootObject;
}
void SettingsFilePrivate::sync()
{
if (filePath.isEmpty())
return;
syncTimer.stop();
writeFile();
}
bool SettingsFilePrivate::readFile()
{
QFile file(filePath);
if (!file.open(QIODevice::ReadWrite)) {
setError(file.errorString());
return false;
}
QByteArray data = file.readAll();
if (data.isEmpty() && (file.error() != QFileDevice::NoError || file.size() > 0)) {
setError(file.errorString());
return false;
}
if (data.isEmpty()) {
jsonRoot = QJsonObject();
return true;
}
QJsonParseError parseError;
QJsonDocument document = QJsonDocument::fromJson(data, &parseError);
if (document.isNull()) {
setError(parseError.errorString());
return false;
}
if (!document.isObject()) {
setError(QStringLiteral("Invalid configuration file (expected object)"));
return false;
}
jsonRoot = document.object();
emit modified(QStringList(), jsonRoot);
return true;
}
bool SettingsFilePrivate::writeFile()
{
QSaveFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
setError(file.errorString());
return false;
}
QJsonDocument document(jsonRoot);
QByteArray data = document.toJson();
if (data.isEmpty() && !document.isEmpty()) {
setError(QStringLiteral("Encoding failure"));
return false;
}
if (file.write(data) < data.size() || !file.commit()) {
setError(file.errorString());
return false;
}
return true;
}
QStringList SettingsFilePrivate::splitPath(const QString &input, bool &ok)
{
QStringList components = input.split(QLatin1Char('.'));
// Allow a leading '.' to simplify concatenation
if (!components.isEmpty() && components.first().isEmpty())
components.takeFirst();
// No other empty components, including a trailing .
foreach (const QString &word, components) {
if (word.isEmpty()) {
ok = false;
return QStringList();
}
}
ok = true;
return components;
}
QJsonValue SettingsFilePrivate::read(const QJsonObject &base, const QStringList &path)
{
QJsonValue current = base;
foreach (const QString &key, path) {
QJsonObject object = current.toObject();
if (object.isEmpty() || (current = object.value(key)).isUndefined())
return QJsonValue::Undefined;
}
return current;
}
// Compare two QJsonValue to find keys that have changed,
// recursing into objects and building paths as necessary.
typedef QList<QPair<QStringList, QJsonValue> > ModifiedList;
static void findModifiedRecursive(ModifiedList &modified, const QStringList &path, const QJsonValue &oldValue, const QJsonValue &newValue)
{
if (oldValue.isObject() || newValue.isObject()) {
// If either is a non-object type, this returns an empty object
QJsonObject oldObject = oldValue.toObject();
QJsonObject newObject = newValue.toObject();
// Iterate keys of the original object and compare to new
for (QJsonObject::iterator it = oldObject.begin(); it != oldObject.end(); it++) {
QJsonValue newSubValue = newObject.value(it.key());
if (*it == newSubValue)
continue;
if ((*it).isObject() || newSubValue.isObject())
findModifiedRecursive(modified, QStringList() << path << it.key(), *it, newSubValue);
else
modified.append(qMakePair(QStringList() << path << it.key(), newSubValue));
}
// Iterate keys of the new object that may not be in original
for (QJsonObject::iterator it = newObject.begin(); it != newObject.end(); it++) {
if (oldObject.contains(it.key()))
continue;
if ((*it).isObject())
findModifiedRecursive(modified, QStringList() << path << it.key(), QJsonValue::Undefined, it.value());
else
modified.append(qMakePair(QStringList() << path << it.key(), it.value()));
}
} else
modified.append(qMakePair(path, newValue));
}
bool SettingsFilePrivate::write(const QStringList &path, const QJsonValue &value)
{
typedef QVarLengthArray<QPair<QString,QJsonObject> > ObjectStack;
ObjectStack stack;
QJsonValue current = jsonRoot;
QJsonValue originalValue;
QString currentKey;
foreach (const QString &key, path) {
const QJsonObject &parent = current.toObject();
stack.append(qMakePair(currentKey, parent));
current = parent.value(key);
currentKey = key;
}
// Stack now contains parent objects starting with the root, and current
// is the old value. Write back changes in reverse.
if (current == value)
return false;
originalValue = current;
current = value;
ObjectStack::const_iterator it = stack.end(), begin = stack.begin();
while (it != begin) {
--it;
QJsonObject update = it->second;
update.insert(currentKey, current);
current = update;
currentKey = it->first;
}
// current is now the updated jsonRoot
jsonRoot = current.toObject();
syncTimer.start();
ModifiedList modified;
findModifiedRecursive(modified, path, originalValue, value);
for (ModifiedList::iterator it = modified.begin(); it != modified.end(); it++)
emit this->modified(it->first, it->second);
return true;
}
class SettingsObjectPrivate : public QObject
{
Q_OBJECT
public:
explicit SettingsObjectPrivate(SettingsObject *q);
SettingsObject *q;
SettingsFile *file;
QStringList path;
QJsonObject object;
bool invalid;
void setFile(SettingsFile *file);
public slots:
void modified(const QStringList &absolutePath, const QJsonValue &value);
};
SettingsObject::SettingsObject(QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(defaultFile());
if (d->file)
setPath(QString());
}
SettingsObject::SettingsObject(const QString &path, QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(defaultFile());
setPath(path);
}
SettingsObject::SettingsObject(SettingsFile *file, const QString &path, QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(file);
setPath(path);
}
SettingsObject::SettingsObject(SettingsObject *base, const QString &path, QObject *parent)
: QObject(parent)
, d(new SettingsObjectPrivate(this))
{
d->setFile(base->d->file);
setPath(base->path() + QLatin1Char('.') + path);
}
SettingsObjectPrivate::SettingsObjectPrivate(SettingsObject *qp)
: QObject(qp)
, q(qp)
, file(0)
, invalid(true)
{
}
void SettingsObjectPrivate::setFile(SettingsFile *value)
{
if (file == value)
return;
if (file)
disconnect(file, 0, this, 0);
file = value;
if (file)
connect(file->d, &SettingsFilePrivate::modified, this, &SettingsObjectPrivate::modified);
}
// Emit SettingsObject::modified with a relative path if path is matched
void SettingsObjectPrivate::modified(const QStringList &key, const QJsonValue &value)
{
if (key.size() < path.size())
return;
for (int i = 0; i < path.size(); i++) {
if (path[i] != key[i])
return;
}
object = file->d->read(file->d->jsonRoot, path).toObject();
emit q->modified(QStringList(key.mid(path.size())).join(QLatin1Char('.')), value);
emit q->dataChanged();
}
static QPointer<SettingsFile> defaultObjectFile;
SettingsFile *SettingsObject::defaultFile()
{
return defaultObjectFile;
}
void SettingsObject::setDefaultFile(SettingsFile *file)
{
defaultObjectFile = file;
}
QString SettingsObject::path() const
{
return d->path.join(QLatin1Char('.'));
}
void SettingsObject::setPath(const QString &input)
{
bool ok = false;
QStringList newPath = SettingsFilePrivate::splitPath(input, ok);
if (!ok) {
d->invalid = true;
d->path.clear();
d->object = QJsonObject();
emit pathChanged();
emit dataChanged();
return;
}
if (!d->invalid && d->path == newPath)
return;
d->path = newPath;
if (d->file) {
d->invalid = false;
d->object = d->file->d->read(d->file->d->jsonRoot, d->path).toObject();
emit dataChanged();
}
emit pathChanged();
}
QJsonObject SettingsObject::data() const
{
return d->object;
}
void SettingsObject::setData(const QJsonObject &input)
{
if (d->invalid || d->object == input)
return;
d->object = input;
d->file->d->write(d->path, d->object);
}
QJsonValue SettingsObject::read(const QString &key, const QJsonValue &defaultValue) const
{
bool ok = false;
QStringList splitKey = SettingsFilePrivate::splitPath(key, ok);
if (d->invalid || !ok || splitKey.isEmpty()) {
qDebug() << "Invalid settings read of path" << key;
return defaultValue;
}
QJsonValue ret = d->file->d->read(d->object, splitKey);
if (ret.isUndefined())
ret = defaultValue;
return ret;
}
void SettingsObject::write(const QString &key, const QJsonValue &value)
{
bool ok = false;
QStringList splitKey = SettingsFilePrivate::splitPath(key, ok);
if (d->invalid || !ok || splitKey.isEmpty()) {
qDebug() << "Invalid settings write of path" << key;
return;
}
splitKey = d->path + splitKey;
d->file->d->write(splitKey, value);
}
void SettingsObject::unset(const QString &key)
{
write(key, QJsonValue());
}
void SettingsObject::undefine()
{
if (d->invalid)
return;
d->object = QJsonObject();
d->file->d->write(d->path, QJsonValue::Undefined);
}
#include "Settings.moc"

View file

@ -1,257 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef SETTINGS_H
#define SETTINGS_H
#include <QObject>
#include <QJsonValue>
#include <QJsonObject>
#include <QJsonArray>
#include <QStringList>
#include <QDateTime>
class SettingsObject;
class SettingsFilePrivate;
class SettingsObjectPrivate;
/* SettingsFile represents a JSON-encoded configuration file.
*
* SettingsFile is an API for reading, writing, and change notification
* on JSON-encoded settings files.
*
* Data is accessed via SettingsObject, either using the root property
* or by creating a SettingsObject, optionally using a base path.
*/
class SettingsFile : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(SettingsFile)
Q_PROPERTY(SettingsObject *root READ root CONSTANT)
Q_PROPERTY(QString filePath READ filePath WRITE setFilePath NOTIFY filePathChanged)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY error)
Q_PROPERTY(bool hasError READ hasError NOTIFY error)
public:
explicit SettingsFile(QObject *parent = 0);
virtual ~SettingsFile();
QString filePath() const;
bool setFilePath(const QString &filePath);
QString errorMessage() const;
bool hasError() const;
SettingsObject *root();
const SettingsObject *root() const;
signals:
void filePathChanged();
void error();
private:
SettingsFilePrivate *d;
friend class SettingsObject;
friend class SettingsObjectPrivate;
};
/* SettingsObject reads and writes data within a SettingsFile
*
* A SettingsObject is associated with a SettingsFile and represents an object
* tree within that file. It refers to the JSON object tree using a path
* notation with keys separated by '.'. For example:
*
* {
* "one": {
* "two": {
* "three": "value"
* }
* }
* }
*
* With this data, a SettingsObject with an empty path can read with the path
* "one.two.three", and a SettingsObject with a path of "one.two" can simply
* read or write on "three".
*
* Multiple SettingsObjects may be created for the same path, and will be kept
* synchronized with changes. The modified signal is emitted for all changes
* affecting keys within a path, including writes of object trees and from other
* instances.
*/
class SettingsObject : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(SettingsObject)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QJsonObject data READ data WRITE setData NOTIFY dataChanged)
public:
explicit SettingsObject(QObject *parent = 0);
explicit SettingsObject(const QString &path, QObject *parent = 0);
explicit SettingsObject(SettingsFile *file, const QString &path, QObject *parent = 0);
explicit SettingsObject(SettingsObject *base, const QString &path, QObject *parent = 0);
/* Specify a SettingsFile to use by default on SettingsObject instances.
*
* After calling setDefaultFile, a SettingsObject created without any file, e.g.:
*
* SettingsObject settings;
* SettingsObject animals(QStringLiteral("animals"));
*
* Will use the specified SettingsFile instance by default. This is a convenience
* over passing around instances of SettingsFile in application use cases, and is
* particularly useful for QML.
*/
static SettingsFile *defaultFile();
static void setDefaultFile(SettingsFile *file);
QString path() const;
void setPath(const QString &path);
QJsonObject data() const;
void setData(const QJsonObject &data);
Q_INVOKABLE QJsonValue read(const QString &key, const QJsonValue &defaultValue = QJsonValue::Undefined) const;
template<typename T> T read(const QString &key) const;
Q_INVOKABLE void write(const QString &key, const QJsonValue &value);
template<typename T> void write(const QString &key, const T &value);
Q_INVOKABLE void unset(const QString &key);
// const char* key overloads
QJsonValue read(const char *key, const QJsonValue &defaultValue = QJsonValue::Undefined) const
{
return read(QString::fromLatin1(key), defaultValue);
}
template<typename T> T read(const char *key) const
{
return read<T>(QString::fromLatin1(key));
}
void write(const char *key, const QJsonValue &value)
{
write(QString::fromLatin1(key), value);
}
template<typename T> void write(const char *key, const T &value)
{
write<T>(QString::fromLatin1(key), value);
}
void unset(const char *key)
{
unset(QString::fromLatin1(key));
}
Q_INVOKABLE void undefine();
signals:
void pathChanged();
void dataChanged();
void modified(const QString &path, const QJsonValue &value);
private:
SettingsObjectPrivate *d;
};
template<typename T> inline void SettingsObject::write(const QString &key, const T &value)
{
write(key, QJsonValue(value));
}
template<> inline QString SettingsObject::read<QString>(const QString &key) const
{
return read(key).toString();
}
template<> inline QJsonArray SettingsObject::read<QJsonArray>(const QString &key) const
{
return read(key).toArray();
}
template<> inline QJsonObject SettingsObject::read<QJsonObject>(const QString &key) const
{
return read(key).toObject();
}
template<> inline double SettingsObject::read<double>(const QString &key) const
{
return read(key).toDouble();
}
template<> inline int SettingsObject::read<int>(const QString &key) const
{
return read(key).toInt();
}
template<> inline bool SettingsObject::read<bool>(const QString &key) const
{
return read(key).toBool();
}
template<> inline QDateTime SettingsObject::read<QDateTime>(const QString &key) const
{
QString value = read(key).toString();
if (value.isEmpty())
return QDateTime();
return QDateTime::fromString(value, Qt::ISODate).toLocalTime();
}
template<> inline void SettingsObject::write<QDateTime>(const QString &key, const QDateTime &value)
{
write(key, QJsonValue(value.toUTC().toString(Qt::ISODate)));
}
// Explicitly store value encoded as base64. Decodes and casts implicitly to QByteArray for reads.
class Base64Encode
{
public:
explicit Base64Encode(const QByteArray &value) : d(value) { }
operator QByteArray() { return d; }
QByteArray encoded() const { return d.toBase64(); }
private:
QByteArray d;
};
template<> inline Base64Encode SettingsObject::read<Base64Encode>(const QString &key) const
{
return Base64Encode(QByteArray::fromBase64(read(key).toString().toLatin1()));
}
template<> inline void SettingsObject::write<Base64Encode>(const QString &key, const Base64Encode &value)
{
write(key, QJsonValue(QString::fromLatin1(value.encoded())));
}
#endif

View file

@ -1,118 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "StrUtil.h"
QByteArray quotedString(const QByteArray &string)
{
QByteArray out;
out.reserve(string.size() * 2);
out.append('"');
for (int i = 0; i < string.size(); ++i)
{
switch (string[i])
{
case '"':
out.append("\\\"");
break;
case '\\':
out.append("\\\\");
break;
default:
out.append(string[i]);
break;
}
}
out.append('"');
return out;
}
QByteArray unquotedString(const QByteArray &string)
{
if (string.size() < 2 || string[0] != '"')
return string;
QByteArray out;
out.reserve(string.size() - 2);
for (int i = 1; i < string.size(); ++i)
{
switch (string[i])
{
case '\\':
if (++i < string.size())
out.append(string[i]);
break;
case '"':
return out;
default:
out.append(string[i]);
}
}
return out;
}
QList<QByteArray> splitQuotedStrings(const QByteArray &input, char separator)
{
QList<QByteArray> out;
bool inquote = false;
int start = 0;
for (int i = 0; i < input.size(); ++i)
{
switch (input[i])
{
case '"':
inquote = !inquote;
break;
case '\\':
if (inquote)
++i;
break;
}
if (!inquote && input[i] == separator)
{
out.append(input.mid(start, i - start));
start = i+1;
}
}
if (start < input.size())
out.append(input.mid(start));
return out;
}

View file

@ -1,46 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef STRINGUTIL_H
#define STRINGUTIL_H
#include <QByteArray>
#include <QList>
QByteArray quotedString(const QByteArray &string);
/* Return the unquoted contents of a string, either until an end quote or an unescaped separator character. */
QByteArray unquotedString(const QByteArray &string);
QList<QByteArray> splitQuotedStrings(const QByteArray &input, char separator);
#endif // STRINGUTIL_H

View file

@ -1,788 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <time.h>
#include "TorControl.h"
#include "TorControlSocket.h"
#include "HiddenService.h"
#include "ProtocolInfoCommand.h"
#include "AuthenticateCommand.h"
#include "SetConfCommand.h"
#include "GetConfCommand.h"
#include "AddOnionCommand.h"
#include "StrUtil.h"
#include "Settings.h"
#include "PendingOperation.h"
#include <QHostAddress>
#include <QDir>
#include <QNetworkProxy>
//#include <QQmlEngine>
#include <QTimer>
#include <QSaveFile>
#include <QRegularExpression>
#include <QDebug>
Tor::TorControl *torControl = 0;
class nullstream: public std::ostream {};
static std::ostream& torctrldebug()
{
static nullstream null ;
if(true)
return std::cerr << time(NULL) << ":TOR CONTROL: " ;
else
return null ;
}
#define torCtrlDebug torctrldebug
using namespace Tor;
namespace Tor {
class TorControlPrivate : public QObject
{
Q_OBJECT
public:
TorControl *q;
TorControlSocket *socket;
QHostAddress torAddress;
QString errorMessage;
QString torVersion;
QByteArray authPassword;
QHostAddress socksAddress;
QList<HiddenService*> services;
quint16 controlPort, socksPort;
TorControl::Status status;
TorControl::TorStatus torStatus;
QVariantMap bootstrapStatus;
bool hasOwnership;
TorControlPrivate(TorControl *parent);
void setStatus(TorControl::Status status);
void setTorStatus(TorControl::TorStatus status);
void getTorInfo();
void publishServices();
public slots:
void socketConnected();
void socketDisconnected();
void socketError();
void authenticateReply();
void protocolInfoReply();
void getTorInfoReply();
void setError(const QString &message);
void statusEvent(int code, const QByteArray &data);
void updateBootstrap(const QList<QByteArray> &data);
};
}
TorControl::TorControl(QObject *parent)
: QObject(parent), d(new TorControlPrivate(this))
{
}
TorControlPrivate::TorControlPrivate(TorControl *parent)
: QObject(parent), q(parent), controlPort(0), socksPort(0),
status(TorControl::NotConnected), torStatus(TorControl::TorUnknown),
hasOwnership(false)
{
socket = new TorControlSocket(this);
QObject::connect(socket, SIGNAL(connected()), this, SLOT(socketConnected()));
QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError()));
QObject::connect(socket, SIGNAL(error(QString)), this, SLOT(setError(QString)));
}
QNetworkProxy TorControl::connectionProxy()
{
return QNetworkProxy(QNetworkProxy::Socks5Proxy, d->socksAddress.toString(), d->socksPort);
}
void TorControlPrivate::setStatus(TorControl::Status n)
{
if (n == status)
return;
TorControl::Status old = status;
status = n;
if (old == TorControl::Error)
errorMessage.clear();
emit q->statusChanged(status, old);
if (status == TorControl::Connected && old < TorControl::Connected)
emit q->connected();
else if (status < TorControl::Connected && old >= TorControl::Connected)
emit q->disconnected();
}
void TorControlPrivate::setTorStatus(TorControl::TorStatus n)
{
if (n == torStatus)
return;
TorControl::TorStatus old = torStatus;
torStatus = n;
emit q->torStatusChanged(torStatus, old);
emit q->connectivityChanged();
if (torStatus == TorControl::TorReady && socksAddress.isNull()) {
// Request info again to read the SOCKS port
getTorInfo();
}
}
void TorControlPrivate::setError(const QString &message)
{
errorMessage = message;
setStatus(TorControl::Error);
qWarning() << "torctrl: Error:" << errorMessage;
socket->abort();
QTimer::singleShot(15000, q, SLOT(reconnect()));
}
TorControl::Status TorControl::status() const
{
return d->status;
}
TorControl::TorStatus TorControl::torStatus() const
{
return d->torStatus;
}
QString TorControl::torVersion() const
{
return d->torVersion;
}
QString TorControl::errorMessage() const
{
return d->errorMessage;
}
bool TorControl::hasConnectivity() const
{
return torStatus() == TorReady && !d->socksAddress.isNull();
}
QHostAddress TorControl::socksAddress() const
{
return d->socksAddress;
}
quint16 TorControl::socksPort() const
{
return d->socksPort;
}
QList<HiddenService*> TorControl::hiddenServices() const
{
return d->services;
}
QVariantMap TorControl::bootstrapStatus() const
{
return d->bootstrapStatus;
}
void TorControl::setAuthPassword(const QByteArray &password)
{
d->authPassword = password;
}
void TorControl::connect(const QHostAddress &address, quint16 port)
{
if (status() > Connecting)
{
torCtrlDebug() << "Ignoring TorControl::connect due to existing connection" << std::endl;
return;
}
d->torAddress = address;
d->controlPort = port;
d->setTorStatus(TorUnknown);
bool b = d->socket->blockSignals(true);
d->socket->abort();
d->socket->blockSignals(b);
d->setStatus(Connecting);
d->socket->connectToHost(address, port);
}
void TorControl::reconnect()
{
Q_ASSERT(!d->torAddress.isNull() && d->controlPort);
if (d->torAddress.isNull() || !d->controlPort || status() >= Connecting)
return;
d->setStatus(Connecting);
d->socket->connectToHost(d->torAddress, d->controlPort);
}
void TorControlPrivate::authenticateReply()
{
AuthenticateCommand *command = qobject_cast<AuthenticateCommand*>(sender());
Q_ASSERT(command);
Q_ASSERT(status == TorControl::Authenticating);
if (!command)
return;
if (!command->isSuccessful()) {
setError(command->errorMessage());
return;
}
torCtrlDebug() << "torctrl: Authentication successful" << std::endl;
setStatus(TorControl::Connected);
setTorStatus(TorControl::TorUnknown);
TorControlCommand *clientEvents = new TorControlCommand;
connect(clientEvents, &TorControlCommand::replyLine, this, &TorControlPrivate::statusEvent);
socket->registerEvent("STATUS_CLIENT", clientEvents);
getTorInfo();
publishServices();
// XXX Fix old configurations that would store unwanted options in torrc.
// This can be removed some suitable amount of time after 1.0.4.
if (hasOwnership)
q->saveConfiguration();
}
void TorControlPrivate::socketConnected()
{
Q_ASSERT(status == TorControl::Connecting);
torCtrlDebug() << "torctrl: Connected socket; querying information" << std::endl;
setStatus(TorControl::Authenticating);
ProtocolInfoCommand *command = new ProtocolInfoCommand(q);
connect(command, &TorControlCommand::finished, this, &TorControlPrivate::protocolInfoReply);
socket->sendCommand(command, command->build());
}
void TorControlPrivate::socketDisconnected()
{
/* Clear some internal state */
torVersion.clear();
socksAddress.clear();
socksPort = 0;
setTorStatus(TorControl::TorUnknown);
/* This emits the disconnected() signal as well */
setStatus(TorControl::NotConnected);
}
void TorControlPrivate::socketError()
{
setError(QStringLiteral("Connection failed: %1").arg(socket->errorString()));
}
void TorControlPrivate::protocolInfoReply()
{
ProtocolInfoCommand *info = qobject_cast<ProtocolInfoCommand*>(sender());
if (!info)
return;
torVersion = info->torVersion();
if (status == TorControl::Authenticating)
{
AuthenticateCommand *auth = new AuthenticateCommand;
connect(auth, &TorControlCommand::finished, this, &TorControlPrivate::authenticateReply);
QByteArray data;
ProtocolInfoCommand::AuthMethods methods = info->authMethods();
if (methods.testFlag(ProtocolInfoCommand::AuthNull))
{
torCtrlDebug() << "torctrl: Using null authentication" << std::endl;
data = auth->build();
}
else if (methods.testFlag(ProtocolInfoCommand::AuthCookie) && !info->cookieFile().isEmpty())
{
QString cookieFile = info->cookieFile();
QString cookieError;
torCtrlDebug() << "torctrl: Using cookie authentication with file" << cookieFile.toStdString() << std::endl;
QFile file(cookieFile);
if (file.open(QIODevice::ReadOnly))
{
QByteArray cookie = file.readAll();
file.close();
/* Simple test to avoid a vulnerability where any process listening on what we think is
* the control port could trick us into sending the contents of an arbitrary file */
if (cookie.size() == 32)
data = auth->build(cookie);
else
cookieError = QStringLiteral("Unexpected file size");
}
else
cookieError = file.errorString();
if (!cookieError.isNull() || data.isNull())
{
/* If we know a password and password authentication is allowed, try using that instead.
* This is a strange corner case that will likely never happen in a normal configuration,
* but it has happened. */
if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword) && !authPassword.isEmpty())
{
torCtrlDebug() << "torctrl: Unable to read authentication cookie file:" << cookieError.toStdString() << std::endl;
goto usePasswordAuth;
}
setError(QStringLiteral("Unable to read authentication cookie file: %1").arg(cookieError));
delete auth;
return;
}
}
else if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword) && !authPassword.isEmpty())
{
usePasswordAuth:
torCtrlDebug() << "torctrl: Using hashed password authentication" << std::endl;
data = auth->build(authPassword);
}
else
{
if (methods.testFlag(ProtocolInfoCommand::AuthHashedPassword))
setError(QStringLiteral("Tor requires a control password to connect, but no password is configured."));
else
setError(QStringLiteral("Tor is not configured to accept any supported authentication methods."));
delete auth;
return;
}
socket->sendCommand(auth, data);
}
}
void TorControlPrivate::getTorInfo()
{
Q_ASSERT(q->isConnected());
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetInfo);
connect(command, &TorControlCommand::finished, this, &TorControlPrivate::getTorInfoReply);
QList<QByteArray> keys;
keys << QByteArray("status/circuit-established") << QByteArray("status/bootstrap-phase");
/* If these are set in the config, they override the automatic behavior. */
SettingsObject settings(QStringLiteral("tor"));
QHostAddress forceAddress(settings.read("socksAddress").toString());
quint16 port = (quint16)settings.read("socksPort").toInt();
if (!forceAddress.isNull() && port) {
torCtrlDebug() << "torctrl: Using manually specified SOCKS connection settings";
socksAddress = forceAddress;
socksPort = port;
emit q->connectivityChanged();
} else
keys << QByteArray("net/listeners/socks");
socket->sendCommand(command, command->build(keys));
}
void TorControlPrivate::getTorInfoReply()
{
GetConfCommand *command = qobject_cast<GetConfCommand*>(sender());
if (!command || !q->isConnected())
return;
QList<QByteArray> listenAddresses = splitQuotedStrings(command->get(QByteArray("net/listeners/socks")).toString().toLatin1(), ' ');
for (QList<QByteArray>::Iterator it = listenAddresses.begin(); it != listenAddresses.end(); ++it) {
QByteArray value = unquotedString(*it);
int sepp = value.indexOf(':');
QHostAddress address(QString::fromLatin1(value.mid(0, sepp)));
quint16 port = (quint16)value.mid(sepp+1).toUInt();
/* Use the first address that matches the one used for this control connection. If none do,
* just use the first address and rely on the user to reconfigure if necessary (not a problem;
* their setup is already very customized) */
if (socksAddress.isNull() || address == socket->peerAddress()) {
socksAddress = address;
socksPort = port;
if (address == socket->peerAddress())
break;
}
}
/* It is not immediately an error to have no SOCKS address; when DisableNetwork is set there won't be a
* listener yet. To handle that situation, we'll try to read the socks address again when TorReady state
* is reached. */
if (!socksAddress.isNull()) {
torCtrlDebug() << "torctrl: SOCKS address is " << socksAddress.toString().toStdString() << ":" << socksPort << std::endl;
emit q->connectivityChanged();
}
if (command->get(QByteArray("status/circuit-established")).toInt() == 1) {
torCtrlDebug() << "torctrl: Tor indicates that circuits have been established; state is TorReady" << std::endl;
setTorStatus(TorControl::TorReady);
} else {
setTorStatus(TorControl::TorOffline);
}
QByteArray bootstrap = command->get(QByteArray("status/bootstrap-phase")).toString().toLatin1();
if (!bootstrap.isEmpty())
updateBootstrap(splitQuotedStrings(bootstrap, ' '));
}
void TorControl::addHiddenService(HiddenService *service)
{
if (d->services.contains(service))
return;
d->services.append(service);
}
void TorControlPrivate::publishServices()
{
torCtrlDebug() << "Publish Services... " ;
Q_ASSERT(q->isConnected());
if (services.isEmpty())
{
std::cerr << "No service regstered!" << std::endl;
return;
}
std::cerr << std::endl;
SettingsObject settings(QStringLiteral("tor"));
if (settings.read("neverPublishServices").toBool())
{
torCtrlDebug() << "torctrl: Skipping service publication because neverPublishService is enabled" << std::endl;
/* Call servicePublished under the assumption that they're published externally. */
for (QList<HiddenService*>::Iterator it = services.begin(); it != services.end(); ++it)
(*it)->servicePublished();
return;
}
if (q->torVersionAsNewAs(QStringLiteral("0.2.7"))) {
foreach (HiddenService *service, services) {
if (service->hostname().isEmpty())
torCtrlDebug() << "torctrl: Creating a new hidden service" << std::endl;
else
torCtrlDebug() << "torctrl: Publishing hidden service: " << service->hostname().toStdString() << std::endl;
AddOnionCommand *onionCommand = new AddOnionCommand(service);
QObject::connect(onionCommand, &AddOnionCommand::succeeded, service, &HiddenService::servicePublished);
socket->sendCommand(onionCommand, onionCommand->build());
}
} else {
torCtrlDebug() << "torctrl: Using legacy SETCONF hidden service configuration for tor" << torVersion.toStdString() << std::endl;
SetConfCommand *command = new SetConfCommand;
QList<QPair<QByteArray,QByteArray> > torConfig;
foreach (HiddenService *service, services)
{
if (service->dataPath().isEmpty())
continue;
if (service->privateKey().isLoaded() && !QFile::exists(service->dataPath() + QStringLiteral("/private_key"))) {
// This case can happen if tor is downgraded after the profile is created
qWarning() << "Cannot publish ephemeral hidden services with this version of tor; skipping";
continue;
}
torCtrlDebug() << "torctrl: Configuring hidden service at" << service->dataPath().toStdString() << std::endl;
QDir dir(service->dataPath());
torConfig.append(qMakePair(QByteArray("HiddenServiceDir"), dir.absolutePath().toLocal8Bit()));
const QList<HiddenService::Target> &targets = service->targets();
for (QList<HiddenService::Target>::ConstIterator tit = targets.begin(); tit != targets.end(); ++tit)
{
QString target = QString::fromLatin1("%1 %2:%3").arg(tit->servicePort)
.arg(tit->targetAddress.toString())
.arg(tit->targetPort);
torConfig.append(qMakePair(QByteArray("HiddenServicePort"), target.toLatin1()));
}
QObject::connect(command, &SetConfCommand::setConfSucceeded, service, &HiddenService::servicePublished);
}
if (!torConfig.isEmpty())
socket->sendCommand(command, command->build(torConfig));
}
}
void TorControl::shutdown()
{
if (!hasOwnership()) {
qWarning() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
return;
}
d->socket->sendCommand("SIGNAL SHUTDOWN\r\n");
}
void TorControl::shutdownSync()
{
if (!hasOwnership()) {
qWarning() << "torctrl: Ignoring shutdown command for a tor instance I don't own";
return;
}
shutdown();
while (d->socket->bytesToWrite())
{
if (!d->socket->waitForBytesWritten(5000))
return;
}
}
void TorControlPrivate::statusEvent(int code, const QByteArray &data)
{
Q_UNUSED(code);
QList<QByteArray> tokens = splitQuotedStrings(data.trimmed(), ' ');
if (tokens.size() < 3)
return;
torCtrlDebug() << "torctrl: status event:" << QString(data.trimmed()).toStdString() << std::endl;
if (tokens[2] == "CIRCUIT_ESTABLISHED") {
setTorStatus(TorControl::TorReady);
} else if (tokens[2] == "CIRCUIT_NOT_ESTABLISHED") {
setTorStatus(TorControl::TorOffline);
} else if (tokens[2] == "BOOTSTRAP") {
tokens.takeFirst();
updateBootstrap(tokens);
}
}
void TorControlPrivate::updateBootstrap(const QList<QByteArray> &data)
{
bootstrapStatus.clear();
// WARN or NOTICE
bootstrapStatus[QStringLiteral("severity")] = data.value(0);
for (int i = 1; i < data.size(); i++) {
int equals = data[i].indexOf('=');
QString key = QString::fromLatin1(data[i].mid(0, equals));
QString value;
if (equals >= 0)
value = QString::fromLatin1(unquotedString(data[i].mid(equals + 1)));
bootstrapStatus[key.toLower()] = value;
}
//torCtrlDebug() << bootstrapStatus << std::endl;
emit q->bootstrapStatusChanged();
}
QObject *TorControl::getConfiguration(const QString &options)
{
GetConfCommand *command = new GetConfCommand(GetConfCommand::GetConf);
d->socket->sendCommand(command, command->build(options.toLatin1()));
//QQmlEngine::setObjectOwnership(command, QQmlEngine::CppOwnership);
return command;
}
QObject *TorControl::setConfiguration(const QVariantMap &options)
{
SetConfCommand *command = new SetConfCommand;
command->setResetMode(true);
d->socket->sendCommand(command, command->build(options));
//QQmlEngine::setObjectOwnership(command, QQmlEngine::CppOwnership);
return command;
}
namespace Tor {
class SaveConfigOperation : public PendingOperation
{
Q_OBJECT
public:
SaveConfigOperation(QObject *parent)
: PendingOperation(parent), command(0)
{
}
void start(TorControlSocket *socket)
{
Q_ASSERT(!command);
command = new GetConfCommand(GetConfCommand::GetInfo);
QObject::connect(command, &TorControlCommand::finished, this, &SaveConfigOperation::configTextReply);
socket->sendCommand(command, command->build(QList<QByteArray>() << "config-text" << "config-file"));
}
private slots:
void configTextReply()
{
Q_ASSERT(command);
if (!command)
return;
QString path = QFile::decodeName(command->get("config-file").toByteArray());
if (path.isEmpty()) {
finishWithError(QStringLiteral("Cannot write torrc without knowing its path"));
return;
}
// Out of paranoia, refuse to write any file not named 'torrc', or if the
// file doesn't exist
QFileInfo fileInfo(path);
if (fileInfo.fileName() != QStringLiteral("torrc") || !fileInfo.exists()) {
finishWithError(QStringLiteral("Refusing to write torrc to unacceptable path %1").arg(path));
return;
}
QSaveFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
finishWithError(QStringLiteral("Failed opening torrc file for writing: %1").arg(file.errorString()));
return;
}
// Remove these keys when writing torrc; they are set at runtime and contain
// absolute paths or port numbers
static const char *bannedKeys[] = {
"ControlPortWriteToFile",
"DataDirectory",
"HiddenServiceDir",
"HiddenServicePort",
0
};
QVariantList configText = command->get("config-text").toList();
foreach (const QVariant &value, configText) {
QByteArray line = value.toByteArray();
bool skip = false;
for (const char **key = bannedKeys; *key; key++) {
if (line.startsWith(*key)) {
skip = true;
break;
}
}
if (skip)
continue;
file.write(line);
file.write("\n");
}
if (!file.commit()) {
finishWithError(QStringLiteral("Failed writing torrc: %1").arg(file.errorString()));
return;
}
torCtrlDebug() << "torctrl: Wrote torrc file" << std::endl;
finishWithSuccess();
}
private:
GetConfCommand *command;
};
}
PendingOperation *TorControl::saveConfiguration()
{
if (!hasOwnership()) {
qWarning() << "torctrl: Ignoring save configuration command for a tor instance I don't own";
return 0;
}
SaveConfigOperation *operation = new SaveConfigOperation(this);
QObject::connect(operation, &PendingOperation::finished, operation, &QObject::deleteLater);
operation->start(d->socket);
//QQmlEngine::setObjectOwnership(operation, QQmlEngine::CppOwnership);
return operation;
}
bool TorControl::hasOwnership() const
{
return d->hasOwnership;
}
void TorControl::takeOwnership()
{
d->hasOwnership = true;
d->socket->sendCommand("TAKEOWNERSHIP\r\n");
// Reset PID-based polling
QVariantMap options;
options[QStringLiteral("__OwningControllerProcess")] = QVariant();
setConfiguration(options);
}
bool TorControl::torVersionAsNewAs(const QString &match) const
{
QRegularExpression r(QStringLiteral("[.-]"));
QStringList split = torVersion().split(r);
QStringList matchSplit = match.split(r);
for (int i = 0; i < matchSplit.size(); i++) {
if (i >= split.size())
return false;
bool ok1 = false, ok2 = false;
int currentVal = split[i].toInt(&ok1);
int matchVal = matchSplit[i].toInt(&ok2);
if (!ok1 || !ok2)
return false;
if (currentVal > matchVal)
return true;
if (currentVal < matchVal)
return false;
}
// Versions are equal, up to the length of match
return true;
}
#include "TorControl.moc"

View file

@ -1,144 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef TORCONTROL_H
#define TORCONTROL_H
#include <iostream>
#include <QObject>
#include <QHostAddress>
#include "PendingOperation.h"
class QNetworkProxy;
namespace Tor
{
class HiddenService;
class TorControlPrivate;
class TorControl : public QObject
{
Q_OBJECT
Q_ENUMS(Status TorStatus)
// Status of the control connection
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
// Status of Tor (and whether it believes it can connect)
Q_PROPERTY(TorStatus torStatus READ torStatus NOTIFY torStatusChanged)
// Whether it's possible to make a SOCKS connection and connect
Q_PROPERTY(bool hasConnectivity READ hasConnectivity NOTIFY connectivityChanged)
Q_PROPERTY(QString torVersion READ torVersion NOTIFY connected)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY statusChanged)
Q_PROPERTY(QVariantMap bootstrapStatus READ bootstrapStatus NOTIFY bootstrapStatusChanged)
Q_PROPERTY(bool hasOwnership READ hasOwnership NOTIFY hasOwnershipChanged)
public:
enum Status
{
Error = -1,
NotConnected = 0x00,
Connecting = 0x01,
Authenticating = 0x02,
Connected = 0x03
};
enum TorStatus
{
TorUnknown = 0x00,
TorOffline = 0x01,
TorReady = 0x02
};
explicit TorControl(QObject *parent = 0);
/* Information */
Status status() const;
TorStatus torStatus() const;
QString torVersion() const;
bool torVersionAsNewAs(const QString &version) const;
QString errorMessage() const;
bool hasConnectivity() const;
QHostAddress socksAddress() const;
quint16 socksPort() const;
QNetworkProxy connectionProxy();
/* Authentication */
void setAuthPassword(const QByteArray &password);
/* Connection */
bool isConnected() const { return status() == Connected; }
void connect(const QHostAddress &address, quint16 port);
/* Ownership means that tor is managed by this socket, and we
* can shut it down, own its configuration, etc. */
bool hasOwnership() const;
void takeOwnership();
/* Hidden Services */
QList<HiddenService*> hiddenServices() const;
void addHiddenService(HiddenService *service);
QVariantMap bootstrapStatus() const;
Q_INVOKABLE QObject *getConfiguration(const QString &options);
Q_INVOKABLE QObject *setConfiguration(const QVariantMap &options);
Q_INVOKABLE PendingOperation *saveConfiguration();
signals:
void statusChanged(int newStatus, int oldStatus);
void torStatusChanged(int newStatus, int oldStatus);
void connected();
void disconnected();
void connectivityChanged();
void bootstrapStatusChanged();
void hasOwnershipChanged();
public slots:
/* Instruct Tor to shutdown */
void shutdown();
/* Call shutdown(), and wait synchronously for the command to be written */
void shutdownSync();
void reconnect();
private:
TorControlPrivate *d;
};
}
extern Tor::TorControl *torControl;
#endif // TORCONTROLMANAGER_H

View file

@ -1,63 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "TorControlCommand.h"
#include <QDebug>
using namespace Tor;
TorControlCommand::TorControlCommand()
: m_finalStatus(0)
{
}
void TorControlCommand::onReply(int statusCode, const QByteArray &data)
{
emit replyLine(statusCode, data);
}
void TorControlCommand::onFinished(int statusCode)
{
m_finalStatus = statusCode;
emit finished();
}
void TorControlCommand::onDataLine(const QByteArray &data)
{
Q_UNUSED(data);
}
void TorControlCommand::onDataFinished()
{
qWarning() << "torctrl: Unexpected data response for command";
}

View file

@ -1,70 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef TORCONTROLCOMMAND_H
#define TORCONTROLCOMMAND_H
#include <QObject>
#include <QByteArray>
namespace Tor
{
class TorControlCommand : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(TorControlCommand)
friend class TorControlSocket;
public:
TorControlCommand();
int statusCode() const { return m_finalStatus; }
signals:
void replyLine(int statusCode, const QByteArray &data);
void finished();
protected:
virtual void onReply(int statusCode, const QByteArray &data);
virtual void onFinished(int statusCode);
virtual void onDataLine(const QByteArray &data);
virtual void onDataFinished();
private:
int m_finalStatus;
};
}
#endif // TORCONTROLCOMMAND_H

View file

@ -1,178 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <iostream>
#include "TorControlSocket.h"
#include "TorControlCommand.h"
#include <QDebug>
using namespace Tor;
TorControlSocket::TorControlSocket(QObject *parent)
: QTcpSocket(parent), currentCommand(0), inDataReply(false)
{
connect(this, SIGNAL(readyRead()), this, SLOT(process()));
connect(this, SIGNAL(disconnected()), this, SLOT(clear()));
}
TorControlSocket::~TorControlSocket()
{
clear();
}
void TorControlSocket::sendCommand(TorControlCommand *command, const QByteArray &data)
{
Q_ASSERT(data.endsWith("\r\n"));
commandQueue.append(command);
write(data);
std::cerr << "[TOR CTRL] Sent: \"" << QString(data.trimmed()).toStdString() << "\"" << std::endl;
}
void TorControlSocket::registerEvent(const QByteArray &event, TorControlCommand *command)
{
eventCommands.insert(event, command);
QByteArray data("SETEVENTS");
foreach (const QByteArray &key, eventCommands.keys()) {
data += ' ';
data += key;
}
data += "\r\n";
sendCommand(data);
}
void TorControlSocket::clear()
{
qDeleteAll(commandQueue);
commandQueue.clear();
qDeleteAll(eventCommands);
eventCommands.clear();
inDataReply = false;
currentCommand = 0;
}
void TorControlSocket::setError(const QString &message)
{
m_errorMessage = message;
emit error(message);
abort();
}
void TorControlSocket::process()
{
for (;;) {
if (!canReadLine())
return;
QByteArray line = readLine(5120);
if (!line.endsWith("\r\n")) {
setError(QStringLiteral("Invalid control message syntax"));
return;
}
line.chop(2);
if (inDataReply) {
if (line == ".") {
inDataReply = false;
if (currentCommand)
currentCommand->onDataFinished();
currentCommand = 0;
} else {
if (currentCommand)
currentCommand->onDataLine(line);
}
continue;
}
if (line.size() < 4) {
setError(QStringLiteral("Invalid control message syntax"));
return;
}
int statusCode = line.left(3).toInt();
char type = line[3];
bool isFinalReply = (type == ' ');
inDataReply = (type == '+');
// Trim down to just data
line = line.mid(4);
if (!isFinalReply && !inDataReply && type != '-') {
setError(QStringLiteral("Invalid control message syntax"));
return;
}
// 6xx replies are asynchronous responses
if (statusCode >= 600 && statusCode < 700) {
if (!currentCommand) {
int space = line.indexOf(' ');
if (space > 0)
currentCommand = eventCommands.value(line.mid(0, space));
if (!currentCommand) {
qWarning() << "torctrl: Ignoring unknown event";
continue;
}
}
currentCommand->onReply(statusCode, line);
if (isFinalReply) {
currentCommand->onFinished(statusCode);
currentCommand = 0;
}
continue;
}
if (commandQueue.isEmpty()) {
qWarning() << "torctrl: Received unexpected data";
continue;
}
TorControlCommand *command = commandQueue.first();
if (command)
command->onReply(statusCode, line);
if (inDataReply) {
currentCommand = command;
} else if (isFinalReply) {
commandQueue.takeFirst();
if (command) {
command->onFinished(statusCode);
command->deleteLater();
}
}
}
}

View file

@ -1,77 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef TORCONTROLSOCKET_H
#define TORCONTROLSOCKET_H
#include <QTcpSocket>
#include <QQueue>
namespace Tor
{
class TorControlCommand;
class TorControlSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit TorControlSocket(QObject *parent = 0);
virtual ~TorControlSocket();
QString errorMessage() const { return m_errorMessage; }
void registerEvent(const QByteArray &event, TorControlCommand *handler);
void sendCommand(const QByteArray &data) { sendCommand(0, data); }
void sendCommand(TorControlCommand *command, const QByteArray &data);
signals:
void error(const QString &message);
private slots:
void process();
void clear();
private:
QQueue<TorControlCommand*> commandQueue;
QHash<QByteArray,TorControlCommand*> eventCommands;
QString m_errorMessage;
TorControlCommand *currentCommand;
bool inDataReply;
void setError(const QString &message);
};
}
#endif // TORCONTROLSOCKET_H

View file

@ -9,42 +9,59 @@
#include <iostream>
#include "util/rstime.h"
#include "retroshare/rstor.h"
#include "retroshare/rsevents.h"
#include "TorControlWindow.h"
#include "TorManager.h"
#include "TorControl.h"
#include "HiddenService.h"
#include "util/qtthreadsutils.h"
TorControlDialog::TorControlDialog(Tor::TorManager *tm,QWidget *parent)
: mTorManager(tm)
TorControlDialog::TorControlDialog(QWidget *)
{
setupUi(this) ;
setupUi(this) ;
QObject::connect(tm->control(),SIGNAL(statusChanged(int,int)),this,SLOT(statusChanged())) ;
QObject::connect(tm->control(),SIGNAL(connected()),this,SLOT(statusChanged()));
QObject::connect(tm->control(),SIGNAL(disconnected()),this,SLOT(statusChanged()));
QObject::connect(tm->control(),SIGNAL(bootstrapStatusChanged()),this,SLOT(statusChanged()));
QObject::connect(tm->control(),SIGNAL(connectivityChanged()),this,SLOT(statusChanged()));
QObject::connect(tm ,SIGNAL(errorChanged()),this,SLOT(statusChanged()));
mEventHandlerId = 0; // very important!
//QTimer::singleShot(2000,this,SLOT(checkForHiddenService())) ;
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
{
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
}, mEventHandlerId, RsEventType::TOR_MANAGER );
mIncomingServer = new QTcpServer(this) ;
mHiddenService = NULL ;
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_UNKNOWN;
//mBootstrapPhaseFinished = false ;
mIncomingServer = new QTcpServer(this) ;
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection()));
QTimer *timer = new QTimer ;
QTimer *timer = new QTimer ;
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
timer->start(500) ;
QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ;
timer->start(300) ;
// Hide some debug output for the released version
// Hide some debug output for the released version
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint );
adjustSize();
adjustSize();
}
TorControlDialog::~TorControlDialog()
{
rsEvents->unregisterEventsHandler(mEventHandlerId);
}
void TorControlDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
{
if(event->mType != RsEventType::TOR_MANAGER) return;
const RsTorManagerEvent *fe = dynamic_cast<const RsTorManagerEvent*>(event.get());
if(!fe)
return;
switch(fe->mTorManagerEventType)
{
case RsTorManagerEventCode::TOR_STATUS_CHANGED:
case RsTorManagerEventCode::TOR_CONNECTIVITY_CHANGED: statusChanged(fe->mTorStatus,fe->mTorConnectivityStatus);
break;
default: ;
}
}
void TorControlDialog::onIncomingConnection()
@ -52,67 +69,66 @@ void TorControlDialog::onIncomingConnection()
std::cerr << "Incoming connection !!" << std::endl;
}
void TorControlDialog::statusChanged()
void TorControlDialog::statusChanged(RsTorStatus torstatus, RsTorConnectivityStatus tor_control_status)
{
int tor_control_status = mTorManager->control()->status();
int torstatus = mTorManager->control()->torStatus();
QString tor_control_status_str,torstatus_str ;
if(mTorManager->hasError())
mErrorMsg = mTorManager->errorMessage() ;
if(RsTor::hasError())
mErrorMsg = QString::fromStdString(RsTor::errorMessage()) ;
switch(tor_control_status)
{
default:
case Tor::TorControl::Error : tor_control_status_str = "Error" ; break ;
case Tor::TorControl::NotConnected: tor_control_status_str = "Not connected" ; break ;
case Tor::TorControl::Connecting: tor_control_status_str = "Connecting" ; break ;
case Tor::TorControl::Authenticating: tor_control_status_str = "Authenticating" ; break ;
case Tor::TorControl::Connected: tor_control_status_str = "Connected" ; break ;
}
case RsTorConnectivityStatus::ERROR: tor_control_status_str = tr("Error") ; break ;
case RsTorConnectivityStatus::NOT_CONNECTED: tor_control_status_str = tr("Not connected") ; break ;
case RsTorConnectivityStatus::CONNECTING: tor_control_status_str = tr("Connecting") ; break ;
case RsTorConnectivityStatus::SOCKET_CONNECTED: tor_control_status_str = tr("Socket connected") ; break ;
case RsTorConnectivityStatus::AUTHENTICATING: tor_control_status_str = tr("Authenticating") ; break ;
case RsTorConnectivityStatus::AUTHENTICATED: tor_control_status_str = tr("Authenticated") ; break ;
case RsTorConnectivityStatus::HIDDEN_SERVICE_READY: tor_control_status_str = tr("Hidden service ready") ; break ;
case RsTorConnectivityStatus::UNKNOWN: tor_control_status_str = tr("Unknown") ; break ;
}
switch(torstatus)
{
default:
case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ;
case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ;
case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ;
case RsTorStatus::UNKNOWN: torstatus_str = tr("Unknown") ; break ;
case RsTorStatus::OFFLINE: torstatus_str = tr("Tor offline") ; break ;
case RsTorStatus::READY: torstatus_str = tr("Tor ready") ; break ;
}
torStatus_LB->setText(torstatus_str) ;
if(torstatus == Tor::TorControl::TorUnknown)
if(torstatus == RsTorStatus::UNKNOWN)
torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ;
else
torStatus_LB->setToolTip("") ;
QVariantMap qvm = mTorManager->control()->bootstrapStatus();
std::map<std::string,std::string> qvm = RsTor::bootstrapStatus();
QString bootstrapstatus_str ;
std::cerr << "Tor control status: " << tor_control_status_str.toStdString() << std::endl;
std::cerr << "Tor status: " << torstatus_str.toStdString() << std::endl;
std::cerr << "Bootstrap status map: " << std::endl;
for(auto it(qvm.begin());it!=qvm.end();++it)
std::cerr << " " << it.key().toStdString() << " : " << it.value().toString().toStdString() << std::endl;
std::cerr << " " << it->first << " : " << it->second << std::endl;
if(!qvm["progress"].toString().isNull())
torBootstrapStatus_LB->setText(qvm["progress"].toString() + " % (" + qvm["summary"].toString() + ")") ;
if(!qvm["progress"].empty())
torBootstrapStatus_LB->setText(QString::fromStdString(qvm["progress"]) + " % (" + QString::fromStdString(qvm["summary"]) + ")") ;
else
torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ;
QString service_id ;
QString onion_address ;
QHostAddress service_target_address ;
std::string service_id ;
std::string onion_address ;
std::string service_target_address ;
uint16_t service_port ;
uint16_t target_port ;
if(mTorManager->getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
if(RsTor::getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port))
{
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + service_target_address.toString() + ":" + QString::number(target_port));
onionAddress_LB->setText(onion_address);
hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + QString::fromStdString(service_target_address) + ":" + QString::number(target_port));
onionAddress_LB->setText(QString::fromStdString(onion_address));
}
else
{
@ -123,34 +139,39 @@ void TorControlDialog::statusChanged()
showLog();
adjustSize();
QCoreApplication::processEvents(); // forces update
QCoreApplication::processEvents(); // forces update
}
void TorControlDialog::showLog()
{
static std::set<QString> already_seen ;
static std::set<std::string> already_seen ;
QString s ;
QStringList logmsgs = mTorManager->logMessages() ;
bool can_print = false ;
std::string s ;
std::list<std::string> logmsgs = RsTor::logMessages() ;
bool can_print = false ;
for(QStringList::const_iterator it(logmsgs.begin());it!=logmsgs.end();++it)
{
for(auto it(logmsgs.begin());it!=logmsgs.end();++it)
{
s += *it + "\n" ;
if(already_seen.find(*it) == already_seen.end())
{
can_print = true ;
already_seen.insert(*it);
}
if(already_seen.find(*it) == already_seen.end())
{
can_print = true ;
already_seen.insert(*it);
}
if(can_print)
std::cerr << "[TOR DEBUG LOG] " << (*it).toStdString() << std::endl;
}
if(can_print)
{
std::cerr << "[TOR DEBUG LOG] " << *it << std::endl;
// torLog_TB->setText(s) ;:
QString s = QString::fromStdString(*it);
int n = s.indexOf(QString("Bootstrapped"));
std::cerr << "Connexion Proxy: " << mTorManager->control()->socksAddress().toString().toStdString() << ":" << mTorManager->control()->socksPort() << std::endl;
if(n >= 0)
torBootstrapStatus_LB->setText(s.mid(n+QString("Bootstrapped").length()));
}
}
//std::cerr << "Connexion Proxy: " << RsTor::socksAddress() << ":" << QString::number(RsTor::socksPort()).toStdString() << std::endl;
}
TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
@ -161,9 +182,9 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
return TorControlDialog::TOR_STATUS_FAIL ;
}
switch(mTorManager->control()->torStatus())
switch(RsTor::torStatus())
{
case Tor::TorControl::TorReady: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
case RsTorStatus::READY: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ;
default:
return TOR_STATUS_UNKNOWN ;
}
@ -171,60 +192,35 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg)
TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService()
{
std::cerr << "Checking for hidden services:" ;
std::cerr << "Checking for hidden services:" ;
switch(mHiddenServiceStatus)
{
default:
case HIDDEN_SERVICE_STATUS_UNKNOWN: {
std::string service_id;
std::cerr << " trying to setup. " ;
RsTorHiddenServiceStatus service_status = RsTor::getHiddenServiceStatus(service_id);
if(!mTorManager->setupHiddenService())
{
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_FAIL ;
std::cerr << "Failed." << std::endl;
return mHiddenServiceStatus ;
}
std::cerr << "Done." << std::endl;
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_REQUESTED ;
return mHiddenServiceStatus ;
}
if(service_id.empty())
{
std::cerr << "Not ready yet." << std::endl;
return HIDDEN_SERVICE_STATUS_REQUESTED ;
}
else
{
if(mHiddenService.empty())
mHiddenService = service_id ;
case HIDDEN_SERVICE_STATUS_REQUESTED: {
QList<Tor::HiddenService*> hidden_services = mTorManager->control()->hiddenServices();
std::cerr << "New service acquired. Status is " << (int)service_status ;
if(hidden_services.empty())
{
std::cerr << "Not ready yet." << std::endl;
return mHiddenServiceStatus ;
}
else
{
if(mHiddenService == NULL)
mHiddenService = *(hidden_services.begin()) ;
if(service_status == RsTorHiddenServiceStatus::ONLINE)
{
std::cerr << ": published and running!" << std::endl;
Tor::HiddenService::Status hss = mHiddenService->status();
std::cerr << "New service acquired. Status is " << hss ;
if(hss == Tor::HiddenService::Online)
{
mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_OK ;
std::cerr << ": published and running!" << std::endl;
return mHiddenServiceStatus ;
}
else
{
std::cerr << ": not ready yet." << std::endl;
return mHiddenServiceStatus ;
}
}
}
case HIDDEN_SERVICE_STATUS_OK :
std::cerr << "New service acquired." << std::endl;
return mHiddenServiceStatus ;
}
return HIDDEN_SERVICE_STATUS_OK ;
}
else
{
std::cerr << ": not ready yet." << std::endl;
return HIDDEN_SERVICE_STATUS_REQUESTED ;
}
}
}

View file

@ -1,3 +1,5 @@
#include "retroshare/rsevents.h"
#include "retroshare/rstor.h"
#include "ui_TorControlWindow.h"
class QTcpServer ;
@ -12,7 +14,8 @@ class TorControlDialog: public QWidget, public Ui::TorControlDialog
Q_OBJECT
public:
TorControlDialog(Tor::TorManager *tm,QWidget *parent =NULL);
TorControlDialog(QWidget *parent =NULL);
virtual ~TorControlDialog();
enum TorStatus {
TOR_STATUS_UNKNOWN = 0x00,
@ -34,15 +37,14 @@ public:
protected slots:
void showLog();
void statusChanged();
void onIncomingConnection();
void statusChanged(RsTorStatus torstatus,RsTorConnectivityStatus tor_control_status);
void onIncomingConnection();
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
private:
QString mErrorMsg ;
HiddenServiceStatus mHiddenServiceStatus ;
Tor::TorManager *mTorManager ;
Tor::HiddenService *mHiddenService ;
std::string mHiddenService;
QTcpServer *mIncomingServer ;
RsEventsHandlerId_t mEventHandlerId;
};

View file

@ -40,6 +40,12 @@
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
@ -55,6 +61,12 @@
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="torStatusTxt_LB">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Tor status:</string>
</property>
@ -79,8 +91,14 @@
</item>
<item row="2" column="0">
<widget class="QLabel" name="HiddenServiceAddressTxt_LB">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Hidden service address:</string>
<string>Hidden address:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -89,8 +107,14 @@
</item>
<item row="1" column="0">
<widget class="QLabel" name="torBootstrapStatusTxt_LB">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Tor bootstrap status:</string>
<string>Tor status:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -106,6 +130,12 @@
</item>
<item row="3" column="0">
<widget class="QLabel" name="onionAddressTxt_LB">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Onion address:</string>
</property>

View file

@ -1,526 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <iostream>
#include "TorManager.h"
#include "TorProcess.h"
#include "TorControl.h"
#include "CryptoKey.h"
#include "HiddenService.h"
#include "GetConfCommand.h"
#include "Settings.h"
#include <QFile>
#include <QDir>
#include <QCoreApplication>
#include <QTcpServer>
#include <QTextStream>
using namespace Tor;
namespace Tor
{
class TorManagerPrivate : public QObject
{
Q_OBJECT
public:
TorManager *q;
TorProcess *process;
TorControl *control;
QString dataDir;
QString hiddenServiceDir;
QStringList logMessages;
QString errorMessage;
bool configNeeded;
HiddenService *hiddenService ;
explicit TorManagerPrivate(TorManager *parent = 0);
QString torExecutablePath() const;
bool createDataDir(const QString &path);
bool createDefaultTorrc(const QString &path);
void setError(const QString &errorMessage);
public slots:
void processStateChanged(int state);
void processErrorChanged(const QString &errorMessage);
void processLogMessage(const QString &message);
void controlStatusChanged(int status);
void getConfFinished();
};
}
TorManager::TorManager(QObject *parent)
: QObject(parent), d(new TorManagerPrivate(this))
{
}
TorManagerPrivate::TorManagerPrivate(TorManager *parent)
: QObject(parent)
, q(parent)
, process(0)
, control(new TorControl(this))
, configNeeded(false)
, hiddenService(NULL)
{
connect(control, SIGNAL(statusChanged(int,int)), SLOT(controlStatusChanged(int)));
}
TorManager *TorManager::instance()
{
static TorManager *p = 0;
if (!p)
p = new TorManager(qApp);
return p;
}
TorControl *TorManager::control()
{
return d->control;
}
TorProcess *TorManager::process()
{
return d->process;
}
bool TorManager::isTorAvailable()
{
return !instance()->d->torExecutablePath().isNull();
}
QString TorManager::torDataDirectory() const
{
return d->dataDir;
}
void TorManager::setTorDataDirectory(const QString &path)
{
d->dataDir = QDir::fromNativeSeparators(path);
if (!d->dataDir.isEmpty() && !d->dataDir.endsWith(QLatin1Char('/')))
d->dataDir.append(QLatin1Char('/'));
}
QString TorManager::hiddenServiceDirectory() const
{
return d->hiddenServiceDir;
}
void TorManager::setHiddenServiceDirectory(const QString &path)
{
d->hiddenServiceDir = QDir::fromNativeSeparators(path);
if (!d->hiddenServiceDir.isEmpty() && !d->hiddenServiceDir.endsWith(QLatin1Char('/')))
d->hiddenServiceDir.append(QLatin1Char('/'));
}
bool TorManager::setupHiddenService()
{
if(d->hiddenService != NULL)
{
std::cerr << "TorManager: setupHiddenService() called twice! Not doing anything this time." << std::endl;
return true ;
}
QString keyData ;//= m_settings->read("serviceKey").toString();
QString legacyDir = d->hiddenServiceDir;
std::cerr << "TorManager: setting up hidden service." << std::endl;
if(legacyDir.isNull())
{
std::cerr << "legacy dir not set! Cannot proceed." << std::endl;
return false ;
}
std::cerr << "Using legacy dir: " << legacyDir.toStdString() << std::endl;
if (!legacyDir.isEmpty() && QFile::exists(legacyDir + QLatin1String("/private_key")))
{
std::cerr << "Attempting to load key from legacy filesystem format in " << legacyDir.toStdString() << std::endl;
CryptoKey key;
if (!key.loadFromFile(legacyDir + QLatin1String("/private_key")))
{
qWarning() << "Cannot load legacy format key from" << legacyDir << "for conversion";
return false;
}
d->hiddenService = new Tor::HiddenService(key, legacyDir, this);
std::cerr << "Got key from legacy dir: " << std::endl;
std::cerr << key.bytes().toStdString() << std::endl;
}
else
{
d->hiddenService = new Tor::HiddenService(legacyDir, this);
std::cerr << "Creating new hidden service." << std::endl;
connect(d->hiddenService, SIGNAL(privateKeyChanged()), this, SLOT(hiddenServicePrivateKeyChanged())) ;
connect(d->hiddenService, SIGNAL(hostnameChanged()), this, SLOT(hiddenServiceHostnameChanged())) ;
}
Q_ASSERT(d->hiddenService);
connect(d->hiddenService, SIGNAL(statusChanged(int,int)), this, SLOT(hiddenServiceStatusChanged(int,int)));
// Generally, these are not used, and we bind to localhost and port 0
// for an automatic (and portable) selection.
QHostAddress address = QHostAddress::LocalHost; // we only listen from localhost
quint16 port = 7934;//(quint16)m_settings->read("localListenPort").toInt();
std::cerr << "Testing host address: " << address.toString().toStdString() << ":" << port ;
if (!QTcpServer().listen(address, port))
{
// XXX error case
std::cerr << " Failed to open incoming socket" << std::endl;
return false;
}
std::cerr << " OK - Adding hidden service to TorControl." << std::endl;
//d->hiddenService->addTarget(9878, mIncomingServer->serverAddress(), mIncomingServer->serverPort());
d->hiddenService->addTarget(9878, QHostAddress::LocalHost,7934);
control()->addHiddenService(d->hiddenService);
return true ;
}
void TorManager::hiddenServiceStatusChanged(int old_status,int new_status)
{
std::cerr << "Hidden service status changed from " << old_status << " to " << new_status << std::endl;
}
void TorManager::hiddenServicePrivateKeyChanged()
{
QString key = QString::fromLatin1(d->hiddenService->privateKey().bytes());
QFile outfile(d->hiddenServiceDir + QLatin1String("/private_key")) ;
outfile.open( QIODevice::WriteOnly | QIODevice::Text );
QTextStream s(&outfile);
#ifdef TO_REMOVE
s << "-----BEGIN RSA PRIVATE KEY-----" << endl;
for(int i=0;i<key.length();i+=64)
s << key.mid(i,64) << endl ;
s << "-----END RSA PRIVATE KEY-----" << endl;
#endif
s << key ;
outfile.close();
std::cerr << "Hidden service private key changed!" << std::endl;
std::cerr << key.toStdString() << std::endl;
}
void TorManager::hiddenServiceHostnameChanged()
{
QFile outfile2(d->hiddenServiceDir + QLatin1String("/hostname")) ;
outfile2.open( QIODevice::WriteOnly | QIODevice::Text );
QTextStream t(&outfile2);
QString hostname(d->hiddenService->hostname());
t << hostname << endl;
outfile2.close();
std::cerr << "Hidden service hostname changed: " << hostname.toStdString() << std::endl;
}
bool TorManager::configurationNeeded() const
{
return d->configNeeded;
}
QStringList TorManager::logMessages() const
{
return d->logMessages;
}
bool TorManager::hasError() const
{
return !d->errorMessage.isEmpty();
}
QString TorManager::errorMessage() const
{
return d->errorMessage;
}
bool TorManager::start()
{
if (!d->errorMessage.isEmpty()) {
d->errorMessage.clear();
emit errorChanged();
}
SettingsObject settings(QStringLiteral("tor"));
// If a control port is defined by config or environment, skip launching tor
if (!settings.read("controlPort").isUndefined() ||
!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT"))
{
QHostAddress address(settings.read("controlAddress").toString());
quint16 port = (quint16)settings.read("controlPort").toInt();
QByteArray password = settings.read("controlPassword").toString().toLatin1();
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_HOST"))
address = QHostAddress(QString::fromLatin1(qgetenv("TOR_CONTROL_HOST")));
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT")) {
bool ok = false;
port = qgetenv("TOR_CONTROL_PORT").toUShort(&ok);
if (!ok)
port = 0;
}
if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PASSWD"))
password = qgetenv("TOR_CONTROL_PASSWD");
if (!port) {
d->setError(QStringLiteral("Invalid control port settings from environment or configuration"));
return false;
}
if (address.isNull())
address = QHostAddress::LocalHost;
d->control->setAuthPassword(password);
d->control->connect(address, port);
} else {
// Launch a bundled Tor instance
QString executable = d->torExecutablePath();
std::cerr << "Executable path: " << executable.toStdString() << std::endl;
if (executable.isEmpty()) {
d->setError(QStringLiteral("Cannot find tor executable"));
return false;
}
if (!d->process) {
d->process = new TorProcess(this);
connect(d->process, SIGNAL(stateChanged(int)), d, SLOT(processStateChanged(int)));
connect(d->process, SIGNAL(errorMessageChanged(QString)), d,
SLOT(processErrorChanged(QString)));
connect(d->process, SIGNAL(logMessage(QString)), d, SLOT(processLogMessage(QString)));
}
if (!QFile::exists(d->dataDir) && !d->createDataDir(d->dataDir)) {
d->setError(QStringLiteral("Cannot write data location: %1").arg(d->dataDir));
return false;
}
QString defaultTorrc = d->dataDir + QStringLiteral("default_torrc");
if (!QFile::exists(defaultTorrc) && !d->createDefaultTorrc(defaultTorrc)) {
d->setError(QStringLiteral("Cannot write data files: %1").arg(defaultTorrc));
return false;
}
QFile torrc(d->dataDir + QStringLiteral("torrc"));
if (!torrc.exists() || torrc.size() == 0) {
d->configNeeded = true;
emit configurationNeededChanged();
}
d->process->setExecutable(executable);
d->process->setDataDir(d->dataDir);
d->process->setDefaultTorrc(defaultTorrc);
d->process->start();
}
return true ;
}
bool TorManager::getProxyServerInfo(QHostAddress& proxy_server_adress,uint16_t& proxy_server_port)
{
proxy_server_adress = control()->socksAddress();
proxy_server_port = control()->socksPort();
return proxy_server_port > 1023 ;
}
bool TorManager::getHiddenServiceInfo(QString& service_id,QString& service_onion_address,uint16_t& service_port, QHostAddress& service_target_address,uint16_t& target_port)
{
QList<Tor::HiddenService*> hidden_services = control()->hiddenServices();
if(hidden_services.empty())
return false ;
// Only return the first one.
for(auto it(hidden_services.begin());it!=hidden_services.end();++it)
{
service_onion_address = (*it)->hostname();
service_id = (*it)->serviceId();
for(auto it2((*it)->targets().begin());it2!=(*it)->targets().end();++it2)
{
service_port = (*it2).servicePort ;
service_target_address = (*it2).targetAddress ;
target_port = (*it2).targetPort;
break ;
}
break ;
}
return true ;
}
void TorManagerPrivate::processStateChanged(int state)
{
std::cerr << Q_FUNC_INFO << "state: " << state << " passwd=\"" << QString(process->controlPassword()).toStdString() << "\" " << process->controlHost().toString().toStdString()
<< ":" << process->controlPort() << std::endl;
if (state == TorProcess::Ready) {
control->setAuthPassword(process->controlPassword());
control->connect(process->controlHost(), process->controlPort());
}
}
void TorManagerPrivate::processErrorChanged(const QString &errorMessage)
{
std::cerr << "tor error:" << errorMessage.toStdString() << std::endl;
setError(errorMessage);
}
void TorManagerPrivate::processLogMessage(const QString &message)
{
std::cerr << "tor:" << message.toStdString() << std::endl;
if (logMessages.size() >= 50)
logMessages.takeFirst();
logMessages.append(message);
}
void TorManagerPrivate::controlStatusChanged(int status)
{
if (status == TorControl::Connected) {
if (!configNeeded) {
// If DisableNetwork is 1, trigger configurationNeeded
connect(control->getConfiguration(QStringLiteral("DisableNetwork")),
SIGNAL(finished()), SLOT(getConfFinished()));
}
if (process) {
// Take ownership via this control socket
control->takeOwnership();
}
}
}
void TorManagerPrivate::getConfFinished()
{
GetConfCommand *command = qobject_cast<GetConfCommand*>(sender());
if (!command)
return;
if (command->get("DisableNetwork").toInt() == 1 && !configNeeded) {
configNeeded = true;
emit q->configurationNeededChanged();
}
}
QString TorManagerPrivate::torExecutablePath() const
{
SettingsObject settings(QStringLiteral("tor"));
QString path = settings.read("executablePath").toString();
if (!path.isEmpty() && QFile::exists(path))
return path;
#ifdef Q_OS_WIN
QString filename(QStringLiteral("/tor.exe"));
#else
QString filename(QStringLiteral("/tor"));
#endif
path = qApp->applicationDirPath();
if (QFile::exists(path + filename))
return path + filename;
#ifdef BUNDLED_TOR_PATH
path = QStringLiteral(BUNDLED_TOR_PATH);
if (QFile::exists(path + filename))
return path + filename;
#endif
#ifdef __APPLE__
// on MacOS, try traditional brew installation path
path = QStringLiteral("/usr/local/opt/tor/bin") ;
if (QFile::exists(path + filename))
return path + filename;
#endif
// Try $PATH
return filename.mid(1);
}
bool TorManagerPrivate::createDataDir(const QString &path)
{
QDir dir(path);
return dir.mkpath(QStringLiteral("."));
}
bool TorManagerPrivate::createDefaultTorrc(const QString &path)
{
static const char defaultTorrcContent[] =
"SocksPort auto\n"
"AvoidDiskWrites 1\n"
// "DisableNetwork 1\n" // (cyril) I removed this because it prevents Tor to bootstrap.
"__ReloadTorrcOnSIGHUP 0\n";
QFile file(path);
if (!file.open(QIODevice::WriteOnly))
return false;
if (file.write(defaultTorrcContent) < 0)
return false;
return true;
}
void TorManagerPrivate::setError(const QString &message)
{
errorMessage = message;
emit q->errorChanged();
}
#include "TorManager.moc"

View file

@ -1,111 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
// This code has been further modified to fit Retroshare context.
#ifndef TORMANAGER_H
#define TORMANAGER_H
#include <QObject>
#include <QStringList>
#include <QHostAddress>
namespace Tor
{
class TorProcess;
class TorControl;
class TorManagerPrivate;
/* Run/connect to an instance of Tor according to configuration, and manage
* UI interaction, first time configuration, etc. */
class TorManager : public QObject
{
Q_OBJECT
Q_PROPERTY(bool configurationNeeded READ configurationNeeded NOTIFY configurationNeededChanged)
Q_PROPERTY(QStringList logMessages READ logMessages CONSTANT)
Q_PROPERTY(Tor::TorProcess* process READ process CONSTANT)
Q_PROPERTY(Tor::TorControl* control READ control CONSTANT)
Q_PROPERTY(bool hasError READ hasError NOTIFY errorChanged)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorChanged)
Q_PROPERTY(QString torDataDirectory READ torDataDirectory WRITE setTorDataDirectory)
public:
static bool isTorAvailable() ;
static TorManager *instance();
TorProcess *process();
TorControl *control();
QString torDataDirectory() const;
void setTorDataDirectory(const QString &path);
QString hiddenServiceDirectory() const;
void setHiddenServiceDirectory(const QString &path);
// Starts a hidden service, loading it from the config directory that has been set earlier.
bool setupHiddenService() ;
// True on first run or when the Tor configuration wizard needs to be shown
bool configurationNeeded() const;
QStringList logMessages() const;
bool hasError() const;
QString errorMessage() const;
bool getHiddenServiceInfo(QString& service_id,QString& service_onion_address,uint16_t& service_port, QHostAddress& service_target_address,uint16_t& target_port);
bool getProxyServerInfo(QHostAddress& proxy_server_adress,uint16_t& proxy_server_port);
public slots:
bool start();
private slots:
void hiddenServicePrivateKeyChanged();
void hiddenServiceHostnameChanged();
void hiddenServiceStatusChanged(int old_status,int new_status);
signals:
void configurationNeededChanged();
void errorChanged();
private:
explicit TorManager(QObject *parent = 0);
TorManagerPrivate *d;
};
}
#endif
#

View file

@ -1,311 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "TorProcess_p.h"
#include "CryptoKey.h"
#include "SecureRNG.h"
#include <QDir>
#include <QDebug>
#include <QCoreApplication>
using namespace Tor;
TorProcess::TorProcess(QObject *parent)
: QObject(parent), d(new TorProcessPrivate(this))
{
}
TorProcess::~TorProcess()
{
if (state() > NotStarted)
stop();
}
TorProcessPrivate::TorProcessPrivate(TorProcess *q)
: QObject(q), q(q), state(TorProcess::NotStarted), controlPort(0), controlPortAttempts(0)
{
connect(&process, &QProcess::started, this, &TorProcessPrivate::processStarted);
connect(&process, (void (QProcess::*)(int, QProcess::ExitStatus))&QProcess::finished,
this, &TorProcessPrivate::processFinished);
connect(&process, (void (QProcess::*)(QProcess::ProcessError))&QProcess::error,
this, &TorProcessPrivate::processError);
connect(&process, &QProcess::readyRead, this, &TorProcessPrivate::processReadable);
controlPortTimer.setInterval(500);
connect(&controlPortTimer, &QTimer::timeout, this, &TorProcessPrivate::tryReadControlPort);
}
QString TorProcess::executable() const
{
return d->executable;
}
void TorProcess::setExecutable(const QString &path)
{
d->executable = path;
}
QString TorProcess::dataDir() const
{
return d->dataDir;
}
void TorProcess::setDataDir(const QString &path)
{
d->dataDir = path;
}
QString TorProcess::defaultTorrc() const
{
return d->defaultTorrc;
}
void TorProcess::setDefaultTorrc(const QString &path)
{
d->defaultTorrc = path;
}
QStringList TorProcess::extraSettings() const
{
return d->extraSettings;
}
void TorProcess::setExtraSettings(const QStringList &settings)
{
d->extraSettings = settings;
}
TorProcess::State TorProcess::state() const
{
return d->state;
}
QString TorProcess::errorMessage() const
{
return d->errorMessage;
}
void TorProcess::start()
{
if (state() > NotStarted)
return;
d->errorMessage.clear();
if (d->executable.isEmpty() || d->dataDir.isEmpty()) {
d->errorMessage = QStringLiteral("Tor executable and data directory not specified");
d->state = Failed;
emit errorMessageChanged(d->errorMessage);
emit stateChanged(d->state);
return;
}
if (!d->ensureFilesExist()) {
d->state = Failed;
emit errorMessageChanged(d->errorMessage);
emit stateChanged(d->state);
return;
}
QByteArray password = controlPassword();
QByteArray hashedPassword = torControlHashedPassword(password);
if (password.isEmpty() || hashedPassword.isEmpty()) {
d->errorMessage = QStringLiteral("Random password generation failed");
d->state = Failed;
emit errorMessageChanged(d->errorMessage);
emit stateChanged(d->state);
}
QStringList args;
if (!d->defaultTorrc.isEmpty())
args << QStringLiteral("--defaults-torrc") << d->defaultTorrc;
args << QStringLiteral("-f") << d->torrcPath();
args << QStringLiteral("DataDirectory") << d->dataDir;
args << QStringLiteral("HashedControlPassword") << QString::fromLatin1(hashedPassword);
args << QStringLiteral("ControlPort") << QStringLiteral("auto");
args << QStringLiteral("ControlPortWriteToFile") << d->controlPortFilePath();
args << QStringLiteral("__OwningControllerProcess") << QString::number(qApp->applicationPid());
args << d->extraSettings;
d->state = Starting;
emit stateChanged(d->state);
if (QFile::exists(d->controlPortFilePath()))
QFile::remove(d->controlPortFilePath());
d->controlPort = 0;
d->controlHost.clear();
d->process.setProcessChannelMode(QProcess::MergedChannels);
d->process.start(d->executable, args, QIODevice::ReadOnly);
}
void TorProcess::stop()
{
if (state() < Starting)
return;
d->controlPortTimer.stop();
if (d->process.state() == QProcess::Starting)
d->process.waitForStarted(2000);
d->state = NotStarted;
// Windows can't terminate the process well, but Tor will clean itself up
#ifndef Q_OS_WIN
if (d->process.state() == QProcess::Running) {
d->process.terminate();
if (!d->process.waitForFinished(5000)) {
qWarning() << "Tor process" << d->process.pid() << "did not respond to terminate, killing...";
d->process.kill();
if (!d->process.waitForFinished(2000)) {
qCritical() << "Tor process" << d->process.pid() << "did not respond to kill!";
}
}
}
#endif
emit stateChanged(d->state);
}
QByteArray TorProcess::controlPassword()
{
if (d->controlPassword.isEmpty())
d->controlPassword = SecureRNG::randomPrintable(16);
return d->controlPassword;
}
QHostAddress TorProcess::controlHost()
{
return d->controlHost;
}
quint16 TorProcess::controlPort()
{
return d->controlPort;
}
bool TorProcessPrivate::ensureFilesExist()
{
QFile torrc(torrcPath());
if (!torrc.exists()) {
QDir dir(dataDir);
if (!dir.exists() && !dir.mkpath(QStringLiteral("."))) {
errorMessage = QStringLiteral("Cannot create Tor data directory: %1").arg(dataDir);
return false;
}
if (!torrc.open(QIODevice::ReadWrite)) {
errorMessage = QStringLiteral("Cannot create Tor configuration file: %1").arg(torrcPath());
return false;
}
}
return true;
}
QString TorProcessPrivate::torrcPath() const
{
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("torrc");
}
QString TorProcessPrivate::controlPortFilePath() const
{
return QDir::toNativeSeparators(dataDir) + QDir::separator() + QStringLiteral("control-port");
}
void TorProcessPrivate::processStarted()
{
state = TorProcess::Connecting;
emit q->stateChanged(state);
controlPortAttempts = 0;
controlPortTimer.start();
}
void TorProcessPrivate::processFinished()
{
if (state < TorProcess::Starting)
return;
controlPortTimer.stop();
errorMessage = process.errorString();
if (errorMessage.isEmpty())
errorMessage = QStringLiteral("Process exited unexpectedly (code %1)").arg(process.exitCode());
state = TorProcess::Failed;
emit q->errorMessageChanged(errorMessage);
emit q->stateChanged(state);
}
void TorProcessPrivate::processError(QProcess::ProcessError error)
{
if (error == QProcess::FailedToStart || error == QProcess::Crashed)
processFinished();
}
void TorProcessPrivate::processReadable()
{
while (process.bytesAvailable() > 0) {
QByteArray line = process.readLine(2048).trimmed();
if (!line.isEmpty())
emit q->logMessage(QString::fromLatin1(line));
}
}
void TorProcessPrivate::tryReadControlPort()
{
QFile file(controlPortFilePath());
if (file.open(QIODevice::ReadOnly)) {
QByteArray data = file.readLine().trimmed();
int p;
if (data.startsWith("PORT=") && (p = data.lastIndexOf(':')) > 0) {
controlHost = QHostAddress(QString::fromLatin1(data.mid(5, p - 5)));
controlPort = data.mid(p+1).toUShort();
if (!controlHost.isNull() && controlPort > 0) {
controlPortTimer.stop();
state = TorProcess::Ready;
emit q->stateChanged(state);
return;
}
}
}
if (++controlPortAttempts * controlPortTimer.interval() > 10000) {
errorMessage = QStringLiteral("No control port available after launching process");
state = TorProcess::Failed;
emit q->errorMessageChanged(errorMessage);
emit q->stateChanged(state);
}
}

View file

@ -1,100 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef TORPROCESS_H
#define TORPROCESS_H
#include <QObject>
#include <QHostAddress>
namespace Tor
{
class TorProcessPrivate;
/* Launches and controls a Tor instance with behavior suitable for bundling
* an instance with the application. */
class TorProcess : public QObject
{
Q_OBJECT
Q_ENUMS(State)
Q_PROPERTY(State state READ state NOTIFY stateChanged)
Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged)
public:
enum State {
Failed = -1,
NotStarted,
Starting,
Connecting,
Ready
};
explicit TorProcess(QObject *parent = 0);
virtual ~TorProcess();
QString executable() const;
void setExecutable(const QString &path);
QString dataDir() const;
void setDataDir(const QString &path);
QString defaultTorrc() const;
void setDefaultTorrc(const QString &path);
QStringList extraSettings() const;
void setExtraSettings(const QStringList &settings);
State state() const;
QString errorMessage() const;
QHostAddress controlHost();
quint16 controlPort();
QByteArray controlPassword();
public slots:
void start();
void stop();
signals:
void stateChanged(int newState);
void errorMessageChanged(const QString &errorMessage);
void logMessage(const QString &message);
private:
TorProcessPrivate *d;
};
}
#endif

View file

@ -1,79 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef TORPROCESS_P_H
#define TORPROCESS_P_H
#include "TorProcess.h"
#include <QProcess>
#include <QTimer>
namespace Tor {
class TorProcessPrivate : public QObject
{
Q_OBJECT
public:
TorProcess *q;
QProcess process;
QString executable;
QString dataDir;
QString defaultTorrc;
QStringList extraSettings;
TorProcess::State state;
QString errorMessage;
QHostAddress controlHost;
quint16 controlPort;
QByteArray controlPassword;
QTimer controlPortTimer;
int controlPortAttempts;
TorProcessPrivate(TorProcess *q);
QString torrcPath() const;
QString controlPortFilePath() const;
bool ensureFilesExist();
public slots:
void processStarted();
void processFinished();
void processError(QProcess::ProcessError error);
void processReadable();
void tryReadControlPort();
};
}
#endif

View file

@ -1,155 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "TorSocket.h"
#include "TorControl.h"
#include <QNetworkProxy>
using namespace Tor;
TorSocket::TorSocket(QObject *parent)
: QTcpSocket(parent)
, m_port(0)
, m_reconnectEnabled(true)
, m_maxInterval(900)
, m_connectAttempts(0)
{
connect(torControl, SIGNAL(connectivityChanged()), SLOT(connectivityChanged()));
connect(&m_connectTimer, SIGNAL(timeout()), SLOT(reconnect()));
connect(this, SIGNAL(disconnected()), SLOT(onFailed()));
connect(this, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onFailed()));
m_connectTimer.setSingleShot(true);
connectivityChanged();
}
TorSocket::~TorSocket()
{
}
void TorSocket::setReconnectEnabled(bool enabled)
{
if (enabled == m_reconnectEnabled)
return;
m_reconnectEnabled = enabled;
if (m_reconnectEnabled) {
m_connectAttempts = 0;
reconnect();
} else {
m_connectTimer.stop();
}
}
void TorSocket::setMaxAttemptInterval(int interval)
{
m_maxInterval = interval;
}
void TorSocket::resetAttempts()
{
m_connectAttempts = 0;
if (m_connectTimer.isActive()) {
m_connectTimer.stop();
m_connectTimer.start(reconnectInterval() * 1000);
}
}
int TorSocket::reconnectInterval()
{
int delay = 0;
if (m_connectAttempts <= 4)
delay = 30;
else if (m_connectAttempts <= 6)
delay = 120;
else
delay = m_maxInterval;
return qMin(delay, m_maxInterval);
}
void TorSocket::reconnect()
{
if (!torControl->hasConnectivity() || !reconnectEnabled())
return;
m_connectTimer.stop();
if (!m_host.isEmpty() && m_port) {
std::cerr << "Attempting reconnection of socket to" << m_host.toStdString() << ":" << m_port << std::endl;
connectToHost(m_host, m_port);
}
}
void TorSocket::connectivityChanged()
{
if (torControl->hasConnectivity()) {
setProxy(torControl->connectionProxy());
if (state() == QAbstractSocket::UnconnectedState)
reconnect();
} else {
m_connectTimer.stop();
m_connectAttempts = 0;
}
}
void TorSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode,
NetworkLayerProtocol protocol)
{
m_host = hostName;
m_port = port;
if (!torControl->hasConnectivity())
return;
if (proxy() != torControl->connectionProxy())
setProxy(torControl->connectionProxy());
QAbstractSocket::connectToHost(hostName, port, openMode, protocol);
}
void TorSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode)
{
TorSocket::connectToHost(address.toString(), port, openMode);
}
void TorSocket::onFailed()
{
// Make sure the internal connection to the SOCKS proxy is closed
// Otherwise reconnect attempts will fail (#295)
close();
if (reconnectEnabled() && !m_connectTimer.isActive()) {
m_connectAttempts++;
m_connectTimer.start(reconnectInterval() * 1000);
std::cerr << "Reconnecting socket to" << m_host.toStdString() << ":" << m_port << "in" << m_connectTimer.interval() / 1000 << "seconds" << std::endl;
}
}

View file

@ -1,97 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef TORSOCKET_H
#define TORSOCKET_H
#include <QTcpSocket>
#include <QTimer>
namespace Tor {
/* Specialized QTcpSocket which makes connections over the SOCKS proxy
* from a TorControl instance, automatically attempts reconnections, and
* reacts to Tor's connectivity state.
*
* Use normal QTcpSocket/QAbstractSocket API. When a connection fails, it
* will be retried automatically after the correct interval and when
* connectivity is available.
*
* To fully disconnect, destroy the object, or call
* setReconnectEnabled(false) and disconnect the socket with
* disconnectFromHost or abort.
*
* The caller is responsible for resetting the attempt counter if a
* connection was successful and reconnection will be used again.
*/
class TorSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit TorSocket(QObject *parent = 0);
virtual ~TorSocket();
bool reconnectEnabled() const { return m_reconnectEnabled; }
void setReconnectEnabled(bool enabled);
int maxAttemptInterval() { return m_maxInterval; }
void setMaxAttemptInterval(int interval);
void resetAttempts();
virtual void connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
virtual void connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);
QString hostName() const { return m_host; }
quint16 port() const { return m_port; }
protected:
virtual int reconnectInterval();
private slots:
void reconnect();
void connectivityChanged();
void onFailed();
private:
QString m_host;
quint16 m_port;
QTimer m_connectTimer;
bool m_reconnectEnabled;
int m_maxInterval;
int m_connectAttempts;
using QAbstractSocket::connectToHost;
};
}
#endif

View file

@ -1,71 +0,0 @@
/* Ricochet - https://ricochet.im/
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * 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.
*
* * Neither the names of the copyright owners nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* OWNER OR CONTRIBUTORS 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.
*/
#ifndef UTILS_USEFUL_H
#define UTILS_USEFUL_H
#include <QtGlobal>
#include <QDebug>
/* Print a warning for bug conditions, and assert on a debug build.
*
* This should be used in place of Q_ASSERT for bug conditions, along
* with a proper error case for release-mode builds. For example:
*
* if (!connection || !user) {
* BUG() << "Request" << request << "should have a connection and user";
* return false;
* }
*
* Do not confuse bugs with actual error cases; BUG() should never be
* triggered unless the code or logic is wrong.
*/
#if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS)
# define BUG() Explode(__FILE__,__LINE__), qWarning() << "BUG:"
namespace {
class Explode
{
public:
const char *file;
int line;
Explode(const char *file, int line) : file(file), line(line) { }
~Explode() {
qt_assert("something broke!", file, line);
}
};
}
#else
# define BUG() qWarning() << "BUG:"
#endif
#endif

View file

@ -439,7 +439,7 @@ Changes for 0.5.4e
- Enabled embedded images in private chat and messages (only for QT version 4.7.0 and higher)
- added tooltips to GroupFlagsWidget when buttons are unchecked (Patch from Anonym)
- when an unknow user attempt to connect, show the name in the security item (Patch from Anonym)
- Load new stylesheets for locale depended things. Loading order: qss.default (internal), qss.<locale> (internal e.g. qss.de_DE)
- Load new stylesheets for locale depended things. Loading order: default.qss (internal), <locale>.qss (internal e.g. de_DE.qss)
stylesheet.qss (internal or external), stylesheet_<locale>.lqss (parallel to stylesheet)
- Added api for news feeds to the plugin interface. Added news feeds to the FeedReader plugin.
- Removed toaster for muted participant of a chat lobby.
@ -586,7 +586,7 @@ Changes for 0.5.4c
- GUI
* patch from AC to perform html optimization of forum posts using the canonical function optimizeHtml()
* fixed bug preventing share manager to modify more than one directory at once
* Moved most of the hardcoded colors of lists and trees to the file qss.default (with help from braindead).
* Moved most of the hardcoded colors of lists and trees to the file default.qss (with help from braindead).
Now the stylesheet can redefine these colors.
* Added multiselective mute/unmute to chat lobby. Added mute/unmute with double click on icon.
* Added flag for hungarian language.
@ -790,10 +790,10 @@ Changes for 0.5.4a
* Set the built-in stylesheet "Standard" as default for new profiles.
* Removed some unnecessary style sheets.
* Added two internal stylesheets:
- qss.default - This file is used as default for all stylesheets (e.g. the frames of
- default.qss - This file is used as default for all stylesheets (e.g. the frames of
the AvatarWidget) and can be overloaded from the selected stylesheet
- Standard.qss - The standard stylesheet for the current look of RetroShare. More internal stylesheets can be added.
The plan is to move nearly all internal stylesheets to the files Standard.qss/qss.default.
The plan is to move nearly all internal stylesheets to the files Standard.qss/default.qss.
After that the "empty" stylesheet should represent the system theme of the os.
- Added clear chat history to the context menu of the message text browser
@ -3121,4 +3121,4 @@ We have available for those interested in retroshare:
(2) deb installation files for debian/etch and kubuntu/feisty
(3) Language pack for those interested in translating.

View file

@ -21,7 +21,7 @@
#include "AboutDialog.h"
#include "HelpDialog.h"
#include "qmake_info.h"
#include "include/qmake_info.h"
#include "rshare.h"
#ifdef RS_JSONAPI
@ -52,8 +52,8 @@ AboutWidget::AboutWidget(QWidget* parent)
l->setMargin(0);
l->addStretch(1);
l->addStretch(1);
frame->setContentsMargins(0, 0, 0, 0);
frame->setLayout(l);
specialFrame->setContentsMargins(0, 0, 0, 0);
specialFrame->setLayout(l);
tWidget = NULL;
aWidget = NULL;
installAWidget();
@ -67,7 +67,7 @@ AboutWidget::AboutWidget(QWidget* parent)
void AboutWidget::installAWidget() {
assert(tWidget == NULL);
aWidget = new AWidget();
QVBoxLayout* l = (QVBoxLayout*)frame->layout();
QVBoxLayout* l = (QVBoxLayout*)specialFrame->layout();
l->insertWidget(0, aWidget);
l->setStretchFactor(aWidget, 100);
aWidget->setFocus();
@ -96,10 +96,10 @@ void AboutWidget::installTWidget() {
vl->addWidget(levelLabel);
vl->addStretch();
QHBoxLayout* l = (QHBoxLayout*)frame->layout();
QHBoxLayout* l = (QHBoxLayout*)specialFrame->layout();
l->insertWidget(0, pan);
l->insertWidget(0, tWidget);
QRect cRect = frame->contentsRect();
QRect cRect = specialFrame->contentsRect();
int height = tWidget->heightForWidth(cRect.width());
tWidget->setFixedSize(cRect.width() * cRect.height() / height, cRect.height());
npLabel->setFixedSize(tWidget->squareWidth()*4, tWidget->squareHeight()*5);
@ -117,7 +117,7 @@ void AboutWidget::installTWidget() {
void AboutWidget::switchPages() {
QLayoutItem* li = NULL;
QLayout* l = frame->layout();
QLayout* l = specialFrame->layout();
while ((li = l->takeAt(0)) && li->widget()) {
li->widget()->deleteLater();
}
@ -158,9 +158,9 @@ void AboutWidget::updateTitle()
void AboutWidget::mousePressEvent(QMouseEvent *e)
{
QPoint globalPos = mapToGlobal(e->pos());
QPoint framePos = frame->mapFromGlobal(globalPos);
QPoint framePos = specialFrame->mapFromGlobal(globalPos);
if (frame->contentsRect().contains(framePos)) {
if (specialFrame->contentsRect().contains(framePos)) {
{
if(e->modifiers() & Qt::ControlModifier)
switchPages();

View file

@ -57,7 +57,7 @@
</layout>
</item>
<item>
<widget class="QFrame" name="frame">
<widget class="QFrame" name="specialFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>

View file

@ -22,19 +22,20 @@
#include "ChatLobbyWidget.h"
#include "notifyqt.h"
#include "RetroShareLink.h"
#include "chat/ChatLobbyDialog.h"
#include "chat/ChatLobbyUserNotify.h"
#include "chat/ChatTabWidget.h"
#include "chat/CreateLobbyDialog.h"
#include "common/FilesDefs.h"
#include "common/RSTreeWidgetItem.h"
#include "common/RSElidedItemDelegate.h"
#include "gui/RetroShareLink.h"
#include "gui/gxs/GxsIdDetails.h"
#include "gui/Identity/IdEditDialog.h"
#include "gui/settings/rsharesettings.h"
#include "gxs/GxsIdDetails.h"
#include "Identity/IdEditDialog.h"
#include "settings/rsharesettings.h"
#include "util/HandleRichText.h"
#include "util/misc.h"
#include "util/QtVersion.h"
#include "gui/common/FilesDefs.h"
#include "retroshare/rsmsgs.h"
#include "retroshare/rspeers.h"
@ -59,6 +60,10 @@
#define COLUMN_COUNT 3
#define COLUMN_DATA 0
#define COLUMN_NAME_NB_CHAR 30
#define COLUMN_USER_COUNT_NB_CHAR 4
#define COLUMN_TOPIC_NB_CHAR 25
#define ROLE_SORT Qt::UserRole
#define ROLE_ID Qt::UserRole + 1
#define ROLE_SUBSCRIBED Qt::UserRole + 2
@ -88,26 +93,37 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
{
ui.setupUi(this);
int H = QFontMetricsF(ui.lobbyTreeWidget->font()).height();
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
int W = QFontMetricsF(ui.lobbyTreeWidget->font()).width("_");
#else
int W = QFontMetricsF(ui.lobbyTreeWidget->font()).horizontalAdvance("_");
#endif
m_bProcessSettings = false;
myChatLobbyUserNotify = NULL;
myInviteYesButton = NULL;
myInviteIdChooser = NULL;
QObject::connect( NotifyQt::getInstance(), SIGNAL(lobbyListChanged()), SLOT(lobbyChanged()));
QObject::connect( NotifyQt::getInstance(), SIGNAL(chatLobbyEvent(qulonglong,int,const RsGxsId&,const QString&)), this, SLOT(displayChatLobbyEvent(qulonglong,int,const RsGxsId&,const QString&)));
QObject::connect( NotifyQt::getInstance(), SIGNAL(chatLobbyEvent(qulonglong,int,RsGxsId,QString)), this, SLOT(displayChatLobbyEvent(qulonglong,int,RsGxsId,QString)));
QObject::connect( NotifyQt::getInstance(), SIGNAL(chatLobbyInviteReceived()), this, SLOT(readChatLobbyInvites()));
QObject::connect( ui.lobbyTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(lobbyTreeWidgetCustomPopupMenu(QPoint)));
QObject::connect( ui.lobbyTreeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*,int)));
QObject::connect( ui.lobbyTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(updateCurrentLobby()));
QObject::connect( ui.filterLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(filterItems(QString)));
QObject::connect( ui.filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterItems(QString)));
QObject::connect( ui.filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int)));
QObject::connect( ui.createLobbyToolButton, SIGNAL(clicked()), this, SLOT(createChatLobby()));
compareRole = new RSTreeWidgetItemCompareRole;
compareRole->setRole(COLUMN_NAME, ROLE_SORT);
RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this);
itemDelegate->setSpacing(QSize(W/2, H/4));
ui.lobbyTreeWidget->setItemDelegate(itemDelegate);
ui.lobbyTreeWidget->setColumnCount(COLUMN_COUNT);
ui.lobbyTreeWidget->sortItems(COLUMN_NAME, Qt::AscendingOrder);
@ -159,14 +175,11 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
ui.lobbyTreeWidget->setColumnHidden(COLUMN_USER_COUNT,true) ;
ui.lobbyTreeWidget->setColumnHidden(COLUMN_TOPIC,true) ;
ui.lobbyTreeWidget->setSortingEnabled(true) ;
ui.lobbyTreeWidget->setItemDelegateForColumn(COLUMN_NAME, new RSElidedItemDelegate());
float fact = QFontMetricsF(font()).height()/14.0f;
ui.lobbyTreeWidget->adjustSize();
ui.lobbyTreeWidget->setColumnWidth(COLUMN_NAME,100*fact);
ui.lobbyTreeWidget->setColumnWidth(COLUMN_USER_COUNT, 50*fact);
ui.lobbyTreeWidget->setColumnWidth(COLUMN_TOPIC, 50*fact);
ui.lobbyTreeWidget->setColumnWidth(COLUMN_NAME,COLUMN_NAME_NB_CHAR*W);
ui.lobbyTreeWidget->setColumnWidth(COLUMN_USER_COUNT, COLUMN_USER_COUNT_NB_CHAR*W);
ui.lobbyTreeWidget->setColumnWidth(COLUMN_TOPIC, COLUMN_TOPIC_NB_CHAR*W);
/** Setup the actions for the header context menu */
showUserCountAct= new QAction(headerItem->text(COLUMN_USER_COUNT),this);
@ -181,7 +194,7 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
ui.splitter->setStretchFactor(1, 1);
QList<int> sizes;
sizes << 200*fact << width(); // Qt calculates the right sizes
sizes << ui.lobbyTreeWidget->columnWidth(COLUMN_NAME) << width(); // Qt calculates the right sizes
ui.splitter->setSizes(sizes);
lobbyChanged();
@ -194,29 +207,29 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
// load settings
processSettings(true);
int S = QFontMetricsF(font()).height();
QString help_str = tr("\
<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Chat Rooms</h1> \
<p>Chat rooms work pretty much like IRC. \
They allow you to talk anonymously with tons of people without the need to make friends.</p> \
<p>A chat room can be public (your friends see it) or private (your friends can't see it, unless you \
invite them with <img src=\":/icons/png/add.png\" width=%2/>). \
Once you have been invited to a private room, you will be able to see it when your friends \
are using it.</p> \
<p>The list at left shows \
chat lobbies your friends are participating in. You can either \
<ul> \
<li>Right click to create a new chat room</li> \
<li>Double click a chat room to enter, chat, and show it to your friends</li> \
</ul> \
Note: For the chat rooms to work properly, your computer needs be on time. So check your system clock!\
</p> \
"
).arg(QString::number(2*S)).arg(QString::number(S)) ;
int hbH = misc::getFontSizeFactor("HelpButton").height();
QString help_str = tr(
"<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Chat Rooms</h1>"
"<p>Chat rooms work pretty much like IRC."
" They allow you to talk anonymously with tons of people without the need to make friends.</p>"
"<p>A chat room can be public (your friends see it) or private (your friends can't see it, unless you"
" invite them with <img src=\":/icons/png/add.png\" width=%2/>)."
" Once you have been invited to a private room, you will be able to see it when your friends"
" are using it.</p>"
"<p>The list at left shows"
" chat lobbies your friends are participating in. You can either"
" <ul>"
" <li>Right click to create a new chat room</li>"
" <li>Double click a chat room to enter, chat, and show it to your friends</li>"
" </ul>"
" Note: For the chat rooms to work properly, your computer needs be on time. So check your system clock!"
"</p>"
).arg(QString::number(2*hbH), QString::number(hbH)) ;
registerHelpButton(ui.helpButton,help_str,"ChatLobbyDialog") ;
ui.lobbyTreeWidget->setIconSize(QSize(S*1.5,S*1.5));
registerHelpButton(ui.helpButton,help_str,"ChatLobbyDialog") ;
int ltwH = misc::getFontSizeFactor("LobbyTreeWidget", 1.5).height();
ui.lobbyTreeWidget->setIconSize(QSize(ltwH,ltwH));
}
ChatLobbyWidget::~ChatLobbyWidget()
@ -232,7 +245,7 @@ ChatLobbyWidget::~ChatLobbyWidget()
UserNotify *ChatLobbyWidget::createUserNotify(QObject *parent)
{
myChatLobbyUserNotify = new ChatLobbyUserNotify(parent);
connect(myChatLobbyUserNotify, SIGNAL(countChanged(ChatLobbyId, unsigned int)), this, SLOT(updateNotify(ChatLobbyId, unsigned int)));
connect(myChatLobbyUserNotify, SIGNAL(countChanged(ChatLobbyId,uint)), this, SLOT(updateNotify(ChatLobbyId,uint)));
return myChatLobbyUserNotify;
}
@ -282,87 +295,84 @@ void ChatLobbyWidget::lobbyTreeWidgetCustomPopupMenu(QPoint)
QMenu contextMnu(this);
if (item && item->type() == TYPE_FOLDER) {
QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CREATE), tr("Create chat room"), this, SLOT(createChatLobby()));
QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_CREATE), tr("Create chat room"), this, SLOT(createChatLobby()));
action->setData(item->data(COLUMN_DATA, ROLE_PRIVACYLEVEL).toInt());
}
if (item && item->type() == TYPE_LOBBY)
{
std::list<RsGxsId> own_identities ;
rsIdentity->getOwnIds(own_identities) ;
if (item && item->type() == TYPE_LOBBY)
{
std::list<RsGxsId> own_identities ;
rsIdentity->getOwnIds(own_identities) ;
if (item->data(COLUMN_DATA, ROLE_SUBSCRIBED).toBool())
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_UNSUBSCRIBE), tr("Leave this room"), this, SLOT(unsubscribeItem()));
else
{
QTreeWidgetItem *item = ui.lobbyTreeWidget->currentItem();
if (item->data(COLUMN_DATA, ROLE_SUBSCRIBED).toBool())
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_UNSUBSCRIBE), tr("Leave this room"), this, SLOT(unsubscribeItem()));
else
{
ChatLobbyFlags flags(item->data(COLUMN_DATA, ROLE_FLAGS).toUInt());
//ChatLobbyId id = item->data(COLUMN_DATA, ROLE_ID).toULongLong();
ChatLobbyFlags flags(item->data(COLUMN_DATA, ROLE_FLAGS).toUInt());
bool removed = false ;
if(flags & RS_CHAT_LOBBY_FLAGS_PGP_SIGNED)
removed = trimAnonIds(own_identities) ;
bool removed = false ;
if(flags & RS_CHAT_LOBBY_FLAGS_PGP_SIGNED)
removed = trimAnonIds(own_identities) ;
if(own_identities.empty())
{
if(removed)
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Create a non anonymous identity and enter this room"), this, SLOT(createIdentityAndSubscribe()));
else
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Create an identity and enter this chat room"), this, SLOT(createIdentityAndSubscribe()));
}
else if(own_identities.size() == 1)
{
QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Enter this chat room"), this, SLOT(subscribeChatLobbyAs()));
action->setData(QString::fromStdString((own_identities.front()).toStdString())) ;
}
else
{
QMenu *mnu = contextMnu.addMenu(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE),tr("Enter this chat room as...")) ;
if(own_identities.empty())
{
if(removed)
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Create a non anonymous identity and enter this room"), this, SLOT(createIdentityAndSubscribe()));
else
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Create an identity and enter this chat room"), this, SLOT(createIdentityAndSubscribe()));
}
else if(own_identities.size() == 1)
{
QAction *action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE), tr("Enter this chat room"), this, SLOT(subscribeChatLobbyAs()));
action->setData(QString::fromStdString((own_identities.front()).toStdString())) ;
}
else
{
QMenu *mnu = contextMnu.addMenu(FilesDefs::getIconFromQtResourcePath(IMAGE_SUBSCRIBE),tr("Enter this chat room as...")) ;
for(std::list<RsGxsId>::const_iterator it=own_identities.begin();it!=own_identities.end();++it)
{
RsIdentityDetails idd ;
rsIdentity->getIdDetails(*it,idd) ;
for(std::list<RsGxsId>::const_iterator it=own_identities.begin();it!=own_identities.end();++it)
{
RsIdentityDetails idd ;
rsIdentity->getIdDetails(*it,idd) ;
QPixmap pixmap ;
QPixmap pixmap ;
if(idd.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idd.mAvatar.mData, idd.mAvatar.mSize, pixmap, GxsIdDetails::SMALL))
pixmap = GxsIdDetails::makeDefaultIcon(*it,GxsIdDetails::SMALL) ;
if(idd.mAvatar.mSize == 0 || !GxsIdDetails::loadPixmapFromData(idd.mAvatar.mData, idd.mAvatar.mSize, pixmap, GxsIdDetails::SMALL))
pixmap = GxsIdDetails::makeDefaultIcon(*it,GxsIdDetails::SMALL) ;
QAction *action = mnu->addAction(QIcon(pixmap), QString("%1 (%2)").arg(QString::fromUtf8(idd.mNickname.c_str()), QString::fromStdString((*it).toStdString())), this, SLOT(subscribeChatLobbyAs()));
action->setData(QString::fromStdString((*it).toStdString())) ;
}
}
}
QAction *action = mnu->addAction(QIcon(pixmap), QString("%1 (%2)").arg(QString::fromUtf8(idd.mNickname.c_str()), QString::fromStdString((*it).toStdString())), this, SLOT(subscribeChatLobbyAs()));
action->setData(QString::fromStdString((*it).toStdString())) ;
}
}
}
#ifdef TO_BE_REMOVED
// This code is not needed anymore because AutoSubscribe is now automatically handled with chat room subscription.
// This code is not needed anymore because AutoSubscribe is now automatically handled with chat room subscription.
if (item->data(COLUMN_DATA, ROLE_AUTOSUBSCRIBE).toBool())
contextMnu.addAction(QIcon(IMAGE_AUTOSUBSCRIBE), tr("Remove Auto Subscribe"), this, SLOT(autoSubscribeItem()));
else if(!own_identities.empty())
contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Add Auto Subscribe"), this, SLOT(autoSubscribeItem()));
if (item->data(COLUMN_DATA, ROLE_AUTOSUBSCRIBE).toBool())
contextMnu.addAction(QIcon(IMAGE_AUTOSUBSCRIBE), tr("Remove Auto Subscribe"), this, SLOT(autoSubscribeItem()));
else if(!own_identities.empty())
contextMnu.addAction(QIcon(IMAGE_SUBSCRIBE), tr("Add Auto Subscribe"), this, SLOT(autoSubscribeItem()));
#endif
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYRSLINK), tr("Copy RetroShare Link"), this, SLOT(copyItemLink()));
}
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYRSLINK), tr("Copy RetroShare Link"), this, SLOT(copyItemLink()));
}
contextMnu.addSeparator();//-------------------------------------------------------------------
contextMnu.addSeparator();//-------------------------------------------------------------------
showUserCountAct->setChecked(!ui.lobbyTreeWidget->isColumnHidden(COLUMN_USER_COUNT));
showTopicAct->setChecked(!ui.lobbyTreeWidget->isColumnHidden(COLUMN_TOPIC));
showUserCountAct->setChecked(!ui.lobbyTreeWidget->isColumnHidden(COLUMN_USER_COUNT));
showTopicAct->setChecked(!ui.lobbyTreeWidget->isColumnHidden(COLUMN_TOPIC));
QMenu *menu = contextMnu.addMenu(tr("Columns"));
menu->addAction(showUserCountAct);
menu->addAction(showTopicAct);
QMenu *menu = contextMnu.addMenu(tr("Columns"));
menu->addAction(showUserCountAct);
menu->addAction(showTopicAct);
contextMnu.exec(QCursor::pos());
contextMnu.exec(QCursor::pos());
}
void ChatLobbyWidget::lobbyChanged()
{
updateDisplay();
ChatLobbyWidget::updateDisplay();
}
static void updateItem(QTreeWidget *treeWidget, QTreeWidgetItem *item, ChatLobbyId id, const std::string &name, const std::string &topic, int count, bool subscribed, bool autoSubscribe,ChatLobbyFlags lobby_flags)
@ -596,7 +606,7 @@ void ChatLobbyWidget::updateDisplay()
QTreeWidgetItem *itemLoop = lobby_other_item->child(childIndex);
if (itemLoop->type() == TYPE_LOBBY && itemLoop->data(COLUMN_DATA, ROLE_ID).toULongLong() == lobby.lobby_id) {
delete(lobby_other_item->takeChild(lobby_other_item->indexOfChild(itemLoop)));
childCnt = lobby_other_item->childCount();
//childCnt = lobby_other_item->childCount();
break;
}
}
@ -920,7 +930,7 @@ void ChatLobbyWidget::showBlankPage(ChatLobbyId id, bool subscribed /*= false*/)
}
}
ui.lobbyInfoLabel->setText(text);
ui.info_Label_Lobby->setText(text);
return ;
}
@ -932,7 +942,7 @@ void ChatLobbyWidget::showBlankPage(ChatLobbyId id, bool subscribed /*= false*/)
ui.lobbysec_lineEdit->clear();
QString text = tr("No chat room selected. \nSelect chat rooms at left to show details.\nDouble click a chat room to enter and chat.") ;
ui.lobbyInfoLabel->setText(text) ;
ui.info_Label_Lobby->setText(text) ;
}
void ChatLobbyWidget::subscribeItem()
@ -1116,6 +1126,7 @@ void ChatLobbyWidget::updateCurrentLobby()
filterItems(ui.filterLineEdit->text());
}
}
void ChatLobbyWidget::updateMessageChanged(bool incoming, ChatLobbyId id, QDateTime time, QString senderName, QString msg)
{
QTreeWidgetItem *current_item = ui.lobbyTreeWidget->currentItem();
@ -1174,9 +1185,11 @@ void ChatLobbyWidget::readChatLobbyInvites()
if(found)
continue ;
QMessageBox mb(QObject::tr("Join chat room"),
tr("%1 invites you to chat room named %2").arg(QString::fromUtf8(rsPeers->getPeerName((*it).peer_id).c_str())).arg(RsHtml::plainText(it->lobby_name)),
QMessageBox::Question, QMessageBox::Yes,QMessageBox::No, 0);
QMessageBox mb(QObject::tr("Join chat room")
, tr("%1 invites you to chat room named %2")
.arg(QString::fromUtf8(rsPeers->getPeerName((*it).peer_id).c_str())
, RsHtml::plainText(it->lobby_name))
, QMessageBox::Question, QMessageBox::Yes,QMessageBox::No, 0);
QLabel *label;

View file

@ -56,18 +56,18 @@ class ChatLobbyWidget : public RsAutoUpdatePage
public:
/** Default constructor */
ChatLobbyWidget(QWidget *parent = 0, Qt::WindowFlags flags = 0);
ChatLobbyWidget(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags());
/** Default destructor */
~ChatLobbyWidget();
virtual QIcon iconPixmap() const { return QIcon(IMAGE_CHATLOBBY) ; } //MainPage
virtual QString pageName() const { return tr("Chats") ; } //MainPage
virtual QString helpText() const { return ""; } //MainPage
virtual QIcon iconPixmap() const override { return QIcon(IMAGE_CHATLOBBY) ; } //MainPage
virtual QString pageName() const override { return tr("Chats") ; } //MainPage
virtual QString helpText() const override { return ""; } //MainPage
virtual UserNotify *createUserNotify(QObject *parent) override; //MainPage
virtual void updateDisplay();
virtual void updateDisplay() override; //RsAutoUpdatePage
void setCurrentChatPage(ChatLobbyDialog *) ; // used by ChatLobbyDialog to raise.
void addChatPage(ChatLobbyDialog *) ;

View file

@ -70,7 +70,14 @@
</widget>
</item>
<item>
<widget class="StyledLabel" name="titleBarLabel">
<widget class="QLabel" name="titleBarLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Chat rooms</string>
</property>
@ -95,7 +102,7 @@
<enum>Qt::NoFocus</enum>
</property>
<property name="icon">
<iconset resource="images.qrc">
<iconset resource="icons.qrc">
<normaloff>:/icons/help_64.png</normaloff>:/icons/help_64.png</iconset>
</property>
<property name="checkable">
@ -183,7 +190,12 @@
</widget>
</item>
<item>
<widget class="QTreeWidget" name="lobbyTreeWidget">
<widget class="RSTreeWidget" name="lobbyTreeWidget">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="iconSize">
<size>
<width>16</width>
@ -228,6 +240,13 @@
</property>
<item row="0" column="0">
<widget class="QGroupBox" name="lobbyinfo_groupBox">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
@ -244,6 +263,7 @@
<widget class="QLabel" name="lobbyname_label">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
@ -261,6 +281,13 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
@ -270,6 +297,7 @@
<widget class="QLabel" name="lobbyid_label">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
@ -296,6 +324,7 @@
<widget class="QLabel" name="lobbytopic_label">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
@ -325,6 +354,7 @@
<widget class="QLabel" name="lobbytype_label">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
@ -351,6 +381,7 @@
<widget class="QLabel" name="lobbysec_label">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
@ -371,6 +402,7 @@
<widget class="QLabel" name="lobbypeers_label">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
@ -396,7 +428,7 @@
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lobbyInfoLabel">
<widget class="QLabel" name="info_Label_Lobby">
<property name="text">
<string notr="true">TextLabel</string>
</property>
@ -435,13 +467,12 @@
<header location="global">gui/common/LineEditClear.h</header>
</customwidget>
<customwidget>
<class>StyledLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledLabel.h</header>
<class>RSTreeWidget</class>
<extends>QTreeWidget</extends>
<header>gui/common/RSTreeWidget.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="images.qrc"/>
<include location="icons.qrc"/>
</resources>
<connections/>

View file

@ -25,6 +25,7 @@
#include "gui/Circles/CirclesDialog.h"
#include "gui/Circles/CreateCircleDialog.h"
#include "gui/common/UIStateHelper.h"
#include "util/qtthreadsutils.h"
#include <retroshare/rsgxscircles.h>
#include <retroshare/rspeers.h>
@ -72,9 +73,6 @@ CirclesDialog::CirclesDialog(QWidget *parent)
connect(ui.treeWidget_membership, SIGNAL(itemSelectionChanged()), this, SLOT(circle_selected()));
/* Setup TokenQueue */
mCircleQueue = new TokenQueue(rsGxsCircles->getTokenService(), this);
/* Set header resize modes and initial section sizes */
QHeaderView * membership_header = ui.treeWidget_membership->header () ;
membership_header->resizeSection ( CIRCLEGROUP_CIRCLE_COL_GROUPNAME, 200 );
@ -82,7 +80,6 @@ CirclesDialog::CirclesDialog(QWidget *parent)
CirclesDialog::~CirclesDialog()
{
delete mCircleQueue;
}
void CirclesDialog::todo()
@ -564,38 +561,36 @@ void CirclesDialog::requestGroupMeta()
{
mStateHelper->setLoading(CIRCLESDIALOG_GROUPMETA, true);
std::cerr << "CirclesDialog::requestGroupMeta()";
std::cerr << std::endl;
RsThread::async([this]()
{
std::list<RsGroupMetaData> circles;
mCircleQueue->cancelActiveRequestTokens(CIRCLESDIALOG_GROUPMETA);
if(!rsGxsCircles->getCirclesSummaries(circles))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to get circles summaries " << std::endl;
return;
}
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_META;
RsQThreadUtils::postToObject( [this,circles]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete, note that
* Qt::QueuedConnection is important!
*/
uint32_t token;
mCircleQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_SUMMARY, opts, CIRCLESDIALOG_GROUPMETA);
loadGroupMeta(circles);
}, this );
});
}
void CirclesDialog::loadGroupMeta(const uint32_t &token)
void CirclesDialog::loadGroupMeta(const std::list<RsGroupMetaData>& groupInfo)
{
mStateHelper->setLoading(CIRCLESDIALOG_GROUPMETA, false);
std::cerr << "CirclesDialog::loadGroupMeta()";
std::cerr << std::endl;
ui.treeWidget_membership->clear();
std::list<RsGroupMetaData> groupInfo;
std::list<RsGroupMetaData>::iterator vit;
if (!rsGxsCircles->getGroupSummary(token,groupInfo))
{
std::cerr << "CirclesDialog::loadGroupMeta() Error getting GroupMeta";
std::cerr << std::endl;
mStateHelper->setActive(CIRCLESDIALOG_GROUPMETA, false);
return;
}
mStateHelper->setActive(CIRCLESDIALOG_GROUPMETA, true);
/* add the top level item */
@ -615,7 +610,7 @@ void CirclesDialog::loadGroupMeta(const uint32_t &token)
externalOtherCirclesItem->setText(0, tr("External Circles (Other)"));
ui.treeWidget_membership->addTopLevelItem(externalOtherCirclesItem);
for(vit = groupInfo.begin(); vit != groupInfo.end(); ++vit)
for(auto vit = groupInfo.begin(); vit != groupInfo.end(); ++vit)
{
/* Add Widget, and request Pages */
std::cerr << "CirclesDialog::loadGroupMeta() GroupId: " << vit->mGroupId;
@ -647,25 +642,3 @@ void CirclesDialog::loadGroupMeta(const uint32_t &token)
}
}
}
void CirclesDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req)
{
std::cerr << "CirclesDialog::loadRequest() UserType: " << req.mUserType;
std::cerr << std::endl;
if (queue == mCircleQueue)
{
/* now switch on req */
switch(req.mUserType)
{
case CIRCLESDIALOG_GROUPMETA:
loadGroupMeta(req.mToken);
break;
default:
std::cerr << "CirclesDialog::loadRequest() ERROR: INVALID TYPE";
std::cerr << std::endl;
break;
}
}
}

View file

@ -24,14 +24,13 @@
#define MRK_CIRCLE_DIALOG_H
#include "gui/gxs/RsGxsUpdateBroadcastPage.h"
#include "util/TokenQueue.h"
#include "ui_CirclesDialog.h"
#define IMAGE_CIRCLES ":/icons/png/circles.png"
class UIStateHelper;
class CirclesDialog : public MainPage, public TokenResponse
class CirclesDialog : public MainPage
{
Q_OBJECT
@ -43,8 +42,6 @@ public:
virtual QString pageName() const { return tr("Circles") ; } //MainPage
virtual QString helpText() const { return ""; } //MainPage
void loadRequest(const TokenQueue *queue, const TokenRequest &req);
protected:
virtual void updateDisplay(bool complete);
@ -62,9 +59,8 @@ private:
void reloadAll();
void requestGroupMeta();
void loadGroupMeta(const uint32_t &token);
void loadGroupMeta(const std::list<RsGroupMetaData>& groupInfo);
TokenQueue *mCircleQueue;
UIStateHelper *mStateHelper;
/* UI - from Designer */

View file

@ -49,7 +49,14 @@
</widget>
</item>
<item>
<widget class="StyledLabel" name="titleBarLabel">
<widget class="QLabel" name="titleBarLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Circles</string>
</property>
@ -247,13 +254,6 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StyledLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledLabel.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>

View file

@ -98,7 +98,7 @@ CreateCircleDialog::CreateCircleDialog()
connect(ui.addButton, SIGNAL(clicked()), this, SLOT(addMember()));
connect(ui.removeButton, SIGNAL(clicked()), this, SLOT(removeMember()));
connect(ui.createButton, SIGNAL(clicked()), this, SLOT(createCircle()));
connect(ui.postButton, SIGNAL(clicked()), this, SLOT(createCircle()));
connect(ui.cancelButton, SIGNAL(clicked()), this, SLOT(close()));
connect(ui.treeWidget_membership, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(selectedMember(QTreeWidgetItem*, QTreeWidgetItem*)));
@ -187,14 +187,14 @@ void CreateCircleDialog::editExistingId(const RsGxsGroupId &circleId, const bool
ui.idChooser->setVisible(true) ;
}
ui.createButton->setText(tr("Update"));
ui.postButton->setText(tr("Update"));
ui.addButton->setEnabled(!readonly) ;
ui.removeButton->setEnabled(!readonly) ;
if(readonly)
{
ui.createButton->hide() ;
ui.postButton->hide() ;
ui.cancelButton->setText(tr("Close"));
ui.peersSelection_GB->hide() ;
ui.addButton->hide() ;
@ -216,7 +216,7 @@ void CreateCircleDialog::editNewId(bool isExternal)
{
setupForExternalCircle();
ui.headerFrame->setHeaderText(tr("Create New Circle"));
ui.createButton->setText(tr("Create"));
ui.postButton->setText(tr("Create"));
}
else
{
@ -357,8 +357,8 @@ void CreateCircleDialog::addMember(const QString& keyId, const QString& idtype,
member->setText(RSCIRCLEID_COL_IDTYPE, idtype);
tree->addTopLevelItem(member);
ui.members_groupBox->setTitle( tr("Invited Members") + " (" + QString::number(ui.treeWidget_membership->topLevelItemCount()) + ")" );
updateMembership();
}
/** Maybe we can use RsGxsCircleGroup instead of RsGxsCircleDetails ??? (TODO)**/
@ -413,6 +413,8 @@ void CreateCircleDialog::removeMember()
// does this just work? (TODO)
delete(item);
updateMembership();
}
void CreateCircleDialog::createCircle()
@ -422,7 +424,7 @@ void CreateCircleDialog::createCircle()
close() ;
return ;
}
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle()";
std::cerr << std::endl;
@ -431,10 +433,10 @@ void CreateCircleDialog::createCircle()
QString name = ui.circleName->text();
if(name.isEmpty()) {
/* error message */
QMessageBox::warning(this, tr("RetroShare"),tr("Please set a name for your Circle"), QMessageBox::Ok, QMessageBox::Ok);
/* error message */
QMessageBox::warning(this, tr("RetroShare"),tr("Please set a name for your Circle"), QMessageBox::Ok, QMessageBox::Ok);
return; //Don't add a empty Subject!!
return; //Don't add a empty Subject!!
}
RsGxsCircleGroup circle = mCircleGroup; // init with loaded group
@ -448,19 +450,23 @@ void CreateCircleDialog::createCircle()
{
case GxsIdChooser::KnowId:
case GxsIdChooser::UnKnowId:
circle.mMeta.mAuthorId = authorId;
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() AuthorId: " << authorId;
std::cerr << std::endl;
circle.mMeta.mAuthorId = authorId;
circle.mMeta.mAuthenFlags = GXS_SERV::GRP_OPTION_AUTHEN_AUTHOR_SIGN;
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() AuthorId: " << authorId;
std::cerr << std::endl;
#endif
break;
break;
case GxsIdChooser::NoId:
case GxsIdChooser::None:
circle.mMeta.mAuthorId.clear();
circle.mMeta.mAuthenFlags = 0;
default: ;
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() No AuthorId Chosen!";
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() No AuthorId Chosen!";
std::cerr << std::endl;
#endif
}
@ -468,144 +474,164 @@ void CreateCircleDialog::createCircle()
/* copy Ids from GUI */
QTreeWidget *tree = ui.treeWidget_membership;
int count = tree->topLevelItemCount();
for(int i = 0; i < count; ++i)
for(int i = 0; i < count; ++i)
{
QTreeWidgetItem *item = tree->topLevelItem(i);
QString keyId = item->text(RSCIRCLEID_COL_KEYID);
QTreeWidgetItem *item = tree->topLevelItem(i);
QString keyId = item->text(RSCIRCLEID_COL_KEYID);
/* insert into circle */
if (mIsExternalCircle)
{
RsGxsId key_id_gxs(keyId.toStdString()) ;
/* insert into circle */
if (mIsExternalCircle)
{
RsGxsId key_id_gxs(keyId.toStdString()) ;
if(key_id_gxs.isNull())
{
std::cerr << "Error: Not a proper keyID: " << keyId.toStdString() << std::endl;
continue ;
}
if(key_id_gxs.isNull())
{
std::cerr << "Error: Not a proper keyID: " << keyId.toStdString() << std::endl;
continue ;
}
circle.mInvitedMembers.insert(key_id_gxs) ;
circle.mInvitedMembers.insert(key_id_gxs) ;
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() Inserting Member: " << keyId.toStdString();
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() Inserting Member: " << keyId.toStdString();
std::cerr << std::endl;
#endif
}
else
{
RsPgpId key_id_pgp(keyId.toStdString()) ;
}
else
{
RsPgpId key_id_pgp(keyId.toStdString()) ;
if(key_id_pgp.isNull())
{
std::cerr << "Error: Not a proper PGP keyID: " << keyId.toStdString() << std::endl;
continue ;
}
if(key_id_pgp.isNull())
{
std::cerr << "Error: Not a proper PGP keyID: " << keyId.toStdString() << std::endl;
continue ;
}
circle.mLocalFriends.insert(key_id_pgp) ;
circle.mLocalFriends.insert(key_id_pgp) ;
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() Inserting Friend: " << keyId.toStdString();
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() Inserting Friend: " << keyId.toStdString();
std::cerr << std::endl;
#endif
}
}
}
if (mIsExternalCircle)
if (mIsExternalCircle)
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() External Circle";
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() External Circle";
std::cerr << std::endl;
#endif
// set distribution from GUI.
circle.mMeta.mCircleId.clear() ;
// set distribution from GUI.
circle.mMeta.mCircleId.clear() ;
circle.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC;
if (ui.radioButton_Public->isChecked()) {
if (ui.radioButton_Public->isChecked()) {
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() Public Circle";
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() Public Circle";
std::cerr << std::endl;
#endif
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_PUBLIC;
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_PUBLIC;
} else if (ui.radioButton_Self->isChecked()) {
} else if (ui.radioButton_Self->isChecked()) {
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() ExtSelfRef Circle";
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() ExtSelfRef Circle";
std::cerr << std::endl;
#endif
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_EXT_SELF;
} else if (ui.radioButton_Restricted->isChecked()) {
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_EXT_SELF;
} else if (ui.radioButton_Restricted->isChecked()) {
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() External (Other) Circle";
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() External (Other) Circle";
std::cerr << std::endl;
#endif
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_EXTERNAL;
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_EXTERNAL;
/* grab circle ID from chooser */
RsGxsCircleId chosenId;
if (ui.circleComboBox->getChosenCircle(chosenId)) {
/* grab circle ID from chooser */
RsGxsCircleId chosenId;
if (ui.circleComboBox->getChosenCircle(chosenId)) {
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() ChosenId: " << chosenId;
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() ChosenId: " << chosenId;
std::cerr << std::endl;
#endif
circle.mMeta.mCircleId = chosenId;
} else {//if (ui.circleComboBox->getChosenCircle(chosenId))
circle.mMeta.mCircleId = chosenId;
} else {//if (ui.circleComboBox->getChosenCircle(chosenId))
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() Error no Id Chosen";
std::cerr << std::endl;
std::cerr << "CreateCircleDialog::createCircle() Error no Id Chosen";
std::cerr << std::endl;
#endif
QMessageBox::warning(this, tr("RetroShare"),tr("No Restriction Circle Selected"), QMessageBox::Ok, QMessageBox::Ok);
return;
}//else (ui.circleComboBox->getChosenCircle(chosenId))
}
else
{
QMessageBox::warning(this, tr("RetroShare"),tr("No Circle Limitations Selected"), QMessageBox::Ok, QMessageBox::Ok);
return;
}
}
else
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() Personal Circle";
std::cerr << std::endl;
#endif
// set personal distribution
circle.mMeta.mCircleId.clear() ;
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_LOCAL;
}
uint32_t token;
if(mIsExistingCircle)
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::updateCircle() : mCircleType: " << circle.mMeta.mCircleType << std::endl;
std::cerr << "CreateCircleDialog::updateCircle() : mCircleId: " << circle.mMeta.mCircleId << std::endl;
std::cerr << "CreateCircleDialog::updateCircle() : mGroupId: " << circle.mMeta.mGroupId << std::endl;
std::cerr << "CreateCircleDialog::updateCircle() Checks and Balances Okay - calling service proper.."<< std::endl;
#endif
rsGxsCircles->updateGroup(token, circle);
QMessageBox::warning(this, tr("RetroShare"),tr("No Restriction Circle Selected"), QMessageBox::Ok, QMessageBox::Ok);
return;
}//else (ui.circleComboBox->getChosenCircle(chosenId))
}
else
{
QMessageBox::warning(this, tr("RetroShare"),tr("No Circle Limitations Selected"), QMessageBox::Ok, QMessageBox::Ok);
return;
}
}
else
{
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() : mCircleType: " << circle.mMeta.mCircleType << std::endl;
std::cerr << "CreateCircleDialog::createCircle() : mCircleId: " << circle.mMeta.mCircleId << std::endl;
std::cerr << "CreateCircleDialog::createCircle() Checks and Balances Okay - calling service proper.."<< std::endl;
std::cerr << "CreateCircleDialog::createCircle() Personal Circle";
std::cerr << std::endl;
#endif
rsGxsCircles->createGroup(token, circle);
// set personal distribution
circle.mMeta.mCircleId.clear() ;
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_LOCAL;
}
bool is_existing_circle(mIsExistingCircle);
RsThread::async([circle,is_existing_circle]()
{
RsGxsCircleId circleId;
if(is_existing_circle)
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::updateCircle() : mCircleType: " << circle.mMeta.mCircleType << std::endl;
std::cerr << "CreateCircleDialog::updateCircle() : mCircleId: " << circle.mMeta.mCircleId << std::endl;
std::cerr << "CreateCircleDialog::updateCircle() : mGroupId: " << circle.mMeta.mGroupId << std::endl;
std::cerr << "CreateCircleDialog::updateCircle() Checks and Balances Okay - calling service proper.."<< std::endl;
#endif
rsGxsCircles->editCircle(*const_cast<RsGxsCircleGroup*>(&circle)); // const_cast: Not nice, but simpler.
circleId = RsGxsCircleId(circle.mMeta.mGroupId);
}
else
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::createCircle() : mCircleType: " << circle.mMeta.mCircleType << std::endl;
std::cerr << "CreateCircleDialog::createCircle() : mCircleId: " << circle.mMeta.mCircleId << std::endl;
std::cerr << "CreateCircleDialog::createCircle() Checks and Balances Okay - calling service proper.."<< std::endl;
#endif
rsGxsCircles->createCircle(circle.mMeta.mGroupName,
static_cast<RsGxsCircleType>(circle.mMeta.mCircleType),
circleId,
circle.mMeta.mCircleId,
circle.mMeta.mAuthorId,
circle.mInvitedMembers,
circle.mLocalFriends);
}
if(!is_existing_circle)
RsQThreadUtils::postToObject( [circle,circleId]()
{
QMessageBox::information(nullptr,tr("Circle created"),
tr("Your new circle has been created:\n Name: %1\n Id: %2.")
.arg(QString::fromUtf8(circle.mMeta.mGroupName.c_str()))
.arg(QString::fromStdString(circleId.toStdString())));
});
});
close();
}
@ -806,11 +832,6 @@ void CreateCircleDialog::loadIdentities()
void CreateCircleDialog::fillIdentitiesList(const std::vector<RsGxsIdGroup>& id_groups)
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::loadIdentities(" << token << ")";
std::cerr << std::endl;
#endif
QTreeWidget *tree = ui.treeWidget_IdList;
tree->clear();
@ -826,7 +847,6 @@ void CreateCircleDialog::fillIdentitiesList(const std::vector<RsGxsIdGroup>& id_
bool isSignedByFriendNode = isSigned && rsPeers->isPgpFriend(idGroup.mPgpId);
/* do filtering */
bool ok = false;
if(!(acceptAll ||(acceptOnlySignedIdentities && isSigned) ||(acceptOnlyIdentitiesSignedByFriend && isSignedByFriendNode)))
{
@ -934,3 +954,8 @@ void CreateCircleDialog::MembershipListCustomPopupMenu( QPoint )
contextMnu.exec(QCursor::pos());
}
void CreateCircleDialog::updateMembership()
{
ui.members_groupBox->setTitle( tr("Invited Members") + " (" + QString::number(ui.treeWidget_membership->topLevelItemCount()) + ")" );
}

View file

@ -56,6 +56,7 @@ private slots:
void filterChanged(const QString &text);
void createNewGxsId();
void idTypeChanged();
void updateMembership();
/** Create the context popup menu and it's submenus */
void IdListCustomPopupMenu( QPoint point );

View file

@ -13,7 +13,7 @@
<property name="windowTitle">
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="CreateCircleDialog_GL">
<property name="leftMargin">
<number>0</number>
</property>
@ -40,226 +40,225 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QFrame" name="frame">
<widget class="QFrame" name="gradFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="gradFrame_GL">
<item row="0" column="0" colspan="3">
<widget class="QGroupBox" name="groupBox_title">
<property name="title">
<string>Circle Details</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<layout class="QGridLayout" name="groupBox_title_GL">
<property name="leftMargin">
<number>11</number>
</property>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="circleName">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The circle name, contact author and invited member list will be visible to all invited members. If the circle is not private, it will also be visible to neighbor nodes of the nodes who host the invited members.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="idChooserLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Contact author:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="GxsIdChooser" name="idChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The creator of a circle is purely optional. It is however useful for public circles so that people know with whom to discuss membership aspects.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="GxsIdLabel" name="circleAdminLabel">
<property name="text">
<string>[Circle Admin]</string>
</property>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Distribution:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QFrame" name="frame_Distribution">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>2</number>
<widget class="QLabel" name="circleNameLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Name:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="circleName">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The circle name, contact author and invited member list will be visible to all invited members. If the circle is not private, it will also be visible to neighbor nodes of the nodes who host the invited members.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="idChooserLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Contact author:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="GxsIdChooser" name="idChooser">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The creator of a circle is purely optional. It is however useful for public circles so that people know with whom to discuss membership aspects.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="GxsIdLabel" name="circleAdminLabel">
<property name="text">
<string>[Circle Admin]</string>
</property>
</widget>
</item>
<item row="1" column="3">
<spacer name="idChooserHSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="distributionLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Distribution:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QFrame" name="frame_Distribution">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="frame_Distribution_GL">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="radioButton_Public">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Publicly distributed circles are visible to your friends, which will get to know the circle data (Creator, members, etc)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="topMargin">
<number>2</number>
<property name="text">
<string>Public</string>
</property>
<property name="rightMargin">
<number>2</number>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/network-puplic.png</normaloff>:/icons/png/network-puplic.png</iconset>
</property>
<property name="bottomMargin">
<number>2</number>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="radioButton_Public">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Publicly distributed circles are visible to your friends, which will get to know the circle data (Creator, members, etc)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Public</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/network-puplic.png</normaloff>:/icons/png/network-puplic.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="radioButton_Self">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Private (a.k.a. self-restricted) circles are only visible to the invited members of these circles. In practice the circle uses its own list of invited members to limit its own distribution. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Private</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/person.png</normaloff>:/icons/png/person.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="radioButton_Restricted">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Circles can be restricted to the members of another circle. Only the members of that second circle will be allowed to see the new circle and its content (list of members, etc).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Only &amp;visible to members of:</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/circles.png</normaloff>:/icons/png/circles.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="GxsCircleChooser" name="circleComboBox"/>
</item>
<item row="0" column="4">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>147</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="radioButton_Self">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Private (a.k.a. self-restricted) circles are only visible to the invited members of these circles. In practice the circle uses its own list of invited members to limit its own distribution. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Private</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/person.png</normaloff>:/icons/png/person.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="radioButton_Restricted">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Circles can be restricted to the members of another circle. Only the members of that second circle will be allowed to see the new circle and its content (list of members, etc).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Only &amp;visible to members of:</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/circles.png</normaloff>:/icons/png/circles.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="GxsCircleChooser" name="circleComboBox"/>
</item>
<item row="0" column="4">
<spacer name="frame_Distribution_HSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>147</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="id_HL">
<item>
<widget class="QGroupBox" name="members_groupBox">
<property name="title">
<string>Invited Members</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="members_groupBox_VL">
<item>
<widget class="QTreeWidget" name="treeWidget_membership">
<property name="contextMenuPolicy">
@ -287,9 +286,9 @@
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QVBoxLayout" name="add_rem_VL">
<item>
<spacer name="verticalSpacer">
<spacer name="add_rem_Top_VSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@ -330,7 +329,7 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<spacer name="add_rem_Bot_VSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@ -378,7 +377,7 @@
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<layout class="QHBoxLayout" name="frame_PgpTypes_HL">
<property name="spacing">
<number>0</number>
</property>
@ -419,9 +418,9 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<layout class="QHBoxLayout" name="IdFilter_HL">
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="IdFilter_Label">
<property name="text">
<string>Filter</string>
</property>
@ -438,7 +437,7 @@
</layout>
</item>
<item row="2" column="0">
<spacer name="horizontalSpacer_3">
<spacer name="bottumHSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -451,7 +450,14 @@
</spacer>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="createButton">
<widget class="QPushButton" name="postButton">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Create</string>
</property>
@ -503,7 +509,6 @@
</customwidget>
</customwidgets>
<resources>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources>
<connections/>

View file

@ -32,6 +32,8 @@
Q_DECLARE_METATYPE(FileProgressInfo)
#define MAX_CHAR_TMP 128
DLListDelegate::DLListDelegate(QObject *parent) : QAbstractItemDelegate(parent)
{
}
@ -180,7 +182,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
}
painter->drawText(option.rect, Qt::AlignCenter, newopt.text);
break;
case COLUMN_SOURCES:
case COLUMN_SOURCES:
{
double dblValue = index.data().toDouble();
@ -188,7 +190,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
painter->drawText(option.rect, Qt::AlignCenter, temp);
}
break;
case COLUMN_PRIORITY:
case COLUMN_PRIORITY:
{
double dblValue = index.data().toDouble();
if (dblValue == PRIORITY_NULL)
@ -205,7 +207,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
painter->drawText(option.rect, Qt::AlignCenter, temp);
}
break;
case COLUMN_DOWNLOADTIME:
case COLUMN_DOWNLOADTIME:
downloadtime = index.data().toLongLong();
minutes = downloadtime / 60;
seconds = downloadtime % 60;
@ -225,7 +227,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
temp = "" ;
painter->drawText(option.rect, Qt::AlignCenter, temp);
break;
case COLUMN_NAME:
case COLUMN_NAME:
{
// decoration
int pixOffset = 0;

View file

@ -18,55 +18,40 @@
* *
*******************************************************************************/
#ifndef DLLISTDELEGATE_H
#define DLLISTDELEGATE_H
#pragma once
#include <QAbstractItemDelegate>
#include "xprogressbar.h"
// Defines for download list list columns
#define COLUMN_NAME 0
#define COLUMN_SIZE 1
#define COLUMN_COMPLETED 2
#define COLUMN_DLSPEED 3
#define COLUMN_PROGRESS 4
#define COLUMN_SOURCES 5
#define COLUMN_STATUS 6
#define COLUMN_PRIORITY 7
#define COLUMN_REMAINING 8
#define COLUMN_DOWNLOADTIME 9
#define COLUMN_ID 10
#define COLUMN_LASTDL 11
#define COLUMN_PATH 12
#define COLUMN_COUNT 13
#define PRIORITY_NULL 0.0
#define PRIORITY_FASTER 0.1
#define PRIORITY_AVERAGE 0.2
#define PRIORITY_SLOWER 0.3
#define MAX_CHAR_TMP 128
class QModelIndex;
class QPainter;
class DLListDelegate: public QAbstractItemDelegate
{
public:
DLListDelegate(QObject *parent=0);
virtual ~DLListDelegate(){}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const;
class DLListDelegate: public QAbstractItemDelegate {
static constexpr int COLUMN_NAME = 0;
static constexpr int COLUMN_SIZE = 1;
static constexpr int COLUMN_COMPLETED = 2;
static constexpr int COLUMN_DLSPEED = 3;
static constexpr int COLUMN_PROGRESS = 4;
static constexpr int COLUMN_SOURCES = 5;
static constexpr int COLUMN_STATUS = 6;
static constexpr int COLUMN_PRIORITY = 7;
static constexpr int COLUMN_REMAINING = 8;
static constexpr int COLUMN_DOWNLOADTIME= 9;
static constexpr int COLUMN_ID = 10;
static constexpr int COLUMN_LASTDL = 11;
static constexpr int COLUMN_PATH = 12;
static constexpr int COLUMN_COUNT = 13;
Q_OBJECT
public:
DLListDelegate(QObject *parent=0);
virtual ~DLListDelegate(){}
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const;
private:
public slots:
signals:
static constexpr float PRIORITY_NULL = 0.0;
static constexpr float PRIORITY_FASTER = 0.1;
static constexpr float PRIORITY_AVERAGE = 0.2;
static constexpr float PRIORITY_SLOWER = 0.3;
};
#endif

View file

@ -37,6 +37,7 @@
#include "gui/advsearch/advancedsearchdialog.h"
#include "gui/common/RSTreeWidgetItem.h"
#include "util/QtVersion.h"
#include "util/qtthreadsutils.h"
#include <retroshare/rsfiles.h>
#include <retroshare/rsturtle.h>
@ -85,6 +86,18 @@ const int SearchDialog::FILETYPE_IDX_DIRECTORY = 8;
QMap<int, QString> * SearchDialog::FileTypeExtensionMap = new QMap<int, QString>();
bool SearchDialog::initialised = false;
struct SearchDialog::FileDetail
{
public:
RsPeerId id;
std::string name;
RsFileHash hash;
std::string path;
uint64_t size;
uint32_t mtime;
uint32_t rank;
};
/** Constructor */
SearchDialog::SearchDialog(QWidget *parent)
: MainPage(parent),
@ -121,10 +134,10 @@ SearchDialog::SearchDialog(QWidget *parent)
connect( ui.searchSummaryWidget, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( searchSummaryWidgetCustomPopupMenu( QPoint ) ) );
connect( ui.showBannedFiles_TB, SIGNAL( clicked() ), this, SLOT( openBannedFiles() ) );
connect( ui.lineEdit, SIGNAL( returnPressed ( void ) ), this, SLOT( searchKeywords( void ) ) );
connect( ui.lineEdit, SIGNAL( returnPressed () ), this, SLOT( searchKeywords() ) );
connect( ui.lineEdit, SIGNAL( textChanged ( const QString& ) ), this, SLOT( checkText( const QString& ) ) );
connect( ui.pushButtonSearch, SIGNAL( released ( void ) ), this, SLOT( searchKeywords( void ) ) );
connect( ui.pushButtonDownload, SIGNAL( released ( void ) ), this, SLOT( download( void ) ) );
connect( ui.searchButton, SIGNAL( released () ), this, SLOT( searchKeywords() ) );
connect( ui.pushButtonDownload, SIGNAL( released () ), this, SLOT( download() ) );
connect( ui.cloaseallsearchresultsButton, SIGNAL(clicked()), this, SLOT(searchRemoveAll()));
connect( ui.searchResultWidget, SIGNAL( itemDoubleClicked ( QTreeWidgetItem *, int)), this, SLOT(download()));
@ -186,13 +199,12 @@ SearchDialog::SearchDialog(QWidget *parent)
_smheader->resizeSection ( SR_AGE_COL, 90*f );
_smheader->resizeSection ( SR_HASH_COL, 240*f );
// set header text aligment
QTreeWidgetItem * headerItem = ui.searchResultWidget->headerItem();
headerItem->setTextAlignment(SR_NAME_COL, Qt::AlignRight | Qt::AlignRight);
headerItem->setTextAlignment(SR_SIZE_COL, Qt::AlignRight | Qt::AlignRight);
ui.searchResultWidget->sortItems(SR_NAME_COL, Qt::AscendingOrder);
QFontMetricsF fontMetrics(ui.searchResultWidget->font());
int iconHeight = fontMetrics.height() * 1.4;
ui.searchResultWidget->setIconSize(QSize(iconHeight, iconHeight));
/* Set initial size the splitter */
QList<int> sizes;
sizes << 250 << width(); // Qt calculates the right sizes
@ -218,6 +230,15 @@ SearchDialog::SearchDialog(QWidget *parent)
checkText(ui.lineEdit->text());
// add an event handler to get search results (previously available through notifyQt)
mEventHandlerId = 0;
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
{
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
}, mEventHandlerId, RsEventType::FILE_TRANSFER );
}
SearchDialog::~SearchDialog()
@ -236,6 +257,31 @@ SearchDialog::~SearchDialog()
ui.searchResultWidget->setItemDelegateForColumn(SR_SIZE_COL, nullptr);
ui.searchResultWidget->setItemDelegateForColumn(SR_AGE_COL, nullptr);
rsEvents->unregisterEventsHandler(mEventHandlerId);
}
void SearchDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
{
if(event->mType != RsEventType::FILE_TRANSFER)
return;
auto fe = dynamic_cast<const RsFileTransferEvent*>(event.get());
if(!fe || fe->mFileTransferEventCode!=RsFileTransferEventCode::NEW_DISTANT_SEARCH_RESULTS)
return;
for(uint32_t i=0;i<fe->mResults.size();++i)
{
FileDetail f;
f.hash = fe->mResults[i].fHash;
f.name = fe->mResults[i].fName;
f.size = fe->mResults[i].fSize;
f.mtime = 0; // zero what's not available, otherwise we'll get some random values displayed.
f.rank = 0;
updateFiles(fe->mRequestId,f);
}
}
void SearchDialog::processSettings(bool bLoad)
@ -276,30 +322,9 @@ void SearchDialog::processSettings(bool bLoad)
void SearchDialog::checkText(const QString& txt)
{
bool valid;
QColor color;
if(txt.length() < 3)
{
std::cout << "setting palette 1" << std::endl ;
valid = false;
color = QApplication::palette().color(QPalette::Disabled, QPalette::Base);
}
else
{
std::cout << "setting palette 2" << std::endl ;
valid = true;
color = QApplication::palette().color(QPalette::Active, QPalette::Base);
}
/* unpolish widget to clear the stylesheet's palette cache */
ui.searchButton->setDisabled(txt.length() < 3);
ui.searchLineFrame->setProperty("valid", (txt.length() >= 3));
ui.searchLineFrame->style()->unpolish(ui.searchLineFrame);
QPalette palette = ui.lineEdit->palette();
palette.setColor(ui.lineEdit->backgroundRole(), color);
ui.lineEdit->setPalette(palette);
ui.searchLineFrame->setProperty("valid", valid);
Rshare::refreshStyleSheet(ui.searchLineFrame, false);
}
@ -308,7 +333,7 @@ void SearchDialog::initialiseFileTypeMappings()
/* edit these strings to change the range of extensions recognised by the search */
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_ANY, "");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_AUDIO,
"aac aif flac iff m3u m4a mid midi mp3 mpa ogg ra ram wav wma");
"aac aif flac iff m3u m4a mid midi mp3 mpa ogg ra ram wav wma weba");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_ARCHIVE,
"7z bz2 gz pkg rar sea sit sitx tar zip tgz");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_CDIMAGE,
@ -317,11 +342,11 @@ void SearchDialog::initialiseFileTypeMappings()
"doc odt ott rtf pdf ps txt log msg wpd wps ods xls epub" );
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_PICTURE,
"3dm 3dmf ai bmp drw dxf eps gif ico indd jpe jpeg jpg mng pcx pcc pct pgm "
"pix png psd psp qxd qxprgb sgi svg tga tif tiff xbm xcf");
"pix png psd psp qxd qxprgb sgi svg tga tif tiff xbm xcf webp");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_PROGRAM,
"app bat cgi com bin exe js pif py pl sh vb ws bash");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_VIDEO,
"3gp asf asx avi mov mp4 mkv flv mpeg mpg qt rm swf vob wmv");
"3gp asf asx avi mov mp4 mkv flv mpeg mpg qt rm swf vob wmv webm");
SearchDialog::initialised = true;
}
@ -393,11 +418,10 @@ void SearchDialog::download()
/* should also be able to handle multi-selection */
QList<QTreeWidgetItem*> itemsForDownload = ui.searchResultWidget->selectedItems() ;
int numdls = itemsForDownload.size() ;
QTreeWidgetItem * item ;
bool attemptDownloadLocal = false ;
for (int i = 0; i < numdls; ++i) {
item = itemsForDownload.at(i) ;
QTreeWidgetItem *item = itemsForDownload.at(i) ;
// call the download
// *
if (item->text(SR_HASH_COL).isEmpty()) { // we have a folder
@ -421,8 +445,8 @@ void SearchDialog::download()
std::cout << *it << "-" << std::endl;
QColor foreground = textColorDownloading();
for (int i = 0; i < item->columnCount(); ++i)
item->setData(i, Qt::ForegroundRole, foreground );
for (int j = 0; j < item->columnCount(); ++j)
item->setData(j, Qt::ForegroundRole, foreground );
}
}
}
@ -828,28 +852,29 @@ void SearchDialog::advancedSearch(RsRegularExpression::Expression* expression)
{
advSearchDialog->hide();
/* call to core */
std::list<DirDetails> results;
// send a turtle search request
RsRegularExpression::LinearizedExpression e ;
expression->linearize(e) ;
TurtleRequestId req_id = rsFiles->turtleSearch(e) ;
TurtleRequestId req_id ;
// This will act before turtle results come to the interface, thanks to the signals scheduling policy.
initSearchResult(QString::fromStdString(e.GetStrings()),req_id, ui.FileTypeComboBox->currentIndex(), true) ;
if(ui._anonF2Fsearch_CB->isChecked())
req_id = rsFiles->turtleSearch(e) ;
else
req_id = RSRandom::random_u32() ; // generate a random 32 bits request id
rsFiles -> SearchBoolExp(expression, results, RS_FILE_HINTS_REMOTE);// | DIR_FLAGS_NETWORK_WIDE | DIR_FLAGS_BROWSABLE);
initSearchResult(QString::fromStdString(e.GetStrings()),req_id, ui.FileTypeComboBox->currentIndex(), true) ;
std::list<DirDetails> results;
FileSearchFlags flags(0);
if(ui._ownFiles_CB->isChecked()) flags |= RS_FILE_HINTS_LOCAL;
if(ui._friendListsearch_SB->isChecked()) flags |= RS_FILE_HINTS_REMOTE;
rsFiles -> SearchBoolExp(expression, results, flags);
/* abstraction to allow reusee of tree rendering code */
resultsToTree(advSearchDialog->getSearchAsString(),req_id, results);
// // debug stuff
// Expression *expression2 = LinearizedExpression::toExpr(e) ;
// results.clear() ;
// rsFiles -> SearchBoolExp(expression2, results, DIR_FLAGS_REMOTE | DIR_FLAGS_NETWORK_WIDE | DIR_FLAGS_BROWSABLE);
// resultsToTree((advSearchDialog->getSearchAsString()).toStdString(),req_id+1, results);
resultsToTree(advSearchDialog->getSearchAsString(),req_id, results);
}
void SearchDialog::searchKeywords()
@ -955,7 +980,7 @@ void SearchDialog::searchKeywords(const QString& keywords)
}
}
void SearchDialog::updateFiles(qulonglong search_id,FileDetail file)
void SearchDialog::updateFiles(qulonglong search_id,const FileDetail& file)
{
searchResultsQueue.push_back(std::pair<qulonglong,FileDetail>(search_id,file)) ;
@ -983,7 +1008,7 @@ void SearchDialog::processResultQueue()
while(!searchResultsQueue.empty() && nb_treated_elements++ < 250)
{
qulonglong search_id = searchResultsQueue.back().first ;
FileDetail& file = searchResultsQueue.back().second ;
const FileDetail& file = searchResultsQueue.back().second ;
#ifdef DEBUG
std::cout << "Updating file detail:" << std::endl ;
@ -1209,17 +1234,16 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
//
bool found = false ;
bool altname = false ;
int sources;
int friendSource = 0;
int anonymousSource = 0;
QString modifiedResult;
QList<QTreeWidgetItem*> itms = ui.searchResultWidget->findItems(QString::fromStdString(file.hash.toStdString()),Qt::MatchExactly,SR_HASH_COL) ;
QList<QTreeWidgetItem*> itms = ui.searchResultWidget->findItems(QString::fromStdString(file.hash.toStdString()),Qt::MatchExactly,SR_HASH_COL) ;
for(QList<QTreeWidgetItem*>::const_iterator it(itms.begin());it!=itms.end();++it)
if((*it)->text(SR_SEARCH_ID_COL) == sid_hexa)
for(auto &it : itms)
if(it->text(SR_SEARCH_ID_COL) == sid_hexa)
{
QString resultCount = (*it)->text(SR_SOURCES_COL);
int friendSource = 0;
int anonymousSource = 0;
QString resultCount = it->text(SR_SOURCES_COL);
QStringList modifiedResultCount = resultCount.split("/", QString::SkipEmptyParts);
if(searchType == FRIEND_SEARCH)
{
@ -1233,13 +1257,13 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
}
modifiedResult = QString::number(friendSource) + "/" + QString::number(anonymousSource);
float fltRes = friendSource + (float)anonymousSource/1000;
(*it)->setText(SR_SOURCES_COL,modifiedResult);
(*it)->setData(SR_SOURCES_COL, ROLE_SORT, fltRes);
QTreeWidgetItem *item = (*it);
it->setText(SR_SOURCES_COL,modifiedResult);
it->setData(SR_SOURCES_COL, ROLE_SORT, fltRes);
QTreeWidgetItem *item = it;
found = true ;
if(QString::compare((*it)->text(SR_NAME_COL), QString::fromUtf8(file.name.c_str()), Qt::CaseSensitive)!=0)
if(QString::compare(it->text(SR_NAME_COL), QString::fromUtf8(file.name.c_str()), Qt::CaseSensitive)!=0)
altname = true;
if (!item->data(SR_DATA_COL, SR_ROLE_LOCAL).toBool()) {
@ -1280,19 +1304,18 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
}
}
if(altname)
{
QTreeWidgetItem *item = new RSTreeWidgetItem(compareResultRole);
item->setText(SR_NAME_COL, QString::fromUtf8(file.name.c_str()));
item->setText(SR_HASH_COL, QString::fromStdString(file.hash.toStdString()));
setIconAndType(item, QString::fromUtf8(file.name.c_str()));
item->setText(SR_SIZE_COL, QString::number(file.size));
setIconAndType(item, QString::fromUtf8(file.name.c_str()));
(*it)->addChild(item);
if(altname)
{
QTreeWidgetItem *altItem = new RSTreeWidgetItem(compareResultRole);
altItem->setText(SR_NAME_COL, QString::fromUtf8(file.name.c_str()));
altItem->setText(SR_HASH_COL, QString::fromStdString(file.hash.toStdString()));
setIconAndType(altItem, QString::fromUtf8(file.name.c_str()));
altItem->setText(SR_SIZE_COL, QString::number(file.size));
setIconAndType(altItem, QString::fromUtf8(file.name.c_str()));
it->addChild(altItem);
}
}
}
if(!found)
{
++nb_results[searchId] ;
@ -1301,7 +1324,7 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
QTreeWidgetItem *item = new RSTreeWidgetItem(compareResultRole);
item->setText(SR_NAME_COL, QString::fromUtf8(file.name.c_str()));
item->setText(SR_HASH_COL, QString::fromStdString(file.hash.toStdString()));
item->setText(SR_HASH_COL, QString::fromStdString(file.hash.toStdString()));
setIconAndType(item, QString::fromUtf8(file.name.c_str()));
@ -1311,9 +1334,11 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
item->setText(SR_SIZE_COL, QString::number(file.size));
item->setData(SR_SIZE_COL, ROLE_SORT, (qulonglong) file.size);
item->setText(SR_AGE_COL, QString::number(file.age));
item->setData(SR_AGE_COL, ROLE_SORT, file.age);
item->setText(SR_AGE_COL, QString::number(file.mtime));
item->setData(SR_AGE_COL, ROLE_SORT, file.mtime);
item->setTextAlignment( SR_SIZE_COL, Qt::AlignRight );
int friendSource = 0;
int anonymousSource = 0;
if(searchType == FRIEND_SEARCH)
{
friendSource = 1;
@ -1344,7 +1369,7 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
} else {
item->setData(SR_DATA_COL, SR_ROLE_LOCAL, false);
sources = item->text(SR_SOURCES_COL).toInt();
int sources = item->text(SR_SOURCES_COL).toInt();
if (sources == 1)
{
foreground = ui.searchResultWidget->palette().color(QPalette::Text);
@ -1369,11 +1394,8 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
/* hide/show this search result */
hideOrShowSearchResult(item);
}
/* update the summary as well */
if(!found) // only increment result when it's a new item.
{
// only increment result when it's a new item.
int s = ui.searchSummaryWidget->topLevelItem(summaryItemIndex)->text(SS_RESULTS_COL).toInt() ;
ui.searchSummaryWidget->topLevelItem(summaryItemIndex)->setText(SS_RESULTS_COL, QString::number(s+1));
ui.searchSummaryWidget->topLevelItem(summaryItemIndex)->setData(SS_RESULTS_COL, ROLE_SORT, s+1);
@ -1388,21 +1410,21 @@ void SearchDialog::resultsToTree(const QString& txt,qulonglong searchId, const s
std::list<DirDetails>::const_iterator it;
for(it = results.begin(); it != results.end(); ++it)
if (it->type == DIR_TYPE_FILE) {
if (it->type == DIR_TYPE_FILE)
{
FileDetail fd;
fd.id = it->id;
fd.name = it->name;
fd.hash = it->hash;
fd.path = it->path;
fd.size = it->size;
fd.age = it->mtime;
fd.mtime= it->mtime;
fd.rank = 0;
insertFile(searchId,fd, FRIEND_SEARCH);
} else if (it->type == DIR_TYPE_DIR) {
// insertDirectory(txt, searchId, *it, NULL);
}
else if (it->type == DIR_TYPE_DIR)
insertDirectory(txt, searchId, *it);
}
ui.searchResultWidget->setSortingEnabled(true);
}
@ -1481,33 +1503,38 @@ void SearchDialog::copyResultLink()
{
/* should also be able to handle multi-selection */
QList<QTreeWidgetItem*> itemsForCopy = ui.searchResultWidget->selectedItems();
int numdls = itemsForCopy.size();
QTreeWidgetItem * item;
QList<RetroShareLink> urls ;
std::set<RsFileHash> already_seen_hashes;
QList<RetroShareLink> urls ;
for (int i = 0; i < numdls; ++i)
{
item = itemsForCopy.at(i);
// call copy
for (auto item:itemsForCopy)
{
// call copy
if (!item->childCount())
{
std::cerr << "SearchDialog::copyResultLink() Calling set retroshare link";
std::cerr << std::endl;
QString fhash = item->text(SR_HASH_COL);
RsFileHash hash(fhash.toStdString());
QString fhash = item->text(SR_HASH_COL);
qulonglong fsize = item->text(SR_SIZE_COL).toULongLong();
QString fname = item->text(SR_NAME_COL);
if(!hash.isNull() && (already_seen_hashes.end() == already_seen_hashes.find(hash)))
{
std::cerr << "SearchDialog::copyResultLink() Calling set retroshare link";
std::cerr << std::endl;
RetroShareLink link = RetroShareLink::createFile(fname, fsize, fhash);
if (link.valid()) {
std::cerr << "new link added to clipboard: " << link.toString().toStdString() << std::endl ;
urls.push_back(link) ;
}
}
}
RSLinkClipboard::copyLinks(urls) ;
qulonglong fsize = item->text(SR_SIZE_COL).toULongLong();
QString fname = item->text(SR_NAME_COL);
RetroShareLink link = RetroShareLink::createFile(fname, fsize, fhash);
if (link.valid())
{
std::cerr << "new link added to clipboard: " << link.toString().toStdString() << std::endl ;
urls.push_back(link);
already_seen_hashes.insert(hash);
}
}
}
if(!urls.empty())
RSLinkClipboard::copyLinks(urls) ;
}
void SearchDialog::sendLinkTo( )

View file

@ -21,9 +21,10 @@
#ifndef _SEARCHDIALOG_H
#define _SEARCHDIALOG_H
#include <retroshare/rstypes.h>
#include "retroshare/rstypes.h"
#include "retroshare/rsevents.h"
#include "ui_SearchDialog.h"
#include <retroshare-gui/mainpage.h>
#include "retroshare-gui/mainpage.h"
class AdvancedSearchDialog;
class RSTreeWidgetItemCompareRole;
@ -42,6 +43,7 @@ class SearchDialog : public MainPage
Q_PROPERTY(QColor textColorLowSources READ textColorLowSources WRITE setTextColorLowSources)
Q_PROPERTY(QColor textColorHighSources READ textColorHighSources WRITE setTextColorHighSources)
struct FileDetail; // useful structure to store search results.
public:
/** Default Constructor */
SearchDialog(QWidget *parent = 0);
@ -62,8 +64,7 @@ public:
void setTextColorLowSources(QColor color) { mTextColorLowSources = color; }
void setTextColorHighSources(QColor color) { mTextColorHighSources = color; }
public slots:
void updateFiles(qulonglong request_id,FileDetail file) ;
void updateFiles(qulonglong request_id, const FileDetail& file) ;
private slots:
@ -125,6 +126,7 @@ private:
void setIconAndType(QTreeWidgetItem *item, const QString& filename);
void downloadDirectory(const QTreeWidgetItem *item, const QString &base);
void getSourceFriendsForHash(const RsFileHash &hash,std::list<RsPeerId> &srcIds);
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
/** the advanced search dialog instance */
AdvancedSearchDialog * advSearchDialog;
@ -159,7 +161,7 @@ private:
QAbstractItemDelegate *mAgeColumnDelegate;
QAbstractItemDelegate *mSizeColumnDelegate;
/* Color definitions (for standard see qss.default) */
/* Color definitions (for standard see default.qss) */
QColor mTextColorLocal;
QColor mTextColorDownloading;
QColor mTextColorNoSources;
@ -176,6 +178,8 @@ private:
bool _queueIsAlreadyTakenCareOf ;
std::vector<std::pair<qulonglong,FileDetail> > searchResultsQueue ;
RsEventsHandlerId_t mEventHandlerId ;
};
#endif

View file

@ -16,7 +16,7 @@
<verstretch>1</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="SearchDialog_VL">
<item>
<widget class="QFrame" name="titleBarFrame">
<property name="sizePolicy">
@ -31,7 +31,7 @@
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<layout class="QHBoxLayout" name="titleBarFrame_HL">
<property name="leftMargin">
<number>3</number>
</property>
@ -45,43 +45,28 @@
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="searchButton_HL">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QFrame" name="searchLineFrame">
<property name="minimumSize">
<size>
<width>0</width>
<height>22</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>22</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="searchLineFrame_GL">
<property name="leftMargin">
<number>0</number>
<number>2</number>
</property>
<property name="topMargin">
<number>0</number>
<number>2</number>
</property>
<property name="rightMargin">
<number>0</number>
<number>2</number>
</property>
<property name="bottomMargin">
<number>0</number>
<number>2</number>
</property>
<property name="horizontalSpacing">
<property name="spacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>1</number>
</property>
<item row="0" column="0">
<widget class="LineEditClear" name="lineEdit">
<property name="toolTip">
@ -93,7 +78,14 @@
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonSearch">
<widget class="QPushButton" name="searchButton">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
@ -223,6 +215,9 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>KeyWords</string>
@ -261,6 +256,9 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>false</bool>
</property>
<column>
<property name="text">
<string>Filename</string>
@ -336,14 +334,14 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="FileTypeComboBox">
<widget class="RSComboBox" name="FileTypeComboBox">
<item>
<property name="text">
<string>Any</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeAny.png</normaloff>:/images/FileTypeAny.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/any.svg</normaloff>:/icons/filetype/any.svg</iconset>
</property>
</item>
<item>
@ -351,8 +349,8 @@
<string>Archive</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeArchive.png</normaloff>:/images/FileTypeArchive.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/archive.svg</normaloff>:/icons/filetype/archive.svg</iconset>
</property>
</item>
<item>
@ -360,8 +358,8 @@
<string>Audio</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeAudio.png</normaloff>:/images/FileTypeAudio.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/audio.svg</normaloff>:/icons/filetype/audio.svg</iconset>
</property>
</item>
<item>
@ -369,8 +367,8 @@
<string>CD-Image</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeCDImage.png</normaloff>:/images/FileTypeCDImage.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/img.svg</normaloff>:/icons/filetype/img.svg</iconset>
</property>
</item>
<item>
@ -378,8 +376,8 @@
<string>Document</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeDocument.png</normaloff>:/images/FileTypeDocument.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/document.svg</normaloff>:/icons/filetype/document.svg</iconset>
</property>
</item>
<item>
@ -387,8 +385,8 @@
<string>Picture</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypePicture.png</normaloff>:/images/FileTypePicture.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/picture.svg</normaloff>:/icons/filetype/picture.svg</iconset>
</property>
</item>
<item>
@ -396,8 +394,8 @@
<string>Program</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeProgram.png</normaloff>:/images/FileTypeProgram.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/program.svg</normaloff>:/icons/filetype/program.svg</iconset>
</property>
</item>
<item>
@ -405,8 +403,8 @@
<string>Video</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeVideo.png</normaloff>:/images/FileTypeVideo.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/video.svg</normaloff>:/icons/filetype/video.svg</iconset>
</property>
</item>
<item>
@ -414,8 +412,8 @@
<string>Directory</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/foldermail.png</normaloff>:/images/foldermail.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/folder.png</normaloff>:/icons/folder.png</iconset>
</property>
</item>
</widget>
@ -460,6 +458,11 @@
<extends>QLineEdit</extends>
<header location="global">gui/common/LineEditClear.h</header>
</customwidget>
<customwidget>
<class>RSComboBox</class>
<extends>QComboBox</extends>
<header>gui/common/RSComboBox.h</header>
</customwidget>
<customwidget>
<class>SearchTreeWidget</class>
<extends>QTreeWidget</extends>
@ -467,8 +470,8 @@
</customwidget>
</customwidgets>
<resources>
<include location="../icons.qrc"/>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -55,6 +55,16 @@
#include <set>
#define SHARED_FILES_DIALOG_COLUMN_NAME 0
#define SHARED_FILES_DIALOG_COLUMN_FILENB 1
#define SHARED_FILES_DIALOG_COLUMN_SIZE 2
#define SHARED_FILES_DIALOG_COLUMN_AGE 3
#define SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS 4
#define SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR 5
#define SHARED_FILES_DIALOG_COLUMN_COUNT 6
#define SHARED_FILES_DIALOG_FILTER_STRING "filtered"
/* Images for context menu icons */
#define IMAGE_DOWNLOAD ":/icons/png/download.png"
#define IMAGE_PLAY ":/images/start.png"
@ -75,7 +85,7 @@
#define IMAGE_COLLOPEN ":/icons/collections.png"
#define IMAGE_EDITSHARE ":/icons/png/pencil-edit-button.png"
#define IMAGE_MYFILES ":/icons/svg/folders1.svg"
#define IMAGE_UNSHAREEXTRA ":/images/button_cancel.png"
#define IMAGE_UNSHAREEXTRA ":/icons/cancel.svg"
/*define viewType_CB value */
#define VIEW_TYPE_TREE 0
@ -91,7 +101,7 @@
//#define DEBUG_SHARED_FILES_DIALOG 1
const QString Image_AddNewAssotiationForFile = ":/images/kcmsystem24.png";
const QString Image_AddNewAssotiationForFile = ":/icons/svg/options.svg";
class SFDSortFilterProxyModel : public QSortFilterProxyModel
{
@ -187,17 +197,17 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent)
tree_proxyModel->setSourceModel(tree_model);
tree_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
tree_proxyModel->setSortRole(RetroshareDirModel::SortRole);
tree_proxyModel->sort(COLUMN_NAME);
tree_proxyModel->sort(SHARED_FILES_DIALOG_COLUMN_NAME);
tree_proxyModel->setFilterRole(RetroshareDirModel::FilterRole);
tree_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ;
tree_proxyModel->setFilterRegExp(QRegExp(QString(SHARED_FILES_DIALOG_FILTER_STRING))) ;
flat_proxyModel = new SFDSortFilterProxyModel(flat_model, this);
flat_proxyModel->setSourceModel(flat_model);
flat_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
flat_proxyModel->setSortRole(RetroshareDirModel::SortRole);
flat_proxyModel->sort(COLUMN_NAME);
flat_proxyModel->sort(SHARED_FILES_DIALOG_COLUMN_NAME);
flat_proxyModel->setFilterRole(RetroshareDirModel::FilterRole);
flat_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ;
flat_proxyModel->setFilterRegExp(QRegExp(QString(SHARED_FILES_DIALOG_FILTER_STRING))) ;
connect(ui.filterClearButton, SIGNAL(clicked()), this, SLOT(clearFilter()));
connect(ui.filterStartButton, SIGNAL(clicked()), this, SLOT(startFilter()));
@ -213,15 +223,20 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent)
/* Set header resize modes and initial section sizes */
QHeaderView * header = ui.dirTreeView->header () ;
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
int charWidth = ui.dirTreeView->fontMetrics().width("_");
#else
int charWidth = ui.dirTreeView->fontMetrics().horizontalAdvance("_");
#endif
header->resizeSection ( COLUMN_NAME, 490 );
header->resizeSection ( COLUMN_FILENB, 70 );
header->resizeSection ( COLUMN_SIZE, 70 );
header->resizeSection ( COLUMN_AGE, 100 );
header->resizeSection ( COLUMN_FRIEND_ACCESS,100);
header->resizeSection ( COLUMN_WN_VISU_DIR, 100 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_NAME , charWidth*100 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FILENB , charWidth*15 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_SIZE , charWidth*10 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_AGE , charWidth*6 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, charWidth*10 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR , charWidth*20 );
header->setStretchLastSection(false);
header->setStretchLastSection(true);
/* Set Multi Selection */
ui.dirTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
@ -251,7 +266,7 @@ LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent)
: SharedFilesDialog(false,parent)
{
// Hide columns after loading the settings
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
ui.downloadButton->hide() ;
// load settings
@ -270,14 +285,14 @@ LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent)
ui.titleBarPixmap->setPixmap(FilesDefs::getPixmapFromQtResourcePath(IMAGE_MYFILES)) ;
ui.dirTreeView->setItemDelegateForColumn(COLUMN_FRIEND_ACCESS,new ShareFlagsItemDelegate()) ;
ui.dirTreeView->setItemDelegateForColumn(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS,new ShareFlagsItemDelegate()) ;
}
RemoteSharedFilesDialog::RemoteSharedFilesDialog(QWidget *parent)
: SharedFilesDialog(true,parent)
{
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, true) ;
ui.checkButton->hide() ;
connect(ui.downloadButton, SIGNAL(clicked()), this, SLOT(downloadRemoteSelected()));
@ -304,7 +319,7 @@ void SharedFilesDialog::hideEvent(QHideEvent *)
model->setVisible(false) ;
}
void SharedFilesDialog::showEvent(QShowEvent *)
void SharedFilesDialog::showEvent(QShowEvent *event)
{
if(model!=NULL)
{
@ -317,6 +332,10 @@ void SharedFilesDialog::showEvent(QShowEvent *)
restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes);
}
if (!event->spontaneous()) {
updateFontSize();
}
}
RemoteSharedFilesDialog::~RemoteSharedFilesDialog()
{
@ -431,9 +450,9 @@ void SharedFilesDialog::changeCurrentViewModel(int viewTypeIndex)
restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes);
QHeaderView * header = ui.dirTreeView->header () ;
QHeaderView_setSectionResizeModeColumn(header, COLUMN_NAME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(header, SHARED_FILES_DIALOG_COLUMN_NAME, QHeaderView::Interactive);
ui.dirTreeView->header()->headerDataChanged(Qt::Horizontal, COLUMN_NAME, COLUMN_WN_VISU_DIR) ;
ui.dirTreeView->header()->headerDataChanged(Qt::Horizontal, SHARED_FILES_DIALOG_COLUMN_NAME, SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR) ;
// recursRestoreExpandedItems(ui.dirTreeView->rootIndex(),expanded_indexes);
FilterItems();
@ -443,9 +462,9 @@ void LocalSharedFilesDialog::showProperColumns()
{
if(model == tree_model)
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->hide();
ui.filterPatternLineEdit->hide();
@ -455,9 +474,9 @@ void LocalSharedFilesDialog::showProperColumns()
}
else
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->show();
ui.filterPatternLineEdit->show();
@ -468,9 +487,9 @@ void RemoteSharedFilesDialog::showProperColumns()
{
if(model == tree_model)
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, true) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->hide();
ui.filterPatternLineEdit->hide();
@ -480,9 +499,9 @@ void RemoteSharedFilesDialog::showProperColumns()
}
else
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->show();
ui.filterPatternLineEdit->show();
@ -510,7 +529,6 @@ void LocalSharedFilesDialog::checkUpdate()
void LocalSharedFilesDialog::forceCheck()
{
rsFiles->ForceDirectoryCheck();
return;
}
void RemoteSharedFilesDialog::spawnCustomPopupMenu( QPoint point )
@ -1328,9 +1346,9 @@ void SharedFilesDialog::indicatorChanged(int index)
ui.dirTreeView->update(ui.dirTreeView->rootIndex());
if (correct_indicator[index] != IND_ALWAYS)
ui.dirTreeView->sortByColumn(COLUMN_AGE, Qt::AscendingOrder);
ui.dirTreeView->sortByColumn(SHARED_FILES_DIALOG_COLUMN_AGE, Qt::AscendingOrder);
else
ui.dirTreeView->sortByColumn(COLUMN_NAME, Qt::AscendingOrder);
ui.dirTreeView->sortByColumn(SHARED_FILES_DIALOG_COLUMN_NAME, Qt::AscendingOrder);
updateDisplay() ;
}
@ -1667,3 +1685,16 @@ bool SharedFilesDialog::tree_FilterItem(const QModelIndex &index, const QString
return (visible || visibleChildCount);
}
#endif
void SharedFilesDialog::updateFontSize()
{
int customFontSize = Settings->valueFromGroup("File", "MinimumFontSize", 11).toInt();
QFont newFont = ui.dirTreeView->font();
if (newFont.pointSize() != customFontSize) {
newFont.setPointSize(customFontSize);
QFontMetricsF fontMetrics(newFont);
int iconHeight = fontMetrics.height();
ui.dirTreeView->setFont(newFont);
ui.dirTreeView->setIconSize(QSize(iconHeight, iconHeight));
}
}

View file

@ -117,6 +117,8 @@ protected:
QModelIndexList getSelected();
void updateFontSize();
/** Defines the actions for the context menu for QTreeWidget */
QAction* copylinkAct;
QAction* sendlinkAct;

View file

@ -61,7 +61,14 @@
</widget>
</item>
<item>
<widget class="StyledLabel" name="titleBarLabel">
<widget class="QLabel" name="titleBarLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Files</string>
</property>
@ -216,7 +223,7 @@ border-image: url(:/images/closepressed.png)
</layout>
</item>
<item>
<widget class="QComboBox" name="viewType_CB">
<widget class="RSComboBox" name="viewType_CB">
<item>
<property name="text">
<string>Tree view</string>
@ -230,14 +237,14 @@ border-image: url(:/images/closepressed.png)
</widget>
</item>
<item>
<widget class="QComboBox" name="indicatorCBox">
<widget class="RSComboBox" name="indicatorCBox">
<item>
<property name="text">
<string>All</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_list.png</normaloff>:/images/view_calendar_list.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-blank.svg</normaloff>:/icons/svg/calendar-blank.svg</iconset>
</property>
</item>
<item>
@ -245,8 +252,8 @@ border-image: url(:/images/closepressed.png)
<string>One day old</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_day.png</normaloff>:/images/view_calendar_day.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-today.svg</normaloff>:/icons/svg/calendar-today.svg</iconset>
</property>
</item>
<item>
@ -254,8 +261,8 @@ border-image: url(:/images/closepressed.png)
<string>One Week old</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_week.png</normaloff>:/images/view_calendar_week.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-week.svg</normaloff>:/icons/svg/calendar-week.svg</iconset>
</property>
</item>
<item>
@ -263,8 +270,8 @@ border-image: url(:/images/closepressed.png)
<string>One month old</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_month.png</normaloff>:/images/view_calendar_month.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-month.svg</normaloff>:/icons/svg/calendar-month.svg</iconset>
</property>
</item>
</widget>
@ -278,9 +285,16 @@ border-image: url(:/images/closepressed.png)
</item>
<item>
<widget class="QPushButton" name="checkButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Forces the re-check of all shared directories. While automatic file checking only cares for new/removed files for efficiency reasons, this button will force the re-scan of all files, possibly re-hashing existing files that may have changed. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>check files</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/refresh.svg</normaloff>:/icons/svg/refresh.svg</iconset>
</property>
</widget>
</item>
<item>
@ -304,8 +318,8 @@ border-image: url(:/images/closepressed.png)
<string>Download</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/download16.png</normaloff>:/images/download16.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/download.png</normaloff>:/icons/png/download.png</iconset>
</property>
</widget>
</item>
@ -366,9 +380,9 @@ border-image: url(:/images/closepressed.png)
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerStretchLastSection">
<property name="animated">
<bool>false</bool>
</attribute>
</property>
</widget>
</item>
</layout>
@ -379,14 +393,14 @@ border-image: url(:/images/closepressed.png)
</widget>
<customwidgets>
<customwidget>
<class>StyledLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledLabel.h</header>
<class>RSComboBox</class>
<extends>QComboBox</extends>
<header>gui/common/RSComboBox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="../icons.qrc"/>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -27,8 +27,6 @@ TransferUserNotify::TransferUserNotify(QObject *parent) :
UserNotify(parent)
{
newTransferCount = 0;
// connect(NotifyQt::getInstance(), SIGNAL(downloadCompleteCountChanged(int)), this, SLOT(downloadCountChanged(int)));
}
bool TransferUserNotify::hasSetting(QString *name, QString *group)

View file

@ -21,6 +21,7 @@
#include "TransfersDialog.h"
#include "gui/notifyqt.h"
#include "gui/SoundManager.h"
#include "gui/RetroShareLink.h"
#include "gui/common/FilesDefs.h"
#include "gui/common/RsCollection.h"
@ -144,7 +145,7 @@ public:
}
int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const
{
return COLUMN_COUNT ;
return DLListDelegate::COLUMN_COUNT ;
}
bool hasChildren(const QModelIndex &parent = QModelIndex()) const
{
@ -176,7 +177,7 @@ public:
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const
{
if(row < 0 || column < 0 || column >= COLUMN_COUNT)
if(row < 0 || column < 0 || column >= DLListDelegate::COLUMN_COUNT)
return QModelIndex();
void *parent_ref = (parent.isValid())?parent.internalPointer():NULL ;
@ -258,19 +259,19 @@ public:
switch(section)
{
default:
case COLUMN_NAME: return tr("Name", "i.e: file name");
case COLUMN_SIZE: return tr("Size", "i.e: file size");
case COLUMN_COMPLETED: return tr("Completed", "");
case COLUMN_DLSPEED: return tr("Speed", "i.e: Download speed");
case COLUMN_PROGRESS: return tr("Progress / Availability", "i.e: % downloaded");
case COLUMN_SOURCES: return tr("Sources", "i.e: Sources");
case COLUMN_STATUS: return tr("Status");
case COLUMN_PRIORITY: return tr("Speed / Queue position");
case COLUMN_REMAINING: return tr("Remaining");
case COLUMN_DOWNLOADTIME: return tr("Download time", "i.e: Estimated Time of Arrival / Time left");
case COLUMN_ID: return tr("Hash");
case COLUMN_LASTDL: return tr("Last Time Seen", "i.e: Last Time Receiced Data");
case COLUMN_PATH: return tr("Path", "i.e: Where file is saved");
case DLListDelegate::COLUMN_NAME: return tr("Name", "i.e: file name");
case DLListDelegate::COLUMN_SIZE: return tr("Size", "i.e: file size");
case DLListDelegate::COLUMN_COMPLETED: return tr("Completed", "");
case DLListDelegate::COLUMN_DLSPEED: return tr("Speed", "i.e: Download speed");
case DLListDelegate::COLUMN_PROGRESS: return tr("Progress / Availability", "i.e: % downloaded");
case DLListDelegate::COLUMN_SOURCES: return tr("Sources", "i.e: Sources");
case DLListDelegate::COLUMN_STATUS: return tr("Status");
case DLListDelegate::COLUMN_PRIORITY: return tr("Speed / Queue position");
case DLListDelegate::COLUMN_REMAINING: return tr("Remaining");
case DLListDelegate::COLUMN_DOWNLOADTIME: return tr("Download time", "i.e: Estimated Time of Arrival / Time left");
case DLListDelegate::COLUMN_ID: return tr("Hash");
case DLListDelegate::COLUMN_LASTDL: return tr("Last Time Seen", "i.e: Last Time Receiced Data");
case DLListDelegate::COLUMN_PATH: return tr("Path", "i.e: Where file is saved");
}
}
@ -352,19 +353,19 @@ public:
switch(col)
{
default:
case COLUMN_NAME: return QVariant( QSize(factor * 170, factor*14.0f ));
case COLUMN_SIZE: return QVariant( QSize(factor * 70 , factor*14.0f ));
case COLUMN_COMPLETED: return QVariant( QSize(factor * 75 , factor*14.0f ));
case COLUMN_DLSPEED: return QVariant( QSize(factor * 75 , factor*14.0f ));
case COLUMN_PROGRESS: return QVariant( QSize(factor * 170, factor*14.0f ));
case COLUMN_SOURCES: return QVariant( QSize(factor * 90 , factor*14.0f ));
case COLUMN_STATUS: return QVariant( QSize(factor * 100, factor*14.0f ));
case COLUMN_PRIORITY: return QVariant( QSize(factor * 100, factor*14.0f ));
case COLUMN_REMAINING: return QVariant( QSize(factor * 100, factor*14.0f ));
case COLUMN_DOWNLOADTIME: return QVariant( QSize(factor * 100, factor*14.0f ));
case COLUMN_ID: return QVariant( QSize(factor * 100, factor*14.0f ));
case COLUMN_LASTDL: return QVariant( QSize(factor * 100, factor*14.0f ));
case COLUMN_PATH: return QVariant( QSize(factor * 100, factor*14.0f ));
case DLListDelegate::COLUMN_NAME: return QVariant( QSize(factor * 170, factor*14.0f ));
case DLListDelegate::COLUMN_SIZE: return QVariant( QSize(factor * 70 , factor*14.0f ));
case DLListDelegate::COLUMN_COMPLETED: return QVariant( QSize(factor * 75 , factor*14.0f ));
case DLListDelegate::COLUMN_DLSPEED: return QVariant( QSize(factor * 75 , factor*14.0f ));
case DLListDelegate::COLUMN_PROGRESS: return QVariant( QSize(factor * 170, factor*14.0f ));
case DLListDelegate::COLUMN_SOURCES: return QVariant( QSize(factor * 90 , factor*14.0f ));
case DLListDelegate::COLUMN_STATUS: return QVariant( QSize(factor * 100, factor*14.0f ));
case DLListDelegate::COLUMN_PRIORITY: return QVariant( QSize(factor * 100, factor*14.0f ));
case DLListDelegate::COLUMN_REMAINING: return QVariant( QSize(factor * 100, factor*14.0f ));
case DLListDelegate::COLUMN_DOWNLOADTIME: return QVariant( QSize(factor * 100, factor*14.0f ));
case DLListDelegate::COLUMN_ID: return QVariant( QSize(factor * 100, factor*14.0f ));
case DLListDelegate::COLUMN_LASTDL: return QVariant( QSize(factor * 100, factor*14.0f ));
case DLListDelegate::COLUMN_PATH: return QVariant( QSize(factor * 100, factor*14.0f ));
}
}
@ -374,11 +375,11 @@ public:
if(source_id == -1) // toplevel
switch(col)
{
case COLUMN_NAME: return QVariant(QString::fromUtf8(fileInfo.fname.c_str()));
case COLUMN_COMPLETED: return QVariant((qlonglong)fileInfo.transfered);
case COLUMN_DLSPEED: return QVariant((double)((fileInfo.downloadStatus == FT_STATE_DOWNLOADING) ? (fileInfo.tfRate * 1024.0) : 0.0));
case COLUMN_PROGRESS: return QVariant((float)((fileInfo.size == 0) ? 0 : (fileInfo.transfered * 100.0 / (float)fileInfo.size)));
case COLUMN_STATUS:
case DLListDelegate::COLUMN_NAME: return QVariant(QString::fromUtf8(fileInfo.fname.c_str()));
case DLListDelegate::COLUMN_COMPLETED: return QVariant((qlonglong)fileInfo.transfered);
case DLListDelegate::COLUMN_DLSPEED: return QVariant((double)((fileInfo.downloadStatus == FT_STATE_DOWNLOADING) ? (fileInfo.tfRate * 1024.0) : 0.0));
case DLListDelegate::COLUMN_PROGRESS: return QVariant((float)((fileInfo.size == 0) ? 0 : (fileInfo.transfered * 100.0 / (float)fileInfo.size)));
case DLListDelegate::COLUMN_STATUS:
{
QString status;
switch (fileInfo.downloadStatus)
@ -396,9 +397,9 @@ public:
return QVariant(status);
}
case COLUMN_PRIORITY:
case DLListDelegate::COLUMN_PRIORITY:
{
double priority = PRIORITY_NULL;
double priority = DLListDelegate::PRIORITY_NULL;
if (fileInfo.downloadStatus == FT_STATE_QUEUED)
priority = fileInfo.queue_position;
@ -407,18 +408,18 @@ public:
else
switch (fileInfo.priority)
{
case SPEED_LOW: priority = PRIORITY_SLOWER; break;
case SPEED_NORMAL: priority = PRIORITY_AVERAGE; break;
case SPEED_HIGH: priority = PRIORITY_FASTER; break;
default: priority = PRIORITY_AVERAGE; break;
case SPEED_LOW: priority = DLListDelegate::PRIORITY_SLOWER; break;
case SPEED_NORMAL: priority = DLListDelegate::PRIORITY_AVERAGE; break;
case SPEED_HIGH: priority = DLListDelegate::PRIORITY_FASTER; break;
default: priority = DLListDelegate::PRIORITY_AVERAGE; break;
}
return QVariant(priority);
}
case COLUMN_REMAINING: return QVariant((qlonglong)(fileInfo.size - fileInfo.transfered));
case COLUMN_DOWNLOADTIME: return QVariant((qlonglong)(fileInfo.tfRate > 0)?( (fileInfo.size - fileInfo.transfered) / (fileInfo.tfRate * 1024.0) ) : 0);
case COLUMN_LASTDL:
case DLListDelegate::COLUMN_REMAINING: return QVariant((qlonglong)(fileInfo.size - fileInfo.transfered));
case DLListDelegate::COLUMN_DOWNLOADTIME: return QVariant((qlonglong)(fileInfo.tfRate > 0)?( (fileInfo.size - fileInfo.transfered) / (fileInfo.tfRate * 1024.0) ) : 0);
case DLListDelegate::COLUMN_LASTDL:
{
qint64 qi64LastDL = fileInfo.lastTS ;
@ -437,19 +438,19 @@ public:
}
return QVariant(qi64LastDL) ;
}
case COLUMN_PATH:
case DLListDelegate::COLUMN_PATH:
{
QString strPath = QString::fromUtf8(fileInfo.path.c_str());
QString strPathAfterDL = strPath;
strPathAfterDL.replace(QString::fromUtf8(rsFiles->getDownloadDirectory().c_str()),"");
strPathAfterDL.replace(QString::fromUtf8(rsFiles->getDownloadDirectory().c_str()),"[Download Dir]");
return QVariant(strPathAfterDL);
}
case COLUMN_SOURCES:
case DLListDelegate::COLUMN_SOURCES:
{
int active = 0;
QString fileHash = QString::fromStdString(fileInfo.hash.toStdString());
//QString fileHash = QString::fromStdString(fileInfo.hash.toStdString());
if (fileInfo.downloadStatus != FT_STATE_COMPLETE)
for (std::vector<TransferInfo>::const_iterator pit = fileInfo.peers.begin() ; pit != fileInfo.peers.end(); ++pit)
@ -478,9 +479,9 @@ public:
}
case COLUMN_SIZE: return QVariant((qlonglong) fileInfo.size);
case DLListDelegate::COLUMN_SIZE: return QVariant((qlonglong) fileInfo.size);
case COLUMN_ID: return QVariant(QString::fromStdString(fileInfo.hash.toStdString()));
case DLListDelegate::COLUMN_ID: return QVariant(QString::fromStdString(fileInfo.hash.toStdString()));
default:
return QVariant("[ TODO ]");
@ -492,16 +493,16 @@ public:
switch(col)
{
default:
case COLUMN_SOURCES:
case COLUMN_COMPLETED:
case COLUMN_REMAINING:
case COLUMN_LASTDL:
case COLUMN_ID:
case COLUMN_PATH:
case COLUMN_DOWNLOADTIME:
case COLUMN_SIZE: return QVariant();
case COLUMN_PROGRESS: return QVariant( (fileInfo.size>0)?((fileInfo.peers[source_id].transfered % chunk_size)*100.0/fileInfo.size):0.0) ;
case COLUMN_DLSPEED:
case DLListDelegate::COLUMN_SOURCES:
case DLListDelegate::COLUMN_COMPLETED:
case DLListDelegate::COLUMN_REMAINING:
case DLListDelegate::COLUMN_LASTDL:
case DLListDelegate::COLUMN_ID:
case DLListDelegate::COLUMN_PATH:
case DLListDelegate::COLUMN_DOWNLOADTIME:
case DLListDelegate::COLUMN_SIZE: return QVariant();
case DLListDelegate::COLUMN_PROGRESS: return QVariant( (fileInfo.size>0)?((fileInfo.peers[source_id].transfered % chunk_size)*100.0/fileInfo.size):0.0) ;
case DLListDelegate::COLUMN_DLSPEED:
{
double peerDlspeed = 0;
if((uint32_t)fileInfo.peers[source_id].status == FT_STATE_DOWNLOADING && fileInfo.downloadStatus != FT_STATE_PAUSED && fileInfo.downloadStatus != FT_STATE_COMPLETE)
@ -509,13 +510,13 @@ public:
return QVariant((double)peerDlspeed) ;
}
case COLUMN_NAME:
case DLListDelegate::COLUMN_NAME:
{
QString iconName,tooltip;
return QVariant(TransfersDialog::getPeerName(fileInfo.peers[source_id].peerId, iconName, tooltip));
}
case COLUMN_PRIORITY: return QVariant((double)PRIORITY_NULL);
case DLListDelegate::COLUMN_PRIORITY: return QVariant((double)DLListDelegate::PRIORITY_NULL);
}
}
@ -527,7 +528,7 @@ public:
if(source_id == -1)
switch(col)
{
case COLUMN_PROGRESS:
case DLListDelegate::COLUMN_PROGRESS:
{
FileChunksInfo fcinfo;
if (!rsFiles->FileDownloadChunksDetails(fileInfo.hash, fcinfo))
@ -554,7 +555,7 @@ public:
return QVariant::fromValue(pinfo);
}
case COLUMN_ID: return QVariant(QString::fromStdString(fileInfo.hash.toStdString()));
case DLListDelegate::COLUMN_ID: return QVariant(QString::fromStdString(fileInfo.hash.toStdString()));
default:
@ -563,7 +564,7 @@ public:
else
switch(col)
{
case COLUMN_PROGRESS:
case DLListDelegate::COLUMN_PROGRESS:
{
FileChunksInfo fcinfo;
if (!rsFiles->FileDownloadChunksDetails(fileInfo.hash, fcinfo))
@ -582,7 +583,7 @@ public:
return QVariant::fromValue(pinfo);
}
case COLUMN_ID: return QVariant(QString::fromStdString(fileInfo.hash.toStdString()) + QString::fromStdString(fileInfo.peers[source_id].peerId.toStdString()));
case DLListDelegate::COLUMN_ID: return QVariant(QString::fromStdString(fileInfo.hash.toStdString()) + QString::fromStdString(fileInfo.peers[source_id].peerId.toStdString()));
default:
return QVariant();
@ -592,7 +593,7 @@ public:
QVariant decorationRole(const FileInfo& fileInfo,int source_id,int col) const
{
if(col == COLUMN_NAME)
if(col == DLListDelegate::COLUMN_NAME)
{
if(source_id == -1)
return QVariant(FilesDefs::getIconFromFileType(QString::fromUtf8(fileInfo.fname.c_str())));
@ -635,27 +636,27 @@ public:
for(auto it(downHashes.begin());it!=downHashes.end();++it,++i)
{
FileInfo fileInfo(mDownloads[i]); // we dont update the data itself but only a copy of it....
int old_size = fileInfo.peers.size() ;
int old_peers_size = fileInfo.peers.size() ;
rsFiles->FileDetails(*it, RS_FILE_HINTS_DOWNLOAD, fileInfo);
int new_size = fileInfo.peers.size() ;
if(old_size < new_size)
if(old_peers_size < new_size)
{
beginInsertRows(index(i,0), old_size, new_size-1);
insertRows(old_size, new_size - old_size,index(i,0));
beginInsertRows(index(i,0), old_peers_size, new_size-1);
insertRows(old_peers_size, new_size - old_peers_size,index(i,0));
#ifdef DEBUG_DOWNLOADLIST
std::cerr << "called insert rows ( " << old_size << ", " << new_size - old_size << ",index(" << index(i,0)<< "))" << std::endl;
std::cerr << "called insert rows ( " << old_peers_size << ", " << new_size - old_peers_size << ",index(" << index(i,0)<< "))" << std::endl;
#endif
endInsertRows();
}
else if(new_size < old_size)
else if(new_size < old_peers_size)
{
beginRemoveRows(index(i,0), new_size, old_size-1);
removeRows(new_size, old_size - new_size,index(i,0));
beginRemoveRows(index(i,0), new_size, old_peers_size-1);
removeRows(new_size, old_peers_size - new_size,index(i,0));
#ifdef DEBUG_DOWNLOADLIST
std::cerr << "called remove rows ( " << old_size << ", " << old_size - new_size << ",index(" << index(i,0)<< "))" << std::endl;
std::cerr << "called remove rows ( " << old_peers_size << ", " << old_peers_size - new_size << ",index(" << index(i,0)<< "))" << std::endl;
#endif
endRemoveRows();
}
@ -666,7 +667,7 @@ public:
if(fileInfo.downloadStatus == FT_STATE_DOWNLOADING || old_status != fileInfo.downloadStatus)
{
QModelIndex topLeft = createIndex(i,0), bottomRight = createIndex(i, COLUMN_COUNT-1);
QModelIndex topLeft = createIndex(i,0), bottomRight = createIndex(i, DLListDelegate::COLUMN_COUNT-1);
emit dataChanged(topLeft, bottomRight);
}
@ -674,7 +675,7 @@ public:
//
// if(!mDownloads.empty())
// {
// QModelIndex topLeft = createIndex(0,0), bottomRight = createIndex(mDownloads.size()-1, COLUMN_COUNT-1);
// QModelIndex topLeft = createIndex(0,0), bottomRight = createIndex(mDownloads.size()-1, DLListDelegate::COLUMN_COUNT-1);
// emit dataChanged(topLeft, bottomRight);
// mDownloads[i] = fileInfo ;
// }
@ -742,7 +743,7 @@ private:
class SortByNameItem : public QStandardItem
{
public:
SortByNameItem(QHeaderView *header) : QStandardItem()
explicit SortByNameItem(QHeaderView *header) : QStandardItem()
{
this->header = header;
}
@ -761,8 +762,8 @@ public:
return QStandardItem::operator<(other);
}
QStandardItem *myName = myParent->child(index().row(), COLUMN_NAME);
QStandardItem *otherName = otherParent->child(other.index().row(), COLUMN_NAME);
QStandardItem *myName = myParent->child(index().row(), DLListDelegate::COLUMN_NAME);
QStandardItem *otherName = otherParent->child(other.index().row(), DLListDelegate::COLUMN_NAME);
if (header == NULL || header->sortIndicatorOrder() == Qt::AscendingOrder) {
/* Ascending */
@ -780,9 +781,9 @@ private:
class ProgressItem : public SortByNameItem
{
public:
ProgressItem(QHeaderView *header) : SortByNameItem(header) {}
explicit ProgressItem(QHeaderView *header) : SortByNameItem(header) {}
virtual bool operator<(const QStandardItem &other) const
virtual bool operator<(const QStandardItem &other) const override
{
const int role = model() ? model()->sortRole() : Qt::DisplayRole;
@ -847,41 +848,41 @@ TransfersDialog::TransfersDialog(QWidget *parent)
// /* Set header resize modes and initial section sizes Downloads TreeView*/
QHeaderView * dlheader = ui.downloadList->header () ;
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_NAME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_SIZE, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_COMPLETED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_DLSPEED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_PROGRESS, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_SOURCES, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_STATUS, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_PRIORITY, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_REMAINING, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_DOWNLOADTIME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_ID, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_LASTDL, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, COLUMN_PATH, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_NAME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_SIZE, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_COMPLETED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_DLSPEED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_PROGRESS, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_SOURCES, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_STATUS, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_PRIORITY, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_REMAINING, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_DOWNLOADTIME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_ID, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_LASTDL, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(dlheader, DLListDelegate::COLUMN_PATH, QHeaderView::Interactive);
// set default column and sort order for download
ui.downloadList->sortByColumn(COLUMN_NAME, Qt::AscendingOrder);
ui.downloadList->sortByColumn(DLListDelegate::COLUMN_NAME, Qt::AscendingOrder);
connect(ui.filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
/* Add filter actions */
QString headerName = DLListModel->headerData(COLUMN_NAME, Qt::Horizontal).toString();
ui.filterLineEdit->addFilter(QIcon(), headerName, COLUMN_NAME , QString("%1 %2").arg(tr("Search"), headerName));
QString headerID = DLListModel->headerData(COLUMN_ID, Qt::Horizontal).toString();
ui.filterLineEdit->addFilter(QIcon(), headerID, COLUMN_ID , QString("%1 %2").arg(tr("Search"), headerID));
QString headerName = DLListModel->headerData(DLListDelegate::COLUMN_NAME, Qt::Horizontal).toString();
ui.filterLineEdit->addFilter(QIcon(), headerName, DLListDelegate::COLUMN_NAME , QString("%1 %2").arg(tr("Search"), headerName));
QString headerID = DLListModel->headerData(DLListDelegate::COLUMN_ID, Qt::Horizontal).toString();
ui.filterLineEdit->addFilter(QIcon(), headerID, DLListDelegate::COLUMN_ID , QString("%1 %2").arg(tr("Search"), headerID));
connect( ui.uploadsList, SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( uploadsListCustomPopupMenu( QPoint ) ) );
// Set Upload list model
ULListModel = new QStandardItemModel(0,COLUMN_UCOUNT);
ULListModel->setHeaderData(COLUMN_UNAME, Qt::Horizontal, tr("Name", "i.e: file name"));
ULListModel->setHeaderData(COLUMN_UPEER, Qt::Horizontal, tr("Peer", "i.e: user name / tunnel id"));
ULListModel->setHeaderData(COLUMN_USIZE, Qt::Horizontal, tr("Size", "i.e: file size"));
ULListModel->setHeaderData(COLUMN_UTRANSFERRED, Qt::Horizontal, tr("Transferred", ""));
ULListModel->setHeaderData(COLUMN_ULSPEED, Qt::Horizontal, tr("Speed", "i.e: upload speed"));
ULListModel->setHeaderData(COLUMN_UPROGRESS, Qt::Horizontal, tr("Progress", "i.e: % uploaded"));
ULListModel->setHeaderData(COLUMN_UHASH, Qt::Horizontal, tr("Hash", ""));
ULListModel = new QStandardItemModel(0,ULListDelegate::COLUMN_UCOUNT);
ULListModel->setHeaderData(ULListDelegate::COLUMN_UNAME, Qt::Horizontal, tr("Name", "i.e: file name"));
ULListModel->setHeaderData(ULListDelegate::COLUMN_UPEER, Qt::Horizontal, tr("Peer", "i.e: user name / tunnel id"));
ULListModel->setHeaderData(ULListDelegate::COLUMN_USIZE, Qt::Horizontal, tr("Size", "i.e: file size"));
ULListModel->setHeaderData(ULListDelegate::COLUMN_UTRANSFERRED, Qt::Horizontal, tr("Transferred", ""));
ULListModel->setHeaderData(ULListDelegate::COLUMN_ULSPEED, Qt::Horizontal, tr("Speed", "i.e: upload speed"));
ULListModel->setHeaderData(ULListDelegate::COLUMN_UPROGRESS, Qt::Horizontal, tr("Progress", "i.e: % uploaded"));
ULListModel->setHeaderData(ULListDelegate::COLUMN_UHASH, Qt::Horizontal, tr("Hash", ""));
ui.uploadsList->setModel(ULListModel);
@ -901,22 +902,22 @@ TransfersDialog::TransfersDialog(QWidget *parent)
/* Set header resize modes and initial section sizes Uploads TreeView*/
QHeaderView * upheader = ui.uploadsList->header () ;
QHeaderView_setSectionResizeModeColumn(upheader, COLUMN_UNAME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, COLUMN_UPEER, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, COLUMN_USIZE, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, COLUMN_UTRANSFERRED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, COLUMN_ULSPEED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, COLUMN_UPROGRESS, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, ULListDelegate::COLUMN_UNAME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, ULListDelegate::COLUMN_UPEER, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, ULListDelegate::COLUMN_USIZE, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, ULListDelegate::COLUMN_UTRANSFERRED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, ULListDelegate::COLUMN_ULSPEED, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(upheader, ULListDelegate::COLUMN_UPROGRESS, QHeaderView::Interactive);
upheader->resizeSection ( COLUMN_UNAME, 260 );
upheader->resizeSection ( COLUMN_UPEER, 120 );
upheader->resizeSection ( COLUMN_USIZE, 70 );
upheader->resizeSection ( COLUMN_UTRANSFERRED, 75 );
upheader->resizeSection ( COLUMN_ULSPEED, 75 );
upheader->resizeSection ( COLUMN_UPROGRESS, 170 );
upheader->resizeSection ( ULListDelegate::COLUMN_UNAME, 260 );
upheader->resizeSection ( ULListDelegate::COLUMN_UPEER, 120 );
upheader->resizeSection ( ULListDelegate::COLUMN_USIZE, 70 );
upheader->resizeSection ( ULListDelegate::COLUMN_UTRANSFERRED, 75 );
upheader->resizeSection ( ULListDelegate::COLUMN_ULSPEED, 75 );
upheader->resizeSection ( ULListDelegate::COLUMN_UPROGRESS, 170 );
// set default column and sort order for upload
ui.uploadsList->sortByColumn(COLUMN_UNAME, Qt::AscendingOrder);
ui.uploadsList->sortByColumn(ULListDelegate::COLUMN_UNAME, Qt::AscendingOrder);
QObject::connect(ui.downloadList->selectionModel(),SIGNAL(selectionChanged (const QItemSelection&, const QItemSelection&)),this,SLOT(showFileDetails())) ;
@ -1077,24 +1078,23 @@ TransfersDialog::TransfersDialog(QWidget *parent)
// load settings
processSettings(true);
int S = static_cast<int>(QFontMetricsF(font()).height());
int H = misc::getFontSizeFactor("HelpButton").height();
QString help_str = tr(
"<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;"
"File Transfer</h1>"
"<p>Retroshare brings two ways of transferring files: direct "
"transfers from your friends, and distant anonymous tunnelled "
"transfers. In addition, file transfer is multi-source and "
"allows swarming (you can be a source while downloading)</p>"
"<p>You can share files using the "
"<img src=\":/images/directoryadd_24x24_shadow.png\" width=%2 />"
" icon from the left side bar. These files will be listed in "
"the My Files tab. You can decide for each friend group whether"
" they can or not see these files in their Friends Files tab</p>"
"<p>The search tab reports files from your friends' file lists,"
" and distant files that can be reached anonymously using the "
"multi-hop tunnelling system.</p>")
.arg(QString::number(2*S)).arg(QString::number(S)) ;
"<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;"
" File Transfer</h1>"
"<p>Retroshare brings two ways of transferring files: direct "
" transfers from your friends, and distant anonymous tunnelled "
" transfers. In addition, file transfer is multi-source and "
" allows swarming (you can be a source while downloading)</p>"
"<p>You can share files using the "
" <img src=\":/images/directoryadd_24x24_shadow.png\" width=%2 />"
" icon from the left side bar. These files will be listed in "
" the My Files tab. You can decide for each friend group whether"
" they can or not see these files in their Friends Files tab</p>"
"<p>The search tab reports files from your friends' file lists,"
" and distant files that can be reached anonymously using the "
" multi-hop tunnelling system.</p>"
).arg(QString::number(2*H), QString::number(H)) ;
registerHelpButton(ui.helpButton,help_str,"TransfersDialog") ;
@ -1114,6 +1114,16 @@ void TransfersDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> eve
switch (fe->mFileTransferEventCode)
{
case RsFileTransferEventCode::DOWNLOAD_COMPLETE:
{
FileInfo nfo ;
if(!rsFiles->FileDetails(fe->mHash, RS_FILE_HINTS_DOWNLOAD, nfo))
break;
SoundManager::play(SOUND_DOWNLOAD_COMPLETE);
NotifyQt::getInstance()->addToaster(RS_POPUP_DOWNLOAD, fe->mHash.toStdString(), nfo.fname.c_str(),"");
}
[[fallthrough]];
case RsFileTransferEventCode::COMPLETED_FILES_REMOVED:
getUserNotify()->updateIcon();
@ -1175,18 +1185,18 @@ void TransfersDialog::processSettings(bool bLoad)
// state of splitter
ui.splitter->restoreState(Settings->value("Splitter").toByteArray());
setShowDLSizeColumn(Settings->value("showDLSizeColumn", !ui.downloadList->isColumnHidden(COLUMN_SIZE)).toBool());
setShowDLCompleteColumn(Settings->value("showDLCompleteColumn", !ui.downloadList->isColumnHidden(COLUMN_COMPLETED)).toBool());
setShowDLDLSpeedColumn(Settings->value("showDLDLSpeedColumn", !ui.downloadList->isColumnHidden(COLUMN_DLSPEED)).toBool());
setShowDLProgressColumn(Settings->value("showDLProgressColumn", !ui.downloadList->isColumnHidden(COLUMN_PROGRESS)).toBool());
setShowDLSourcesColumn(Settings->value("showDLSourcesColumn", !ui.downloadList->isColumnHidden(COLUMN_SOURCES)).toBool());
setShowDLStatusColumn(Settings->value("showDLStatusColumn", !ui.downloadList->isColumnHidden(COLUMN_STATUS)).toBool());
setShowDLPriorityColumn(Settings->value("showDLPriorityColumn", !ui.downloadList->isColumnHidden(COLUMN_PRIORITY)).toBool());
setShowDLRemainingColumn(Settings->value("showDLRemainingColumn", !ui.downloadList->isColumnHidden(COLUMN_REMAINING)).toBool());
setShowDLDownloadTimeColumn(Settings->value("showDLDownloadTimeColumn", !ui.downloadList->isColumnHidden(COLUMN_DOWNLOADTIME)).toBool());
setShowDLIDColumn(Settings->value("showDLIDColumn", !ui.downloadList->isColumnHidden(COLUMN_ID)).toBool());
setShowDLLastDLColumn(Settings->value("showDLLastDLColumn", !ui.downloadList->isColumnHidden(COLUMN_LASTDL)).toBool());
setShowDLPath(Settings->value("showDLPath", !ui.downloadList->isColumnHidden(COLUMN_PATH)).toBool());
setShowDLSizeColumn(Settings->value("showDLSizeColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_SIZE)).toBool());
setShowDLCompleteColumn(Settings->value("showDLCompleteColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_COMPLETED)).toBool());
setShowDLDLSpeedColumn(Settings->value("showDLDLSpeedColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_DLSPEED)).toBool());
setShowDLProgressColumn(Settings->value("showDLProgressColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PROGRESS)).toBool());
setShowDLSourcesColumn(Settings->value("showDLSourcesColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_SOURCES)).toBool());
setShowDLStatusColumn(Settings->value("showDLStatusColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_STATUS)).toBool());
setShowDLPriorityColumn(Settings->value("showDLPriorityColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PRIORITY)).toBool());
setShowDLRemainingColumn(Settings->value("showDLRemainingColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_REMAINING)).toBool());
setShowDLDownloadTimeColumn(Settings->value("showDLDownloadTimeColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_DOWNLOADTIME)).toBool());
setShowDLIDColumn(Settings->value("showDLIDColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_ID)).toBool());
setShowDLLastDLColumn(Settings->value("showDLLastDLColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_LASTDL)).toBool());
setShowDLPath(Settings->value("showDLPath", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PATH)).toBool());
// selected tab
ui.tabWidget->setCurrentIndex(Settings->value("selectedTab").toInt());
@ -1200,18 +1210,18 @@ void TransfersDialog::processSettings(bool bLoad)
// state of splitter
Settings->setValue("Splitter", ui.splitter->saveState());
Settings->setValue("showDLSizeColumn", !ui.downloadList->isColumnHidden(COLUMN_SIZE));
Settings->setValue("showDLCompleteColumn", !ui.downloadList->isColumnHidden(COLUMN_COMPLETED));
Settings->setValue("showDLDLSpeedColumn", !ui.downloadList->isColumnHidden(COLUMN_DLSPEED));
Settings->setValue("showDLProgressColumn", !ui.downloadList->isColumnHidden(COLUMN_PROGRESS));
Settings->setValue("showDLSourcesColumn", !ui.downloadList->isColumnHidden(COLUMN_SOURCES));
Settings->setValue("showDLStatusColumn", !ui.downloadList->isColumnHidden(COLUMN_STATUS));
Settings->setValue("showDLPriorityColumn", !ui.downloadList->isColumnHidden(COLUMN_PRIORITY));
Settings->setValue("showDLRemainingColumn", !ui.downloadList->isColumnHidden(COLUMN_REMAINING));
Settings->setValue("showDLDownloadTimeColumn", !ui.downloadList->isColumnHidden(COLUMN_DOWNLOADTIME));
Settings->setValue("showDLIDColumn", !ui.downloadList->isColumnHidden(COLUMN_ID));
Settings->setValue("showDLLastDLColumn", !ui.downloadList->isColumnHidden(COLUMN_LASTDL));
Settings->setValue("showDLPath", !ui.downloadList->isColumnHidden(COLUMN_PATH));
Settings->setValue("showDLSizeColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_SIZE));
Settings->setValue("showDLCompleteColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_COMPLETED));
Settings->setValue("showDLDLSpeedColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_DLSPEED));
Settings->setValue("showDLProgressColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PROGRESS));
Settings->setValue("showDLSourcesColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_SOURCES));
Settings->setValue("showDLStatusColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_STATUS));
Settings->setValue("showDLPriorityColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PRIORITY));
Settings->setValue("showDLRemainingColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_REMAINING));
Settings->setValue("showDLDownloadTimeColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_DOWNLOADTIME));
Settings->setValue("showDLIDColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_ID));
Settings->setValue("showDLLastDLColumn", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_LASTDL));
Settings->setValue("showDLPath", !ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PATH));
// selected tab
Settings->setValue("selectedTab", ui.tabWidget->currentIndex());
@ -1424,18 +1434,18 @@ void TransfersDialog::downloadListHeaderCustomPopupMenu( QPoint /*point*/ )
std::cerr << "TransfersDialog::downloadListHeaderCustomPopupMenu()" << std::endl;
QMenu contextMnu( this );
showDLSizeAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_SIZE));
showDLCompleteAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_COMPLETED));
showDLDLSpeedAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_DLSPEED));
showDLProgressAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_PROGRESS));
showDLSourcesAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_SOURCES));
showDLStatusAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_STATUS));
showDLPriorityAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_PRIORITY));
showDLRemainingAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_REMAINING));
showDLDownloadTimeAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_DOWNLOADTIME));
showDLIDAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_ID));
showDLLastDLAct->setChecked(!ui.downloadList->isColumnHidden(COLUMN_LASTDL));
showDLPath->setChecked(!ui.downloadList->isColumnHidden(COLUMN_PATH));
showDLSizeAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_SIZE));
showDLCompleteAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_COMPLETED));
showDLDLSpeedAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_DLSPEED));
showDLProgressAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PROGRESS));
showDLSourcesAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_SOURCES));
showDLStatusAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_STATUS));
showDLPriorityAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PRIORITY));
showDLRemainingAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_REMAINING));
showDLDownloadTimeAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_DOWNLOADTIME));
showDLIDAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_ID));
showDLLastDLAct->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_LASTDL));
showDLPath->setChecked(!ui.downloadList->isColumnHidden(DLListDelegate::COLUMN_PATH));
QMenu *menu = contextMnu.addMenu(tr("Columns"));
menu->addAction(showDLSizeAct);
@ -1487,12 +1497,12 @@ void TransfersDialog::uploadsListHeaderCustomPopupMenu( QPoint /*point*/ )
std::cerr << "TransfersDialog::uploadsListHeaderCustomPopupMenu()" << std::endl;
QMenu contextMnu( this );
showULPeerAct->setChecked(!ui.uploadsList->isColumnHidden(COLUMN_UPEER));
showULSizeAct->setChecked(!ui.uploadsList->isColumnHidden(COLUMN_USIZE));
showULTransferredAct->setChecked(!ui.uploadsList->isColumnHidden(COLUMN_UTRANSFERRED));
showULSpeedAct->setChecked(!ui.uploadsList->isColumnHidden(COLUMN_ULSPEED));
showULProgressAct->setChecked(!ui.uploadsList->isColumnHidden(COLUMN_UPROGRESS));
showULHashAct->setChecked(!ui.uploadsList->isColumnHidden(COLUMN_UHASH));
showULPeerAct->setChecked(!ui.uploadsList->isColumnHidden(ULListDelegate::COLUMN_UPEER));
showULSizeAct->setChecked(!ui.uploadsList->isColumnHidden(ULListDelegate::COLUMN_USIZE));
showULTransferredAct->setChecked(!ui.uploadsList->isColumnHidden(ULListDelegate::COLUMN_UTRANSFERRED));
showULSpeedAct->setChecked(!ui.uploadsList->isColumnHidden(ULListDelegate::COLUMN_ULSPEED));
showULProgressAct->setChecked(!ui.uploadsList->isColumnHidden(ULListDelegate::COLUMN_UPROGRESS));
showULHashAct->setChecked(!ui.uploadsList->isColumnHidden(ULListDelegate::COLUMN_UHASH));
QMenu *menu = contextMnu.addMenu(tr("Columns"));
menu->addAction(showULPeerAct);
@ -1555,20 +1565,20 @@ int TransfersDialog::addULItem(int row, const FileInfo &fileInfo)
// change progress column to own class for sorting
//ULListModel->setItem(row, COLUMN_UPROGRESS, new ProgressItem(NULL));
ULListModel->setData(ULListModel->index(row, COLUMN_UNAME), fileName);
ULListModel->setData(ULListModel->index(row, COLUMN_UNAME), FilesDefs::getIconFromFileType(fileName), Qt::DecorationRole);
ULListModel->setData(ULListModel->index(row, COLUMN_UHASH), fileHash);
ULListModel->setData(ULListModel->index(row, COLUMN_UHASH), fileHash, Qt::UserRole);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UNAME), fileName);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UNAME), FilesDefs::getIconFromFileType(fileName), Qt::DecorationRole);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UHASH), fileHash);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UHASH), fileHash, Qt::UserRole);
}
ULListModel->setData(ULListModel->index(row, COLUMN_USIZE), QVariant((qlonglong)fileSize));
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_USIZE), QVariant((qlonglong)fileSize));
//Reset Parent info if child present
ULListModel->setData(ULListModel->index(row, COLUMN_UPEER), QVariant(QString(tr("%1 tunnels").arg(fileInfo.peers.size()))) );
ULListModel->setData(ULListModel->index(row, COLUMN_UPEER), QIcon(), Qt::DecorationRole);
ULListModel->setData(ULListModel->index(row, COLUMN_UPEER), QVariant(), Qt::ToolTipRole);
ULListModel->setData(ULListModel->index(row, COLUMN_UTRANSFERRED), QVariant());
ULListModel->setData(ULListModel->index(row, COLUMN_UPROGRESS), QVariant());
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPEER), QVariant(QString(tr("%1 tunnels").arg(fileInfo.peers.size()))) );
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPEER), QIcon(), Qt::DecorationRole);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPEER), QVariant(), Qt::ToolTipRole);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UTRANSFERRED), QVariant());
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPROGRESS), QVariant());
QStandardItem *ulItem = ULListModel->item(row);
std::set<int> used_rows ;
@ -1584,7 +1594,6 @@ int TransfersDialog::addULItem(int row, const FileInfo &fileInfo)
//unique combination: fileHash + peerId, variant: hash + peerName (too long)
QString hashFileAndPeerId = fileHash + QString::fromStdString(transferInfo.peerId.toStdString());
qlonglong completed = transferInfo.transfered;
double peerULSpeed = transferInfo.tfRate * 1024.0;
@ -1602,6 +1611,7 @@ int TransfersDialog::addULItem(int row, const FileInfo &fileInfo)
peerpinfo.type = FileProgressInfo::UPLOAD_LINE ;
peerpinfo.nb_chunks = peerpinfo.cmap._map.empty()?0:nb_chunks ;
qlonglong completed = 0;
if(filled_chunks > 0 && nb_chunks > 0)
{
completed = peerpinfo.cmap.computeProgress(fileInfo.size,chunk_size) ;
@ -1618,11 +1628,11 @@ int TransfersDialog::addULItem(int row, const FileInfo &fileInfo)
//Only one peer so update parent
QString iconName;
QString tooltip;
ULListModel->setData(ULListModel->index(row, COLUMN_UPEER), QVariant(getPeerName(transferInfo.peerId, iconName, tooltip)));
ULListModel->setData(ULListModel->index(row, COLUMN_UPEER), QIcon(iconName), Qt::DecorationRole);
ULListModel->setData(ULListModel->index(row, COLUMN_UPEER), QVariant(tooltip), Qt::ToolTipRole);
ULListModel->setData(ULListModel->index(row, COLUMN_UTRANSFERRED), QVariant(completed));
ULListModel->setData(ULListModel->index(row, COLUMN_UPROGRESS), QVariant::fromValue(peerpinfo));
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPEER), QVariant(getPeerName(transferInfo.peerId, iconName, tooltip)));
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPEER), QIcon(iconName), Qt::DecorationRole);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPEER), QVariant(tooltip), Qt::ToolTipRole);
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UTRANSFERRED), QVariant(completed));
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_UPROGRESS), QVariant::fromValue(peerpinfo));
} else {
int row_id = addPeerToULItem(ulItem, transferInfo.peerId, hashFileAndPeerId, completed, peerULSpeed, peerpinfo);
@ -1633,7 +1643,7 @@ int TransfersDialog::addULItem(int row, const FileInfo &fileInfo)
}
// Update Parent UpLoad Speed
ULListModel->setData(ULListModel->index(row, COLUMN_ULSPEED), QVariant((double)peerULSpeedTotal));
ULListModel->setData(ULListModel->index(row, ULListDelegate::COLUMN_ULSPEED), QVariant((double)peerULSpeedTotal));
// This is not optimal, but we deal with a small number of elements. The reverse order is really important,
@ -1654,7 +1664,7 @@ int TransfersDialog::addPeerToULItem(QStandardItem *ulItem, const RsPeerId& peer
int childRow = -1;
QStandardItem *childId = NULL;
for (int count = 0; (childId = ulItem->child(count, COLUMN_UHASH)) != NULL; ++count) {
for (int count = 0; (childId = ulItem->child(count, ULListDelegate::COLUMN_UHASH)) != NULL; ++count) {
if (childId->data(Qt::UserRole).toString() == coreID) {
childRow = count;
break;
@ -1701,9 +1711,9 @@ int TransfersDialog::addPeerToULItem(QStandardItem *ulItem, const RsPeerId& peer
childRow = ulItem->rowCount() - 1;
} else {
// just update the child (peer)
ulItem->child(childRow, COLUMN_ULSPEED)->setData(QVariant((double)ulspeed), Qt::DisplayRole);
ulItem->child(childRow, COLUMN_UTRANSFERRED)->setData(QVariant((qlonglong)completed), Qt::DisplayRole);
ulItem->child(childRow, COLUMN_UPROGRESS)->setData(QVariant::fromValue(peerInfo), Qt::DisplayRole);
ulItem->child(childRow, ULListDelegate::COLUMN_ULSPEED)->setData(QVariant((double)ulspeed), Qt::DisplayRole);
ulItem->child(childRow, ULListDelegate::COLUMN_UTRANSFERRED)->setData(QVariant((qlonglong)completed), Qt::DisplayRole);
ulItem->child(childRow, ULListDelegate::COLUMN_UPROGRESS)->setData(QVariant::fromValue(peerInfo), Qt::DisplayRole);
}
return childRow;
@ -1720,9 +1730,6 @@ void TransfersDialog::insertTransfers()
{
// Since downloads use an AstractItemModel, we just need to update it, while saving the selected and expanded items.
std::set<QString> expanded_hashes ;
std::set<QString> selected_hashes ;
DLListModel->update_transfers();
// Now show upload hashes. Here we use the "old" way, since the number of uploads is generally not so large.
@ -1747,7 +1754,7 @@ void TransfersDialog::insertTransfers()
int rowCount = ULListModel->rowCount();
for (int row = 0; row < rowCount; ) {
RsFileHash hash ( ULListModel->item(row, COLUMN_UHASH)->data(Qt::UserRole).toString().toStdString());
RsFileHash hash ( ULListModel->item(row, ULListDelegate::COLUMN_UHASH)->data(Qt::UserRole).toString().toStdString());
std::set<RsFileHash>::iterator hashIt = hashs.find(hash);
if (hashIt == hashs.end()) {
@ -1957,16 +1964,16 @@ void TransfersDialog::pasteLink()
RsCollection col ;
RSLinkClipboard::pasteLinks(links,RetroShareLink::TYPE_FILE_TREE);
for(QList<RetroShareLink>::const_iterator it(links.begin());it!=links.end();++it)
for(auto &it: links)
{
auto ft = RsFileTree::fromRadix64((*it).radix().toStdString());
auto ft = RsFileTree::fromRadix64(it.radix().toStdString());
col.merge_in(*ft);
}
links.clear();
RSLinkClipboard::pasteLinks(links,RetroShareLink::TYPE_FILE);
for(QList<RetroShareLink>::const_iterator it(links.begin());it!=links.end();++it)
col.merge_in((*it).name(),(*it).size(),RsFileHash((*it).hash().toStdString())) ;
for(auto &it : links)
col.merge_in(it.name(),it.size(),RsFileHash(it.hash().toStdString())) ;
col.downloadFiles();
}
@ -1980,13 +1987,13 @@ void TransfersDialog::getDLSelectedItems(std::set<RsFileHash> *ids, std::set<int
if (ids) ids->clear();
if (rows) rows->clear();
QModelIndexList selectedRows = selection->selectedRows(COLUMN_ID);
QModelIndexList selectedRows = selection->selectedRows(DLListDelegate::COLUMN_ID);
int i, imax = selectedRows.count();
for (i = 0; i < imax; ++i) {
QModelIndex index = selectedRows.at(i);
if (index.parent().isValid())
index = index.model()->index(index.parent().row(), COLUMN_ID);
if (index.parent().isValid() && index.model())
index = index.model()->index(index.parent().row(), DLListDelegate::COLUMN_ID);
if (ids) {
ids->insert(RsFileHash(index.data(Qt::DisplayRole).toString().toStdString()));
@ -2014,7 +2021,7 @@ void TransfersDialog::getULSelectedItems(std::set<RsFileHash> *ids, std::set<int
foreach(index, indexes) {
if (ids) {
QStandardItem *id = ULListModel->item(index.row(), COLUMN_UHASH);
QStandardItem *id = ULListModel->item(index.row(), ULListDelegate::COLUMN_UHASH);
ids->insert(RsFileHash(id->data(Qt::DisplayRole).toString().toStdString()));
}
if (rows) {
@ -2067,22 +2074,23 @@ void TransfersDialog::dlOpenFolder()
break;
}
openFolder(info);
}
void TransfersDialog::openFolder(const FileInfo& info)
{
/* make path for downloaded or downloading files */
QFileInfo qinfo;
std::string path;
if (info.downloadStatus == FT_STATE_COMPLETE) {
path = info.path;
} else {
path = rsFiles->getPartialsDirectory();
}
QDir directory;
if (info.downloadStatus == FT_STATE_COMPLETE)
directory = QFileInfo(QString::fromStdString(info.path)).absoluteDir().path();
else
directory = QDir(QString::fromStdString(rsFiles->getPartialsDirectory()));
/* open folder with a suitable application */
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists() && qinfo.isDir()) {
if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) {
std::cerr << "dlOpenFolder(): can't open folder " << path << std::endl;
}
}
if (directory.exists() && !RsUrlHandler::openUrl(QUrl::fromLocalFile(directory.path())))
std::cerr << "dlOpenFolder(): can't open folder " << directory.path().toStdString() << std::endl;
}
void TransfersDialog::ulOpenFolder()
@ -2097,19 +2105,7 @@ void TransfersDialog::ulOpenFolder()
break;
}
/* make path for uploading files */
QFileInfo qinfo;
std::string path;
path = info.path.substr(0,info.path.length()-info.fname.length());
/* open folder with a suitable application */
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists() && qinfo.isDir()) {
if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) {
std::cerr << "ulOpenFolder(): can't open folder " << path << std::endl;
}
}
openFolder(info);
}
void TransfersDialog::dlPreviewFile()
@ -2132,7 +2128,7 @@ void TransfersDialog::dlPreviewFile()
/* make path for downloaded or downloading files */
QFileInfo fileInfo;
if (info.downloadStatus == FT_STATE_COMPLETE) {
fileInfo = QFileInfo(QString::fromUtf8(info.path.c_str()), QString::fromUtf8(info.fname.c_str()));
fileInfo = QFileInfo(QString::fromUtf8(info.path.c_str()));
} else {
fileInfo = QFileInfo(QString::fromUtf8(rsFiles->getPartialsDirectory().c_str()), QString::fromUtf8(info.hash.toStdString().c_str()));
@ -2197,7 +2193,7 @@ void TransfersDialog::dlOpenFile()
/* make path for downloaded or downloading files */
std::string path;
if (info.downloadStatus == FT_STATE_COMPLETE) {
path = info.path + "/" + info.fname;
path = info.path ;
/* open file with a suitable application */
QFileInfo qinfo;
@ -2237,6 +2233,10 @@ void TransfersDialog::chunkStreaming()
}
void TransfersDialog::chunkRandom()
{
#ifdef WINDOWS_SYS
if(QMessageBox::Yes != QMessageBox::warning(nullptr,tr("Warning"),tr("On Windows systems, writing in the middle of large empty files may hang the software for several seconds. Do you want to use this option anyway?"),QMessageBox::Yes,QMessageBox::No))
return;
#endif
setChunkStrategy(FileChunksInfo::CHUNK_STRATEGY_RANDOM) ;
}
void TransfersDialog::chunkProgressive()
@ -2387,72 +2387,72 @@ return 0.0 ;
double TransfersDialog::getSpeed(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_DLSPEED), Qt::DisplayRole).toDouble();
return model->data(model->index(row, DLListDelegate::COLUMN_DLSPEED), Qt::DisplayRole).toDouble();
}
QString TransfersDialog::getFileName(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_NAME), Qt::DisplayRole).toString();
return model->data(model->index(row, DLListDelegate::COLUMN_NAME), Qt::DisplayRole).toString();
}
QString TransfersDialog::getStatus(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_STATUS), Qt::DisplayRole).toString();
return model->data(model->index(row, DLListDelegate::COLUMN_STATUS), Qt::DisplayRole).toString();
}
QString TransfersDialog::getID(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_ID), Qt::UserRole).toString().left(40); // gets only the "hash" part of the name
return model->data(model->index(row, DLListDelegate::COLUMN_ID), Qt::UserRole).toString().left(40); // gets only the "hash" part of the name
}
QString TransfersDialog::getID(int row, QSortFilterProxyModel *filter)
{
QModelIndex index = filter->mapToSource(filter->index(row, COLUMN_ID));
QModelIndex index = filter->mapToSource(filter->index(row, DLListDelegate::COLUMN_ID));
return filter->sourceModel()->data(index, Qt::UserRole).toString().left(40); // gets only the "hash" part of the name
}
QString TransfersDialog::getPriority(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_PRIORITY), Qt::DisplayRole).toString();
return model->data(model->index(row, DLListDelegate::COLUMN_PRIORITY), Qt::DisplayRole).toString();
}
qlonglong TransfersDialog::getFileSize(int row, QStandardItemModel *model)
{
bool ok = false;
return model->data(model->index(row, COLUMN_SIZE), Qt::DisplayRole).toULongLong(&ok);
return model->data(model->index(row, DLListDelegate::COLUMN_SIZE), Qt::DisplayRole).toULongLong(&ok);
}
qlonglong TransfersDialog::getTransfered(int row, QStandardItemModel *model)
{
bool ok = false;
return model->data(model->index(row, COLUMN_COMPLETED), Qt::DisplayRole).toULongLong(&ok);
return model->data(model->index(row, DLListDelegate::COLUMN_COMPLETED), Qt::DisplayRole).toULongLong(&ok);
}
qlonglong TransfersDialog::getRemainingTime(int row, QStandardItemModel *model)
{
bool ok = false;
return model->data(model->index(row, COLUMN_REMAINING), Qt::DisplayRole).toULongLong(&ok);
return model->data(model->index(row, DLListDelegate::COLUMN_REMAINING), Qt::DisplayRole).toULongLong(&ok);
}
qlonglong TransfersDialog::getDownloadTime(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_DOWNLOADTIME), Qt::DisplayRole).toULongLong();
return model->data(model->index(row, DLListDelegate::COLUMN_DOWNLOADTIME), Qt::DisplayRole).toULongLong();
}
qlonglong TransfersDialog::getLastDL(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_LASTDL), Qt::DisplayRole).toULongLong();
return model->data(model->index(row, DLListDelegate::COLUMN_LASTDL), Qt::DisplayRole).toULongLong();
}
qlonglong TransfersDialog::getPath(int row, QStandardItemModel *model)
{
return model->data(model->index(row, COLUMN_PATH), Qt::DisplayRole).toULongLong();
return model->data(model->index(row, DLListDelegate::COLUMN_PATH), Qt::DisplayRole).toULongLong();
}
QString TransfersDialog::getSources(int row, QStandardItemModel *model)
{
double dblValue = model->data(model->index(row, COLUMN_SOURCES), Qt::DisplayRole).toDouble();
double dblValue = model->data(model->index(row, DLListDelegate::COLUMN_SOURCES), Qt::DisplayRole).toDouble();
QString temp = QString("%1 (%2)").arg((int)dblValue).arg((int)((fmod(dblValue,1)*1000)+0.5));
return temp;
@ -2612,25 +2612,25 @@ void TransfersDialog::collAutoOpen(const QString &fileHash)
}
}
void TransfersDialog::setShowDLSizeColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_SIZE, !show); }
void TransfersDialog::setShowDLCompleteColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_COMPLETED, !show); }
void TransfersDialog::setShowDLDLSpeedColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_DLSPEED, !show); }
void TransfersDialog::setShowDLProgressColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_PROGRESS, !show); }
void TransfersDialog::setShowDLSourcesColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_SOURCES, !show); }
void TransfersDialog::setShowDLStatusColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_STATUS, !show); }
void TransfersDialog::setShowDLPriorityColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_PRIORITY, !show); }
void TransfersDialog::setShowDLRemainingColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_REMAINING, !show); }
void TransfersDialog::setShowDLDownloadTimeColumn(bool show) { ui.downloadList->setColumnHidden(COLUMN_DOWNLOADTIME, !show); }
void TransfersDialog::setShowDLIDColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_ID, !show); }
void TransfersDialog::setShowDLLastDLColumn (bool show) { ui.downloadList->setColumnHidden(COLUMN_LASTDL, !show); }
void TransfersDialog::setShowDLPath (bool show) { ui.downloadList->setColumnHidden(COLUMN_PATH, !show); }
void TransfersDialog::setShowDLSizeColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_SIZE, !show); }
void TransfersDialog::setShowDLCompleteColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_COMPLETED, !show); }
void TransfersDialog::setShowDLDLSpeedColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_DLSPEED, !show); }
void TransfersDialog::setShowDLProgressColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_PROGRESS, !show); }
void TransfersDialog::setShowDLSourcesColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_SOURCES, !show); }
void TransfersDialog::setShowDLStatusColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_STATUS, !show); }
void TransfersDialog::setShowDLPriorityColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_PRIORITY, !show); }
void TransfersDialog::setShowDLRemainingColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_REMAINING, !show); }
void TransfersDialog::setShowDLDownloadTimeColumn(bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_DOWNLOADTIME, !show); }
void TransfersDialog::setShowDLIDColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_ID, !show); }
void TransfersDialog::setShowDLLastDLColumn (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_LASTDL, !show); }
void TransfersDialog::setShowDLPath (bool show) { ui.downloadList->setColumnHidden(DLListDelegate::COLUMN_PATH, !show); }
void TransfersDialog::setShowULPeerColumn (bool show) { ui.uploadsList->setColumnHidden(COLUMN_UPEER, !show); }
void TransfersDialog::setShowULSizeColumn (bool show) { ui.uploadsList->setColumnHidden(COLUMN_USIZE, !show); }
void TransfersDialog::setShowULTransferredColumn(bool show) { ui.uploadsList->setColumnHidden(COLUMN_UTRANSFERRED, !show); }
void TransfersDialog::setShowULSpeedColumn (bool show) { ui.uploadsList->setColumnHidden(COLUMN_ULSPEED, !show); }
void TransfersDialog::setShowULProgressColumn (bool show) { ui.uploadsList->setColumnHidden(COLUMN_UPROGRESS, !show); }
void TransfersDialog::setShowULHashColumn (bool show) { ui.uploadsList->setColumnHidden(COLUMN_UHASH, !show); }
void TransfersDialog::setShowULPeerColumn (bool show) { ui.uploadsList->setColumnHidden(ULListDelegate::COLUMN_UPEER, !show); }
void TransfersDialog::setShowULSizeColumn (bool show) { ui.uploadsList->setColumnHidden(ULListDelegate::COLUMN_USIZE, !show); }
void TransfersDialog::setShowULTransferredColumn(bool show) { ui.uploadsList->setColumnHidden(ULListDelegate::COLUMN_UTRANSFERRED, !show); }
void TransfersDialog::setShowULSpeedColumn (bool show) { ui.uploadsList->setColumnHidden(ULListDelegate::COLUMN_ULSPEED, !show); }
void TransfersDialog::setShowULProgressColumn (bool show) { ui.uploadsList->setColumnHidden(ULListDelegate::COLUMN_UPROGRESS, !show); }
void TransfersDialog::setShowULHashColumn (bool show) { ui.uploadsList->setColumnHidden(ULListDelegate::COLUMN_UHASH, !show); }
void TransfersDialog::expandAllDL()
{

View file

@ -173,6 +173,7 @@ signals:
void playFiles(QStringList files);
private:
void openFolder(const FileInfo& info);
RsDownloadListModel *DLListModel;
QSortFilterProxyModel *DLLFilterModel;

View file

@ -70,7 +70,14 @@
</widget>
</item>
<item>
<widget class="StyledLabel" name="titleBarLabel">
<widget class="QLabel" name="titleBarLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>File Transfers</string>
</property>
@ -213,6 +220,9 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>false</bool>
</property>
@ -280,6 +290,9 @@
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="animated">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
@ -294,11 +307,6 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>StyledLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledLabel.h</header>
</customwidget>
<customwidget>
<class>LineEditClear</class>
<extends>QLineEdit</extends>

View file

@ -25,14 +25,14 @@
Q_DECLARE_METATYPE(FileProgressInfo)
#define MAX_CHAR_TMP 128
ULListDelegate::ULListDelegate(QObject *parent) : QAbstractItemDelegate(parent)
{
;
}
ULListDelegate::~ULListDelegate(void)
{
;
}
void ULListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
@ -65,7 +65,7 @@ void ULListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
// draw the background color
bool bDrawBackground = true;
if(index.column() == COLUMN_UPROGRESS) {
if(index.column() == COLUMN_UPROGRESS) {
FileProgressInfo pinfo = index.data().value<FileProgressInfo>() ;
bDrawBackground = (pinfo.type == FileProgressInfo::UNINIT);
}
@ -133,7 +133,7 @@ void ULListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
}
painter->drawText(option.rect, Qt::AlignRight, temp);
break;
case COLUMN_UPROGRESS:
case COLUMN_UPROGRESS:
{
FileProgressInfo pinfo = index.data().value<FileProgressInfo>() ;
if (pinfo.type == FileProgressInfo::UNINIT)
@ -143,7 +143,7 @@ void ULListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
painter->save() ;
xProgressBar progressBar(pinfo,option.rect,painter,0);// the 3rd param is the color schema (0 is the default value)
QString ext = QFileInfo(QString::fromStdString(index.sibling(index.row(), COLUMN_UNAME).data().toString().toStdString())).suffix();;
QString ext = QFileInfo(QString::fromStdString(index.sibling(index.row(), COLUMN_UNAME).data().toString().toStdString())).suffix();;
if (ext == "rsfc" || ext == "rsrl" || ext == "dist" || ext == "rsfb")
progressBar.setColorSchema( 9);
else
@ -157,8 +157,8 @@ void ULListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
}
painter->drawText(option.rect, Qt::AlignCenter, newopt.text);
break;
case COLUMN_UNAME:
case COLUMN_UPEER:
case COLUMN_UNAME:
case COLUMN_UPEER:
// decoration
value = index.data(Qt::DecorationRole);
pixmap = qvariant_cast<QIcon>(value).pixmap(option.decorationSize, option.state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled, option.state & QStyle::State_Open ? QIcon::On : QIcon::Off);

View file

@ -18,43 +18,27 @@
* *
*******************************************************************************/
#ifndef ULLISTDELEGATE_H
#define ULLISTDELEGATE_H
#pragma once
#include <QAbstractItemDelegate>
// Defines for upload list list columns
#define COLUMN_UNAME 0
#define COLUMN_UPEER 1
#define COLUMN_USIZE 2
#define COLUMN_UTRANSFERRED 3
#define COLUMN_ULSPEED 4
#define COLUMN_UPROGRESS 5
#define COLUMN_UHASH 6
#define COLUMN_UCOUNT 7
#define MAX_CHAR_TMP 128
class QModelIndex;
class QPainter;
class ULListDelegate: public QAbstractItemDelegate
{
public:
ULListDelegate(QObject *parent=0);
~ULListDelegate();
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const;
class ULListDelegate: public QAbstractItemDelegate {
Q_OBJECT
public:
ULListDelegate(QObject *parent=0);
~ULListDelegate();
void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const;
private:
public slots:
signals:
static constexpr int COLUMN_UNAME = 0;
static constexpr int COLUMN_UPEER = 1;
static constexpr int COLUMN_USIZE = 2;
static constexpr int COLUMN_UTRANSFERRED = 3;
static constexpr int COLUMN_ULSPEED = 4;
static constexpr int COLUMN_UPROGRESS = 5;
static constexpr int COLUMN_UHASH = 6;
static constexpr int COLUMN_UCOUNT = 7;
};
#endif

View file

@ -0,0 +1,362 @@
/*******************************************************************************
* gui/FriendServer.cpp *
* *
* Copyright (c) 2021 Retroshare Team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* 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. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <QTimer>
#include <QMovie>
#include <QMessageBox>
#include <QTcpSocket>
#include <QMenu>
#include "retroshare/rsfriendserver.h"
#include "retroshare/rstor.h"
#include "util/qtthreadsutils.h"
#include "util/misc.h"
#include "gui/common/FilesDefs.h"
#include "FriendServerControl.h"
#include <iostream>
#define ICON_STATUS_UNKNOWN ":/images/ledoff1.png"
#define ICON_STATUS_OK ":/images/ledon1.png"
#define NAME_COLUMN 0
#define NODE_COLUMN 1
#define ADDR_COLUMN 2
#define STAT_COLUMN 3
/** Constructor */
FriendServerControl::FriendServerControl(QWidget *parent)
: MainPage(parent)
{
/* Invoke the Qt Designer generated object setup routine */
setupUi(this);
friendServerOnOff_CB->setEnabled(false); // until FS is connected.
mCurrentlyCheckingServerAddress = false;
if(!rsFriendServer)
{
setEnabled(false);
return;
}
int H = QFontMetricsF(torServerAddress_LE->font()).height();
QString help_str = tr("\
<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Friend Server</h1> \
<p>This configuration panel allows you to specify the onion address of a \
friend server. Retroshare will talk to that server anonymously through Tor \
and use it to acquire a fixed number of friends.</p> \
<p>The friend server will continue supplying new friends until that number is reached \
in particular if you add your own friends manually, the friend server may become useless \
and you will save bandwidth disabling it. When disabling it, you will keep existing friends.</p> \
<p>The friend server only knows your peer ID and profile public key. It doesn't know your IP address.</p> \
"
).arg(QString::number(2*H), QString::number(2*H)) ;
registerHelpButton(helpButton,help_str,"Friend Server") ;
mConnectionCheckTimer = new QTimer;
whileBlocking(autoAccept_CB)->setChecked(rsFriendServer->autoAddFriends());
// Init values
whileBlocking(torServerFriendsToRequest_SB)->setValue(rsFriendServer->friendsToRequest());
// Connect slignals/slots
QObject::connect(friendServerOnOff_CB,SIGNAL(toggled(bool)),this,SLOT(onOnOffClick(bool)));
QObject::connect(torServerFriendsToRequest_SB,SIGNAL(valueChanged(int)),this,SLOT(onFriendsToRequestChanged(int)));
QObject::connect(torServerAddress_LE,SIGNAL(textEdited(const QString&)),this,SLOT(onOnionAddressEdit(const QString&)));
QObject::connect(torServerPort_SB,SIGNAL(valueChanged(int)),this,SLOT(onOnionPortEdit(int)));
QObject::connect(autoAccept_CB,SIGNAL(toggled(bool)),this,SLOT(onAutoAddFriends(bool)));
QObject::connect(mConnectionCheckTimer,SIGNAL(timeout()),this,SLOT(checkServerAddress()));
QObject::connect(status_TW, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(launchStatusContextMenu(QPoint)));
mCheckingServerMovie = new QMovie(":/images/loader/circleball-16.gif");
// Do not block signal for these two, since we want to trigger a test to initialise the online status.
torServerAddress_LE->setText(QString::fromStdString(rsFriendServer->friendsServerAddress().c_str()));
torServerPort_SB->setValue(rsFriendServer->friendsServerPort());
updateFriendServerStatusIcon(false);
makeFriend_ACT = new QAction(tr("Make friend")); // makes SSL-only friend with the peer
QObject::connect(makeFriend_ACT,SIGNAL(triggered()),this,SLOT(makeFriend()));
mEventHandlerId_fs = 0;
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
{
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
}, mEventHandlerId_fs, RsEventType::FRIEND_SERVER );
mEventHandlerId_peer = 0;
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
{
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
}, mEventHandlerId_peer, RsEventType::PEER_CONNECTION );
}
void FriendServerControl::onAutoAddFriends(bool b)
{
rsFriendServer->setAutoAddFriends(b);
}
void FriendServerControl::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
{
{
const RsFriendServerEvent *fe = dynamic_cast<const RsFriendServerEvent*>(event.get());
if(fe)
switch(fe->mFriendServerEventType)
{
case RsFriendServerEventCode::PEER_INFO_CHANGED: updateContactsStatus();
break;
case RsFriendServerEventCode::FRIEND_SERVER_STATUS_CHANGED: updateFriendServerStatusIcon(fe->mFriendServerStatus == RsFriendServerStatus::ONLINE);
break;
default:
case RsFriendServerEventCode::UNKNOWN: break;
}
}
{
const RsConnectionEvent *pe = dynamic_cast<const RsConnectionEvent*>(event.get());
if(pe)
switch(pe->mConnectionInfoCode)
{
case RsConnectionEventCode::PEER_ADDED:
case RsConnectionEventCode::PEER_REMOVED:
case RsConnectionEventCode::PEER_CONNECTED: updateContactsStatus();
break;
default: ;
}
}
}
FriendServerControl::~FriendServerControl()
{
delete mCheckingServerMovie;
delete mConnectionCheckTimer;
rsEvents->unregisterEventsHandler(mEventHandlerId_fs);
rsEvents->unregisterEventsHandler(mEventHandlerId_peer);
}
void FriendServerControl::launchStatusContextMenu(QPoint p)
{
RsPeerId peer_id = getCurrentPeer();
RsPeerDetails det;
if(rsPeers->getPeerDetails(peer_id,det) && det.accept_connection)
return;
QMenu contextMnu(this);
contextMnu.addAction(makeFriend_ACT);
contextMnu.exec(QCursor::pos());
}
void FriendServerControl::onOnOffClick(bool b)
{
if(b)
{
if(passphrase_LE->text().isNull())
{
QMessageBox::critical(nullptr,tr("Missing profile passphrase."),tr("Your profile passphrase is missing. Please enter is in the field below before enabling the friend server."));
whileBlocking(friendServerOnOff_CB)->setCheckState(Qt::Unchecked);
return;
}
rsFriendServer->setProfilePassphrase(passphrase_LE->text().toStdString());
rsFriendServer->startServer();
}
else
rsFriendServer->stopServer();
}
void FriendServerControl::onOnionPortEdit(int)
{
// Setup timer to auto-check the friend server address
mConnectionCheckTimer->stop();
mConnectionCheckTimer->setSingleShot(true);
mConnectionCheckTimer->setInterval(5000); // check in 5 secs unless something is changed in the mean time.
mConnectionCheckTimer->start();
if(mCheckingServerMovie->fileName() != QString(":/images/loader/circleball-16.gif" ))
{
mCheckingServerMovie->setFileName(":/images/loader/circleball-16.gif");
mCheckingServerMovie->start();
}
}
void FriendServerControl::onOnionAddressEdit(const QString&)
{
while(mCurrentlyCheckingServerAddress)
{
std::cerr << " waiting for free slot" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500)); // wait for ongoing check to finish.
}
std::cerr << "Resetting connection proxy timer" << std::endl;
// Setup timer to auto-check the friend server address
mConnectionCheckTimer->setSingleShot(true);
mConnectionCheckTimer->setInterval(5000); // check in 5 secs unless something is changed in the mean time.
mConnectionCheckTimer->start();
}
void FriendServerControl::checkServerAddress()
{
std::cerr << "In checkServerAddress() ..." << std::endl;
mCurrentlyCheckingServerAddress = true;
serverStatusCheckResult_LB->setMovie(mCheckingServerMovie);
serverStatusCheckResult_LB->setToolTip(tr("Trying to contact friend server\nThis may take up to 1 min.")) ;
mCheckingServerMovie->setFileName(":/images/loader/circleball-16.gif");
mCheckingServerMovie->start();
RsThread::async( [this]()
{
auto port = torServerPort_SB->value();
auto addr = torServerAddress_LE->text().toStdString();
std::cerr << "calling sync test..." << std::endl;
bool succeed = rsFriendServer->checkServerAddress(addr,port,5000);
std::cerr << "result is " << succeed << std::endl;
RsQThreadUtils::postToObject( [addr,port,succeed,this]()
{
if(succeed)
rsFriendServer->setServerAddress(addr,port);
mCheckingServerMovie->stop();
updateFriendServerStatusIcon(succeed);
mCurrentlyCheckingServerAddress = false;
},this);
});
}
void FriendServerControl::onNbFriendsToRequestsChanged(int n)
{
rsFriendServer->setFriendsToRequest(n);
}
void FriendServerControl::updateFriendServerStatusIcon(bool ok)
{
std::cerr << "updating proxy status icon" << std::endl;
if(ok)
{
serverStatusCheckResult_LB->setToolTip(tr("Friend server is currently reachable.")) ;
serverStatusCheckResult_LB->setPixmap(QPixmap(ICON_STATUS_OK));
friendServerOnOff_CB->setEnabled(true);
status_TW->setEnabled(true);
}
else
{
rsFriendServer->stopServer();
serverStatusCheckResult_LB->setToolTip(tr("The proxy is not enabled or broken.\nAre all services up and running fine??\nAlso check your ports!")) ;
serverStatusCheckResult_LB->setPixmap(QPixmap(ICON_STATUS_UNKNOWN));
friendServerOnOff_CB->setChecked(false);
friendServerOnOff_CB->setEnabled(false);
status_TW->setEnabled(false);
}
}
void FriendServerControl::updateContactsStatus()
{
std::map<RsPeerId,RsFriendServer::RsFsPeerInfo> pinfo = rsFriendServer->getPeersInfo();
status_TW->clear();
int row = 0;
status_TW->setRowCount(pinfo.size());
status_TW->setColumnCount(4);
status_TW->setHorizontalHeaderItem(NAME_COLUMN,new QTableWidgetItem(QObject::tr("Name")));
status_TW->setHorizontalHeaderItem(NODE_COLUMN,new QTableWidgetItem(QObject::tr("Node")));
status_TW->setHorizontalHeaderItem(ADDR_COLUMN,new QTableWidgetItem(QObject::tr("Address")));
status_TW->setHorizontalHeaderItem(STAT_COLUMN,new QTableWidgetItem(QObject::tr("Status")));
for(auto it:pinfo)
{
uint32_t err_code=0;
RsPeerDetails details;
rsPeers->parseShortInvite(it.second.mInvite,details,err_code);
status_TW->setItem(row,NAME_COLUMN,new QTableWidgetItem(QString::fromStdString(details.name)));
status_TW->setItem(row,NODE_COLUMN,new QTableWidgetItem(QString::fromStdString(details.id.toStdString())));
status_TW->setItem(row,ADDR_COLUMN,new QTableWidgetItem(QString::fromStdString(details.hiddenNodeAddress)+":"+QString::number(details.hiddenNodePort)));
QString status_string;
if(details.accept_connection)
status_string += QString("Friend");
else
status_string += QString("Not friend");
status_string += QString(" / ");
switch(it.second.mPeerLevel)
{
case RsFriendServer::PeerFriendshipLevel::NO_KEY: status_string += "Doesn't have my key" ; break;
case RsFriendServer::PeerFriendshipLevel::HAS_KEY: status_string += "Has my key" ; break;
case RsFriendServer::PeerFriendshipLevel::HAS_ACCEPTED_KEY: status_string += "Has friended me" ; break;
default:
case RsFriendServer::PeerFriendshipLevel::UNKNOWN: status_string += "Unkn" ; break;
}
status_TW->setItem(row,STAT_COLUMN,new QTableWidgetItem(status_string));
row++;
}
}
RsPeerId FriendServerControl::getCurrentPeer()
{
QTableWidgetItem *item = status_TW->currentItem();
if(!item)
return RsPeerId();
return RsPeerId(status_TW->item(item->row(),NODE_COLUMN)->text().toStdString());
}
void FriendServerControl::makeFriend()
{
RsPeerId peer_id = getCurrentPeer();
rsFriendServer->allowPeer(peer_id);
}

View file

@ -1,7 +1,7 @@
/*******************************************************************************
* gui/common/StyleElidedLabel.cpp *
* gui/NetworkView.h *
* *
* Copyright (c) 2014, RetroShare Team <retroshare.project@gmail.com> *
* Copyright (c) 2008 Robert Fernie <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
@ -18,29 +18,48 @@
* *
*******************************************************************************/
#include <QFont>
#include "StyledElidedLabel.h"
#pragma once
/** Constructor */
StyledElidedLabel::StyledElidedLabel(QWidget *parent)
: ElidedLabel(parent), _lastFactor(-1)
{
}
#include <QGraphicsScene>
StyledElidedLabel::StyledElidedLabel(const QString &text, QWidget *parent)
: ElidedLabel(text, parent), _lastFactor(-1)
{
}
#include "retroshare/rsevents.h"
void StyledElidedLabel::setFontSizeFactor(int factor)
#include "retroshare-gui/mainpage.h"
#include "ui_FriendServerControl.h"
class FriendServerControl : public MainPage, public Ui::FriendServerControl
{
int newFactor = factor;
if (factor > 0) {
if (_lastFactor > 0) newFactor = 100 + factor - _lastFactor;
_lastFactor = factor;
QFont f = font();
qreal fontSize = newFactor * f.pointSizeF() / 100;
f.setPointSizeF(fontSize);
setFont(f);
}
}
Q_OBJECT
public:
FriendServerControl(QWidget *parent = 0);
virtual ~FriendServerControl();
protected slots:
void onOnOffClick(bool b);
void onOnionAddressEdit(const QString &);
void onOnionPortEdit(int);
void onNbFriendsToRequestsChanged(int n);
void checkServerAddress();
void onAutoAddFriends(bool b);
void launchStatusContextMenu(QPoint p);
void makeFriend();
private:
RsPeerId getCurrentPeer();
void updateContactsStatus();
void updateFriendServerStatusIcon(bool ok);
void handleEvent_main_thread(std::shared_ptr<const RsEvent> event);
QTimer *mConnectionCheckTimer;
QMovie *mCheckingServerMovie;
bool mCurrentlyCheckingServerAddress;
RsEventsHandlerId_t mEventHandlerId_fs;
RsEventsHandlerId_t mEventHandlerId_peer;
QAction *makeFriend_ACT;
QAction *unmakeFriend_ACT;
QAction *removePeer_ACT;
QAction *removePeerPermanently_ACT;
};

View file

@ -0,0 +1,241 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FriendServerControl</class>
<widget class="QWidget" name="FriendServerControl">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>620</width>
<height>499</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Server onion address:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="torServerAddress_LE">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter here the onion address of the Friend Server that was given to you. The address will be automatically checked after you enter it and a green bullet will appear if the server is online.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="placeholderText">
<string>Onion address of the friend server</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="torServerPort_SB">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Communication port of the server. You usually get a server address as somestring.onion:port. The port is the number right after &amp;quot;:&amp;quot;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1025</number>
</property>
<property name="maximum">
<number>65536</number>
</property>
<property name="value">
<number>2017</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="serverStatusCheckResult_LB">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="helpButton">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/icons/help_64.png</normaloff>:/icons/help_64.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Retroshare passphrase:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="passphrase_LE">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Your Retroshare login passphrase is needed to ensure the security of data exchange with the friend server.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
<property name="placeholderText">
<string>Your retroshare passphrase</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="friendServerOnOff_CB">
<property name="text">
<string>On/Off</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>302</width>
<height>26</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Friends to request: </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="torServerFriendsToRequest_SB">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>15</number>
</property>
<property name="value">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoAccept_CB">
<property name="toolTip">
<string>Auto accept received certificates as friends</string>
</property>
<property name="text">
<string>Auto-accept</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTableWidget" name="status_TW">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Node ID</string>
</property>
</column>
<column>
<property name="text">
<string>Address</string>
</property>
</column>
<column>
<property name="text">
<string>Status</string>
</property>
</column>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>380</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="icons.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -27,6 +27,7 @@
#include <retroshare/rspeers.h>
#include <retroshare/rshistory.h>
#include <retroshare/rsinit.h>
#include "chat/ChatUserNotify.h"
#include "connect/ConnectFriendWizard.h"
@ -44,6 +45,9 @@
#include "NetworkView.h"
#include "NetworkDialog.h"
#include "gui/common/NewFriendList.h"
#ifdef RS_EMBEDED_FRIEND_SERVER
#include "gui/FriendServerControl.h"
#endif
#include "gui/Identity/IdDialog.h"
/* Images for Newsfeed icons */
//#define IMAGE_NEWSFEED ""
@ -88,6 +92,10 @@ FriendsDialog::FriendsDialog(QWidget *parent) : MainPage(parent)
ui.avatar->setFrameType(AvatarWidget::STATUS_FRAME);
ui.tabWidget->setTabPosition(QTabWidget::North);
#ifdef RS_EMBEDED_FRIEND_SERVER
if(RsAccounts::isTorAuto())
ui.tabWidget->addTab(friendServerControl = new FriendServerControl(),QIcon(IMAGE_PEERS), tr("Friend Server"));
#endif
ui.tabWidget->addTab(networkView = new NetworkView(),QIcon(IMAGE_NETWORK2), tr("Network graph"));
ui.tabWidget->addTab(networkDialog = new NetworkDialog(),QIcon(IMAGE_PEERS), tr("Keyring"));
@ -117,25 +125,24 @@ FriendsDialog::FriendsDialog(QWidget *parent) : MainPage(parent)
// add self nick and Avatar to Friends.
RsPeerDetails pd ;
if (rsPeers->getPeerDetails(rsPeers->getOwnId(),pd)) {
if (rsPeers->getPeerDetails(rsPeers->getOwnId(),pd))
ui.nicknameLabel->setText(QString::fromUtf8(pd.name.c_str()) + " (" + QString::fromUtf8(pd.location.c_str())+")");
}
QString hlp_str = tr(
" <h1><img width=\"32\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Network</h1> \
<p>The Network tab shows your friend Retroshare nodes: the neighbor Retroshare nodes that are connected to you. \
</p> \
<p>You can group nodes together to allow a finer level of information access, for instance to only allow \
some nodes to see some of your files.</p> \
<p>On the right, you will find 3 useful tabs: \
<ul> \
<li>Broadcast sends messages to all connected nodes at once</li> \
<li>Local network graph shows the network around you, based on discovery information</li> \
<li>Keyring contains node keys you collected, mostly forwarded to you by your friend nodes</li> \
</ul> </p> \
") ;
int H = misc::getFontSizeFactor("HelpButton").height();
QString hlp_str = tr(
"<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Network</h1>"
"<p>The Network tab shows your friend Retroshare nodes: the neighbor Retroshare nodes that are connected to you.</p>"
"<p>You can group nodes together to allow a finer level of information access, for instance to only allow"
" some nodes to see some of your files.</p>"
"<p>On the right, you will find 3 useful tabs:"
" <ul>"
" <li>Broadcast sends messages to all connected nodes at once</li>"
" <li>Local network graph shows the network around you, based on discovery information</li>"
" <li>Keyring contains node keys you collected, mostly forwarded to you by your friend nodes</li>"
" </ul> </p>"
).arg(QString::number(2*H));
registerHelpButton(ui.helpButton, hlp_str,"FriendsDialog") ;
registerHelpButton(ui.helpButton, hlp_str,"FriendsDialog") ;
}
FriendsDialog::~FriendsDialog ()
@ -252,13 +259,9 @@ void FriendsDialog::loadmypersonalstatus()
QString statustring = QString::fromUtf8(rsMsgs->getCustomStateString().c_str());
if (statustring.isEmpty())
{
ui.mypersonalstatusLabel->setText(tr("Set your status message here."));
}
else
{
ui.mypersonalstatusLabel->setText(statustring);
}
}
void FriendsDialog::clearChatNotify()
@ -275,13 +278,11 @@ void FriendsDialog::statusmessage()
/*static*/ bool FriendsDialog::isGroupChatActive()
{
FriendsDialog *friendsDialog = dynamic_cast<FriendsDialog*>(MainWindow::getPage(MainWindow::Friends));
if (!friendsDialog) {
if (!friendsDialog)
return false;
}
if (friendsDialog->ui.tabWidget->currentWidget() == friendsDialog->ui.groupChatTab) {
if (friendsDialog->ui.tabWidget->currentWidget() == friendsDialog->ui.groupChatTab)
return true;
}
return false;
}

View file

@ -30,6 +30,7 @@ class NetworkDialog;
class NetworkView;
class IdDialog;
class CirclesDialog;
class FriendServerControl;
class FriendsDialog : public MainPage
{
@ -64,7 +65,8 @@ public:
NetworkDialog *networkDialog ;
NetworkView *networkView ;
FriendServerControl *friendServerControl ;
IdDialog *idDialog;
private slots:

View file

@ -10,7 +10,7 @@
<height>491</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="FriendsDialog_GL">
<property name="leftMargin">
<number>0</number>
</property>
@ -37,7 +37,7 @@
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="titleBarFrame_HL">
<property name="leftMargin">
<number>2</number>
</property>
@ -70,14 +70,21 @@
</widget>
</item>
<item>
<widget class="StyledLabel" name="titleBarLabel">
<widget class="QLabel" name="titleBarLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Network</string>
</property>
</widget>
</item>
<item>
<spacer>
<spacer name="titleBar_HSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@ -115,12 +122,12 @@
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="left_VL">
<property name="spacing">
<number>1</number>
</property>
<item>
<widget class="QFrame" name="headFrame">
<widget class="QFrame" name="headerBFrame">
<property name="styleSheet">
<string notr="true"/>
</property>
@ -130,7 +137,7 @@
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="headerBFrame_GL">
<property name="leftMargin">
<number>1</number>
</property>
@ -192,7 +199,14 @@
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="StyledElidedLabel" name="nicknameLabel">
<widget class="ElidedLabel" name="nicknameLabel">
<property name="font">
<font>
<pointsize>20</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string notr="true">Nickname (Location)</string>
</property>
@ -240,7 +254,7 @@
<attribute name="title">
<string>Broadcast</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<layout class="QGridLayout" name="groupChatTab_GL">
<item row="0" column="0">
<widget class="ChatWidget" name="chatWidget" native="true"/>
</item>
@ -362,11 +376,6 @@
<header>gui/common/AvatarWidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>StyledLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledLabel.h</header>
</customwidget>
<customwidget>
<class>ChatWidget</class>
<extends>QWidget</extends>
@ -380,9 +389,9 @@
<container>1</container>
</customwidget>
<customwidget>
<class>StyledElidedLabel</class>
<class>ElidedLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledElidedLabel.h</header>
<header>gui/common/ElidedLabel.h</header>
</customwidget>
<customwidget>
<class>NewFriendList</class>

View file

@ -33,27 +33,27 @@
#include <rshare.h>
#include "gui/settings/rsharesettings.h"
#include "TorControl/TorManager.h"
#include "util/misc.h"
#include "gui/common/FilesDefs.h"
#include <retroshare/rsidentity.h>
#include <retroshare/rsinit.h>
#include <retroshare/rsnotify.h>
#include <rsserver/rsaccounts.h>
#include <util/rsrandom.h>
#include "retroshare/rstor.h"
#include "retroshare/rsidentity.h"
#include "retroshare/rsinit.h"
#include "retroshare/rsnotify.h"
#include "rsserver/rsaccounts.h"
#include "util/rsrandom.h"
#include <time.h>
#include <math.h>
#include <iostream>
#define IMAGE_GOOD ":/images/accepted16.png"
#define IMAGE_BAD ":/images/cancel.png"
#define IMAGE_BAD ":/icons/cancel.svg"
class EntropyCollectorWidget: public QTextBrowser
{
public:
EntropyCollectorWidget(QProgressBar *pr,QWidget *p = NULL)
explicit EntropyCollectorWidget(QProgressBar *pr,QWidget *p = NULL)
: QTextBrowser(p)
{
progress = pr ;
@ -150,7 +150,7 @@ GenCertDialog::GenCertDialog(bool onlyGenerateIdentity, QWidget *parent)
connect(ui.node_input, SIGNAL(textChanged(QString)), this, SLOT(updateCheckLabels()));
connect(ui.reuse_existing_node_CB, SIGNAL(toggled(bool)), this, SLOT(updateCheckLabels()));
connect(ui.cbUseBob, SIGNAL(clicked(bool)), this, SLOT(useBobChecked(bool)));;
connect(ui.cbUseBob, SIGNAL(clicked(bool)), this, SLOT(useI2pChecked(bool)));;
entropy_timer = new QTimer ;
entropy_timer->start(20) ;
@ -288,8 +288,6 @@ void GenCertDialog::setupState()
break;
}
//ui.no_node_label->setVisible(false);
setWindowTitle(generate_new?tr("Create new profile and new Retroshare node"):tr("Create new Retroshare node"));
//ui.headerFrame->setHeaderText(generate_new?tr("Create a new profile and node"):tr("Create a new node"));
@ -300,15 +298,9 @@ void GenCertDialog::setupState()
ui.genPGPuser->setVisible(adv_state && haveGPGKeys && !generate_new) ;
//ui.genprofileinfo_label->setVisible(false);
//ui.no_gpg_key_label->setText(tr("Welcome to Retroshare. Before you can proceed you need to create a profile and associate a node with it. To do so please fill out this form.\nAlternatively you can import a (previously exported) profile. Just uncheck \"Create a new profile\""));
//no_gpg_key_label->setVisible(false);
ui.name_label->setVisible(true);
ui.name_input->setVisible(generate_new);
//ui.header_label->setVisible(false) ;
ui.nickname_label->setVisible(adv_state && !mOnlyGenerateIdentity);
ui.nickname_input->setVisible(adv_state && !mOnlyGenerateIdentity);
@ -341,9 +333,9 @@ void GenCertDialog::setupState()
ui.hiddenport_spinBox->setVisible(hidden_state && !tor_auto);
ui.cbUseBob->setVisible(hidden_state && !tor_auto);
#ifndef RS_USE_I2P_BOB
#ifndef RS_USE_I2P_SAM3
ui.cbUseBob->setDisabled(true);
ui.cbUseBob->setToolTip(tr("BOB support is not available"));
ui.cbUseBob->setToolTip(tr("SAMv3 support is not available"));
#endif
if(!mAllFieldsOk)
@ -352,8 +344,8 @@ void GenCertDialog::setupState()
ui.genButton->setVisible(false) ;
ui.generate_label->setVisible(false) ;
ui.info_label->setText("Please choose a profile name and password...") ;
ui.info_label->setVisible(true) ;
ui.info_Label->setText("Please choose a profile name and password...") ;
ui.info_Label->setVisible(true) ;
}
else if(!mEntropyOk)
{
@ -361,8 +353,8 @@ void GenCertDialog::setupState()
ui.genButton->setVisible(false) ;
ui.generate_label->setVisible(false) ;
ui.info_label->setText("Please move your mouse randomly to generate enough random data to create your profile.") ;
ui.info_label->setVisible(true) ;
ui.info_Label->setText("Please move your mouse randomly to generate enough random data to create your profile.") ;
ui.info_Label->setVisible(true) ;
}
else
{
@ -371,7 +363,7 @@ void GenCertDialog::setupState()
ui.genButton->setToolTip(tr("Click to create your node and/or profile")) ;
ui.genButton->setVisible(true) ;
ui.generate_label->setVisible(false) ;
ui.info_label->setVisible(false) ;
ui.info_Label->setVisible(false) ;
}
}
@ -407,7 +399,7 @@ void GenCertDialog::updateCheckLabels()
ui.node_name_check_LB ->setPixmap(bad) ;
}
if(!generate_new || ui.name_input->text().length() >= 3)
if(!generate_new || ui.name_input->text().trimmed().length() >= 3)
ui.profile_name_check_LB ->setPixmap(good) ;
else
{
@ -440,10 +432,10 @@ void GenCertDialog::updateCheckLabels()
setupState();
}
void GenCertDialog::useBobChecked(bool checked)
void GenCertDialog::useI2pChecked(bool checked)
{
if (checked) {
ui.hiddenaddr_input->setPlaceholderText(tr("I2P instance address with BOB enabled"));
ui.hiddenaddr_input->setPlaceholderText(tr("I2P instance address with SAMv3 enabled"));
ui.hiddenaddr_label->setText(tr("I2P instance address"));
ui.hiddenport_spinBox->setEnabled(false);
@ -528,7 +520,7 @@ void GenCertDialog::genPerson()
bool isHiddenLoc = (ui.nodeType_CB->currentIndex()>0);
bool isAutoTor = (ui.nodeType_CB->currentIndex()==1);
if(isAutoTor && !Tor::TorManager::isTorAvailable())
if(isAutoTor && !RsTor::isTorAvailable())
{
QMessageBox::critical(this,tr("Tor is not available"),tr("No Tor executable has been found on your system. You need to install Tor before creating a hidden identity.")) ;
return ;
@ -539,12 +531,12 @@ void GenCertDialog::genPerson()
std::string hl = ui.hiddenaddr_input->text().toStdString();
uint16_t port = ui.hiddenport_spinBox->value();
bool useBob = ui.cbUseBob->isChecked();
bool useI2p = ui.cbUseBob->isChecked();
if (useBob && hl.empty())
if (useI2p && hl.empty())
hl = "127.0.0.1";
RsInit::SetHiddenLocation(hl, port, useBob); /* parses it */
RsInit::SetHiddenLocation(hl, port, useI2p); /* parses it */
}
@ -591,7 +583,6 @@ void GenCertDialog::genPerson()
//generate a new gpg key
std::string err_string;
//_key_label->setText(tr("Generating new node key, please be patient: this process needs generating large prime numbers, and can take some minutes on slow computers. \n\nFill in your password when asked, to sign your new key."));
//ui.no_gpg_key_label->show();
//ui.reuse_existing_node_CB->hide();
ui.name_label->hide();
ui.name_input->hide();
@ -609,7 +600,6 @@ void GenCertDialog::genPerson()
ui.node_input->hide();
ui.genButton->hide();
ui.importIdentity_PB->hide();
//ui.genprofileinfo_label->hide();
ui.nodeType_CB->hide();
//ui.adv_checkbox->hide();
ui.keylength_label->hide();

View file

@ -31,7 +31,7 @@ class GenCertDialog : public QDialog
public:
/** Default constructor */
GenCertDialog(bool onlyGenerateIdentity, QWidget *parent = 0);
explicit GenCertDialog(bool onlyGenerateIdentity, QWidget *parent = 0);
virtual ~GenCertDialog() ;
virtual void mouseMoveEvent(QMouseEvent *e) ;
@ -44,7 +44,7 @@ private slots:
void switchReuseExistingNode();
void grabMouse();
void updateCheckLabels();
void useBobChecked(bool checked);
void useI2pChecked(bool checked);
private:
void initKeyList();

View file

@ -111,7 +111,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="nodeType_CB">
<widget class="RSComboBox" name="nodeType_CB">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -187,7 +187,7 @@
<item row="1" column="0">
<layout class="QGridLayout" name="profilGLayout">
<item row="14" column="2">
<widget class="QComboBox" name="keylength_comboBox">
<widget class="RSComboBox" name="keylength_comboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -245,6 +245,12 @@
<height>0</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Put a strong password here. This password protects your private node key!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -303,6 +309,12 @@
<height>0</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Your node name designates the Retroshare instance that&lt;/p&gt;&lt;p&gt;will run on this computer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -340,6 +352,12 @@
<height>32</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The profile name identifies you over the network.&lt;/p&gt;&lt;p&gt;It is used by your friends to accept connections from you.&lt;/p&gt;&lt;p&gt;You can create multiple Retroshare nodes with the&lt;/p&gt;&lt;p&gt;same profile on different computers.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -352,13 +370,19 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="genPGPuser">
<widget class="RSComboBox" name="genPGPuser">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>Your profile is associated with a PGP key pair. RetroShare currently ignores DSA keys.</string>
</property>
@ -417,6 +441,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Put a strong password here. This password protects your private node key!&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -456,6 +486,12 @@
<height>32</height>
</size>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This should be a Tor Onion address of the form: xa76giaf6ifda7ri63i263.onion &lt;br/&gt;or an I2P address in the form: [52 characters].b32.i2p &lt;/p&gt;&lt;p&gt;In order to get one, you must configure either Tor or I2P to create a new hidden service / server tunnel. &lt;/p&gt;&lt;p&gt;You can also leave this blank now, but your node will only work if you correctly set the Tor/I2P service address in Options-&amp;gt;Network-&amp;gt;Hidden Service configuration panel.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -479,6 +515,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This is your connection port.&lt;/p&gt;&lt;p&gt;Any value between 1024 and 65535 &lt;/p&gt;&lt;p&gt;should be ok. You can change it later.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -496,7 +538,7 @@
<item>
<widget class="QCheckBox" name="cbUseBob">
<property name="text">
<string>Use BOB</string>
<string>Use I2P</string>
</property>
</widget>
</item>
@ -510,6 +552,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Identities are used when you write in chat rooms, forums and channel comments. &lt;/p&gt;&lt;p&gt;They also receive/send email over the Retroshare network. You can create&lt;/p&gt;&lt;p&gt;a signed identity now, or do it later on when you get to need it.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -532,6 +580,8 @@
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
@ -721,7 +771,12 @@
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="info_label">
<widget class="QLabel" name="info_Label">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
@ -751,6 +806,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>RSComboBox</class>
<extends>QComboBox</extends>
<header>gui/common/RSComboBox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>name_input</tabstop>
<tabstop>node_input</tabstop>
@ -764,8 +826,8 @@
<tabstop>importIdentity_PB</tabstop>
</tabstops>
<resources>
<include location="icons.qrc"/>
<include location="images.qrc"/>
<include location="icons.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -53,19 +53,19 @@ GetStartedDialog::GetStartedDialog(QWidget *parent)
mFirstShow = true;
connect(ui.inviteCheckBox, SIGNAL(stateChanged( int )), this, SLOT(tickInviteChanged()));
connect(ui.addCheckBox, SIGNAL(stateChanged( int )), this, SLOT(tickAddChanged()));
connect(ui.connectCheckBox, SIGNAL(stateChanged( int )), this, SLOT(tickConnectChanged()));
connect(ui.firewallCheckBox, SIGNAL(stateChanged( int )), this, SLOT(tickFirewallChanged()));
connect(ui.inviteCheckBox, SIGNAL(stateChanged(int)), this, SLOT(tickInviteChanged()));
connect(ui.addCheckBox, SIGNAL(stateChanged(int)), this, SLOT(tickAddChanged()));
connect(ui.connectCheckBox, SIGNAL(stateChanged(int)), this, SLOT(tickConnectChanged()));
connect(ui.firewallCheckBox, SIGNAL(stateChanged(int)), this, SLOT(tickFirewallChanged()));
connect(ui.pushButton_InviteFriends, SIGNAL(clicked( bool )), this, SLOT(inviteFriends()));
connect(ui.pushButton_AddFriend, SIGNAL(clicked( bool )), this, SLOT(addFriends()));
connect(ui.pushButton_InviteFriends, SIGNAL(clicked(bool)), this, SLOT(inviteFriends()));
connect(ui.pushButton_AddFriend, SIGNAL(clicked(bool)), this, SLOT(addFriends()));
connect(ui.pushButton_FAQ, SIGNAL(clicked( bool )), this, SLOT(OpenFAQ()));
connect(ui.pushButton_Forums, SIGNAL(clicked( bool )), this, SLOT(OpenForums()));
connect(ui.pushButton_Website, SIGNAL(clicked( bool )), this, SLOT(OpenWebsite()));
connect(ui.pushButton_EmailFeedback, SIGNAL(clicked( bool )), this, SLOT(emailFeedback()));
connect(ui.pushButton_EmailSupport, SIGNAL(clicked( bool )), this, SLOT(emailSupport()));
connect(ui.pushButton_FAQ, SIGNAL(clicked(bool)), this, SLOT(OpenFAQ()));
connect(ui.pushButton_Forums, SIGNAL(clicked(bool)), this, SLOT(OpenForums()));
connect(ui.pushButton_Website, SIGNAL(clicked(bool)), this, SLOT(OpenWebsite()));
connect(ui.pushButton_EmailFeedback, SIGNAL(clicked(bool)), this, SLOT(emailFeedback()));
connect(ui.pushButton_EmailSupport, SIGNAL(clicked(bool)), this, SLOT(emailSupport()));
}
GetStartedDialog::~GetStartedDialog()
@ -105,8 +105,7 @@ void GetStartedDialog::showEvent ( QShowEvent * /*event*/ )
void GetStartedDialog::updateFromUserLevel()
{
RsConfigUserLvl userLevel = RsConfigUserLvl::NEW;
userLevel = rsConfig->getUserLevel();
RsConfigUserLvl userLevel = rsConfig->getUserLevel();
ui.inviteCheckBox->setChecked(false);
ui.addCheckBox->setChecked(false);
@ -185,17 +184,12 @@ void GetStartedDialog::tickFirewallChanged()
}
}
static void sendMail(const QString &address, const QString &subject, QString body)
static void sendMail(const QString &address, const QString &subject, const QString &body)
{
/* Only under windows do we need to do this! */
#ifdef Q_OS_WIN
/* search and replace the end of lines with: "%0D%0A" */
body.replace("\n", "%0D%0A");
#endif
QString mailstr = "mailto:" + address;
mailstr += "?subject=" + subject;
mailstr += "&body=" + body;
mailstr += "?subject=" + QUrl::toPercentEncoding(subject);
mailstr += "&body=" + QUrl::toPercentEncoding(body);
std::cerr << "MAIL STRING:" << mailstr.toStdString() << std::endl;
@ -235,12 +229,12 @@ void GetStartedDialog::inviteFriends()
{
RsAutoUpdatePage::lockAllEvents();
cert = rsPeers->GetRetroshareInvite(RsPeerId(),RetroshareInviteFlags::DNS | RetroshareInviteFlags::CURRENT_IP | RetroshareInviteFlags::FULL_IP_HISTORY);
cert = rsPeers->GetRetroshareInvite(RsPeerId(),RsPeers::defaultCertificateFlags | RetroshareInviteFlags::FULL_IP_HISTORY);
RsAutoUpdatePage::unlockAllEvents() ;
}
QString text = QString("%1\n%2\n\n%3\n").arg(GetInviteText()).arg(GetCutBelowText()).arg(QString::fromUtf8(cert.c_str()));
QString text = QString("%1\n%2\n\n%3\n").arg(GetInviteText(), GetCutBelowText(), QString::fromUtf8(cert.c_str()));
sendMail("", tr("RetroShare Invitation"), text);
}
@ -427,8 +421,10 @@ void GetStartedDialog::emailSupport()
sysVersion = "Linux";
#endif
#endif
text += QString("My RetroShare Configuration is: (%1, %2, %3)").arg(Rshare::retroshareVersion(true)).arg(sysVersion).arg(static_cast<typename std::underlying_type<RsConfigUserLvl>::type>(userLevel)) + "\n";
text += "\n";
text += QString("My RetroShare Configuration is: (%1, %2, %3)").arg(Rshare::retroshareVersion(true)
, sysVersion
).arg(static_cast<typename std::underlying_type<RsConfigUserLvl>::type>(userLevel)) + "\n";
text += "\n";
text += QString("I am having trouble with RetroShare.");
text += QString(" Can you help me with....") + "\n";

View file

@ -10,7 +10,7 @@
<height>582</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="GetStartedDialog_VL">
<property name="leftMargin">
<number>0</number>
</property>
@ -32,11 +32,20 @@
<attribute name="title">
<string>Getting Started</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="GetStartedTab_VL">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -53,12 +62,12 @@
<height>732</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QVBoxLayout" name="scrollAreaWidgetContents_VL">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="invite_GL">
<item row="0" column="0">
<widget class="QCheckBox" name="inviteCheckBox">
<property name="sizePolicy">
@ -121,20 +130,20 @@
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;RetroShare is nothing without your Friends. Click on the Button to start the process.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Email an Invitation with your &amp;quot;ID Certificate&amp;quot; to your friends.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Be sure to get their invitation back as well... &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;You can only connect with friends if you have both added each other.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;RetroShare is nothing without your Friends. Click on the Button to start the process.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Email an Invitation with your &amp;quot;ID Certificate&amp;quot; to your friends.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Be sure to get their invitation back as well... &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;You can only connect with friends if you have both added each other.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_4">
<layout class="QGridLayout" name="add_GL">
<item row="0" column="0">
<widget class="QCheckBox" name="addCheckBox">
<property name="sizePolicy">
@ -197,17 +206,17 @@ p, li { white-space: pre-wrap; }
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;When your friends send you their invitations, click to open the Add Friends window.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Paste your Friends' &amp;quot;ID Certificates&amp;quot; into the window and add them as friends.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;When your friends send you their invitations, click to open the Add Friends window.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Paste your Friends' &amp;quot;ID Certificates&amp;quot; into the window and add them as friends.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="connect_GL">
<property name="horizontalSpacing">
<number>0</number>
</property>
@ -282,25 +291,25 @@ p, li { white-space: pre-wrap; }
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Be Online at the same time as your friends, and RetroShare will automatically connect you!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Your client needs to find the RetroShare Network before it can make connections.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;This takes 5-30 minutes the first time you start up RetroShare&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;The DHT indicator (in the Status Bar) turns Green when it can make connections.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;After a couple of minutes, the NAT indicator (also in the Status Bar) switch to Yellow or Green.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;If it remains Red, then you have a Nasty Firewall, that RetroShare struggles to connect through.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Look in the Further Help section for more advice about connecting.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Be Online at the same time as your friends, and RetroShare will automatically connect you!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Your client needs to find the RetroShare Network before it can make connections.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;This takes 5-30 minutes the first time you start up RetroShare&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;The DHT indicator (in the Status Bar) turns Green when it can make connections.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;After a couple of minutes, the NAT indicator (also in the Status Bar) switch to Yellow or Green.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;If it remains Red, then you have a Nasty Firewall, that RetroShare struggles to connect through.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Look in the Further Help section for more advice about connecting.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_3">
<layout class="QGridLayout" name="firewall_GL">
<item row="0" column="0">
<widget class="QCheckBox" name="firewallCheckBox">
<property name="sizePolicy">
@ -366,23 +375,23 @@ p, li { white-space: pre-wrap; }
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;You can improve your Retroshare performance by opening an External Port. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;This will speed up connections and allow more people to connect with you. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;The easiest way to do this is by enabling UPnP on your Wireless Box or Router.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;As each router is different, you will need to find out your Router Model and search the Internet for instructions.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;If none of this makes sense to you, don't worry about it Retroshare will still work.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;You can improve your Retroshare performance by opening an External Port. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;This will speed up connections and allow more people to connect with you. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;The easiest way to do this is by enabling UPnP on your Wireless Box or Router.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;As each router is different, you will need to find out your Router Model and search the Internet for instructions.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;If none of this makes sense to you, don't worry about it Retroshare will still work.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<spacer name="GetStartedTab_VSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@ -407,31 +416,31 @@ p, li { white-space: pre-wrap; }
<attribute name="title">
<string>Further Help and Support</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<layout class="QHBoxLayout" name="FurtherHelpTab_HL">
<item>
<widget class="QTextBrowser" name="textBrowser_2">
<widget class="QTextBrowser" name="FurtherHelpTabTextBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Having trouble getting started with RetroShare?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;1) Look at the FAQ Wiki. This is a bit old, we are trying to bring it up to date.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;2) Check out the Online Forums. Ask questions and discuss features.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;3) Try the Internal RetroShare Forums &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt; - These come online once you are connected to friends.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;4) If you are still stuck. Email us.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:12pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Enjoy Retrosharing&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Having trouble getting started with RetroShare?&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;1) Look at the FAQ Wiki. This is a bit old, we are trying to bring it up to date.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;2) Check out the Online Forums. Ask questions and discuss features.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;3) Try the Internal RetroShare Forums &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt; - These come online once you are connected to friends.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;4) If you are still stuck. Email us.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;Enjoy Retrosharing&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<layout class="QVBoxLayout" name="buttons_VL">
<item>
<widget class="QPushButton" name="pushButton_Website">
<property name="text">
@ -454,7 +463,7 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<spacer name="buttonsTop_VSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@ -481,7 +490,7 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<spacer name="buttonsBottom1_VSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
@ -494,7 +503,7 @@ p, li { white-space: pre-wrap; }
</spacer>
</item>
<item>
<spacer name="verticalSpacer_2">
<spacer name="buttonsBottom2_VSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>

View file

@ -56,7 +56,7 @@
<property name="spacing">
<number>6</number>
</property>
<item row="2" column="0" colspan="3">
<item row="1" column="0" colspan="3">
<widget class="QTextBrowser" name="version">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -72,13 +72,13 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<item row="0" column="0" colspan="3">
<widget class="QTextBrowser" name="about">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-weight:600;&quot;&gt;RetroShare is an Open Source cross-platform, &lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-weight:600;&quot;&gt;private and secure decentralized communication platform. &lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;justify&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-weight:600;&quot;&gt;It lets you share securely your friends, &lt;/span&gt;&lt;/p&gt;
@ -97,16 +97,6 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
@ -223,7 +213,7 @@ p, li { white-space: pre-wrap; }
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:600;&quot;&gt;RetroShare Translations:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://retroshare.sourceforge.net/wiki/index.php/Translation&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; text-decoration: underline; color:#0000ff;&quot;&gt;http://retroshare.sourceforge.net/wiki/index.php/Translation&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;

View file

@ -24,6 +24,7 @@
#include "retroshare/rsinit.h"
#include "util/qtthreadsutils.h"
#include "util/misc.h"
#include "gui/notifyqt.h"
#include "gui/msgs/MessageComposer.h"
@ -49,14 +50,10 @@
HomePage::HomePage(QWidget *parent) :
MainPage(parent),
ui(new Ui::HomePage),
mIncludeAllIPs(false),
mUseShortFormat(true)
ui(new Ui::HomePage)
{
ui->setupUi(this);
updateCertificate();
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addFriend()));
connect(ui->copyIDButton, SIGNAL(clicked()), this, SLOT(copyId()));
@ -72,52 +69,72 @@ HomePage::HomePage(QWidget *parent) :
QAction *CopyIdAction = new QAction(QIcon(),tr("Copy your Retroshare ID to Clipboard"), this);
connect(CopyIdAction, SIGNAL(triggered()), this, SLOT(copyId()));
QMenu *menu = new QMenu();
QMenu *menu = new QMenu();
menu->addAction(CopyIdAction);
if(!RsAccounts::isHiddenNode())
{
QAction *includeIPsAct = new QAction(QIcon(), tr("Include all your known IPs"),this);
connect(includeIPsAct, SIGNAL(triggered()), this, SLOT(toggleIncludeAllIPs()));
includeIPsAct->setCheckable(true);
includeIPsAct->setChecked(mIncludeAllIPs);
menu->addAction(includeIPsAct);
}
QAction *useOldFormatAct = new QAction(QIcon(), tr("Use old certificate format"),this);
useOldFormatAct->setToolTip(tr("Displays the certificate format used up to version 0.6.5\nOld Retroshare nodes will not understand the\nnew short format"));
connect(useOldFormatAct, SIGNAL(triggered()), this, SLOT(toggleUseOldFormat()));
useOldFormatAct->setCheckable(true);
useOldFormatAct->setChecked(!mUseShortFormat);
menu->addAction(useOldFormatAct);
menu->addSeparator();
menu->addAction(SendAction);
menu->addAction(WebMailAction);
menu->addAction(RecAction);
menu->addSeparator();
mUseOldFormatact = new QAction(QIcon(), tr("Use old certificate format"),this);
mUseOldFormatact->setToolTip(tr("Displays the certificate format used up to version 0.6.5\nOld Retroshare nodes will not understand the\nnew short format"));
connect(mUseOldFormatact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
mUseOldFormatact->setCheckable(true);
mUseOldFormatact->setChecked(false);
menu->addAction(mUseOldFormatact);
if(!RsAccounts::isHiddenNode())
{
mIncludeLocIPact = new QAction(QIcon(), tr("Include current local IP"),this);
connect(mIncludeLocIPact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
mIncludeLocIPact->setCheckable(true);
mIncludeLocIPact->setChecked(true);
menu->addAction(mIncludeLocIPact);
mIncludeExtIPact = new QAction(QIcon(), tr("Include current external IP"),this);
connect(mIncludeExtIPact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
mIncludeExtIPact->setCheckable(true);
mIncludeExtIPact->setChecked(true);
menu->addAction(mIncludeExtIPact);
mIncludeDNSact = new QAction(QIcon(), tr("Include my DNS"),this);
connect(mIncludeDNSact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
mIncludeDNSact->setCheckable(true);
mIncludeDNSact->setChecked(true);
menu->addAction(mIncludeDNSact);
mIncludeIPHistoryact = new QAction(QIcon(), tr("Include all IPs history"),this);
connect(mIncludeIPHistoryact, SIGNAL(triggered()), this, SLOT(updateOwnCert()));
mIncludeIPHistoryact->setCheckable(true);
mIncludeIPHistoryact->setChecked(false);
menu->addAction(mIncludeIPHistoryact);
}
ui->shareButton->setMenu(menu);
connect(ui->openwebhelp,SIGNAL(clicked()), this,SLOT(openWebHelp())) ;
int S = QFontMetricsF(font()).height();
QString help_str = tr(
" <h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Welcome to Retroshare!</h1>\
<p>You need to <b>make friends</b>! After you create a network of friends or join an existing network,\
you'll be able to exchange files, chat, talk in forums, etc. </p>\
<div align=center>\
<IMG align=\"center\" width=\"%2\" src=\":/images/network_map.png\"/> \
</div>\
<p>To do so, copy your Retroshare ID on this page and send it to friends, and add your friends' Retroshare ID.</p> \
<p>Another option is to search the internet for \"Retroshare chat servers\" (independently administrated). These servers allow you to exchange \
Retroshare ID with a dedicated Retroshare node, through which\
you will be able to anonymously meet other people.</p> ").arg(QString::number(2*S)).arg(width()*0.5);
registerHelpButton(ui->helpButton,help_str,"HomePage") ;
int H = misc::getFontSizeFactor("HelpButton").height();
QString help_str = tr(
"<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Welcome to Retroshare!</h1>"
"<p>You need to <b>make friends</b>! After you create a network of friends or join an existing network,"
" you'll be able to exchange files, chat, talk in forums, etc. </p>"
"<div align=\"center\"><IMG width=\"%2\" height=\"%3\" src=\":/images/network_map.png\" style=\"display: block; margin-left: auto; margin-right: auto; \"/></div>"
"<p>To do so, copy your Retroshare ID on this page and send it to friends, and add your friends' Retroshare ID.</p>"
"<p>Another option is to search the internet for \"Retroshare chat servers\" (independently administrated). These servers allow you to exchange"
" Retroshare ID with a dedicated Retroshare node, through which"
" you will be able to anonymously meet other people.</p>"
).arg(QString::number(2*H), QString::number(width()*0.5), QString::number(width()*0.5*(337.0/800.0)));//<img> needs height and width defined.
registerHelpButton(ui->helpButton,help_str,"HomePage") ;
// register a event handler to catch IP updates
// register a event handler to catch IP updates
mEventHandlerId = 0;
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event) { handleEvent(event); }, mEventHandlerId, RsEventType::NETWORK );
updateOwnCert();
}
void HomePage::handleEvent(std::shared_ptr<const RsEvent> e)
@ -136,16 +153,19 @@ void HomePage::handleEvent(std::shared_ptr<const RsEvent> e)
{
case RsNetworkEventCode::LOCAL_IP_UPDATED: // [fallthrough]
case RsNetworkEventCode::EXTERNAL_IP_UPDATED: // [fallthrough]
case RsNetworkEventCode::DNS_UPDATED: // [fallthrough]
RsQThreadUtils::postToObject( [=]()
{
updateCertificate();
updateOwnCert();
},this);
break;
default:
break;
}
}
void HomePage::certContextMenu(QPoint point)
#ifdef DEAD_CODE
void HomePage::certContextMenu(QPoint /*point*/)
{
QMenu menu(this) ;
@ -177,17 +197,7 @@ void HomePage::certContextMenu(QPoint point)
menu.exec(QCursor::pos());
}
void HomePage::toggleUseShortFormat()
{
mUseShortFormat = !mUseShortFormat;
updateCertificate();
}
void HomePage::toggleIncludeAllIPs()
{
mIncludeAllIPs = !mIncludeAllIPs;
updateCertificate();
}
#endif
HomePage::~HomePage()
{
@ -195,15 +205,29 @@ HomePage::~HomePage()
delete ui;
}
void HomePage::updateCertificate()
RetroshareInviteFlags HomePage::currentInviteFlags() const
{
updateOwnCert();
RetroshareInviteFlags invite_flags = RetroshareInviteFlags::NOTHING;
if(!RsAccounts::isHiddenNode())
{
if(mIncludeLocIPact->isChecked())
invite_flags |= RetroshareInviteFlags::CURRENT_LOCAL_IP;
if(mIncludeExtIPact->isChecked())
invite_flags |= RetroshareInviteFlags::CURRENT_EXTERNAL_IP;
if(mIncludeDNSact->isChecked())
invite_flags |= RetroshareInviteFlags::DNS;
if(mIncludeIPHistoryact->isChecked())
invite_flags |= RetroshareInviteFlags::FULL_IP_HISTORY;
}
return invite_flags;
}
void HomePage::updateOwnCert()
void HomePage::getOwnCert(QString& invite,QString& description) const
{
bool include_extra_locators = mIncludeAllIPs;
RsPeerDetails detail;
if (!rsPeers->getPeerDetails(rsPeers->getOwnId(), detail))
@ -211,24 +235,36 @@ void HomePage::updateOwnCert()
std::cerr << "(EE) Cannot retrieve information about own certificate. That is a real problem!!" << std::endl;
return ;
}
auto invite_flags = currentInviteFlags();
QString invite ;
RetroshareInviteFlags invite_flags = RetroshareInviteFlags::CURRENT_IP;
invite_flags |= RetroshareInviteFlags::SLICE_TO_80_CHARS;
if(mIncludeAllIPs)
invite_flags |= RetroshareInviteFlags::FULL_IP_HISTORY;
if(mUseShortFormat)
if(!mUseOldFormatact->isChecked())
{
std::string short_invite;
rsPeers->getShortInvite(short_invite,rsPeers->getOwnId(),invite_flags | RetroshareInviteFlags::RADIX_FORMAT);
invite = QString::fromStdString(short_invite);
}
else
invite = QString::fromStdString(rsPeers->GetRetroshareInvite(detail.id,invite_flags));
description = ConfCertDialog::getCertificateDescription(detail,false,!mUseOldFormatact->isChecked(),invite_flags);
}
void HomePage::updateOwnCert()
{
QString certificate, description;
getOwnCert(certificate,description);
if(!mUseOldFormatact->isChecked()) // in this case we have to split the cert for a better display
{
QString S;
QString txt;
for(uint32_t i=0;i<short_invite.size();)
for(int i=0;i<certificate.size();)
if(S.length() < 100)
S += short_invite[i++];
S += certificate[i++];
else
{
txt += S + "\n";
@ -236,26 +272,15 @@ void HomePage::updateOwnCert()
}
txt += S;
invite = txt; // the "\n" is here to make some space
certificate = txt; // the "\n" is here to make some space
}
else
invite = QString::fromStdString(rsPeers->GetRetroshareInvite(detail.id,invite_flags));
ui->retroshareid->setText("\n"+invite+"\n");
QString description = ConfCertDialog::getCertificateDescription(detail,false,mUseShortFormat,include_extra_locators);
ui->retroshareid->setText("\n"+certificate+"\n");
ui->retroshareid->setToolTip(description);
}
static void sendMail(QString sAddress, QString sSubject, QString sBody)
static void sendMail(const QString &sAddress, const QString &sSubject, const QString &sBody)
{
#ifdef Q_OS_WIN
/* search and replace the end of lines with: "%0D%0A" */
sBody.replace("\n", "%0D%0A");
#endif
QUrl url = QUrl("mailto:" + sAddress);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
@ -265,7 +290,12 @@ static void sendMail(QString sAddress, QString sSubject, QString sBody)
#endif
urlQuery.addQueryItem("subject", sSubject);
#ifdef Q_OS_WIN
/* search and replace the end of lines with: "%0D%0A" */
urlQuery.addQueryItem("body", QString(sBody).replace("\n", "%0D%0A"));
#else
urlQuery.addQueryItem("body", sBody);
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
url.setQuery(urlQuery);
@ -347,9 +377,3 @@ void HomePage::openWebHelp()
{
QDesktopServices::openUrl(QUrl(QString("https://retrosharedocs.readthedocs.io/en/latest/")));
}
void HomePage::toggleUseOldFormat()
{
mUseShortFormat = !mUseShortFormat;
updateCertificate();
}

View file

@ -46,28 +46,33 @@ public:
virtual QString pageName() const { return tr("Home") ; } //MainPage
virtual QString helpText() const { return ""; } //MainPage
// Returns the certificate along with its description using current options.
void getOwnCert(QString& invite,QString& description) const;
RetroshareInviteFlags currentInviteFlags() const ;
private slots:
void certContextMenu(QPoint);
#ifdef DEAD_CODE
void certContextMenu(QPoint);
#endif
void updateOwnCert();
void updateCertificate();
void runEmailClient();
void copyCert();
void copyId();
void saveCert();
void addFriend();
void webMail();
//void loadCert();
void openWebHelp() ;
void toggleUseOldFormat() ;
void recommendFriends();
void toggleIncludeAllIPs();
void toggleUseShortFormat();
private:
Ui::HomePage *ui;
bool mIncludeAllIPs;
bool mUseShortFormat;
QAction *mIncludeDNSact;
QAction *mIncludeIPHistoryact;
QAction *mIncludeExtIPact;
QAction *mIncludeLocIPact;
QAction *mUseOldFormatact;
RsEventsHandlerId_t mEventHandlerId;

View file

@ -89,6 +89,8 @@
<property name="font">
<font>
<pointsize>11</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
@ -105,7 +107,7 @@
<string>...</string>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<iconset resource="images.qrc">
<normaloff>:/icons/help_64.png</normaloff>:/icons/help_64.png</iconset>
</property>
<property name="checkable">
@ -289,6 +291,13 @@ private and secure decentralized communication platform.
</item>
<item row="1" column="1">
<widget class="QToolButton" name="addButton">
<property name="font">
<font>
<pointsize>15</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Add friend</string>
</property>
@ -415,8 +424,8 @@ private and secure decentralized communication platform.
</layout>
</widget>
<resources>
<include location="icons.qrc"/>
<include location="images.qrc"/>
<include location="icons.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -23,7 +23,6 @@
#include <QDialog>
#include "util/TokenQueue.h"
#include <retroshare/rsidentity.h>
namespace Ui {

View file

@ -355,7 +355,7 @@
</widget>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="ownOpinion_CB">
<widget class="RSComboBox" name="ownOpinion_CB">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -493,12 +493,16 @@ p, li { white-space: pre-wrap; }
</layout>
</widget>
<customwidgets>
<customwidget>
<class>HeaderFrame</class>
<extends>QFrame</extends>
<header>gui/common/HeaderFrame.h</header>
<container>1</container>
<customwidget>
<class>HeaderFrame</class>
<extends>QFrame</extends>
<header>gui/common/HeaderFrame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<customwidget>
<class>RSComboBox</class>
<extends>QComboBox</extends>
<header>gui/common/RSComboBox.h</header>
</customwidget>
</customwidgets>
</ui>

View file

@ -169,15 +169,18 @@ IdDialog::IdDialog(QWidget *parent)
//connect(mCirclesBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateCirclesDisplay(bool)));
ownItem = new QTreeWidgetItem();
ownItem->setText(0, tr("My own identities"));
ownItem->setText(RSID_COL_NICKNAME, tr("My own identities"));
ownItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font());
ownItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); // this is in order to prevent displaying a reputaiton icon next to these items.
allItem = new QTreeWidgetItem();
allItem->setText(0, tr("All"));
allItem->setText(RSID_COL_NICKNAME, tr("All"));
allItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font());
allItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff);
contactsItem = new QTreeWidgetItem();
contactsItem->setText(0, tr("My contacts"));
contactsItem->setText(RSID_COL_NICKNAME, tr("My contacts"));
contactsItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font());
contactsItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff);
@ -252,12 +255,14 @@ IdDialog::IdDialog(QWidget *parent)
connect(ui->ownOpinion_CB, SIGNAL(currentIndexChanged(int)), this, SLOT(modifyReputation()));
connect(ui->inviteButton, SIGNAL(clicked()), this, SLOT(sendInvite()));
connect(ui->editButton, SIGNAL(clicked()), this, SLOT(editIdentity()));
connect( ui->idTreeWidget, &RSTreeWidget::itemDoubleClicked,
this, &IdDialog::chatIdentityItem );
ui->editButton->hide();
ui->avlabel_Circles->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/circles.png"));
ui->avLabel_Circles->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/circles.png"));
ui->headerTextLabel_Circles->setText(tr("Circles"));
@ -364,6 +369,7 @@ IdDialog::IdDialog(QWidget *parent)
ui->idTreeWidget->setColumnWidth(RSID_COL_IDTYPE, 18 * fontWidth);
ui->idTreeWidget->setColumnWidth(RSID_COL_VOTES, 2 * fontWidth);
ui->idTreeWidget->setItemDelegate(new RSElidedItemDelegate());
ui->idTreeWidget->setItemDelegateForColumn(
RSID_COL_NICKNAME,
new GxsIdTreeItemDelegate());
@ -379,20 +385,22 @@ IdDialog::IdDialog(QWidget *parent)
mStateHelper->setActive(IDDIALOG_IDDETAILS, false);
mStateHelper->setActive(IDDIALOG_REPLIST, false);
int H = misc::getFontSizeFactor("HelpButton").height();
QString hlp_str = tr(
" <h1><img width=\"32\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Identities</h1> \
<p>In this tab you can create/edit <b>pseudo-anonymous identities</b>, and <b>circles</b>.</p> \
<p><b>Identities</b> are used to securely identify your data: sign messages in chat lobbies, forum and channel posts,\
receive feedback using the Retroshare built-in email system, post comments \
after channel posts, chat using secured tunnels, etc.</p> \
<p>Identities can optionally be <b>signed</b> by your Retroshare node's certificate. \
Signed identities are easier to trust but are easily linked to your node's IP address.</p> \
<p><b>Anonymous identities</b> allow you to anonymously interact with other users. They cannot be \
spoofed, but noone can prove who really owns a given identity.</p> \
<p><b>Circles</b> are groups of identities (anonymous or signed), that are shared at a distance over the network. They can be \
used to restrict the visibility to forums, channels, etc. </p> \
<p>An <b>circle</b> can be restricted to another circle, thereby limiting its visibility to members of that circle \
or even self-restricted, meaning that it is only visible to invited members.</p>") ;
"<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Identities</h1>"
"<p>In this tab you can create/edit <b>pseudo-anonymous identities</b>, and <b>circles</b>.</p>"
"<p><b>Identities</b> are used to securely identify your data: sign messages in chat lobbies, forum and channel posts,"
" receive feedback using the Retroshare built-in email system, post comments"
" after channel posts, chat using secured tunnels, etc.</p>"
"<p>Identities can optionally be <b>signed</b> by your Retroshare node's certificate."
" Signed identities are easier to trust but are easily linked to your node's IP address.</p>"
"<p><b>Anonymous identities</b> allow you to anonymously interact with other users. They cannot be"
" spoofed, but noone can prove who really owns a given identity.</p>"
"<p><b>Circles</b> are groups of identities (anonymous or signed), that are shared at a distance over the network. They can be"
" used to restrict the visibility to forums, channels, etc. </p>"
"<p>An <b>circle</b> can be restricted to another circle, thereby limiting its visibility to members of that circle"
" or even self-restricted, meaning that it is only visible to invited members.</p>"
).arg(QString::number(2*H));
registerHelpButton(ui->helpButton, hlp_str,"PeopleDialog") ;
@ -406,9 +414,7 @@ IdDialog::IdDialog(QWidget *parent)
connect(ui->autoBanIdentities_CB, SIGNAL(toggled(bool)), this, SLOT(toggleAutoBanIdentities(bool)));
updateIdTimer.setSingleShot(true);
updateCircleTimer.setSingleShot(true);
connect(&updateIdTimer, SIGNAL(timeout()), this, SLOT(updateIdList()));
connect(&updateCircleTimer, SIGNAL(timeout()), this, SLOT(updateCircles()));
}
void IdDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
@ -427,7 +433,10 @@ void IdDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
case RsGxsIdentityEventCode::UPDATED_IDENTITY:
if (isVisible())
{
updateIdTimer.start(5000);
if(rsIdentity->isOwnId(RsGxsId(e->mIdentityId)))
updateIdList();
else
updateIdTimer.start(3000); // use a timer for events not generated by local changes
}
else
needUpdateIdsOnNextShow = true;
@ -458,9 +467,7 @@ void IdDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
case RsGxsCircleEventCode::CACHE_DATA_UPDATED:
if (isVisible())
{
updateCircleTimer.start(5000);
}
updateCircles();
else
needUpdateCirclesOnNextShow = true;
default:
@ -474,10 +481,10 @@ void IdDialog::clearPerson()
{
QFontMetricsF f(ui->avLabel_Person->font()) ;
ui->avLabel_Person->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/people.png").scaled(f.height()*4,f.height()*4,Qt::IgnoreAspectRatio,Qt::SmoothTransformation));
ui->avLabel_Person->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/people.png").scaled(f.height()*4,f.height()*4,Qt::KeepAspectRatio,Qt::SmoothTransformation));
ui->headerTextLabel_Person->setText(tr("People"));
ui->inviteFrame->hide();
ui->info_Frame_Invite->hide();
ui->avatarLabel->clear();
whileBlocking(ui->ownOpinion_CB)->setCurrentIndex(1);
@ -647,15 +654,16 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
if(!mExternalOtherCircleItem)
{
mExternalOtherCircleItem = new QTreeWidgetItem();
mExternalOtherCircleItem->setText(0, tr("Other circles"));
mExternalOtherCircleItem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Other circles"));
mExternalOtherCircleItem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, ui->treeWidget_membership->font());
ui->treeWidget_membership->addTopLevelItem(mExternalOtherCircleItem);
}
if(!mExternalBelongingCircleItem )
{
mExternalBelongingCircleItem = new QTreeWidgetItem();
mExternalBelongingCircleItem->setText(0, tr("Circles I belong to"));
mExternalBelongingCircleItem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Circles I belong to"));
mExternalBelongingCircleItem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, ui->treeWidget_membership->font());
ui->treeWidget_membership->addTopLevelItem(mExternalBelongingCircleItem);
}
#endif
@ -731,14 +739,11 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
item->setToolTip(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,tooltip);
if (am_I_admin)
{
QFont font = item->font(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) ;
font.setBold(true) ;
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
}
QFont font = ui->treeWidget_membership->font() ;
font.setBold(am_I_admin) ;
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
// now determine for this circle wether we have pending invites
// we add a sub-item to the circle (to show the invite system info) in the following two cases:
@ -853,12 +858,10 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole, QVariant(it->second)) ;
subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPID, Qt::UserRole, QString::fromStdString(it->first.toStdString())) ;
//subitem->setIcon(RSID_COL_NICKNAME, QIcon(pixmap));
new_sub_items.push_back(subitem);
new_sub_items.push_back(subitem);
}
else
subitem = item->child(subitem_index);
else
subitem = item->child(subitem_index);
if(invited && !subscrb)
{
@ -877,15 +880,12 @@ void IdDialog::loadCircles(const std::list<RsGroupMetaData>& groupInfo)
if(invited && subscrb)
subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPID, tr("Member")) ;
if (is_own_id)
{
QFont font = subitem->font(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) ;
font.setBold(true) ;
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
}
}
QFont font = ui->treeWidget_membership->font() ;
font.setBold(is_own_id) ;
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ;
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ;
subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ;
}
// add all items
item->addChildren(new_sub_items);
@ -1111,7 +1111,7 @@ void IdDialog::CircleListCustomPopupMenu( QPoint )
static const int CANCEL = 3 ; // Admin list: no Subscription request: yes
const QString menu_titles[4] = { tr("Request subscription"), tr("Accept circle invitation"), tr("Quit this circle"),tr("Cancel subscribe request")} ;
const QString image_names[4] = { ":/images/edit_add24.png",":/images/accepted16.png",":/icons/png/enter.png",":/images/cancel.png" } ;
const QString image_names[4] = { ":/images/edit_add24.png",":/images/accepted16.png",":/icons/png/enter.png",":/icons/cancel.svg" } ;
std::vector< std::vector<RsGxsId> > ids(4) ;
@ -1388,73 +1388,42 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
uint32_t item_flags = 0;
/* do filtering */
bool ok = false;
if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)
{
if (isLinkedToOwnNode && (accept & RSID_FILTER_YOURSELF))
{
ok = true;
item_flags |= RSID_FILTER_YOURSELF ;
}
{
if (isLinkedToOwnNode && (accept & RSID_FILTER_YOURSELF))
item_flags |= RSID_FILTER_YOURSELF ;
if (data.mPgpKnown && (accept & RSID_FILTER_FRIENDS))
{
ok = true;
item_flags |= RSID_FILTER_FRIENDS ;
}
if (data.mPgpKnown && (accept & RSID_FILTER_FRIENDS))
item_flags |= RSID_FILTER_FRIENDS ;
if (accept & RSID_FILTER_OTHERS)
{
ok = true;
item_flags |= RSID_FILTER_OTHERS ;
}
}
else if (accept & RSID_FILTER_PSEUDONYMS)
{
ok = true;
item_flags |= RSID_FILTER_PSEUDONYMS ;
}
if (accept & RSID_FILTER_OTHERS)
item_flags |= RSID_FILTER_OTHERS ;
}
else if (accept & RSID_FILTER_PSEUDONYMS)
item_flags |= RSID_FILTER_PSEUDONYMS ;
if (isOwnId && (accept & RSID_FILTER_OWNED_BY_YOU))
{
ok = true;
item_flags |= RSID_FILTER_OWNED_BY_YOU ;
}
if (isOwnId && (accept & RSID_FILTER_OWNED_BY_YOU))
item_flags |= RSID_FILTER_OWNED_BY_YOU ;
if (isBanned && (accept & RSID_FILTER_BANNED))
{
ok = true;
item_flags |= RSID_FILTER_BANNED ;
}
if (!ok)
if (item_flags == 0)
return false;
if (!item)
{
item = new TreeWidgetItem();
}
item = new TreeWidgetItem();
item->setText(RSID_COL_NICKNAME, QString::fromUtf8(data.mMeta.mGroupName.c_str()).left(RSID_MAXIMUM_NICKNAME_SIZE));
item->setData(RSID_COL_NICKNAME, Qt::UserRole, QString::fromStdString(data.mMeta.mGroupId.toStdString()));
item->setText(RSID_COL_KEYID, QString::fromStdString(data.mMeta.mGroupId.toStdString()));
if(isBanned)
{
//TODO (Phenom): Add qproperty for these text colors in stylesheets
item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QColor(Qt::red));
item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QColor(Qt::red));
item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QColor(Qt::red));
item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QColor(Qt::red));
}
else
{
item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QVariant());
item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QVariant());
item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QVariant());
item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QVariant());
}
//TODO (Phenom): Add qproperty for these text colors in stylesheets
item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
item->setData(RSID_COL_KEYID , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
item->setData(RSID_COL_VOTES , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() );
item->setData(RSID_COL_KEYID, Qt::UserRole,QVariant(item_flags)) ;
item->setTextAlignment(RSID_COL_VOTES, Qt::AlignRight | Qt::AlignVCenter);
@ -1465,16 +1434,9 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
RSID_COL_VOTES,SortRole,
static_cast<uint32_t>(idd.mReputation.mOverallReputationLevel));
if(isOwnId)
{
QFont font = item->font(RSID_COL_NICKNAME) ;
font.setBold(true) ;
item->setFont(RSID_COL_NICKNAME,font) ;
item->setFont(RSID_COL_IDTYPE,font) ;
item->setFont(RSID_COL_KEYID,font) ;
QString tooltip = tr("This identity is owned by you");
if(isOwnId)
{
QString tooltip = tr("This identity is owned by you");
if(idd.mFlags & RS_IDENTITY_FLAGS_IS_DEPRECATED)
{
@ -1486,10 +1448,16 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
tooltip += tr("\nThis identity has a unsecure fingerprint (It's probably quite old).\nYou should get rid of it now and use a new one.\nThese identities will soon be not supported anymore.") ;
}
item->setToolTip(RSID_COL_NICKNAME, tooltip) ;
item->setToolTip(RSID_COL_KEYID, tooltip) ;
item->setToolTip(RSID_COL_IDTYPE, tooltip) ;
}
item->setToolTip(RSID_COL_NICKNAME, tooltip) ;
item->setToolTip(RSID_COL_KEYID, tooltip) ;
item->setToolTip(RSID_COL_IDTYPE, tooltip) ;
}
QFont font = ui->idTreeWidget->font() ;
font.setBold(isOwnId) ;
item->setFont(RSID_COL_NICKNAME,font) ;
item->setFont(RSID_COL_IDTYPE,font) ;
item->setFont(RSID_COL_KEYID,font) ;
//QPixmap pixmap ;
//
@ -1500,8 +1468,6 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
// Icon Place Holder
item->setIcon(RSID_COL_NICKNAME,FilesDefs::getIconFromQtResourcePath(":/icons/png/anonymous.png"));
QString tooltip;
if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)
{
if (data.mPgpKnown)
@ -1512,25 +1478,25 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item,
item->setToolTip(RSID_COL_IDTYPE,"Verified signature from node "+QString::fromStdString(data.mPgpId.toStdString())) ;
tooltip += tr("Node name:")+" " + QString::fromUtf8(details.name.c_str()) + "\n";
tooltip += tr("Node Id :")+" " + QString::fromStdString(data.mPgpId.toStdString()) ;
QString tooltip = tr("Node name:")+" " + QString::fromUtf8(details.name.c_str()) + "\n";
tooltip += tr("Node Id :")+" " + QString::fromStdString(data.mPgpId.toStdString()) ;
item->setToolTip(RSID_COL_KEYID,tooltip) ;
}
else
{
QString txt = tr("[Unknown node]");
QString txt = tr("[Unknown node]");
item->setText(RSID_COL_IDTYPE, txt);
if(!data.mPgpId.isNull())
{
item->setToolTip(RSID_COL_IDTYPE,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
item->setToolTip(RSID_COL_KEYID,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
}
else
{
item->setToolTip(RSID_COL_IDTYPE,tr("Unchecked signature")) ;
item->setToolTip(RSID_COL_KEYID,tr("Unchecked signature")) ;
}
if(!data.mPgpId.isNull())
{
item->setToolTip(RSID_COL_IDTYPE,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
item->setToolTip(RSID_COL_KEYID,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ;
}
else
{
item->setToolTip(RSID_COL_IDTYPE,tr("Unchecked signature")) ;
item->setToolTip(RSID_COL_KEYID,tr("Unchecked signature")) ;
}
}
}
else
@ -1810,7 +1776,8 @@ void IdDialog::loadIdentity(RsGxsIdGroup data)
// ui->editIdentity->setEnabled(true);
// ui->removeIdentity->setEnabled(true);
ui->chatIdentity->setEnabled(false);
ui->inviteButton->setEnabled(false);
ui->inviteButton->hide();
ui->editButton->show();
}
else
{
@ -1820,7 +1787,8 @@ void IdDialog::loadIdentity(RsGxsIdGroup data)
// ui->editIdentity->setEnabled(false);
// ui->removeIdentity->setEnabled(false);
ui->chatIdentity->setEnabled(true);
ui->inviteButton->setEnabled(true);
ui->inviteButton->show();
ui->editButton->hide();
}
ui->autoBanIdentities_CB->setChecked(rsReputations->isNodeBanned(data.mPgpId));
@ -2221,8 +2189,9 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
if(!one_item_owned_by_you)
{
QWidget *widget = new QWidget(contextMenu);
widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
QFrame *widget = new QFrame(contextMenu);
widget->setObjectName("gradFrame"); //Use qss
//widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}");
// create menu header
QHBoxLayout *hbox = new QHBoxLayout(widget);
@ -2230,12 +2199,14 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
hbox->setSpacing(6);
QLabel *iconLabel = new QLabel(widget);
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
iconLabel->setObjectName("trans_Icon");
QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5);
iconLabel->setPixmap(pix);
iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width());
hbox->addWidget(iconLabel);
QLabel *textLabel = new QLabel("<strong>" + ui->titleBarLabel->text() + "</strong>", widget);
textLabel->setObjectName("trans_Text");
hbox->addWidget(textLabel);
QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
@ -2289,7 +2260,7 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
contextMenu->addAction(QIcon(""),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ;
if(n_is_not_a_contact == 0)
contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/images/cancel.png"), tr("Remove from Contacts"), this, SLOT(removefromContacts()));
contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/cancel.svg"), tr("Remove from Contacts"), this, SLOT(removefromContacts()));
contextMenu->addSeparator();
@ -2308,8 +2279,8 @@ void IdDialog::IdListCustomPopupMenu( QPoint )
contextMenu->addSeparator();
contextMenu->addAction(QIcon(""),tr("Copy identity to clipboard"),this,SLOT(copyRetroshareLink())) ;
contextMenu->addAction(ui->editIdentity);
contextMenu->addAction(ui->removeIdentity);
contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_EDIT),tr("Edit identity"),this,SLOT(editIdentity())) ;
contextMenu->addAction(FilesDefs::getIconFromQtResourcePath(":/icons/cancel.svg"),tr("Delete identity"),this,SLOT(removeIdentity())) ;
}
}
@ -2487,7 +2458,7 @@ void IdDialog::sendInvite()
{
MessageComposer::sendInvite(id,false);
ui->inviteFrame->show();
ui->info_Frame_Invite->show();
ui->inviteButton->setEnabled(false);
}
@ -2569,9 +2540,9 @@ void IdDialog::removefromContacts()
updateIdList();
}
void IdDialog::on_closeInfoFrameButton_clicked()
void IdDialog::on_closeInfoFrameButton_Invite_clicked()
{
ui->inviteFrame->setVisible(false);
ui->info_Frame_Invite->setVisible(false);
}
// We need to use indexes here because saving items is not possible since they can be re-created.

View file

@ -89,7 +89,7 @@ private slots:
void chatIdentityItem(QTreeWidgetItem* item);
void sendMsg();
void copyRetroshareLink();
void on_closeInfoFrameButton_clicked();
void on_closeInfoFrameButton_Invite_clicked();
void updateSelection();
@ -154,7 +154,6 @@ private:
RsEventsHandlerId_t mEventHandlerId_circles;
QTimer updateIdTimer;
QTimer updateCircleTimer;
bool needUpdateIdsOnNextShow;
bool needUpdateCirclesOnNextShow;

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>987</width>
<width>800</width>
<height>584</height>
</rect>
</property>
@ -88,7 +88,14 @@
</widget>
</item>
<item>
<widget class="StyledLabel" name="titleBarLabel">
<widget class="QLabel" name="titleBarLabel">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>People</string>
</property>
@ -211,6 +218,11 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@ -289,13 +301,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>634</width>
<height>523</height>
<width>456</width>
<height>731</height>
</rect>
</property>
<layout class="QVBoxLayout" name="scrollAreaWidgetContentsVLayout">
<item>
<widget class="QFrame" name="headerFramePerson">
<widget class="QFrame" name="headerBFramePerson">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
@ -308,37 +320,44 @@
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="headerFramePersonGLayout">
<layout class="QGridLayout" name="headerBFramePersonGLayout">
<property name="horizontalSpacing">
<number>12</number>
</property>
<item row="0" column="3">
<layout class="QVBoxLayout" name="headerFramePersonVLayout">
<item>
<widget class="StyledElidedLabel" name="headerTextLabel_Person">
<widget class="ElidedLabel" name="headerTextLabel_Person">
<property name="font">
<font>
<pointsize>22</pointsize>
</font>
</property>
<property name="text">
<string>People</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<widget class="QFrame" name="info_Frame_Invite">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>518</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="inviteFrame">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
@ -359,6 +378,15 @@
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
@ -379,6 +407,15 @@
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>154</red>
<green>154</green>
<blue>154</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
@ -409,7 +446,7 @@
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<layout class="QHBoxLayout" name="inviteFrameHLayout">
<layout class="QHBoxLayout" name="info_Frame_Invite_HL">
<property name="leftMargin">
<number>6</number>
</property>
@ -423,7 +460,7 @@
<number>6</number>
</property>
<item>
<widget class="QLabel" name="infoPixmap">
<widget class="QLabel" name="infoPixmap_Invite">
<property name="maximumSize">
<size>
<width>16</width>
@ -442,14 +479,17 @@
</widget>
</item>
<item>
<widget class="QLabel" name="infoLabel">
<widget class="QLabel" name="infoLabel_Invite">
<property name="text">
<string notr="true">Invite messages stay into your Outbox until an acknowledgement of receipt has been received.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="closeInfoFrameButton">
<widget class="QToolButton" name="closeInfoFrameButton_Invite">
<property name="maximumSize">
<size>
<width>16</width>
@ -609,6 +649,13 @@ border-image: url(:/images/closepressed.png)
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="editButton">
<property name="text">
<string>Edit Identity</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="avatarOpinionHLayout">
<property name="spacing">
@ -796,7 +843,7 @@ border-image: url(:/images/closepressed.png)
</widget>
</item>
<item row="9" column="1" rowspan="2">
<widget class="QComboBox" name="ownOpinion_CB">
<widget class="RSComboBox" name="ownOpinion_CB">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-family:'Sans'; font-size:9pt;&quot;&gt;Your own opinion about an identity rules the visibility of that identity for yourself and your friend nodes. Your own opinion is shared among friends and used to compute a reputation score: If your opinion about an identity is neutral, the reputation score is the difference between friend's positive and negative opinions. If not, your own opinion gives the score.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-family:'Sans'; font-size:9pt;&quot;&gt;The overall score is used in chat lobbies, forums and channels to decide on the actions to take for each specific identity. When the overall score is lower than -1, the identity is banned, which prevents all messages and forums/channels authored by this identity to be forwarded, both ways. Some forums also have special anti-spam flags that require a non negative reputation level, making them more sensitive to bad opinions. Banned identities gradually lose their activity and eventually disappear (after 5 days).&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-family:'Sans'; font-size:9pt;&quot;&gt;You can change the thresholds and the time of inactivity to delete identities in preferences -&amp;gt; people. &lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -844,9 +891,12 @@ border-image: url(:/images/closepressed.png)
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="autoBanIdentities_CB">
<property name="text">
<property name="toolTip">
<string>Auto-Ban all identities signed by the same node</string>
</property>
<property name="text">
<string>Auto-Ban profile</string>
</property>
</widget>
</item>
<item row="4" column="1">
@ -931,7 +981,7 @@ border-image: url(:/images/closepressed.png)
</layout>
<zorder>detailsGroupBox</zorder>
<zorder>usageStatisticsGBox</zorder>
<zorder>headerFramePerson</zorder>
<zorder>headerBFramePerson</zorder>
</widget>
</widget>
</item>
@ -947,19 +997,19 @@ border-image: url(:/images/closepressed.png)
</attribute>
<layout class="QVBoxLayout" name="circleTabVLayout">
<item>
<widget class="QFrame" name="headerFrameCircle">
<widget class="QFrame" name="headerBFrameCircle">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="headerFrameCircleGLayout">
<layout class="QGridLayout" name="headerBFrameCircleGLayout">
<property name="horizontalSpacing">
<number>12</number>
</property>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="avlabel_Circles">
<widget class="QLabel" name="avLabel_Circles">
<property name="minimumSize">
<size>
<width>64</width>
@ -984,7 +1034,12 @@ border-image: url(:/images/closepressed.png)
</widget>
</item>
<item row="0" column="1" rowspan="2" colspan="2">
<widget class="StyledElidedLabel" name="headerTextLabel_Circles">
<widget class="ElidedLabel" name="headerTextLabel_Circles">
<property name="font">
<font>
<pointsize>22</pointsize>
</font>
</property>
<property name="text">
<string>Circles</string>
</property>
@ -995,6 +1050,11 @@ border-image: url(:/images/closepressed.png)
</item>
<item>
<widget class="QTreeWidget" name="treeWidget_membership">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@ -1030,10 +1090,6 @@ border-image: url(:/images/closepressed.png)
</item>
</layout>
<action name="editIdentity">
<property name="icon">
<iconset>
<normaloff>:/images/edit_16.png</normaloff>:/images/edit_16.png</iconset>
</property>
<property name="text">
<string>Edit identity</string>
</property>
@ -1042,19 +1098,11 @@ border-image: url(:/images/closepressed.png)
</property>
</action>
<action name="removeIdentity">
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/delete.png</normaloff>:/images/delete.png</iconset>
</property>
<property name="text">
<string>Delete identity</string>
</property>
</action>
<action name="chatIdentity">
<property name="icon">
<iconset>
<normaloff>:/images/toaster/chat.png</normaloff>:/images/toaster/chat.png</iconset>
</property>
<property name="text">
<string>Chat with this peer</string>
</property>
@ -1067,7 +1115,7 @@ border-image: url(:/images/closepressed.png)
<customwidget>
<class>LineEditClear</class>
<extends>QLineEdit</extends>
<header>gui/common/LineEditClear.h</header>
<header location="global">gui/common/LineEditClear.h</header>
</customwidget>
<customwidget>
<class>RSTreeWidget</class>
@ -1075,14 +1123,14 @@ border-image: url(:/images/closepressed.png)
<header>gui/common/RSTreeWidget.h</header>
</customwidget>
<customwidget>
<class>StyledElidedLabel</class>
<class>ElidedLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledElidedLabel.h</header>
<header>gui/common/ElidedLabel.h</header>
</customwidget>
<customwidget>
<class>StyledLabel</class>
<extends>QLabel</extends>
<header>gui/common/StyledLabel.h</header>
<class>RSComboBox</class>
<extends>QComboBox</extends>
<header>gui/common/RSComboBox.h</header>
</customwidget>
<customwidget>
<class>RSTextBrowser</class>

View file

@ -31,9 +31,10 @@
#include "util/misc.h"
#include "gui/notifyqt.h"
#include <retroshare/rsidentity.h>
#include <retroshare/rspeers.h>
#include "retroshare/rsidentity.h"
#include "retroshare/rspeers.h"
#include "gui/common/FilesDefs.h"
#include "util/imageutil.h"
#include <iostream>
@ -46,6 +47,7 @@ IdEditDialog::IdEditDialog(QWidget *parent) :
ui(new(Ui::IdEditDialog))
{
mIsNew = true;
mAvatarIsSet = false;
ui->setupUi(this);
@ -76,7 +78,7 @@ IdEditDialog::IdEditDialog(QWidget *parent) :
/* Connect signals */
connect(ui->radioButton_GpgId, SIGNAL(toggled(bool)), this, SLOT(idTypeToggled(bool)));
connect(ui->radioButton_Pseudo, SIGNAL(toggled(bool)), this, SLOT(idTypeToggled(bool)));
connect(ui->createButton, SIGNAL(clicked()), this, SLOT(submit()));
connect(ui->postButton, SIGNAL(clicked()), this, SLOT(submit()));
connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
connect(ui->plainTextEdit_Tag, SIGNAL(textChanged()), this, SLOT(checkNewTag()));
@ -87,6 +89,12 @@ IdEditDialog::IdEditDialog(QWidget *parent) :
connect(ui->toolButton_Tag4, SIGNAL(clicked(bool)), this, SLOT(rmTag4()));
connect(ui->toolButton_Tag5, SIGNAL(clicked(bool)), this, SLOT(rmTag5()));
connect(ui->avatarButton, SIGNAL(clicked(bool)), this, SLOT(changeAvatar()));
connect(ui->removeButton, SIGNAL(clicked(bool)), this, SLOT(removeAvatar()));
connect(ui->avatarLabel,SIGNAL(cleared()),this,SLOT(avatarCleared()));
ui->avatarLabel->setEnableClear(true);
ui->avatarLabel->setToolTip(tr("No Avatar chosen. A default image will be automatically displayed from your new identity."));
/* Initialize ui */
ui->lineEdit_Nickname->setMaxLength(RSID_MAXIMUM_NICKNAME_SIZE);
@ -96,6 +104,8 @@ IdEditDialog::IdEditDialog(QWidget *parent) :
ui->plainTextEdit_Tag->hide();
ui->label_TagCheck->hide();
ui->frame_Tags->setHidden(true);
updateInterface();
}
IdEditDialog::~IdEditDialog() {}
@ -125,10 +135,13 @@ void IdEditDialog::changeAvatar()
ui->avatarLabel->setPicture(QPixmap::fromImage(img));
ui->avatarLabel->setEnableZoom(true);
ui->avatarLabel->setToolTip(tr("Use the mouse to zoom and adjust the image for your avatar."));
ui->avatarLabel->setToolTip(tr("Use the mouse to zoom and adjust the image for your avatar. Hit Del to remove it."));
mAvatarIsSet = true;
// shows the tooltip for a while
QToolTip::showText( ui->avatarLabel->mapToGlobal( QPoint( 0, 0 ) ), ui->avatarLabel->toolTip() );
updateInterface();
}
void IdEditDialog::setupNewId(bool pseudo,bool enable_anon)
@ -205,16 +218,25 @@ void IdEditDialog::updateIdType(bool pseudo)
}
}
void IdEditDialog::avatarCleared()
{
setAvatar(QPixmap());
}
void IdEditDialog::setAvatar(const QPixmap &avatar)
{
mAvatar = avatar;
if (!mAvatar.isNull()) {
ui->avatarLabel->setPicture(avatar);
mAvatarIsSet = true;
ui->avatarLabel->setToolTip(tr("Use the mouse to zoom and adjust the image for your avatar. Hit Del to remove it."));
} else {
// we need to use the default pixmap here, generated from the ID
ui->avatarLabel->setPicture(GxsIdDetails::makeDefaultIcon(RsGxsId(mEditGroup.mMeta.mGroupId)));
}
ui->avatarLabel->setText(tr("No avatar chosen")); // This clears up the image
mAvatarIsSet = false;
ui->avatarLabel->setToolTip(tr("No Avatar chosen. A default image will be automatically displayed from your new identity."));
}
}
void IdEditDialog::setupExistingId(const RsGxsGroupId& keyId)
@ -222,7 +244,7 @@ void IdEditDialog::setupExistingId(const RsGxsGroupId& keyId)
setWindowTitle(tr("Edit identity"));
ui->headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/person.png"));
ui->headerFrame->setHeaderText(tr("Edit identity"));
ui->createButton->setText(tr("Update"));
ui->postButton->setText(tr("Update"));
mStateHelper->setLoading(IDEDITDIALOG_LOADID, true);
@ -337,6 +359,7 @@ void IdEditDialog::loadExistingId(const RsGxsIdGroup& id_group)
ui->frame_Tags->setHidden(true);
loadRecognTags();
updateInterface();
}
#define MAX_RECOGN_TAGS 5
@ -517,13 +540,9 @@ void IdEditDialog::loadRecognTags()
void IdEditDialog::submit()
{
if (mIsNew)
{
createId();
}
else
{
updateId();
}
}
void IdEditDialog::createId()
@ -551,15 +570,17 @@ void IdEditDialog::createId()
params.nickname = groupname.toUtf8().constData();
params.isPgpLinked = (ui->radioButton_GpgId->isChecked());
mAvatar = ui->avatarLabel->extractCroppedScaledPicture();
if (!mAvatar.isNull())
if(mAvatarIsSet)
{
mAvatar = ui->avatarLabel->extractCroppedScaledPicture();
QByteArray ba;
QBuffer buffer(&ba);
bool has_transparency = ImageUtil::hasAlphaContent(mAvatar.toImage());
buffer.open(QIODevice::WriteOnly);
mAvatar.save(&buffer, "PNG"); // writes image into ba in PNG format
mAvatar.save(&buffer, has_transparency?"PNG":"JPG"); // writes image into ba in PNG format
params.mImage.copy((uint8_t *) ba.data(), ba.size());
}
@ -589,7 +610,7 @@ void IdEditDialog::createId()
if(rsIdentity->createIdentity(keyId,params.nickname,params.mImage,!params.isPgpLinked,gpg_password))
{
ui->createButton->setEnabled(false);
ui->postButton->setEnabled(false);
if(!keyId.isNull())
{
@ -630,8 +651,10 @@ void IdEditDialog::updateId()
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
mAvatar.save(&buffer, "PNG"); // writes image into ba in PNG format
bool has_transparency = ImageUtil::hasAlphaContent(mAvatar.toImage());
buffer.open(QIODevice::WriteOnly);
mAvatar.save(&buffer, has_transparency?"PNG":"JPG"); // writes image into ba in PNG format
mEditGroup.mImage.copy((uint8_t *) ba.data(), ba.size());
}
@ -667,3 +690,22 @@ void IdEditDialog::updateId()
accept();
}
void IdEditDialog::removeAvatar()
{
ui->avatarLabel->setPicture(QPixmap());
mEditGroup.mImage.clear();
updateInterface();
}
void IdEditDialog::updateInterface()
{
const QPixmap *pixmap = ui->avatarLabel->pixmap();
if (pixmap && !pixmap->isNull()) {
ui->removeButton->setEnabled(true);
} else if (mEditGroup.mImage.mSize != NULL) {
ui->removeButton->setEnabled(true);
} else {
ui->removeButton->setEnabled(false);
}
}

View file

@ -23,7 +23,6 @@
#include <inttypes.h>
#include "util/TokenQueue.h"
#include <retroshare/rsidentity.h>
#include <retroshare/rsgxsifacetypes.h>
#include <QDialog>
@ -51,8 +50,11 @@ public:
private slots:
void idTypeToggled(bool checked);
void submit();
void avatarCleared();
void changeAvatar();
void removeAvatar();
void updateInterface();
void addRecognTag();
void checkNewTag();
@ -68,7 +70,6 @@ private:
void updateIdType(bool pseudo);
void loadExistingId(const RsGxsIdGroup& id_group);
void setAvatar(const QPixmap &avatar);
void idCreated(uint32_t token);
void loadRecognTags();
// extract details.
@ -84,6 +85,7 @@ protected:
RsGxsGroupId mGroupId;
QPixmap mAvatar; // Avatar from identity (not calculated)
bool mAvatarIsSet;
};
#endif

View file

@ -55,10 +55,19 @@
</property>
<layout class="QVBoxLayout" name="frame_TopVLayout">
<item>
<widget class="QLabel" name="info_label">
<widget class="QLabel" name="info_Label">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
@ -79,6 +88,15 @@
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
@ -99,6 +117,15 @@
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>154</red>
<green>154</green>
<blue>154</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
@ -120,6 +147,11 @@
</disabled>
</palette>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
@ -310,6 +342,9 @@
<height>128</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
@ -334,6 +369,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="avatarVSpacer">
<property name="orientation">
@ -362,7 +404,7 @@
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="frame_Tags_GL">
<item row="2" column="0">
<widget class="QLabel" name="label_Tag2">
<property name="text">
@ -474,7 +516,7 @@
</item>
<item>
<widget class="QFrame" name="frame_Button">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<layout class="QHBoxLayout" name="frame_Button_HL">
<property name="spacing">
<number>9</number>
</property>
@ -504,7 +546,14 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="createButton">
<widget class="QPushButton" name="postButton">
<property name="font">
<font>
<pointsize>12</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Create</string>
</property>

View file

@ -18,12 +18,14 @@
* *
*******************************************************************************/
#include <QToolButton>
#include <QTimer>
#include <retroshare-gui/mainpage.h>
#include "common/FloatingHelpBrowser.h"
#include "gui/settings/rsharesettings.h"
#include "util/misc.h"
#include <QToolButton>
#include <QTimer>
MainPage::MainPage(QWidget *parent , Qt::WindowFlags flags ) : QWidget(parent, flags)
{
@ -44,13 +46,13 @@ UserNotify *MainPage::getUserNotify()
void MainPage::registerHelpButton(QToolButton *button, const QString& help_html_text, const QString &code_name)
{
mHelpCodeName = code_name ;
mHelpCodeName = code_name ;
if (mHelpBrowser == NULL)
if (mHelpBrowser == nullptr)
mHelpBrowser = new FloatingHelpBrowser(this, button) ;
float S = QFontMetricsF(button->font()).height() ;
button->setIconSize(QSize(S,S)) ;
int H = misc::getFontSizeFactor("HelpButton").height();
button->setIconSize(QSize(H, H)) ;//Square Icon
mHelpBrowser->setHelpText(help_html_text) ;
}

View file

@ -21,6 +21,7 @@
#include <QColorDialog>
#include <QDesktopServices>
#include <QIcon>
#include <QInputDialog>
#include <QMessageBox>
#include <QPixmap>
#include <QStatusBar>
@ -140,12 +141,10 @@
#define IMAGE_OVERLAY ":/icons/star_overlay_128.png"
#define IMAGE_BWGRAPH ":/icons/png/bandwidth.png"
#define IMAGE_MESSENGER ":/images/rsmessenger48.png"
#define IMAGE_COLOR ":/images/highlight.png"
#define IMAGE_NEWRSCOLLECTION ":/images/library.png"
#define IMAGE_ADDSHARE ":/images/directoryadd_24x24_shadow.png"
#define IMAGE_OPTIONS ":/images/settings.png"
#define IMAGE_UNFINISHED ":/images/underconstruction.png"
#define IMAGE_MINIMIZE ":/icons/fullscreen.png"
#define IMAGE_MAXIMIZE ":/icons/fullscreen-exit.png"
@ -247,12 +246,13 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags)
statusBar()->setVisible(Settings->valueFromGroup("StatusBar", "ShowStatusBar", QVariant(true)).toBool());
/* initialize combobox in status bar */
statusComboBox = new QComboBox(statusBar());
statusComboBox = new RSComboBox(statusBar());
statusComboBox->setVisible(Settings->valueFromGroup("StatusBar", "ShowStatus", QVariant(true)).toBool());
statusComboBox->setFocusPolicy(Qt::ClickFocus);
initializeStatusObject(statusComboBox, true);
QWidget *widget = new QWidget();
widget->setObjectName("trans_statusComboBoxFrame");
QHBoxLayout *hbox = new QHBoxLayout();
hbox->setMargin(0);
hbox->setSpacing(6);
@ -466,10 +466,8 @@ void MainWindow::initStackedPage()
}
#ifndef RS_RELEASE_VERSION
#ifdef PLUGINMGR
addPage(pluginsPage = new gui::PluginsPage(ui->stackPages), grp, NULL);
#endif
#endif
#undef GETSTARTED_GUI
#ifdef GETSTARTED_GUI
@ -487,11 +485,11 @@ void MainWindow::initStackedPage()
addPage(newsFeed = new NewsFeed(ui->stackPages), grp, &notify);
//List All notify before Setting was created
QList<QPair<MainPage*, QPair<QAction*, QListWidgetItem*> > >::iterator notifyIt;
for (notifyIt = notify.begin(); notifyIt != notify.end(); ++notifyIt) {
UserNotify *userNotify = notifyIt->first->getUserNotify();
for (auto notifyIt:notify)
{
UserNotify *userNotify = notifyIt.first->getUserNotify();
if (userNotify) {
userNotify->initialize(ui->toolBarPage, notifyIt->second.first, notifyIt->second.second);
userNotify->initialize(ui->toolBarPage, notifyIt.second.first, notifyIt.second.second);
connect(userNotify, SIGNAL(countChanged()), this, SLOT(updateTrayCombine()));
userNotifyList.push_back(userNotify);
}
@ -505,7 +503,7 @@ void MainWindow::initStackedPage()
#ifdef UNFINISHED
addAction(new QAction(QIcon(IMAGE_UNFINISHED), tr("Unfinished"), ui->toolBar), &MainWindow::showApplWindow, SLOT(showApplWindow()));
addAction(new QAction(QIcon(), tr("Unfinished"), ui->toolBar), &MainWindow::showApplWindow, SLOT(showApplWindow()));
ui->toolBarAction->addSeparator();
notify += applicationWindow->getNotify();
#endif
@ -623,7 +621,7 @@ void MainWindow::createTrayIcon()
trayMenu->addSeparator();
#ifdef MESSENGER_WINDOW
trayMenu->addAction(QIcon(IMAGE_MESSENGER), tr("Open Messenger"), this, SLOT(showMessengerWindow()));
trayMenu->addAction(QIcon(), tr("Open Messenger"), this, SLOT(showMessengerWindow()));
#endif
trayMenu->addAction(QIcon(IMAGE_MESSAGES), tr("Open Messages"), this, SLOT(showMess()));
#ifdef RS_JSONAPI
@ -636,7 +634,7 @@ void MainWindow::createTrayIcon()
#ifdef UNFINISHED
trayMenu->addAction(QIcon(IMAGE_UNFINISHED), tr("Applications"), this, SLOT(showApplWindow()));
trayMenu->addAction(QIcon(), tr("Applications"), this, SLOT(showApplWindow()));
#endif
trayMenu->addAction(QIcon(IMAGE_PREFERENCES), tr("Options"), this, SLOT(showSettings()));
trayMenu->addAction(QIcon(IMG_HELP), tr("Help"), this, SLOT(showHelpDialog()));
@ -1081,7 +1079,9 @@ void SetForegroundWindowInternal(HWND hWnd)
return _instance->gxsforumDialog;
case Posted:
return _instance->postedDialog;
}
case Home:
return _instance->homePage;
}
return NULL;
}
@ -1300,7 +1300,7 @@ static void setStatusObject(QObject *pObject, int nStatus)
}
return;
}
QComboBox *pComboBox = dynamic_cast<QComboBox*>(pObject);
RSComboBox *pComboBox = dynamic_cast<RSComboBox*>(pObject);
if (pComboBox) {
/* set index of combobox */
int nIndex = pComboBox->findData(nStatus, Qt::UserRole);
@ -1389,7 +1389,7 @@ void MainWindow::initializeStatusObject(QObject *pObject, bool bConnect)
}
} else {
/* initialize combobox */
QComboBox *pComboBox = dynamic_cast<QComboBox*>(pObject);
RSComboBox *pComboBox = dynamic_cast<RSComboBox*>(pObject);
if (pComboBox) {
pComboBox->addItem(QIcon(StatusDefs::imageStatus(RS_STATUS_ONLINE)), StatusDefs::name(RS_STATUS_ONLINE), RS_STATUS_ONLINE);
pComboBox->addItem(QIcon(StatusDefs::imageStatus(RS_STATUS_BUSY)), StatusDefs::name(RS_STATUS_BUSY), RS_STATUS_BUSY);
@ -1613,7 +1613,7 @@ void MainWindow::switchVisibilityStatus(StatusElement e,bool b)
//{
// ServicePermissionDialog::showYourself();
//}
QComboBox *MainWindow::statusComboBoxInstance()
RSComboBox *MainWindow::statusComboBoxInstance()
{
return statusComboBox;
}
@ -1628,6 +1628,11 @@ NATStatus *MainWindow::natstatusInstance()
return natstatus;
}
void MainWindow::torstatusReset()
{
if(torstatus != nullptr)
torstatus->reset();
}
DHTStatus *MainWindow::dhtstatusInstance()
{
return dhtstatus;
@ -1688,3 +1693,19 @@ void MainWindow::setCompactStatusMode(bool compact)
ratesstatus->setCompactMode(compact);
//opModeStatus: TODO Show only ???
}
Gui_InputDialogReturn MainWindow::guiInputDialog(const QString& windowTitle, const QString& labelText, QLineEdit::EchoMode textEchoMode, bool modal)
{
QInputDialog dialog(this);
dialog.setWindowTitle(windowTitle);
dialog.setLabelText(labelText);
dialog.setTextEchoMode(textEchoMode);
dialog.setModal(modal);
Gui_InputDialogReturn ret;
ret.execReturn = dialog.exec();
ret.textValue = dialog.textValue();
return ret;
}

View file

@ -21,16 +21,17 @@
#ifndef _MainWindow_H
#define _MainWindow_H
#include <QLineEdit>
#include <QSystemTrayIcon>
#include <set>
#include "gui/common/rwindow.h"
#include "gui/common/RSComboBox.h"
namespace Ui {
class MainWindow;
}
class QComboBox;
class QLabel;
class QActionGroup;
class QListWidgetItem;
@ -74,6 +75,14 @@ class MessengerWindow;
class ApplicationWindow;
#endif
struct Gui_InputDialogReturn
{
int execReturn;
QString textValue;
};
Q_DECLARE_METATYPE(Gui_InputDialogReturn);
class MainWindow : public RWindow
{
Q_OBJECT
@ -91,9 +100,10 @@ public:
Channels = 6, /** Channels page. */
Forums = 7, /** Forums page. */
Search = 8, /** Search page. */
Posted = 11, /** Posted links */
People = 12, /** People page. */
Options = 13 /** People page. */
Posted = 11, /** Posted links */
People = 12, /** People page. */
Options = 13, /** People page. */
Home = 14 /** Home page. */
};
@ -174,9 +184,10 @@ public:
void removeStatusObject(QObject *pObject);
void setStatus(QObject *pObject, int nStatus);
QComboBox *statusComboBoxInstance();
RSComboBox *statusComboBoxInstance();
PeerStatus *peerstatusInstance();
NATStatus *natstatusInstance();
void torstatusReset();
DHTStatus *dhtstatusInstance();
HashingStatus *hashingstatusInstance();
DiscStatus *discstatusInstance();
@ -192,7 +203,7 @@ public:
}
static bool hiddenmode;
public slots:
void receiveNewArgs(QStringList args);
void displayErrorMessage(int,int,const QString&) ;
@ -210,9 +221,35 @@ public slots:
void showBandwidthGraph();
void toggleStatusToolTip(bool toggle);
/**
* @brief Create a QInputDialog. This must be called in MainWindow thread because Widgets must be created in the GUI thread.
* Here an exemple how to call it:
*
* bool sameThread = QThread::currentThread() == qApp->thread();
* Gui_InputDialogReturn ret;
* qRegisterMetaType<Gui_InputDialogReturn>("Gui_InputDialogReturn");
* QMetaObject::invokeMethod( MainWindow::getInstance()
* , "guiInputDialog"
* , sameThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection
* , Q_RETURN_ARG(Gui_InputDialogReturn, ret)
* , Q_ARG(QString, windowTitle)
* , Q_ARG(QString, labelText)
* , Q_ARG(QLineEdit::EchoMode, textEchoMode)
* , Q_ARG(bool, modal)
* );
*
* @param windowTitle: the window title (caption).
* @param labelText: label's text which describes what needs to be input.
* @param textEchoMode: the echo mode for the text value.
* @param modal: pop up the dialog as modal or modeless.
* @return Gui_InputDialogReturn ( Accepted(1)|Rejected(0), text value for the input dialog)
*/
Gui_InputDialogReturn guiInputDialog(const QString& windowTitle, const QString& labelText, QLineEdit::EchoMode textEchoMode, bool modal);
protected:
/** Default Constructor */
MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0);
MainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
void closeEvent(QCloseEvent *);
@ -294,7 +331,7 @@ private:
QAction *toggleVisibilityAction, *toolAct;
QList<UserNotify*> userNotifyList;
QComboBox *statusComboBox;
RSComboBox *statusComboBox;
PeerStatus *peerstatus;
NATStatus *natstatus;
DHTStatus *dhtstatus;

View file

@ -33,6 +33,11 @@
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
@ -143,7 +148,7 @@
</action>
<action name="actionAdd_Share">
<property name="icon">
<iconset resource="images.qrc">
<iconset>
<normaloff>:/images/add-share24.png</normaloff>:/images/add-share24.png</iconset>
</property>
<property name="text">
@ -161,7 +166,7 @@
</action>
<action name="actionMessenger">
<property name="icon">
<iconset resource="images.qrc">
<iconset>
<normaloff>:/images/messenger.png</normaloff>:/images/messenger.png</iconset>
</property>
<property name="text">
@ -187,7 +192,7 @@
</action>
<action name="actionQuit">
<property name="icon">
<iconset resource="images.qrc">
<iconset>
<normaloff>:/images/exit_24x24.png</normaloff>:/images/exit_24x24.png</iconset>
</property>
<property name="text">

View file

@ -74,7 +74,7 @@
<item row="0" column="1">
<layout class="QGridLayout" name="gridLayout_4">
<item row="1" column="0" colspan="2">
<widget class="QComboBox" name="messagelineEdit">
<widget class="RSComboBox" name="messagelineEdit">
<property name="editable">
<bool>true</bool>
</property>
@ -124,7 +124,7 @@
</widget>
</item>
<item row="2" column="0">
<widget class="LogoBar" name="logoFrame">
<widget class="LogoBar" name="gradFrame_Logo">
<property name="minimumSize">
<size>
<width>0</width>
@ -175,7 +175,7 @@
<x>0</x>
<y>0</y>
<width>258</width>
<height>21</height>
<height>27</height>
</rect>
</property>
</widget>
@ -199,6 +199,11 @@
<header>gui/LogoBar.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RSComboBox</class>
<extends>QComboBox</extends>
<header>gui/common/RSComboBox.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="images.qrc"/>

View file

@ -73,7 +73,7 @@ NetworkDialog::NetworkDialog(QWidget */*parent*/)
PGPIdItemProxy = new pgpid_item_proxy(this);
connect(ui.onlyTrustedKeys, SIGNAL(toggled(bool)), PGPIdItemProxy, SLOT(use_only_trusted_keys(bool)));
PGPIdItemProxy->setSourceModel(PGPIdItemModel);
PGPIdItemProxy->setFilterKeyColumn(COLUMN_PEERNAME);
PGPIdItemProxy->setFilterKeyColumn(pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERNAME);
PGPIdItemProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
PGPIdItemProxy->setSortRole(Qt::EditRole); //use edit role to get raw data since we do not have edit for this model.
ui.connectTreeWidget->setModel(PGPIdItemProxy);
@ -90,9 +90,9 @@ NetworkDialog::NetworkDialog(QWidget */*parent*/)
ui.onlyTrustedKeys->setMinimumWidth(20*f);
/* add filter actions */
ui.filterLineEdit->addFilter(QIcon(), tr("Name"), COLUMN_PEERNAME, tr("Search name"));
ui.filterLineEdit->addFilter(QIcon(), tr("Peer ID"), COLUMN_PEERID, tr("Search peer ID"));
ui.filterLineEdit->setCurrentFilter(COLUMN_PEERNAME);
ui.filterLineEdit->addFilter(QIcon(), tr("Name"), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERNAME, tr("Search name"));
ui.filterLineEdit->addFilter(QIcon(), tr("Peer ID"), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID, tr("Search peer ID"));
ui.filterLineEdit->setCurrentFilter(pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERNAME);
connect(ui.filterLineEdit, SIGNAL(textChanged(QString)), PGPIdItemProxy, SLOT(setFilterWildcard(QString)));
}
@ -118,6 +118,7 @@ void NetworkDialog::connectTreeWidgetCostumPopupMenu( QPoint /*point*/ )
return;
}
QMenu *contextMnu = new QMenu;
contextMnu->addAction(QIcon(IMAGE_PEERDETAILS), tr("Profile details..."), this, SLOT(peerdetails()));
contextMnu->addSeparator() ;
contextMnu->addAction(QIcon(), tr("Remove unused keys..."), this, SLOT(removeUnusedKeys()));
@ -165,6 +166,7 @@ void NetworkDialog::removeSelectedKeys()
if(l.empty())
return;
std::set<RsPgpId> selected;
std::set<RsPgpId> friends;
for (int i = 0; i < l.size(); i++)
{
@ -189,6 +191,7 @@ void NetworkDialog::removeSelectedKeys()
}
if(!selected.empty())
removeKeys(selected);
updateDisplay();
}
@ -236,7 +239,7 @@ void NetworkDialog::denyFriend()
if(l.empty())
return;
RsPgpId peer_id(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString());
RsPgpId peer_id(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString());
rsPeers->removeFriend(peer_id) ;
securedUpdateDisplay();
@ -249,7 +252,7 @@ void NetworkDialog::makeFriend()
if(l.empty())
return;
PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
}
/** Shows Peer Information/Auth Dialog */
@ -259,7 +262,7 @@ void NetworkDialog::peerdetails()
if(l.empty())
return;
PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
PGPKeyDialog::showIt(RsPgpId(ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString()), PGPKeyDialog::PageDetails);
}
void NetworkDialog::copyLink()
@ -269,7 +272,7 @@ void NetworkDialog::copyLink()
return;
RsPgpId peer_id (ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), COLUMN_PEERID)).toString().toStdString()) ;
RsPgpId peer_id (ui.connectTreeWidget->model()->data(ui.connectTreeWidget->model()->index(l.begin()->row(), pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID)).toString().toStdString()) ;
QList<RetroShareLink> urls;
RetroShareLink link = RetroShareLink::createPerson(peer_id);

View file

@ -43,6 +43,7 @@ class NetworkDialog : public RsAutoUpdatePage
Q_PROPERTY(QColor backgroundColorAcceptConnection READ backgroundColorAcceptConnection WRITE setBackgroundColorAcceptConnection)
Q_PROPERTY(QColor backgroundColorHasSignedMe READ backgroundColorHasSignedMe WRITE setBackgroundColorHasSignedMe)
Q_PROPERTY(QColor backgroundColorDenied READ backgroundColorDenied WRITE setBackgroundColorDenied)
Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
public:
/** Default Constructor */
@ -55,12 +56,14 @@ public:
QColor backgroundColorAcceptConnection() const { return mBackgroundColorAcceptConnection; }
QColor backgroundColorHasSignedMe() const { return mBackgroundColorHasSignedMe; }
QColor backgroundColorDenied() const { return mBackgroundColorDenied; }
QColor textColor() const { return mTextColor; }
void setBackgroundColorSelf(QColor color) { PGPIdItemModel->setBackgroundColorSelf(color); mBackgroundColorSelf = color; }
void setBackgroundColorOwnSign(QColor color) { PGPIdItemModel->setBackgroundColorOwnSign(color); mBackgroundColorOwnSign = color; }
void setBackgroundColorAcceptConnection(QColor color) { PGPIdItemModel->setBackgroundColorAcceptConnection(color); mBackgroundColorAcceptConnection = color; }
void setBackgroundColorHasSignedMe(QColor color) { PGPIdItemModel->setBackgroundColorHasSignedMe(color); mBackgroundColorHasSignedMe = color; }
void setBackgroundColorDenied(QColor color) { PGPIdItemModel->setBackgroundColorDenied(color); mBackgroundColorDenied = color; }
void setTextColor(QColor color) { PGPIdItemModel->setTextColor(color); mTextColor = color; }
protected:
void changeEvent(QEvent *e);
@ -89,12 +92,13 @@ private:
void removeKeys(std::set<RsPgpId> selected) ;
/* Color definitions (for standard see qss.default) */
/* Color definitions (for standard see default.qss) */
QColor mBackgroundColorSelf;
QColor mBackgroundColorOwnSign;
QColor mBackgroundColorAcceptConnection;
QColor mBackgroundColorHasSignedMe;
QColor mBackgroundColorDenied;
QColor mTextColor;
RSTreeWidgetItemCompareRole *compareNetworkRole ;

View file

@ -24,6 +24,9 @@
#include <QIcon>
#include <QBrush>
#define IMAGE_AUTHED ":/images/accepted16.png"
#define IMAGE_DENIED ":/images/denied16.png"
#define IMAGE_TRUSTED ":/images/rs-2.png"
/*TODO:
* using list here for internal data storage is not best option
@ -41,22 +44,22 @@ QVariant pgpid_item_model::headerData(int section, Qt::Orientation orientation,
{
switch(section)
{
case COLUMN_CHECK:
case PGP_ITEM_MODEL_COLUMN_CHECK:
return QString(tr(" Do you accept connections signed by this profile?"));
break;
case COLUMN_PEERNAME:
case PGP_ITEM_MODEL_COLUMN_PEERNAME:
return QString(tr("Name of the profile"));
break;
case COLUMN_I_AUTH_PEER:
case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
return QString(tr("This column indicates the trust level you indicated and whether you signed the profile PGP key"));
break;
case COLUMN_PEER_AUTH_ME:
case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return QString(tr("Did that peer sign your own profile PGP key"));
break;
case COLUMN_PEERID:
case PGP_ITEM_MODEL_COLUMN_PEERID:
return QString(tr("PGP Key Id of that profile"));
break;
case COLUMN_LAST_USED:
case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return QString(tr("Last time this key was used (received time, or to check connection)"));
break;
}
@ -65,22 +68,22 @@ QVariant pgpid_item_model::headerData(int section, Qt::Orientation orientation,
{
switch(section)
{
case COLUMN_CHECK:
case PGP_ITEM_MODEL_COLUMN_CHECK:
return QString(tr("Connections"));
break;
case COLUMN_PEERNAME:
case PGP_ITEM_MODEL_COLUMN_PEERNAME:
return QString(tr("Profile"));
break;
case COLUMN_I_AUTH_PEER:
case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
return QString(tr("Trust level"));
break;
case COLUMN_PEER_AUTH_ME:
case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return QString(tr("Has signed your key?"));
break;
case COLUMN_PEERID:
case PGP_ITEM_MODEL_COLUMN_PEERID:
return QString(tr("Id"));
break;
case COLUMN_LAST_USED:
case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return QString(tr("Last used"));
break;
}
@ -98,13 +101,13 @@ QVariant pgpid_item_model::headerData(int section, Qt::Orientation orientation,
{
switch(section)
{
case COLUMN_CHECK:
case PGP_ITEM_MODEL_COLUMN_CHECK:
return 25*font_height;
break;
case COLUMN_PEERNAME: case COLUMN_I_AUTH_PEER: case COLUMN_PEER_AUTH_ME:
case PGP_ITEM_MODEL_COLUMN_PEERNAME: case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER: case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return 200*font_height;
break;
case COLUMN_LAST_USED:
case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return 75*font_height;
break;
}
@ -122,7 +125,7 @@ int pgpid_item_model::rowCount(const QModelIndex &/*parent*/) const
int pgpid_item_model::columnCount(const QModelIndex &/*parent*/) const
{
return COLUMN_COUNT;
return PGP_ITEM_MODEL_COLUMN_COUNT;
}
@ -148,20 +151,20 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
case COLUMN_LAST_USED:
case PGP_ITEM_MODEL_COLUMN_LAST_USED:
return detail.lastUsed;
break;
case COLUMN_I_AUTH_PEER:
case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
{
if (detail.ownsign)
return RS_TRUST_LVL_ULTIMATE;
return detail.trustLvl;
}
break;
case COLUMN_PEER_AUTH_ME:
case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
return detail.hasSignedMe;
break;
case COLUMN_CHECK:
case PGP_ITEM_MODEL_COLUMN_CHECK:
return detail.accept_connection;
break;
default:
@ -174,13 +177,13 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
case COLUMN_PEERNAME:
case PGP_ITEM_MODEL_COLUMN_PEERNAME:
return QString::fromUtf8(detail.name.c_str());
break;
case COLUMN_PEERID:
case PGP_ITEM_MODEL_COLUMN_PEERID:
return QString::fromStdString(detail.gpg_id.toStdString());
break;
case COLUMN_I_AUTH_PEER:
case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
{
if (detail.ownsign)
return tr("Personal signature");
@ -199,7 +202,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
}
}
break;
case COLUMN_PEER_AUTH_ME:
case PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME:
{
if (detail.hasSignedMe)
return tr("Yes");
@ -207,7 +210,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
return tr("No");
}
break;
case COLUMN_LAST_USED:
case PGP_ITEM_MODEL_COLUMN_LAST_USED:
{
time_t now = time(NULL);
uint64_t last_time_used = now - detail.lastUsed ;
@ -226,7 +229,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
return lst_used_str;
}
break;
case COLUMN_CHECK:
case PGP_ITEM_MODEL_COLUMN_CHECK:
{
if (detail.accept_connection || rsPeers->getGPGOwnId() == detail.gpg_id)
return tr("Accepted");
@ -242,7 +245,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
case COLUMN_I_AUTH_PEER:
case PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER:
{
if (detail.ownsign)
return tr("PGP key signed by you");
@ -263,7 +266,7 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
{
switch(index.column())
{
case COLUMN_CHECK:
case PGP_ITEM_MODEL_COLUMN_CHECK:
{
if (detail.accept_connection)
return QIcon(IMAGE_AUTHED);
@ -299,6 +302,10 @@ QVariant pgpid_item_model::data(const QModelIndex &index, int role) const
}
}
}
else if(role == Qt::ForegroundRole)
{
return QBrush(mTextColor);
}
return QVariant();
}
@ -341,14 +348,14 @@ void pgpid_item_model::data_updated(std::list<RsPgpId> &new_neighs)
break;
if(*i1 != *i2)
{
QModelIndex topLeft = createIndex(ii1,0), bottomRight = createIndex(ii1, COLUMN_COUNT-1);
QModelIndex topLeft = createIndex(ii1,0), bottomRight = createIndex(ii1, PGP_ITEM_MODEL_COLUMN_COUNT-1);
emit dataChanged(topLeft, bottomRight);
}
}
}
if(new_size > old_size)
{
QModelIndex topLeft = createIndex(old_size ? old_size -1 : 0 ,0), bottomRight = createIndex(new_size -1, COLUMN_COUNT-1);
QModelIndex topLeft = createIndex(old_size ? old_size -1 : 0 ,0), bottomRight = createIndex(new_size -1, PGP_ITEM_MODEL_COLUMN_COUNT-1);
emit dataChanged(topLeft, bottomRight);
}
//dirty solution for initial data fetch

View file

@ -25,20 +25,6 @@
#include <retroshare/rspeers.h>
#include <QColor>
#define IMAGE_AUTHED ":/images/accepted16.png"
#define IMAGE_DENIED ":/images/denied16.png"
#define IMAGE_TRUSTED ":/images/rs-2.png"
#define COLUMN_CHECK 0
#define COLUMN_PEERNAME 1
#define COLUMN_I_AUTH_PEER 2
#define COLUMN_PEER_AUTH_ME 3
#define COLUMN_PEERID 4
#define COLUMN_LAST_USED 5
#define COLUMN_COUNT 6
class pgpid_item_model : public QAbstractTableModel
{
Q_OBJECT
@ -60,7 +46,15 @@ public:
void setBackgroundColorAcceptConnection(QColor color) { mBackgroundColorAcceptConnection = color; }
void setBackgroundColorHasSignedMe(QColor color) { mBackgroundColorHasSignedMe = color; }
void setBackgroundColorDenied(QColor color) { mBackgroundColorDenied = color; }
void setTextColor(QColor color) { mTextColor = color; }
static constexpr int PGP_ITEM_MODEL_COLUMN_CHECK = 0;
static constexpr int PGP_ITEM_MODEL_COLUMN_PEERNAME = 1;
static constexpr int PGP_ITEM_MODEL_COLUMN_I_AUTH_PEER = 2;
static constexpr int PGP_ITEM_MODEL_COLUMN_PEER_AUTH_ME = 3;
static constexpr int PGP_ITEM_MODEL_COLUMN_PEERID = 4;
static constexpr int PGP_ITEM_MODEL_COLUMN_LAST_USED = 5;
static constexpr int PGP_ITEM_MODEL_COLUMN_COUNT = 6;
public slots:
void data_updated(std::list<RsPgpId> &new_neighs);
@ -73,6 +67,7 @@ private:
QColor mBackgroundColorAcceptConnection;
QColor mBackgroundColorHasSignedMe;
QColor mBackgroundColorDenied;
QColor mTextColor;
};
#endif // KEY_ITEM_MODEL_H

View file

@ -26,17 +26,13 @@
#include <retroshare/rsdisc.h>
#include <retroshare/rsmsgs.h>
//TODO: set this defines in one place
// Defines for key list columns
#define COLUMN_CHECK 0
#define COLUMN_PEERNAME 1
#define COLUMN_I_AUTH_PEER 2
#define COLUMN_PEER_AUTH_ME 3
#define COLUMN_PEERID 4
#define COLUMN_LAST_USED 5
#define COLUMN_COUNT 6
bool pgpid_item_proxy::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
if(left.column() == pgpid_item_model::PGP_ITEM_MODEL_COLUMN_LAST_USED)
return left.data(Qt::EditRole).toUInt() < right.data(Qt::EditRole).toUInt();
else
return left.data(Qt::DisplayRole).toString().toUpper() < right.data(Qt::DisplayRole).toString().toUpper();
}
pgpid_item_proxy::pgpid_item_proxy(QObject *parent) :
@ -57,7 +53,7 @@ bool pgpid_item_proxy::filterAcceptsRow(int sourceRow, const QModelIndex &source
{
if(!rsPeers)
return false;
RsPgpId peer_id (sourceModel()->data(sourceModel()->index(sourceRow, COLUMN_PEERID, sourceParent)).toString().toStdString());
RsPgpId peer_id (sourceModel()->data(sourceModel()->index(sourceRow, pgpid_item_model::PGP_ITEM_MODEL_COLUMN_PEERID, sourceParent)).toString().toStdString());
RsPeerDetails details;
if(!rsPeers->getGPGDetails(peer_id, details))
return false;

Some files were not shown because too many files have changed in this diff Show more