diff --git a/RetroShare.pro b/RetroShare.pro index 833173029..9a6f24d5f 100644 --- a/RetroShare.pro +++ b/RetroShare.pro @@ -6,12 +6,16 @@ TEMPLATE = subdirs SUBDIRS += openpgpsdk openpgpsdk.file = openpgpsdk/src/openpgpsdk.pro -SUBDIRS += libbitdht -libbitdht.file = libbitdht/src/libbitdht.pro +retrotor { + libretroshare.depends = openpgpsdk +} else { + SUBDIRS += libbitdht + libbitdht.file = libbitdht/src/libbitdht.pro + libretroshare.depends = openpgpsdk libbitdht +} SUBDIRS += libretroshare libretroshare.file = libretroshare/src/libretroshare.pro -libretroshare.depends = openpgpsdk libbitdht SUBDIRS += libresapi libresapi.file = libresapi/src/libresapi.pro @@ -24,12 +28,15 @@ retroshare_gui { retroshare_gui.target = retroshare_gui } +retrotor { +} else { retroshare_nogui { SUBDIRS += retroshare_nogui retroshare_nogui.file = retroshare-nogui/src/retroshare-nogui.pro retroshare_nogui.depends = libretroshare libresapi retroshare_nogui.target = retroshare_nogui } +} retroshare_android_service { SUBDIRS += retroshare_android_service diff --git a/build_scripts/AppImage/makeAppImage_retrotor.sh b/build_scripts/AppImage/makeAppImage_retrotor.sh new file mode 100755 index 000000000..5e128fd5c --- /dev/null +++ b/build_scripts/AppImage/makeAppImage_retrotor.sh @@ -0,0 +1,3 @@ +#!/bin/sh +./Recipe retrotor.yml + diff --git a/build_scripts/AppImage/retrotor.yml b/build_scripts/AppImage/retrotor.yml new file mode 100644 index 000000000..e7eba009a --- /dev/null +++ b/build_scripts/AppImage/retrotor.yml @@ -0,0 +1,20 @@ +app: retroshare +union: true + +ingredients: + dist: trusty + sources: + - deb http://archive.ubuntu.com/ubuntu trusty main restricted universe multiverse + - deb http://archive.ubuntu.com/ubuntu trusty-updates main restricted universe multiverse + - deb http://deb.torproject.org/torproject.org/ trusty main + ppas: + - retroshare/testing + packages: + - libqt5svg5 + - tor + - retrotor + +script: + - rm -rf usr/share/glib-2.0/schemas/ + - sed -i -e 's|/usr/share/pixmaps/||g' retroshare.desktop + - sed -i -e 's|/usr/bin/||g' retroshare.desktop diff --git a/build_scripts/Debian+Ubuntu/changelog b/build_scripts/Debian+Ubuntu/changelog index 1d20e719e..c92f9b330 100644 --- a/build_scripts/Debian+Ubuntu/changelog +++ b/build_scripts/Debian+Ubuntu/changelog @@ -1,5 +1,106 @@ retroshare (0.6.3-1.XXXXXX~YYYYYY) YYYYYY; urgency=low + 9352a7c csoler Thu, 8 Feb 2018 10:34:46 +0100 Merge pull request #1168 from PhenomRetroShare/Add_ConnFilterInFriendSel + bf8bd4b Phenom Wed, 7 Feb 2018 19:00:08 +0100 Add Only Connected in FriendSelectionWidget. + 7bb8eb0 csoler Wed, 7 Feb 2018 16:06:54 +0100 Merge pull request #1166 from PhenomRetroShare/Add_ChatRoomRSLink + 02b61b2 csoler Wed, 7 Feb 2018 15:58:12 +0100 Merge pull request #1164 from aral/patch-1 + a496e6f Phenom Sun, 4 Feb 2018 17:51:26 +0100 Add Chat Room RSLink + ed88b34 Aral B Sun, 4 Feb 2018 10:37:45 +0100 Fix typo in Mac install guide + b662e66 Gioacc Fri, 2 Feb 2018 00:20:14 +0100 Qml app fix disappearing sidebar buttons + 6c85075 csoler Thu, 1 Feb 2018 21:35:54 +0100 lobby->room in Identities dialog + 36e20db Gioacc Thu, 1 Feb 2018 21:26:36 +0100 Fix some glitches in qml app menu + 6e093c7 Gioacc Thu, 1 Feb 2018 14:59:08 +0100 Qml token manager removed unuseful debug message + 2d00532 Gioacc Thu, 1 Feb 2018 14:42:46 +0100 Qml app fix sidebar buttons hiding behaviour + 1c698bf Gioacc Thu, 1 Feb 2018 14:31:07 +0100 Qml App add option to control DHT behaviour + 9a9fcca Gioacc Thu, 1 Feb 2018 14:22:33 +0100 Qml App token manager offer unregister token API + 1e3d2c2 csoler Tue, 30 Jan 2018 20:55:01 +0100 Merge pull request #1160 from G10h4ck/android_service_cleaning + 6f91b93 Gioacc Tue, 30 Jan 2018 20:07:51 +0100 Add some sanity check in libresapiclient + 4d60850 Gioacc Tue, 30 Jan 2018 18:25:04 +0100 More cleanup of .pro files + 0012586 Gioacc Tue, 30 Jan 2018 18:24:03 +0100 Add support for SQLCipher on Android + ebfe50f Gioacc Tue, 30 Jan 2018 18:11:31 +0100 Make .pro files compiler agnostic on Android + da2fc01 Gioacc Tue, 30 Jan 2018 18:04:24 +0100 Cleanup a bit libresapi.pro + 180a5b1 Gioacc Tue, 30 Jan 2018 18:02:55 +0100 Remove unnecessary dependencies in android-notify-service + feae9f5 Gioacc Tue, 30 Jan 2018 00:36:19 +0100 Fix some compiler warnings + fc5ed14 Gioacc Mon, 29 Jan 2018 22:44:04 +0100 Android toolchain builder libraries update + fa71ed2 Gioacc Mon, 29 Jan 2018 13:26:43 +0100 Install bdboot.txt in Android + 2d8493f Gioacc Mon, 29 Jan 2018 10:56:12 +0100 Add link on how help confused gdb to give stacktrace + efe78f5 csoler Sun, 28 Jan 2018 21:54:56 +0100 slight optimization to showEvent() and save/restore visible items in file lists + d598a01 Gioacc Sun, 28 Jan 2018 11:38:14 +0100 ApiLocalListener do some sanity check on listen + 5a63ce8 Gioacc Sun, 28 Jan 2018 11:36:51 +0100 Android service avoid shouldexit timer using all CPU available + 3e5ed6a Gioacc Fri, 26 Jan 2018 00:18:31 +0100 Update Androig gdb debug instructions + c39b3e5 Gioacc Thu, 25 Jan 2018 16:20:25 +0100 Better debug messages for Android app + ac9a843 Gioacc Thu, 25 Jan 2018 15:50:12 +0100 Gracefully handle signals in retroshare-android-service + 8c21bfc Gioacc Thu, 25 Jan 2018 15:18:12 +0100 Avoid crash on stop in ApiServerLocal + c1d8d6a Gioacc Thu, 25 Jan 2018 15:14:09 +0100 Properly run Qt event loop + d80a806 csoler Thu, 25 Jan 2018 09:37:31 +0100 Merge pull request #1159 from csoler/v0.6-FileLists + 11eef05 csoler Wed, 24 Jan 2018 22:02:49 +0100 fixed bug causing hidden/expanded items to be reset by show() event in SharedFilesDialog + 1fbd9d1 csoler Wed, 24 Jan 2018 21:26:35 +0100 fixed hiding of top level items when searching + f139685 csoler Wed, 24 Jan 2018 21:14:26 +0100 added a fair limit to search in FL in order to avoid crazy costs + d93e259 csoler Wed, 24 Jan 2018 00:26:22 +0100 fixed expanding/collapsing of items on search + 1ee353c csoler Tue, 23 Jan 2018 22:18:35 +0100 improved efficiency of search in tree visualization mode. Re-enabled the search for testing purpose. + 725abad csoler Tue, 23 Jan 2018 22:18:02 +0100 fixed a bug in filterItems that caused searched files to generate an error in the console + 9b286c4 Gioacc Tue, 23 Jan 2018 11:13:03 +0100 add to README that DHT is not working on android + 000bbbd Gioacc Tue, 23 Jan 2018 10:33:00 +0100 Add link to Android debugging documentation + 7afa19e csoler Sun, 21 Jan 2018 22:23:08 +0100 removed search from tree view in file lists, until I find some way to do it more efficiently + 7448ff4 csoler Sun, 21 Jan 2018 19:07:37 +0100 re-enabled search in file lists tree view + d21a5e5 csoler Sat, 20 Jan 2018 19:25:52 +0100 Merge pull request #1157 from PhenomRetroShare/Fix_IconCacheInFilesDefs + f74ee5c Phenom Sat, 20 Jan 2018 17:21:37 +0100 Move File Icon Cache in FilesDefs + 7652821 Phenom Sat, 20 Jan 2018 17:18:50 +0100 Revert "patch from sss to cache TransfersDialog QIcons" + 4c626e0 csoler Mon, 15 Jan 2018 23:17:09 +0100 Merge pull request #1152 from csoler/v0.6-SecurityFixes + 841fee6 csoler Mon, 15 Jan 2018 23:13:42 +0100 fixed possible crash due to accessing deleted memory in database cache access + 7cac367 csoler Sun, 14 Jan 2018 22:39:50 +0100 removed warnign about bad service string when the service string is actually empty + 388b2c2 csoler Fri, 12 Jan 2018 22:47:42 +0100 patch from sss to cache TransfersDialog QIcons + 783e918 csoler Fri, 12 Jan 2018 22:19:52 +0100 Merge pull request #1151 from csoler/v0.6-SecurityFixes + bbd8afe csoler Thu, 11 Jan 2018 18:14:20 +0100 Merge pull request #1150 from chelovechishko/unfocus_chat_text_edit + 173336e csoler Thu, 11 Jan 2018 18:12:22 +0100 Merge pull request #1148 from chelovechishko/qdarkstyle_fix + b4ff14c csoler Thu, 11 Jan 2018 18:06:48 +0100 Merge pull request #1144 from PhenomRetroShare/Fix_DisableYesButtonOnInvite + bb9dcbb Phenom Fri, 5 Jan 2018 14:45:29 +0100 Disable Yes button on chat room invitation if no Id selected. + add9c1e chelov Wed, 10 Jan 2018 20:53:43 +0900 chatwidget: do not set focus to edit when it is not necessary + 4eea5a8 csoler Wed, 10 Jan 2018 00:10:08 +0100 removed consts in GrpMetaCache pointers, and made it possible to always re=use cache entries, possibly updating them + 553ab93 csoler Tue, 9 Jan 2018 22:18:01 +0100 Merge pull request #1149 from csoler/v0.6-SecurityFixes + 506190a csoler Tue, 9 Jan 2018 22:11:11 +0100 fixed bug causing failure of flag actions on groups + ed81b4c csoler Tue, 9 Jan 2018 21:49:15 +0100 removed delete causing read of deleted memory + 51c7942 csoler Mon, 8 Jan 2018 19:08:10 +0100 Merge pull request #1146 from csoler/v0.6-SecurityFixes + e261d45 chelov Mon, 8 Jan 2018 16:16:10 +0900 gui: fix networkdialog colors once more in qdarkstyle.qss + f8f61cc chelov Mon, 8 Jan 2018 16:02:40 +0900 gui: fix colors for idtree of people in qdarkstyle.qss + 884bbb4 chelov Mon, 8 Jan 2018 15:58:04 +0900 gui: make friendlist more readable in some places in qdarkstyle.qss + 622316a chelov Mon, 8 Jan 2018 15:49:05 +0900 gui: improve readability of sharemanager for qdarkstyle.qss + 5e57cb4 csoler Sun, 7 Jan 2018 23:02:22 +0100 fixed gxs unit tests + 85b94d7 csoler Sun, 7 Jan 2018 22:36:49 +0100 fixed test compilation + 89c538d csoler Sun, 7 Jan 2018 22:36:38 +0100 fixed double deletion problem causing crashes + b42b8e3 csoler Sun, 7 Jan 2018 21:41:41 +0100 moved memory ownership of RsGxsGrpMetaData down into RsGxsDataAccess. Avoids many copy-constructors of RsTlvSecurityKey. Will probably spare a lot of CPU on windows + c3e300e Gioacc Sat, 6 Jan 2018 22:53:26 +0100 Merge pull request #1140 from Emotyco/libresapi_small_corrections + 67fc6a3 Konrad Sat, 6 Jan 2018 19:29:56 +0100 Fixed in Libresapi: Double declaration of StateToken in FileSharingHandler + 575a416 Konrad Sat, 6 Jan 2018 19:09:44 +0100 Changed in Libresapi: Used Doxigen style comments + d53993c Konrad Sat, 6 Jan 2018 18:39:47 +0100 Changed in Libresapi: Pointer to RsNotify object into reference + 57f41d9 csoler Fri, 5 Jan 2018 21:27:18 +0100 Merge pull request #1145 from PhenomRetroShare/Fix_ClearPersonDetailIfNoSelection + 4026040 Konrad Fri, 5 Jan 2018 20:44:48 +0100 Libresapi: Extended ChatInfo class to contain own identity used in chat + 4f902bb Phenom Fri, 5 Jan 2018 17:48:18 +0100 Clear Person Detail when no selection + 0078b9c csoler Wed, 3 Jan 2018 20:33:25 +0100 Merge pull request #1143 from csoler/v0.6-SecurityFixes + 98d1d08 csoler Wed, 3 Jan 2018 20:32:37 +0100 removed extra links from chat widget, since normal links can be used now to DL through encrypted tunnels + ee81eef csoler Wed, 3 Jan 2018 18:42:04 +0100 Merge pull request #1142 from csoler/v0.6-SecurityFixes + ab60f49 csoler Wed, 3 Jan 2018 18:37:43 +0100 prevent sending/receiving of IP addresses to/at hidden nodes + 0a92710 csoler Wed, 3 Jan 2018 15:03:23 +0100 Merge pull request #1141 from csoler/v0.6-SecurityFixes + 0f6006d csoler Wed, 3 Jan 2018 15:01:04 +0100 prevent subscribing to a signed chat room without a signed identity + eea0c64 csoler Wed, 3 Jan 2018 14:05:13 +0100 require signed identity to respond to a signed chat room invite. Avoids "god mode" bug. + 96e7507 Konrad Mon, 1 Jan 2018 23:38:07 +0100 Added in Libresapi: Short comment to FileSharingHandler + 9bb6adc Konrad Mon, 1 Jan 2018 23:27:46 +0100 Added in Libresapi: Missing mutex locking in TransfersHandler + 6a61e0a Konrad Mon, 1 Jan 2018 23:20:26 +0100 Changed in Libresapi: Locking mutex using macro instead of RsStackMutex object creation + 74a56f6 Konrad Fri, 29 Dec 2017 16:07:39 +0100 Changed in Libresapi: Closing distant connection (chat) requires now ChatId that is used in other functions, instead of DistantCharPeerId + 4b25684 Konrad Fri, 29 Dec 2017 16:03:45 +0100 Added in Libresapi: Functions to get and set default identity for lobby + b24d772 Konrad Wed, 27 Dec 2017 22:38:04 +0100 Added in Libresapi: Getting node name in PeersHandler + 2db25d4 Konrad Wed, 27 Dec 2017 22:37:14 +0100 Added in Libresapi: StateToken refreshing to TransferHandler + 53118e5 Konrad Wed, 27 Dec 2017 22:35:51 +0100 Added in Libresapi: Missing bracket in ChatHandler + 92df847 Konrad Wed, 27 Dec 2017 22:33:23 +0100 Added in Libresapi: StateTokens to FileSharingHandler + 55385d7 Konrad Wed, 27 Dec 2017 22:31:17 +0100 Added in Libresapi: Marking particular message as read + 49cacc4 csoler Sun, 24 Dec 2017 12:04:15 +0100 fixed compilation on ubuntu precise + 122f6af csoler Sun, 24 Dec 2017 11:06:03 +0100 fixed compilation on ubuntu precise + 72d64c7 csoler Sun, 24 Dec 2017 10:36:08 +0100 updated changelog + + -- Retroshare Dev Team Sat, 10 Feb 2018 14:00:00 +0100 + +retroshare (0.6.3-1.20171224.72d64c7d~artful) precise; urgency=low + c2d5579 csoler Thu, 21 Dec 2017 17:06:32 +0100 storage of chat room icons in local variables to avoid re-reading them from qrc everytime (patch from sss) acd8355 Gioacc Wed, 20 Dec 2017 18:24:28 +0100 Add some documentation on how to debug retroshare on android c0d8333 csoler Mon, 18 Dec 2017 16:10:52 +0100 Merge pull request #1136 from csoler/v0.6-FT diff --git a/build_scripts/Debian+Ubuntu/control.trusty_retrotor b/build_scripts/Debian+Ubuntu/control.trusty_retrotor new file mode 100644 index 000000000..5de94658f --- /dev/null +++ b/build_scripts/Debian+Ubuntu/control.trusty_retrotor @@ -0,0 +1,20 @@ +Source: retroshare +Section: devel +Priority: standard +Maintainer: Cyril Soler +Build-Depends: debhelper (>= 7), libglib2.0-dev, libupnp-dev, libssl-dev, libxss-dev, libgnome-keyring-dev, libbz2-dev, libspeex-dev, libspeexdsp-dev, libxslt1-dev, cmake, libcurl4-openssl-dev, libopencv-dev, tcl8.6, libsqlcipher-dev, libmicrohttpd-dev, libavcodec-dev, qtmultimedia5-dev, qttools5-dev, libqt5x11extras5-dev, qt5-default +Standards-Version: 3.9.6 +Homepage: http://retroshare.net + +Package: retrotor +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, gnome-keyring, tor (>= 0.2.6) +Conflicts: retroshare-nogui,retroshare06,retroshare +Description: Secure communication with friends + RetroShare is a Open Source cross-platform, private and secure decentralised + commmunication platform. It lets you to securely chat and share files with your + friends and family, using a web-of-trust to authenticate peers and OpenSSL to + encrypt all communication. RetroShare provides filesharing, chat, messages, + forums and channels. + + diff --git a/build_scripts/Debian+Ubuntu/control.xenial_retrotor b/build_scripts/Debian+Ubuntu/control.xenial_retrotor new file mode 100644 index 000000000..4db7a123c --- /dev/null +++ b/build_scripts/Debian+Ubuntu/control.xenial_retrotor @@ -0,0 +1,20 @@ +Source: retroshare +Section: devel +Priority: standard +Maintainer: Cyril Soler +Build-Depends: debhelper (>= 7), libglib2.0-dev, libupnp-dev, libssl-dev, libxss-dev, libgnome-keyring-dev, libbz2-dev, libspeex-dev, libspeexdsp-dev, libxslt1-dev, cmake, libcurl4-openssl-dev, libopencv-dev, tcl8.6, libsqlcipher-dev, libmicrohttpd-dev, libavcodec-dev, qtmultimedia5-dev, qttools5-dev, libqt5x11extras5-dev, qt5-default +Standards-Version: 3.9.6 +Homepage: http://retroshare.sourceforge.net + +Package: retrotor +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, gnome-keyring, tor (>= 0.2.6) +Conflicts: retroshare-nogui,retroshare06,retroshare +Description: Secure communication with friends + RetroShare is a Open Source cross-platform, private and secure decentralised + commmunication platform. It lets you to securely chat and share files with your + friends and family, using a web-of-trust to authenticate peers and OpenSSL to + encrypt all communication. RetroShare provides filesharing, chat, messages, + forums and channels. + + diff --git a/build_scripts/Debian+Ubuntu/debian/retrotor.install b/build_scripts/Debian+Ubuntu/debian/retrotor.install new file mode 100644 index 000000000..2a81067cd --- /dev/null +++ b/build_scripts/Debian+Ubuntu/debian/retrotor.install @@ -0,0 +1,5 @@ +debian/tmp/usr/bin/retroshare +debian/tmp/usr/share/applications/retroshare.desktop +debian/tmp/usr/share/icons/hicolor/* +debian/tmp/usr/share/pixmaps/retroshare.xpm +debian/tmp/usr/share/retroshare/* diff --git a/build_scripts/Debian+Ubuntu/makeSourcePackage.sh b/build_scripts/Debian+Ubuntu/makeSourcePackage.sh index 9e3bd4fff..8d4033e13 100755 --- a/build_scripts/Debian+Ubuntu/makeSourcePackage.sh +++ b/build_scripts/Debian+Ubuntu/makeSourcePackage.sh @@ -27,6 +27,7 @@ time=`git log --pretty=format:"%aD" | head -1 | cut -d\ -f5 | sed -e s/://g` hhsh=`git log --pretty=format:"%H" | head -1 | cut -c1-8` rev=${date}.${hhsh} +useretrotor="false" while [ ${#} -gt 0 ]; do case ${1} in @@ -34,6 +35,9 @@ while [ ${#} -gt 0 ]; do rev=${1} shift ;; + "-retrotor") shift + useretrotor="true" + ;; "-distribution") shift dist=${1} shift @@ -54,6 +58,15 @@ while [ ${#} -gt 0 ]; do esac done +if test "${useretrotor}" = "true"; then + if ! test "${dist}" = "trusty"; then + echo ERROR: retro-tor can only be packaged for trusty for now. + exit 1; + fi + gitpath="https://github.com/csoler/RetroShare.git" + branch="v0.6-TorOnly" +fi + if test "${dist}" = "" ; then dist="precise trusty xenial zesty artful" fi @@ -71,6 +84,10 @@ echo " "Hash : ${hhsh} echo " "Using branch : ${branch} echo " "Using revision : ${rev} +if test ${useretrotor} = "true"; then + echo " "Specific flags : retrotor +fi + echo Done. version="${version}"."${rev}" echo Got version number ${version}. @@ -83,7 +100,7 @@ echo Extracting base archive... mkdir -p ${workdir}/src echo Checking out latest snapshot... cd ${workdir}/src -git clone --depth 1 https://github.com/RetroShare/RetroShare.git --single-branch --branch $branch . +git clone --depth 1 ${gitpath} --single-branch --branch $branch . # if ! test "$hhsh" = "" ; then # echo Checking out revision $hhsh @@ -121,13 +138,16 @@ for i in ${dist}; do echo copying changelog for ${i} sed -e s/XXXXXX/"${rev}"/g -e s/YYYYYY/"${i}"/g ../changelog > debian/changelog - if test -f ../control."${i}" ; then + if test ${useretrotor} = "true"; then + cp ../rules.retrotor debian/rules + cp ../control.trusty_retrotor debian/control + elif test -f ../control."${i}" ; then echo \/\!\\ Using specific control file for distribution "${i}" - cp ../control."${i}" debian/control + cp ../control."${i}" debian/control else echo Using standard control file control."${i}" for distribution "${i}" - cp ../debian/control debian/control - fi + cp ../debian/control debian/control + fi debuild -S -k${gpgkey} done diff --git a/build_scripts/Debian+Ubuntu/rules.retrotor b/build_scripts/Debian+Ubuntu/rules.retrotor new file mode 100755 index 000000000..06655ef88 --- /dev/null +++ b/build_scripts/Debian+Ubuntu/rules.retrotor @@ -0,0 +1,61 @@ +#!/usr/bin/make -f + +configure: configure-stamp +configure-stamp: + dh_testdir + cd src && qmake "CONFIG-=debug" "CONFIG+=release retrotor" "CONFIG+=rs_autologin" PREFIX=/usr LIB_DIR=/usr/lib RetroShare.pro + touch $@ + + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + # Add here commands to compile the package. + # cd libssh-0.6.4 && mkdir -p build && cd build && cmake -DWITH_STATIC_LIB=ON .. && make + # cd sqlcipher && ./configure --disable-shared --enable-static --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="-lcrypto" && make + # mkdir lib + # cp -r libssh-0.6.4 lib/ + #cp -r sqlcipher lib/ + #cd src/rsctrl/src && make + cd src && $(MAKE) + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f configure-stamp build-stamp + # Add here commands to clean up after the build process. + [ ! -f src/Makefile ] || (cd src && $(MAKE) distclean) + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + #dh_installdirs + cd src && $(MAKE) INSTALL_ROOT=$(CURDIR)/debian/tmp install + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_install --list-missing + #dh_installdocs + #dh_installexamples + #dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/libretroshare/src/crypto/chacha20.cpp b/libretroshare/src/crypto/chacha20.cpp index 1240d0cb8..3f12b2086 100644 --- a/libretroshare/src/crypto/chacha20.cpp +++ b/libretroshare/src/crypto/chacha20.cpp @@ -38,7 +38,7 @@ #include "crypto/chacha20.h" #include "util/rsprint.h" #include "util/rsrandom.h" -#include "util/rsscopetimer.h" +#include "util/rstime.h" #define rotl(x,n) { x = (x << n) | (x >> (-n & 31)) ;} @@ -1385,27 +1385,27 @@ bool perform_tests() uint8_t received_tag[16] ; { - RsScopeTimer s("AEAD1") ; + rstime::RsScopeTimer s("AEAD1") ; chacha20_encrypt_rs(key, 1, nonce, ten_megabyte_data,SIZE) ; std::cerr << " Chacha20 encryption speed : " << SIZE / (1024.0*1024.0) / s.duration() << " MB/s" << std::endl; } { - RsScopeTimer s("AEAD2") ; + rstime::RsScopeTimer s("AEAD2") ; AEAD_chacha20_poly1305_rs(key,nonce,ten_megabyte_data,SIZE,aad,12,received_tag,true) ; std::cerr << " AEAD/poly1305 own encryption speed : " << SIZE / (1024.0*1024.0) / s.duration() << " MB/s" << std::endl; } #if OPENSSL_VERSION_NUMBER >= 0x010100000L && !defined(LIBRESSL_VERSION_NUMBER) { - RsScopeTimer s("AEAD3") ; + rstime::RsScopeTimer s("AEAD3") ; AEAD_chacha20_poly1305_openssl(key,nonce,ten_megabyte_data,SIZE,aad,12,received_tag,true) ; std::cerr << " AEAD/poly1305 openssl encryption speed: " << SIZE / (1024.0*1024.0) / s.duration() << " MB/s" << std::endl; } #endif { - RsScopeTimer s("AEAD4") ; + rstime::RsScopeTimer s("AEAD4") ; AEAD_chacha20_sha256(key,nonce,ten_megabyte_data,SIZE,aad,12,received_tag,true) ; std::cerr << " AEAD/sha256 encryption speed : " << SIZE / (1024.0*1024.0) / s.duration() << " MB/s" << std::endl; diff --git a/libretroshare/src/file_sharing/directory_updater.cc b/libretroshare/src/file_sharing/directory_updater.cc index 085f92ebb..6d95961ab 100644 --- a/libretroshare/src/file_sharing/directory_updater.cc +++ b/libretroshare/src/file_sharing/directory_updater.cc @@ -23,6 +23,7 @@ * */ #include "util/folderiterator.h" +#include "util/rstime.h" #include "rsserver/p3face.h" #include "directory_storage.h" @@ -92,7 +93,7 @@ void LocalDirectoryUpdater::data_tick() for(uint32_t i=0;i<10;++i) { - usleep(1*1000*1000); + rstime::rs_usleep(1*1000*1000); { if(mForceUpdate) diff --git a/libretroshare/src/file_sharing/hash_cache.cc b/libretroshare/src/file_sharing/hash_cache.cc index 89a4aec38..f0db680cf 100644 --- a/libretroshare/src/file_sharing/hash_cache.cc +++ b/libretroshare/src/file_sharing/hash_cache.cc @@ -24,7 +24,7 @@ */ #include "util/rsdir.h" #include "util/rsprint.h" -#include "util/rsscopetimer.h" +#include "util/rstime.h" #include "rsserver/p3face.h" #include "pqi/authssl.h" #include "hash_cache.h" @@ -125,7 +125,7 @@ void HashStorage::data_tick() std::cerr << "nothing to hash. Sleeping for " << st << " us" << std::endl; #endif - usleep(st); // when no files to hash, just wait for 2 secs. This avoids a dramatic loop. + rstime::rs_usleep(st); // when no files to hash, just wait for 2 secs. This avoids a dramatic loop. if(st > MAX_INACTIVITY_SLEEP_TIME) { @@ -163,7 +163,7 @@ void HashStorage::data_tick() if(paused) // we need to wait off mutex!! { - usleep(MAX_INACTIVITY_SLEEP_TIME) ; + rstime::rs_usleep(MAX_INACTIVITY_SLEEP_TIME) ; std::cerr << "Hashing process currently paused." << std::endl; return; } @@ -190,7 +190,7 @@ void HashStorage::data_tick() RsServer::notify()->notifyHashingInfo(NOTIFY_HASHTYPE_HASH_FILE, tmpout) ; - double seconds_origin = RsScopeTimer::currentTime() ; + double seconds_origin = rstime::RsScopeTimer::currentTime() ; if(RsDirUtil::getFileHash(job.full_path, hash,size, this)) { @@ -215,7 +215,7 @@ void HashStorage::data_tick() else std::cerr << "ERROR: cannot hash file " << job.full_path << std::endl; - mHashingTime += RsScopeTimer::currentTime() - seconds_origin ; + mHashingTime += rstime::RsScopeTimer::currentTime() - seconds_origin ; mHashedBytes += size ; if(mHashingTime > 3) diff --git a/libretroshare/src/ft/ftcontroller.cc b/libretroshare/src/ft/ftcontroller.cc index 29713cb3e..0cb236358 100644 --- a/libretroshare/src/ft/ftcontroller.cc +++ b/libretroshare/src/ft/ftcontroller.cc @@ -40,6 +40,7 @@ #endif #include "util/rsdiscspace.h" #include "util/rsmemory.h" +#include "util/rstime.h" #include "ft/ftcontroller.h" @@ -219,7 +220,7 @@ void ftController::data_tick() /* check the queues */ //Waiting 1 sec before start - usleep(1*1000*1000); // 1 sec + rstime::rs_usleep(1*1000*1000); // 1 sec #ifdef CONTROL_DEBUG //std::cerr << "ftController::run()"; diff --git a/libretroshare/src/ft/ftextralist.cc b/libretroshare/src/ft/ftextralist.cc index cd52f5ce5..b6fec4076 100644 --- a/libretroshare/src/ft/ftextralist.cc +++ b/libretroshare/src/ft/ftextralist.cc @@ -32,6 +32,7 @@ #include "ft/ftextralist.h" #include "rsitems/rsconfigitems.h" #include "util/rsdir.h" +#include "util/rstime.h" #include #include /* for (u)sleep() */ #include @@ -64,12 +65,8 @@ void ftExtraList::data_tick() /* Hash a file */ hashAFile(); -#ifdef WIN32 - Sleep(1); -#else /* microsleep */ - usleep(10); -#endif + rstime::rs_usleep(10); } else { @@ -513,16 +510,7 @@ bool ftExtraList::loadList(std::list& load) delete (*it); /* short sleep */ -#ifndef WINDOWS_SYS -/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ - usleep(1000); /* 1000 per second */ - -#else -/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ - Sleep(1); -#endif -/********************************** WINDOWS/UNIX SPECIFIC PART ******************/ - + rstime::rs_usleep(1000) ; } load.clear() ; return true; diff --git a/libretroshare/src/gxs/rsdataservice.cc b/libretroshare/src/gxs/rsdataservice.cc index c3252a27c..305f8dee1 100644 --- a/libretroshare/src/gxs/rsdataservice.cc +++ b/libretroshare/src/gxs/rsdataservice.cc @@ -35,7 +35,7 @@ #include #ifdef RS_DATA_SERVICE_DEBUG_TIME -#include +#include #endif #include "rsdataservice.h" @@ -1066,7 +1066,7 @@ bool RsDataService::validSize(RsNxsGrp* grp) const int RsDataService::retrieveNxsGrps(std::map &grp, bool withMeta, bool /* cache */) { #ifdef RS_DATA_SERVICE_DEBUG_TIME - RsScopeTimer timer(""); + rstime::RsScopeTimer timer(""); int resultCount = 0; int requestedGroups = grp.size(); #endif @@ -1166,7 +1166,7 @@ void RsDataService::locked_retrieveGroups(RetroCursor* c, std::vector int RsDataService::retrieveNxsMsgs(const GxsMsgReq &reqIds, GxsMsgResult &msg, bool /* cache */, bool withMeta) { #ifdef RS_DATA_SERVICE_DEBUG_TIME - RsScopeTimer timer(""); + rstime::RsScopeTimer timer(""); int resultCount = 0; #endif @@ -1254,7 +1254,7 @@ int RsDataService::retrieveGxsMsgMetaData(const GxsMsgReq& reqIds, GxsMsgMetaRes RsStackMutex stack(mDbMutex); #ifdef RS_DATA_SERVICE_DEBUG_TIME - RsScopeTimer timer(""); + rstime::RsScopeTimer timer(""); int resultCount = 0; #endif @@ -1341,7 +1341,7 @@ int RsDataService::retrieveGxsGrpMetaData(RsGxsGrpMetaTemporaryMap& grp) RsStackMutex stack(mDbMutex); #ifdef RS_DATA_SERVICE_DEBUG_TIME - RsScopeTimer timer(""); + rstime::RsScopeTimer timer(""); int resultCount = 0; int requestedGroups = grp.size(); #endif @@ -1549,7 +1549,7 @@ int RsDataService::retrieveGroupIds(std::vector &grpIds) RsStackMutex stack(mDbMutex); #ifdef RS_DATA_SERVICE_DEBUG_TIME - RsScopeTimer timer(""); + rstime::RsScopeTimer timer(""); int resultCount = 0; #endif @@ -1586,7 +1586,7 @@ int RsDataService::retrieveGroupIds(std::vector &grpIds) int RsDataService::retrieveMsgIds(const RsGxsGroupId& grpId, RsGxsMessageId::std_vector& msgIds) { #ifdef RS_DATA_SERVICE_DEBUG_TIME - RsScopeTimer timer(""); + rstime::RsScopeTimer timer(""); int resultCount = 0; #endif diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index dd4691306..e1d15d614 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -31,6 +31,7 @@ #include "gxssecurity.h" #include "util/contentvalue.h" #include "util/rsprint.h" +#include "util/rstime.h" #include "retroshare/rsgxsflags.h" #include "retroshare/rsgxscircles.h" #include "retroshare/rsgrouter.h" @@ -138,7 +139,7 @@ void RsGenExchange::data_tick() static const double timeDelta = 0.1; // slow tick in sec tick(); - usleep((int) (timeDelta * 1000 *1000)); // timeDelta sec + rstime::rs_usleep((int) (timeDelta * 1000 *1000)); // timeDelta sec } void RsGenExchange::tick() diff --git a/libretroshare/src/gxs/rsgxsnetservice.cc b/libretroshare/src/gxs/rsgxsnetservice.cc index ec26e0081..8f055bc1b 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.cc +++ b/libretroshare/src/gxs/rsgxsnetservice.cc @@ -210,6 +210,7 @@ #include "retroshare/rspeers.h" #include "pgp/pgpauxutils.h" #include "util/rsdir.h" +#include "util/rstime.h" #include "util/rsmemory.h" #include "util/stacktrace.h" @@ -1787,7 +1788,7 @@ void RsGxsNetService::data_tick() static const double timeDelta = 0.5; //Start waiting as nothing to do in runup - usleep((int) (timeDelta * 1000 * 1000)); // timeDelta sec + rstime::rs_usleep((int) (timeDelta * 1000 * 1000)); // timeDelta sec if(mUpdateCounter >= 120) // 60 seconds { diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index ba9973793..c2604fa4a 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -1,7 +1,7 @@ !include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri") TEMPLATE = lib -CONFIG += staticlib bitdht +CONFIG += staticlib CONFIG += create_prl CONFIG -= qt TARGET = retroshare @@ -10,7 +10,14 @@ DESTDIR = lib #CONFIG += dsdv -# the dht stunner is used to obtain RS' external ip addr. when it is natted +retrotor { + DEFINES *= RETROTOR + CONFIG -= bitdht +} else { + CONFIG += bitdht +} + +# the dht stunner is used to obtain RS external ip addr. when it is natted # this system is unreliable and rs supports a newer and better one (asking connected peers) # CONFIG += useDhtStunner @@ -90,6 +97,7 @@ HEADERS += tcponudp/udppeer.h \ tcponudp/tcpstream.h \ tcponudp/tou.h \ tcponudp/udprelay.h \ + pqi/pqissludp.h \ SOURCES += tcponudp/udppeer.cc \ tcponudp/tcppacket.cc \ @@ -97,6 +105,7 @@ SOURCES += tcponudp/udppeer.cc \ tcponudp/tou.cc \ tcponudp/bss_tou.c \ tcponudp/udprelay.cc \ + pqi/pqissludp.cc \ useDhtStunner { HEADERS += dht/stunaddrassist.h \ @@ -434,7 +443,6 @@ HEADERS += pqi/authssl.h \ pqi/pqissllistener.h \ pqi/pqisslpersongrp.h \ pqi/pqissli2pbob.h \ - pqi/pqissludp.h \ pqi/pqisslproxy.h \ pqi/pqistore.h \ pqi/pqistreamer.h \ @@ -539,7 +547,7 @@ HEADERS += util/folderiterator.h \ util/rsmemcache.h \ util/rstickevent.h \ util/rsrecogn.h \ - util/rsscopetimer.h \ + util/rstime.h \ util/stacktrace.h \ util/rsdeprecate.h \ util/cxx11retrocompat.h @@ -590,7 +598,6 @@ SOURCES += pqi/authgpg.cc \ pqi/pqissllistener.cc \ pqi/pqisslpersongrp.cc \ pqi/pqissli2pbob.cpp \ - pqi/pqissludp.cc \ pqi/pqisslproxy.cc \ pqi/pqistore.cc \ pqi/pqistreamer.cc \ @@ -688,7 +695,7 @@ SOURCES += util/folderiterator.cc \ util/rsrandom.cc \ util/rstickevent.cc \ util/rsrecogn.cc \ - util/rsscopetimer.cc + util/rstime.cc upnp_miniupnpc { diff --git a/libretroshare/src/pqi/authgpg.cc b/libretroshare/src/pqi/authgpg.cc index 8553fc127..ccfb8a260 100644 --- a/libretroshare/src/pqi/authgpg.cc +++ b/libretroshare/src/pqi/authgpg.cc @@ -35,6 +35,7 @@ #include "pgp/pgphandler.h" #include +#include #include #include /* for (u)sleep() */ #include @@ -187,7 +188,7 @@ int AuthGPG::GPGInit(const RsPgpId &ownId) void AuthGPG::data_tick() { - usleep(100 * 1000); //100 msec + rstime::rs_usleep(100 * 1000); //100 msec /// every 100 milliseconds processServices(); diff --git a/libretroshare/src/pqi/p3netmgr.cc b/libretroshare/src/pqi/p3netmgr.cc index 03c809789..2d593e42f 100644 --- a/libretroshare/src/pqi/p3netmgr.cc +++ b/libretroshare/src/pqi/p3netmgr.cc @@ -718,7 +718,7 @@ void p3NetMgrIMPL::netExtCheck() #endif if(sockaddr_storage_isValidNet(tmpip)) { - if(rsBanList->isAddressAccepted(tmpip,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) + if( (rsBanList==NULL) || rsBanList->isAddressAccepted(tmpip,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) { // must be stable??? isStable = true; @@ -761,7 +761,7 @@ void p3NetMgrIMPL::netExtCheck() /* input network bits */ if (mDhtStunner->getExternalAddr(tmpaddr, isstable)) { - if(rsBanList->isAddressAccepted(tmpaddr,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) + if((rsBanList == NULL) || rsBanList->isAddressAccepted(tmpaddr,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) { // must be stable??? isStable = (isstable == 1); diff --git a/libretroshare/src/pqi/p3peermgr.cc b/libretroshare/src/pqi/p3peermgr.cc index 602a89690..9862838aa 100644 --- a/libretroshare/src/pqi/p3peermgr.cc +++ b/libretroshare/src/pqi/p3peermgr.cc @@ -1237,7 +1237,7 @@ bool p3PeerMgrIMPL::UpdateOwnAddress(const struct sockaddr_storage &localAddr, std::cerr << ")" << std::endl; #endif - if(!rsBanList->isAddressAccepted(localAddr, RSBANLIST_CHECKING_FLAGS_BLACKLIST)) + if((rsBanList != NULL) && !rsBanList->isAddressAccepted(localAddr, RSBANLIST_CHECKING_FLAGS_BLACKLIST)) { std::cerr << "(SS) Trying to set own IP to a banned IP " << sockaddr_storage_iptostring(localAddr) << ". This probably means that a friend in under traffic re-routing attack." << std::endl; return false ; @@ -1376,7 +1376,7 @@ bool p3PeerMgrIMPL::setExtAddress(const RsPeerId &id, const struct sockaddr_s bool changed = false; uint32_t check_res = 0 ; - if(!rsBanList->isAddressAccepted(addr,RSBANLIST_CHECKING_FLAGS_BLACKLIST,&check_res)) + if(rsBanList!=NULL && !rsBanList->isAddressAccepted(addr,RSBANLIST_CHECKING_FLAGS_BLACKLIST,&check_res)) { std::cerr << "(SS) trying to set external contact address for peer " << id << " to a banned address " << sockaddr_storage_iptostring(addr )<< std::endl; return false ; @@ -1550,7 +1550,7 @@ bool p3PeerMgrIMPL::addCandidateForOwnExternalAddress(const RsPeerId &from, cons // Notify for every friend that has reported a wrong external address, except if that address is in the IP whitelist. - if((!rsBanList->isAddressAccepted(addr_filtered,RSBANLIST_CHECKING_FLAGS_WHITELIST)) && (!sockaddr_storage_sameip(own_addr,addr_filtered))) + if((rsBanList!=NULL && !rsBanList->isAddressAccepted(addr_filtered,RSBANLIST_CHECKING_FLAGS_WHITELIST)) && (!sockaddr_storage_sameip(own_addr,addr_filtered))) { std::cerr << " Peer " << from << " reports a connection address (" << sockaddr_storage_iptostring(addr_filtered) <<") that is not your current external address (" << sockaddr_storage_iptostring(own_addr) << "). This is weird." << std::endl; @@ -2805,7 +2805,7 @@ bool p3PeerMgrIMPL::removeBannedIps() if(cleanIpList(it->second.ipAddrs.mExt.mAddrs,it->first,mLinkMgr)) changed = true ; if(cleanIpList(it->second.ipAddrs.mLocal.mAddrs,it->first,mLinkMgr)) changed = true ; - if(!rsBanList->isAddressAccepted(it->second.serveraddr,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) + if(rsBanList!=NULL && !rsBanList->isAddressAccepted(it->second.serveraddr,RSBANLIST_CHECKING_FLAGS_BLACKLIST)) { sockaddr_storage_clear(it->second.serveraddr) ; std::cerr << "(SS) Peer " << it->first << " has a banned server address. Wiping it out." << std::endl; diff --git a/libretroshare/src/pqi/pqinetstatebox.cc b/libretroshare/src/pqi/pqinetstatebox.cc index b08994855..128c4d9e5 100644 --- a/libretroshare/src/pqi/pqinetstatebox.cc +++ b/libretroshare/src/pqi/pqinetstatebox.cc @@ -4,7 +4,9 @@ #include "pqi/pqinetstatebox.h" #include "time.h" +#ifdef RS_USE_BITDHT #include "bitdht/bdiface.h" +#endif // External Interface. diff --git a/libretroshare/src/pqi/pqissl.cc b/libretroshare/src/pqi/pqissl.cc index caa2ac6bb..898080262 100644 --- a/libretroshare/src/pqi/pqissl.cc +++ b/libretroshare/src/pqi/pqissl.cc @@ -1321,14 +1321,14 @@ int pqissl::Authorise_SSL_Connection() if (rsPeers->servicePermissionFlags(PeerId()) & RS_NODE_PERM_REQUIRE_WL) checking_flags |= RSBANLIST_CHECKING_FLAGS_WHITELIST; - if(!rsBanList->isAddressAccepted(remote_addr,checking_flags,&check_result)) + if(rsBanList!=NULL && !rsBanList->isAddressAccepted(remote_addr,checking_flags,&check_result)) { - std::cerr << "(SS) refusing connection attempt from IP address " << sockaddr_storage_iptostring(remote_addr) << ". Reason: " << + std::cerr << "(SS) refusing connection attempt from IP address " << sockaddr_storage_iptostring(remote_addr) << ". Reason: " << ((check_result == RSBANLIST_CHECK_RESULT_NOT_WHITELISTED)?"not whitelisted (peer requires whitelist)":"blacklisted") << std::endl; RsServer::notify()->AddFeedItem(RS_FEED_ITEM_SEC_IP_BLACKLISTED, PeerId().toStdString(), sockaddr_storage_iptostring(remote_addr), "", "", check_result); - reset_locked(); - return 0 ; + reset_locked(); + return 0 ; } // check it's the right one. if (certCorrect) @@ -1371,12 +1371,12 @@ int pqissl::accept_locked(SSL *ssl, int fd, const struct sockaddr_storage &forei if (rsPeers->servicePermissionFlags(PeerId()) & RS_NODE_PERM_REQUIRE_WL) checking_flags |= RSBANLIST_CHECKING_FLAGS_WHITELIST; - if(!rsBanList->isAddressAccepted(foreign_addr,checking_flags,&check_result)) + if(rsBanList!=NULL && !rsBanList->isAddressAccepted(foreign_addr,checking_flags,&check_result)) { std::cerr << "(SS) refusing incoming SSL connection from blacklisted foreign address " << sockaddr_storage_iptostring(foreign_addr) << ". Reason: " << check_result << "." << std::endl; RsServer::notify()->AddFeedItem(RS_FEED_ITEM_SEC_IP_BLACKLISTED, PeerId().toStdString(), sockaddr_storage_iptostring(foreign_addr), "", "", check_result); - reset_locked(); + reset_locked(); return -1; } if (waiting != WAITING_NOT) diff --git a/libretroshare/src/pqi/pqisslpersongrp.cc b/libretroshare/src/pqi/pqisslpersongrp.cc index 432ba1bbf..20fe314d4 100644 --- a/libretroshare/src/pqi/pqisslpersongrp.cc +++ b/libretroshare/src/pqi/pqisslpersongrp.cc @@ -36,6 +36,9 @@ static struct RsLog::logInfo pqipersongrpzoneInfo = {RsLog::Default, "pqipersong /**** * #define PQI_DISABLE_UDP 1 ***/ +#ifdef RETROTOR +#define PQI_DISABLE_UDP 1 +#endif /********************************** SSL Specific features ***************************/ diff --git a/libretroshare/src/pqi/pqissludp.cc b/libretroshare/src/pqi/pqissludp.cc index 29bd228c9..f14fb82e5 100644 --- a/libretroshare/src/pqi/pqissludp.cc +++ b/libretroshare/src/pqi/pqissludp.cc @@ -35,6 +35,7 @@ #include "util/rsdebug.h" #include "util/rsnet.h" +#include "util/rstime.h" #include "util/rsstring.h" #include "pqi/p3linkmgr.h" @@ -571,7 +572,7 @@ bool pqissludp::moretoread(uint32_t usec) { return true; } - usleep(usec); + rstime::rs_usleep(usec); } /* check for more to read first ... if nothing... check error @@ -655,7 +656,7 @@ bool pqissludp::cansend(uint32_t usec) return true; } - usleep(usec); + rstime::rs_usleep(usec); } rslog(RSL_DEBUG_ALL, pqissludpzone, diff --git a/libretroshare/src/pqi/pqithreadstreamer.cc b/libretroshare/src/pqi/pqithreadstreamer.cc index f99be143b..54ab925e1 100644 --- a/libretroshare/src/pqi/pqithreadstreamer.cc +++ b/libretroshare/src/pqi/pqithreadstreamer.cc @@ -24,6 +24,7 @@ */ +#include "util/rstime.h" #include "pqi/pqithreadstreamer.h" #include @@ -69,7 +70,7 @@ void pqithreadstreamer::data_tick() if (!isactive) { - usleep(DEFAULT_STREAMER_IDLE_SLEEP); + rstime::rs_usleep(DEFAULT_STREAMER_IDLE_SLEEP); return ; } @@ -92,7 +93,7 @@ void pqithreadstreamer::data_tick() if (sleep_period) { - usleep(sleep_period); + rstime::rs_usleep(sleep_period); } } diff --git a/libretroshare/src/rsserver/p3face-server.cc b/libretroshare/src/rsserver/p3face-server.cc index a7aad022c..d7578b8fd 100644 --- a/libretroshare/src/rsserver/p3face-server.cc +++ b/libretroshare/src/rsserver/p3face-server.cc @@ -25,6 +25,7 @@ */ +#include "util/rstime.h" #include "rsserver/p3face.h" #include "retroshare/rsplugin.h" @@ -134,11 +135,7 @@ RsServer::~RsServer() /* Thread Fn: Run the Core */ void RsServer::data_tick() { -#ifndef WINDOWS_SYS - usleep((int) (mTimeDelta * 1000000)); -#else - Sleep((int) (mTimeDelta * 1000)); -#endif + rstime::rs_usleep(mTimeDelta * 1000000); double ts = getCurrentTS(); double delta = ts - mLastts; diff --git a/libretroshare/src/rsserver/rsaccounts.cc b/libretroshare/src/rsserver/rsaccounts.cc index 45983b5c3..6136a2a87 100644 --- a/libretroshare/src/rsserver/rsaccounts.cc +++ b/libretroshare/src/rsserver/rsaccounts.cc @@ -71,8 +71,11 @@ RsAccountsDetail::RsAccountsDetail() : mAccountsLocked(false), mPreferredId("") bool RsAccountsDetail::loadAccounts() { int failing_accounts ; - - getAvailableAccounts(mAccounts,failing_accounts,mUnsupportedKeys); +#ifdef RETROTOR + getAvailableAccounts(mAccounts,failing_accounts,mUnsupportedKeys,true); +#else + getAvailableAccounts(mAccounts,failing_accounts,mUnsupportedKeys,false); +#endif loadPreferredAccount(); checkPreferredId(); @@ -512,7 +515,7 @@ bool RsAccountsDetail::getAccountOptions(bool &ishidden, bool &isFirstTimeRun) /* directories with valid certificates in the expected location */ -bool RsAccountsDetail::getAvailableAccounts(std::map &accounts,int& failing_accounts,std::map >& unsupported_keys) +bool RsAccountsDetail::getAvailableAccounts(std::map &accounts,int& failing_accounts,std::map >& unsupported_keys,bool hidden_only) { failing_accounts = 0 ; /* get the directories */ @@ -615,6 +618,9 @@ bool RsAccountsDetail::getAvailableAccounts(std::map & continue; } + if(hidden_only && !hidden_location) + continue ; + if(valid_prefix && isHexaString(lochex) && (lochex).length() == 32) { std::string accountdir = mBaseDirectory + "/" + *it; diff --git a/libretroshare/src/rsserver/rsaccounts.h b/libretroshare/src/rsserver/rsaccounts.h index 5832dfa00..8ff253587 100644 --- a/libretroshare/src/rsserver/rsaccounts.h +++ b/libretroshare/src/rsserver/rsaccounts.h @@ -142,9 +142,9 @@ class RsAccountsDetail static bool defaultBaseDirectory(); - bool getAvailableAccounts(std::map &accounts, + bool getAvailableAccounts(std::map &accounts, int& failing_accounts, - std::map >& unsupported_keys); + std::map >& unsupported_keys, bool hidden_only=false); bool setupAccount(const std::string& accountdir); diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 76aa66c9a..6f0b668b5 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -70,6 +70,11 @@ #include #endif +// This needs to be defined here, because when USE_BITDHT is unset, the variable, that is defined in libbitdht (not compiled!) will be missing. +#ifndef RS_USE_BITDHT +RsDht *rsDht = NULL ; +#endif + // for blocking signals #include @@ -830,9 +835,10 @@ RsGRouter *rsGRouter = NULL ; #include "pqi/p3linkmgr.h" #include "pqi/p3netmgr.h" - +#ifndef RETROTOR #include "tcponudp/tou.h" #include "tcponudp/rsudpstack.h" +#endif #ifdef RS_USE_BITDHT @@ -1186,9 +1192,9 @@ int RsServer::StartupRetroShare() #ifdef RS_USE_DHT_STUNNER mNetMgr->setAddrAssist(new stunAddrAssist(mDhtStunner), new stunAddrAssist(mProxyStunner)); #endif // RS_USE_DHT_STUNNER -#else //RS_USE_BITDHT - /* install NULL Pointer for rsDht Interface */ - rsDht = NULL; +// #else //RS_USE_BITDHT +// /* install NULL Pointer for rsDht Interface */ +// rsDht = NULL; #endif //RS_USE_BITDHT @@ -1505,7 +1511,11 @@ int RsServer::StartupRetroShare() interfaces.mMsgs = rsMsgs; interfaces.mTurtle = rsTurtle; interfaces.mDisc = rsDisc; +#ifdef RS_USE_BITDHT interfaces.mDht = rsDht; +#else + interfaces.mDht = NULL; +#endif interfaces.mNotify = mNotify; interfaces.mServiceControl = serviceCtrl; interfaces.mPluginHandler = mPluginsManager; @@ -1539,10 +1549,17 @@ int RsServer::StartupRetroShare() #endif // new services to test. +#ifndef RETROTOR p3BanList *mBanList = new p3BanList(serviceCtrl, mNetMgr); rsBanList = mBanList ; pqih -> addService(mBanList, true); +#else + rsBanList = NULL ; +#endif + +#ifdef RS_USE_BITDHT mBitDht->setupPeerSharer(mBanList); +#endif p3BandwidthControl *mBwCtrl = new p3BandwidthControl(pqih); pqih -> addService(mBwCtrl, true); @@ -1608,7 +1625,9 @@ int RsServer::StartupRetroShare() mConfigMgr->addConfiguration("p3History.cfg", mHistoryMgr); mConfigMgr->addConfiguration("p3Status.cfg", mStatusSrv); mConfigMgr->addConfiguration("turtle.cfg", tr); +#ifndef RETROTOR mConfigMgr->addConfiguration("banlist.cfg", mBanList); +#endif mConfigMgr->addConfiguration("servicecontrol.cfg", serviceCtrl); mConfigMgr->addConfiguration("reputations.cfg", mReputations); #ifdef ENABLE_GROUTER diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index 32be374f4..917b8a8a7 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -8,6 +8,7 @@ #include "util/radix32.h" #include "util/radix64.h" #include "util/rsdebug.h" +#include "util/rstime.h" #include "util/rsprint.h" #include "util/rsrandom.h" @@ -39,7 +40,7 @@ static struct RsLog::logInfo i2pBobLogInfo = {RsLog::Default, "p3I2pBob"}; static const time_t selfCheckPeroid = 30; void doSleep(useconds_t timeToSleepMS) { - usleep((useconds_t) (timeToSleepMS * 1000)); + rstime::rs_usleep((useconds_t) (timeToSleepMS * 1000)); } p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr) diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index 0f5eb22d5..d4d3f87ce 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -1,6 +1,7 @@ #include "rsautoproxymonitor.h" #include /* for usleep() */ +#include "util/rstime.h" rsAutoProxyMonitor *rsAutoProxyMonitor::mInstance = NULL; @@ -93,7 +94,7 @@ void rsAutoProxyMonitor::stopAllRSShutdown() // wait for shutdown of all services uint32_t t = 0, timeout = 15; do { - usleep(1000 * 1000); + rstime::rs_usleep(1000 * 1000); RS_STACK_MUTEX(mLock); std::cout << "(II) waiting for auto proxy service(s) to shut down " << t << "/" << timeout << " (remaining: " << mProxies.size() << ")" << std::endl; if (mProxies.empty()) diff --git a/libretroshare/src/services/p3idservice.cc b/libretroshare/src/services/p3idservice.cc index 2b37b181e..71b0e2274 100644 --- a/libretroshare/src/services/p3idservice.cc +++ b/libretroshare/src/services/p3idservice.cc @@ -34,6 +34,7 @@ #include "util/rsstring.h" #include "util/radix64.h" #include "util/rsdir.h" +#include "util/rstime.h" #include "crypto/hashstream.h" #include "gxs/gxssecurity.h" #include "retroshare/rspeers.h" @@ -1071,7 +1072,7 @@ bool p3IdService::signData(const uint8_t *data,uint32_t data_size,const RsGxsId& #ifdef DEBUG_IDS std::cerr << " Cannot get key. Waiting for caching. try " << i << "/6" << std::endl; #endif - usleep(500 * 1000) ; // sleep for 500 msec. + rstime::rs_usleep(500 * 1000) ; // sleep for 500 msec. } else break ; @@ -1110,7 +1111,7 @@ bool p3IdService::validateData(const uint8_t *data,uint32_t data_size,const RsTl #ifdef DEBUG_IDS std::cerr << " Cannot get key. Waiting for caching. try " << i << "/6" << std::endl; #endif - if(force_load) usleep(500 * 1000) ; // sleep for 500 msec. + if(force_load) rstime::rs_usleep(500 * 1000) ; // sleep for 500 msec. } else break ; @@ -1151,7 +1152,7 @@ bool p3IdService::encryptData( const uint8_t *decrypted_data, if(getKey(encryption_key_id,encryption_key)) break ; else - usleep(500*1000) ; // sleep half a sec. + rstime::rs_usleep(500*1000) ; // sleep half a sec. if(encryption_key.keyId.isNull()) { @@ -1222,7 +1223,7 @@ bool p3IdService::encryptData( const uint8_t* decrypted_data, } if(keyNotYetFoundIds.empty()) break; - else usleep(500*1000); + else rstime::rs_usleep(500*1000); } if(!keyNotYetFoundIds.empty()) @@ -1279,7 +1280,7 @@ bool p3IdService::decryptData( const uint8_t *encrypted_data, int maxRounds = force_load ? 6 : 1; for(int i=0; i= 30) return false ; @@ -917,7 +918,7 @@ RsStackFileLock::RsStackFileLock(const std::string& file_path) while(RsDirUtil::createLockFile(file_path,_file_handle)) { std::cerr << "Cannot acquire file lock " << file_path << ", waiting 1 sec." << std::endl; - usleep(1 * 1000 * 1000) ; // 1 sec + rstime::rs_usleep(1 * 1000 * 1000) ; // 1 sec } #ifdef RSDIR_DEBUG std::cerr << "Acquired file handle " << _file_handle << ", lock file:" << file_path << std::endl; @@ -1321,7 +1322,7 @@ bool RsDirUtil::renameWideFile(const std::wstring& from, const std::wstring& to) #endif /* set errno? */ return false ; - usleep(100 * 1000); //100 msec + rstime::rs_usleep(100 * 1000); //100 msec if (loops >= 30) return false ; diff --git a/libretroshare/src/util/rsthreads.cc b/libretroshare/src/util/rsthreads.cc index 8899ee01c..39eca594a 100644 --- a/libretroshare/src/util/rsthreads.cc +++ b/libretroshare/src/util/rsthreads.cc @@ -30,6 +30,8 @@ #include #include +#include "util/rstime.h" + #ifdef __APPLE__ int __attribute__((weak)) pthread_setname_np(const char *__buf) ; int RS_pthread_setname_np(pthread_t /*__target_thread*/, const char *__buf) { @@ -289,7 +291,7 @@ void RsQueueThread::data_tick() THREAD_DEBUG << "RsQueueThread::data_tick() no work: sleeping for: " << mLastSleep << " ms" << std::endl; #endif } - usleep(mLastSleep * 1000); // mLastSleep msec + rstime::rs_usleep(mLastSleep * 1000); // mLastSleep msec } void RsMutex::unlock() diff --git a/libretroshare/src/util/rsscopetimer.cc b/libretroshare/src/util/rstime.cc similarity index 81% rename from libretroshare/src/util/rsscopetimer.cc rename to libretroshare/src/util/rstime.cc index b3ce8fbc3..067466d65 100644 --- a/libretroshare/src/util/rsscopetimer.cc +++ b/libretroshare/src/util/rstime.cc @@ -24,8 +24,29 @@ */ #include +#include #include -#include "rsscopetimer.h" +#include +#include +#include "rstime.h" + +namespace rstime { + +int rs_usleep(uint32_t micro_seconds) +{ + while(micro_seconds >= 1000000) + { + // usleep cannot be called with 1000000 or more. + + usleep(500000) ; + usleep(500000) ; + + micro_seconds -= 1000000 ; + } + usleep(micro_seconds) ; + + return 0 ; +} RsScopeTimer::RsScopeTimer(const std::string& name) { @@ -57,3 +78,5 @@ double RsScopeTimer::duration() { return currentTime() - _seconds; } + +} diff --git a/libretroshare/src/util/rsscopetimer.h b/libretroshare/src/util/rstime.h similarity index 61% rename from libretroshare/src/util/rsscopetimer.h rename to libretroshare/src/util/rstime.h index 875a8bb5e..4359a28ff 100644 --- a/libretroshare/src/util/rsscopetimer.h +++ b/libretroshare/src/util/rstime.h @@ -23,29 +23,40 @@ * */ -// Use this class to measure and display time duration of a given environment: -// -// { -// RsScopeTimer timer("callToMeasure()") ; -// -// callToMeasure() ; -// } -// - #include -class RsScopeTimer -{ -public: - RsScopeTimer(const std::string& name); - ~RsScopeTimer(); +namespace rstime { - void start(); - double duration(); + /*! + * \brief This is a cross-system definition of usleep, which accepts any 32 bits number of micro-seconds. + */ - static double currentTime(); + int rs_usleep(uint32_t micro_seconds); -private: - std::string _name ; - double _seconds ; -}; + /* Use this class to measure and display time duration of a given environment: + + { + RsScopeTimer timer("callToMeasure()") ; + + callToMeasure() ; + } + + */ + + class RsScopeTimer + { + public: + RsScopeTimer(const std::string& name); + ~RsScopeTimer(); + + void start(); + double duration(); + + static double currentTime(); + + private: + std::string _name ; + double _seconds ; + }; + +} diff --git a/plugins/FeedReader/services/p3FeedReader.cc b/plugins/FeedReader/services/p3FeedReader.cc index efa3b6688..256a1e285 100644 --- a/plugins/FeedReader/services/p3FeedReader.cc +++ b/plugins/FeedReader/services/p3FeedReader.cc @@ -26,6 +26,7 @@ #include "retroshare/rsiface.h" #include "retroshare/rsgxsforums.h" #include "util/rsstring.h" +#include "util/rstime.h" #include "gxs/rsgenexchange.h" #include @@ -2240,7 +2241,7 @@ bool p3FeedReader::waitForToken(uint32_t token) break; } - usleep(500 * 1000); // sleep for 500 msec + rstime::rs_usleep(500 * 1000); // sleep for 500 msec } return false; diff --git a/plugins/FeedReader/services/p3FeedReaderThread.cc b/plugins/FeedReader/services/p3FeedReaderThread.cc index 9ec1106ef..b8db8db46 100644 --- a/plugins/FeedReader/services/p3FeedReaderThread.cc +++ b/plugins/FeedReader/services/p3FeedReaderThread.cc @@ -22,6 +22,7 @@ #include "p3FeedReaderThread.h" #include "rsFeedReaderItems.h" #include "util/rsstring.h" +#include "util/rstime.h" #include "util/CURLWrapper.h" #include "util/XMLWrapper.h" #include "util/HTMLWrapper.h" @@ -51,11 +52,8 @@ p3FeedReaderThread::~p3FeedReaderThread() void p3FeedReaderThread::data_tick() { -#ifdef WIN32 - Sleep(1000); -#else - usleep(1000000); -#endif + rstime::rs_usleep(1000000); + /* every second */ switch (mType) { diff --git a/retroshare-gui/src/TorControl/AddOnionCommand.cpp b/retroshare-gui/src/TorControl/AddOnionCommand.cpp new file mode 100644 index 000000000..f454b690b --- /dev/null +++ b/retroshare-gui/src/TorControl/AddOnionCommand.cpp @@ -0,0 +1,105 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2016, John Brooks + * + * 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 += " RSA1024:"; + out += m_service->privateKey().encodedPrivateKey(CryptoKey::DER).toBase64(); + } else { + out += " NEW:RSA1024"; + } + + 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=RSA1024:"); + if (data.startsWith(keyPrefix)) { + QByteArray keyData(QByteArray::fromBase64(data.mid(keyPrefix.size()))); + CryptoKey key; + if (!key.loadFromData(keyData, CryptoKey::PrivateKey, CryptoKey::DER)) { + m_errorMessage = QStringLiteral("Key decoding failed"); + return; + } + + m_service->setPrivateKey(key); + } +} + +void AddOnionCommand::onFinished(int statusCode) +{ + TorControlCommand::onFinished(statusCode); + if (isSuccessful()) + emit succeeded(); + else + emit failed(statusCode); +} + + diff --git a/retroshare-gui/src/TorControl/AddOnionCommand.h b/retroshare-gui/src/TorControl/AddOnionCommand.h new file mode 100644 index 000000000..7c0afaf5e --- /dev/null +++ b/retroshare-gui/src/TorControl/AddOnionCommand.h @@ -0,0 +1,77 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2016, John Brooks + * + * 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 +#include +#include + +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 + diff --git a/retroshare-gui/src/TorControl/AuthenticateCommand.cpp b/retroshare-gui/src/TorControl/AuthenticateCommand.cpp new file mode 100644 index 000000000..497c28f89 --- /dev/null +++ b/retroshare-gui/src/TorControl/AuthenticateCommand.cpp @@ -0,0 +1,64 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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); +} diff --git a/retroshare-gui/src/TorControl/AuthenticateCommand.h b/retroshare-gui/src/TorControl/AuthenticateCommand.h new file mode 100644 index 000000000..79c901d98 --- /dev/null +++ b/retroshare-gui/src/TorControl/AuthenticateCommand.h @@ -0,0 +1,63 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 diff --git a/retroshare-gui/src/TorControl/CryptoKey.cpp b/retroshare-gui/src/TorControl/CryptoKey.cpp new file mode 100644 index 000000000..8aa035754 --- /dev/null +++ b/retroshare-gui/src/TorControl/CryptoKey.cpp @@ -0,0 +1,477 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 "CryptoKey.h" +#include "SecureRNG.h" +#include "Useful.h" +#include +#include +#include +#include +#include + +#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 + +void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen); +bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen); + +CryptoKey::CryptoKey() +{ +} + +CryptoKey::~CryptoKey() +{ + clear(); +} + +CryptoKey::Data::~Data() +{ + if (key) + { + RSA_free(key); + key = 0; + } +} + +void CryptoKey::clear() +{ + d = 0; +} + +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(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; +} + +bool CryptoKey::loadFromFile(const QString &path, KeyType type, KeyFormat format) +{ + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) + { + qWarning() << "Failed to open" << (type == PrivateKey ? "private" : "public") << "key from" + << path << "-" << file.errorString(); + return false; + } + + QByteArray data = file.readAll(); + file.close(); + + return loadFromData(data, type, format); +} + +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(buf.constData()), buf.size(), + reinterpret_cast(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(data.constData()), data.size(), + reinterpret_cast(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(digest.constData()), digest.size(), + reinterpret_cast(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(data.constData()), data.size(), + reinterpret_cast(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(digest.constData()), digest.size(), + reinterpret_cast(signature.data()), signature.size(), d->key); + if (r != 1) + return false; + 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(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(md), 20).toHex().toUpper(); +} + +/* 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; +} diff --git a/retroshare-gui/src/TorControl/CryptoKey.h b/retroshare-gui/src/TorControl/CryptoKey.h new file mode 100644 index 000000000..70ada1977 --- /dev/null +++ b/retroshare-gui/src/TorControl/CryptoKey.h @@ -0,0 +1,95 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include + +class CryptoKey +{ +public: + enum KeyType { + PrivateKey, + PublicKey + }; + + enum KeyFormat { + PEM, + DER + }; + + CryptoKey(); + CryptoKey(const CryptoKey &other) : d(other.d) { } + ~CryptoKey(); + + bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM); + bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM); + void clear(); + + bool isLoaded() const { return d.data() && d->key != 0; } + 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; + +private: + struct Data : public QSharedData + { + typedef struct rsa_st RSA; + RSA *key; + + Data(RSA *k = 0) : key(k) { } + ~Data(); + }; + + QExplicitlySharedDataPointer d; +}; + +QByteArray torControlHashedPassword(const QByteArray &password); + +#endif // CRYPTOKEY_H diff --git a/retroshare-gui/src/TorControl/GetConfCommand.cpp b/retroshare-gui/src/TorControl/GetConfCommand.cpp new file mode 100644 index 000000000..933def562 --- /dev/null +++ b/retroshare-gui/src/TorControl/GetConfCommand.cpp @@ -0,0 +1,124 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +using namespace Tor; + +GetConfCommand::GetConfCommand(Type t) + : type(t) +{ +} + +QByteArray GetConfCommand::build(const QByteArray &key) +{ + return build(QList() << key); +} + +QByteArray GetConfCommand::build(const QList &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)); +} + diff --git a/retroshare-gui/src/TorControl/GetConfCommand.h b/retroshare-gui/src/TorControl/GetConfCommand.h new file mode 100644 index 000000000..0de97d1b7 --- /dev/null +++ b/retroshare-gui/src/TorControl/GetConfCommand.h @@ -0,0 +1,77 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +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 &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 diff --git a/retroshare-gui/src/TorControl/HiddenService.cpp b/retroshare-gui/src/TorControl/HiddenService.cpp new file mode 100644 index 000000000..1dd1071c8 --- /dev/null +++ b/retroshare-gui/src/TorControl/HiddenService.cpp @@ -0,0 +1,137 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include +#include + +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::setPrivateKey(const CryptoKey &key) +{ + if (m_privateKey.isLoaded()) { + BUG() << "Cannot change the private key on an existing HiddenService"; + return; + } + + if (!key.isPrivate()) { + BUG() << "Cannot create a hidden service with a public key"; + return; + } + + m_privateKey = key; + m_hostname = m_privateKey.torServiceID() + QStringLiteral(".onion"); + emit privateKeyChanged(); +} + +void HiddenService::loadPrivateKey() +{ + if (m_privateKey.isLoaded() || m_dataPath.isEmpty()) + return; + + bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key"), CryptoKey::PrivateKey); + if (!ok) { + qWarning() << "Failed to load hidden service key"; + return; + } + + m_hostname = m_privateKey.torServiceID(); + 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); +} + diff --git a/retroshare-gui/src/TorControl/HiddenService.h b/retroshare-gui/src/TorControl/HiddenService.h new file mode 100644 index 000000000..71c20a972 --- /dev/null +++ b/retroshare-gui/src/TorControl/HiddenService.h @@ -0,0 +1,104 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include +#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 &dataPath() const { return m_dataPath; } + + CryptoKey privateKey() { return m_privateKey; } + void setPrivateKey(const CryptoKey &privateKey); + + const QList &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(); + +private slots: + void servicePublished(); + +private: + QString m_dataPath; + QList m_targets; + QString m_hostname; + Status m_status; + CryptoKey m_privateKey; + + void loadPrivateKey(); + void setStatus(Status newStatus); +}; + +} + +#endif // HIDDENSERVICE_H diff --git a/retroshare-gui/src/TorControl/PendingOperation.cpp b/retroshare-gui/src/TorControl/PendingOperation.cpp new file mode 100644 index 000000000..4a11f3e75 --- /dev/null +++ b/retroshare-gui/src/TorControl/PendingOperation.cpp @@ -0,0 +1,84 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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(); + } +} + diff --git a/retroshare-gui/src/TorControl/PendingOperation.h b/retroshare-gui/src/TorControl/PendingOperation.h new file mode 100644 index 000000000..8f776a8d7 --- /dev/null +++ b/retroshare-gui/src/TorControl/PendingOperation.h @@ -0,0 +1,87 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +/* 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 diff --git a/retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp b/retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp new file mode 100644 index 000000000..a365a5c4d --- /dev/null +++ b/retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp @@ -0,0 +1,85 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +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 tokens = splitQuotedStrings(data.mid(5), ' '); + + foreach (QByteArray token, tokens) + { + if (token.startsWith("METHODS=")) + { + QList textMethods = unquotedString(token.mid(8)).split(','); + for (QList::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)))); + } +} diff --git a/retroshare-gui/src/TorControl/ProtocolInfoCommand.h b/retroshare-gui/src/TorControl/ProtocolInfoCommand.h new file mode 100644 index 000000000..7789cfefd --- /dev/null +++ b/retroshare-gui/src/TorControl/ProtocolInfoCommand.h @@ -0,0 +1,78 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +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 diff --git a/retroshare-gui/src/TorControl/SecureRNG.cpp b/retroshare-gui/src/TorControl/SecureRNG.cpp new file mode 100644 index 000000000..3a6eacd83 --- /dev/null +++ b/retroshare-gui/src/TorControl/SecureRNG.cpp @@ -0,0 +1,146 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#endif + +#if QT_VERSION >= 0x040700 +#include +#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(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(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(&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(value), sizeof(value)); + if (value < cutoff) + return value % max; + } +} diff --git a/retroshare-gui/src/TorControl/SecureRNG.h b/retroshare-gui/src/TorControl/SecureRNG.h new file mode 100644 index 000000000..f3b2a4b64 --- /dev/null +++ b/retroshare-gui/src/TorControl/SecureRNG.h @@ -0,0 +1,51 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +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 diff --git a/retroshare-gui/src/TorControl/SetConfCommand.cpp b/retroshare-gui/src/TorControl/SetConfCommand.cpp new file mode 100644 index 000000000..f13a9fdef --- /dev/null +++ b/retroshare-gui/src/TorControl/SetConfCommand.cpp @@ -0,0 +1,106 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 >() << qMakePair(key, value)); +} + +QByteArray SetConfCommand::build(const QVariantMap &data) +{ + QList > out; + + for (QVariantMap::ConstIterator it = data.begin(); it != data.end(); it++) { + QByteArray key = it.key().toLatin1(); + + if (static_cast(it.value().type()) == QMetaType::QVariantList) { + QVariantList values = it.value().value(); + 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 > &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); +} + diff --git a/retroshare-gui/src/TorControl/SetConfCommand.h b/retroshare-gui/src/TorControl/SetConfCommand.h new file mode 100644 index 000000000..5bdcb9329 --- /dev/null +++ b/retroshare-gui/src/TorControl/SetConfCommand.h @@ -0,0 +1,78 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include + +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 > &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 diff --git a/retroshare-gui/src/TorControl/Settings.cpp b/retroshare-gui/src/TorControl/Settings.cpp new file mode 100644 index 000000000..b20d330b7 --- /dev/null +++ b/retroshare-gui/src/TorControl/Settings.cpp @@ -0,0 +1,553 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 > 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 > 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 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" diff --git a/retroshare-gui/src/TorControl/Settings.h b/retroshare-gui/src/TorControl/Settings.h new file mode 100644 index 000000000..79ad032d1 --- /dev/null +++ b/retroshare-gui/src/TorControl/Settings.h @@ -0,0 +1,257 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include +#include +#include +#include + +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 T read(const QString &key) const; + Q_INVOKABLE void write(const QString &key, const QJsonValue &value); + template 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 T read(const char *key) const + { + return read(QString::fromLatin1(key)); + } + void write(const char *key, const QJsonValue &value) + { + write(QString::fromLatin1(key), value); + } + template void write(const char *key, const T &value) + { + write(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 inline void SettingsObject::write(const QString &key, const T &value) +{ + write(key, QJsonValue(value)); +} + +template<> inline QString SettingsObject::read(const QString &key) const +{ + return read(key).toString(); +} + +template<> inline QJsonArray SettingsObject::read(const QString &key) const +{ + return read(key).toArray(); +} + +template<> inline QJsonObject SettingsObject::read(const QString &key) const +{ + return read(key).toObject(); +} + +template<> inline double SettingsObject::read(const QString &key) const +{ + return read(key).toDouble(); +} + +template<> inline int SettingsObject::read(const QString &key) const +{ + return read(key).toInt(); +} + +template<> inline bool SettingsObject::read(const QString &key) const +{ + return read(key).toBool(); +} + +template<> inline QDateTime SettingsObject::read(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(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(const QString &key) const +{ + return Base64Encode(QByteArray::fromBase64(read(key).toString().toLatin1())); +} + +template<> inline void SettingsObject::write(const QString &key, const Base64Encode &value) +{ + write(key, QJsonValue(QString::fromLatin1(value.encoded()))); +} + +#endif + diff --git a/retroshare-gui/src/TorControl/StrUtil.cpp b/retroshare-gui/src/TorControl/StrUtil.cpp new file mode 100644 index 000000000..81de151b0 --- /dev/null +++ b/retroshare-gui/src/TorControl/StrUtil.cpp @@ -0,0 +1,118 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 splitQuotedStrings(const QByteArray &input, char separator) +{ + QList 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; +} diff --git a/retroshare-gui/src/TorControl/StrUtil.h b/retroshare-gui/src/TorControl/StrUtil.h new file mode 100644 index 000000000..c86d2c6ec --- /dev/null +++ b/retroshare-gui/src/TorControl/StrUtil.h @@ -0,0 +1,46 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +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 splitQuotedStrings(const QByteArray &input, char separator); + +#endif // STRINGUTIL_H diff --git a/retroshare-gui/src/TorControl/TorControl.cpp b/retroshare-gui/src/TorControl/TorControl.cpp new file mode 100644 index 000000000..29979e2b3 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControl.cpp @@ -0,0 +1,788 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +#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 +#include +#include +//#include +#include +#include +#include +#include + +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 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 &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 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(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(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 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(sender()); + if (!command || !q->isConnected()) + return; + + QList listenAddresses = splitQuotedStrings(command->get(QByteArray("net/listeners/socks")).toString().toLatin1(), ' '); + for (QList::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::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 > 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 &targets = service->targets(); + for (QList::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 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 &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() << "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" + diff --git a/retroshare-gui/src/TorControl/TorControl.h b/retroshare-gui/src/TorControl/TorControl.h new file mode 100644 index 000000000..4a47384fa --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControl.h @@ -0,0 +1,144 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +#include +#include +#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, + Connecting, + Authenticating, + Connected + }; + + enum TorStatus + { + TorUnknown, + TorOffline, + TorReady + }; + + + 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 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 diff --git a/retroshare-gui/src/TorControl/TorControlCommand.cpp b/retroshare-gui/src/TorControl/TorControlCommand.cpp new file mode 100644 index 000000000..48d4aab8b --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControlCommand.cpp @@ -0,0 +1,63 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +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"; +} + diff --git a/retroshare-gui/src/TorControl/TorControlCommand.h b/retroshare-gui/src/TorControl/TorControlCommand.h new file mode 100644 index 000000000..894381054 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControlCommand.h @@ -0,0 +1,70 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +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 diff --git a/retroshare-gui/src/TorControl/TorControlSocket.cpp b/retroshare-gui/src/TorControl/TorControlSocket.cpp new file mode 100644 index 000000000..c6b1eb707 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControlSocket.cpp @@ -0,0 +1,178 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +#include "TorControlSocket.h" +#include "TorControlCommand.h" +#include + +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(); + } + } + } +} diff --git a/retroshare-gui/src/TorControl/TorControlSocket.h b/retroshare-gui/src/TorControl/TorControlSocket.h new file mode 100644 index 000000000..2db911503 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControlSocket.h @@ -0,0 +1,77 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +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 commandQueue; + QHash eventCommands; + QString m_errorMessage; + TorControlCommand *currentCommand; + bool inDataReply; + + void setError(const QString &message); +}; + +} + +#endif // TORCONTROLSOCKET_H diff --git a/retroshare-gui/src/TorControl/TorControlWindow.cpp b/retroshare-gui/src/TorControl/TorControlWindow.cpp new file mode 100644 index 000000000..6da6b8d7a --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControlWindow.cpp @@ -0,0 +1,230 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include "util/rstime.h" + +#include "TorControlWindow.h" +#include "TorManager.h" +#include "TorControl.h" +#include "HiddenService.h" + +TorControlDialog::TorControlDialog(Tor::TorManager *tm,QWidget *parent) + : mTorManager(tm) +{ + 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())); + + //QTimer::singleShot(2000,this,SLOT(checkForHiddenService())) ; + + mIncomingServer = new QTcpServer(this) ; + mHiddenService = NULL ; + mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_UNKNOWN; + //mBootstrapPhaseFinished = false ; + + connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection())); + + QTimer *timer = new QTimer ; + + QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ; + timer->start(500) ; + + // Hide some debug output for the released version + + setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint ); + + adjustSize(); +} + +void TorControlDialog::onIncomingConnection() +{ + std::cerr << "Incoming connection !!" << std::endl; +} + +void TorControlDialog::statusChanged() +{ + 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() ; + + 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 ; + } + + 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 ; + } + + torStatus_LB->setText(torstatus_str) ; + + if(torstatus == Tor::TorControl::TorUnknown) + torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ; + else + torStatus_LB->setToolTip("") ; + + QVariantMap qvm = mTorManager->control()->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; + + if(!qvm["progress"].toString().isNull()) + torBootstrapStatus_LB->setText(qvm["progress"].toString() + " % (" + qvm["summary"].toString() + ")") ; + else + torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ; + + QString service_id ; + QString onion_address ; + QHostAddress 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)) + { + hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + service_target_address.toString() + ":" + QString::number(target_port)); + onionAddress_LB->setText(onion_address); + } + else + { + hiddenServiceAddress_LB->setText(QString("[Not ready]")) ; + onionAddress_LB->setText(QString("[Not ready]")) ; + } + + showLog(); + adjustSize(); + + QCoreApplication::processEvents(); // forces update +} + +void TorControlDialog::showLog() +{ + static std::set already_seen ; + + QString s ; + QStringList logmsgs = mTorManager->logMessages() ; + bool can_print = false ; + + for(QStringList::const_iterator 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(can_print) + std::cerr << "[TOR DEBUG LOG] " << (*it).toStdString() << std::endl; + } + +// torLog_TB->setText(s) ;: + + std::cerr << "Connexion Proxy: " << mTorManager->control()->socksAddress().toString().toStdString() << ":" << mTorManager->control()->socksPort() << std::endl; +} + +TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg) +{ + if(!mErrorMsg.isNull()) + { + error_msg = mErrorMsg ; + return TorControlDialog::TOR_STATUS_FAIL ; + } + + switch(mTorManager->control()->torStatus()) + { + case Tor::TorControl::TorReady: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ; + default: + return TOR_STATUS_UNKNOWN ; + } +} + +TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService() +{ + std::cerr << "Checking for hidden services:" ; + + switch(mHiddenServiceStatus) + { + default: + case HIDDEN_SERVICE_STATUS_UNKNOWN: { + + std::cerr << " trying to setup. " ; + + 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 ; + } + + case HIDDEN_SERVICE_STATUS_REQUESTED: { + QList hidden_services = mTorManager->control()->hiddenServices(); + + if(hidden_services.empty()) + { + std::cerr << "Not ready yet." << std::endl; + return mHiddenServiceStatus ; + } + else + { + if(mHiddenService == NULL) + mHiddenService = *(hidden_services.begin()) ; + + 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 ; + } +} + diff --git a/retroshare-gui/src/TorControl/TorControlWindow.h b/retroshare-gui/src/TorControl/TorControlWindow.h new file mode 100644 index 000000000..c4e8ce522 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControlWindow.h @@ -0,0 +1,48 @@ +#include "ui_TorControlWindow.h" + +class QTcpServer ; + +namespace Tor { + class HiddenService ; + class TorManager ; +} + +class TorControlDialog: public QWidget, public Ui::TorControlDialog +{ + Q_OBJECT + +public: + TorControlDialog(Tor::TorManager *tm,QWidget *parent =NULL); + + enum TorStatus { + TOR_STATUS_UNKNOWN = 0x00, + TOR_STATUS_OK = 0x01, + TOR_STATUS_FAIL = 0x02 + }; + + enum HiddenServiceStatus { + HIDDEN_SERVICE_STATUS_UNKNOWN = 0x00, // no information known. + HIDDEN_SERVICE_STATUS_FAIL = 0x01, // some error occurred + HIDDEN_SERVICE_STATUS_REQUESTED = 0x02, // one service at least has been requested. Still being tested. + HIDDEN_SERVICE_STATUS_OK = 0x03 // one service responds and has been tested + }; + + // Should be called multiple times in a loop until it returns something else than *_UNKNOWN + + TorStatus checkForTor(QString& error_msg) ; + HiddenServiceStatus checkForHiddenService() ; + +protected slots: + void showLog(); + void statusChanged(); + void onIncomingConnection(); + +private: + QString mErrorMsg ; + HiddenServiceStatus mHiddenServiceStatus ; + + Tor::TorManager *mTorManager ; + Tor::HiddenService *mHiddenService ; + + QTcpServer *mIncomingServer ; +}; diff --git a/retroshare-gui/src/TorControl/TorControlWindow.ui b/retroshare-gui/src/TorControl/TorControlWindow.ui new file mode 100644 index 000000000..602146641 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorControlWindow.ui @@ -0,0 +1,134 @@ + + + TorControlDialog + + + + 0 + 0 + 600 + 228 + + + + + 0 + 0 + + + + Dialog + + + + + + + 75 + true + + + + Setting up Tor... + + + Qt::AlignCenter + + + + + + + + + + + + :/icons/tor-logo.png + + + false + + + + + + + + + Tor status: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Unknown + + + + + + + Not started + + + + + + + Hidden service address: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Tor bootstrap status: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Not set + + + + + + + Onion address: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Not set + + + + + + + + + + + + + + diff --git a/retroshare-gui/src/TorControl/TorManager.cpp b/retroshare-gui/src/TorControl/TorManager.cpp new file mode 100644 index 000000000..85ff58f7a --- /dev/null +++ b/retroshare-gui/src/TorControl/TorManager.cpp @@ -0,0 +1,501 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +#include "TorManager.h" +#include "TorProcess.h" +#include "TorControl.h" +#include "CryptoKey.h" +#include "HiddenService.h" +#include "GetConfCommand.h" +#include "Settings.h" +#include +#include +#include +#include +#include + +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; +} + +QString TorManager::dataDirectory() const +{ + return d->dataDir; +} + +void TorManager::setDataDirectory(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"), CryptoKey::PrivateKey)) + { + qWarning() << "Cannot load legacy format key from" << legacyDir << "for conversion"; + return false; + } + + keyData = QString::fromLatin1(key.encodedPrivateKey(CryptoKey::DER).toBase64()); + d->hiddenService = new Tor::HiddenService(key, legacyDir, this); + + std::cerr << "Got key from legacy dir: " << std::endl; + std::cerr << keyData.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())) ; + } + + 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().encodedPrivateKey(CryptoKey::DER).toBase64()); + + QFile outfile(d->hiddenServiceDir + QLatin1String("/private_key")) ; + outfile.open( QIODevice::WriteOnly | QIODevice::Text ); + QTextStream s(&outfile); + + s << "-----BEGIN RSA PRIVATE KEY-----" << endl; + + for(uint32_t i=0;ihiddenServiceDir + QLatin1String("/hostname")) ; + outfile2.open( QIODevice::WriteOnly | QIODevice::Text ); + QTextStream t(&outfile2); + + t << d->hiddenService->hostname() << endl; + + outfile2.close(); +} + +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 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)->privateKey().torServiceID(); + + 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(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()) + 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 + + // 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" + diff --git a/retroshare-gui/src/TorControl/TorManager.h b/retroshare-gui/src/TorControl/TorManager.h new file mode 100644 index 000000000..d81d60d57 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorManager.h @@ -0,0 +1,109 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include + +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 dataDirectory READ dataDirectory WRITE setDataDirectory) + +public: + + explicit TorManager(QObject *parent = 0); + static TorManager *instance(); + + TorProcess *process(); + TorControl *control(); + + QString dataDirectory() const; + void setDataDirectory(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 hiddenServiceStatusChanged(int old_status,int new_status); + +signals: + void configurationNeededChanged(); + void errorChanged(); + +private: + TorManagerPrivate *d; +}; + +} + +#endif +# diff --git a/retroshare-gui/src/TorControl/TorProcess.cpp b/retroshare-gui/src/TorControl/TorProcess.cpp new file mode 100644 index 000000000..ca1aecfe5 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorProcess.cpp @@ -0,0 +1,311 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include +#include + +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); + } +} + diff --git a/retroshare-gui/src/TorControl/TorProcess.h b/retroshare-gui/src/TorControl/TorProcess.h new file mode 100644 index 000000000..ad489dc43 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorProcess.h @@ -0,0 +1,100 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +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 + diff --git a/retroshare-gui/src/TorControl/TorProcess_p.h b/retroshare-gui/src/TorControl/TorProcess_p.h new file mode 100644 index 000000000..9aa2585e3 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorProcess_p.h @@ -0,0 +1,79 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +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 + diff --git a/retroshare-gui/src/TorControl/TorSocket.cpp b/retroshare-gui/src/TorControl/TorSocket.cpp new file mode 100644 index 000000000..fb9c07b26 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorSocket.cpp @@ -0,0 +1,155 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 + +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; + } +} diff --git a/retroshare-gui/src/TorControl/TorSocket.h b/retroshare-gui/src/TorControl/TorSocket.h new file mode 100644 index 000000000..0c68f7854 --- /dev/null +++ b/retroshare-gui/src/TorControl/TorSocket.h @@ -0,0 +1,97 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +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 diff --git a/retroshare-gui/src/TorControl/Useful.h b/retroshare-gui/src/TorControl/Useful.h new file mode 100644 index 000000000..6bb6e44c1 --- /dev/null +++ b/retroshare-gui/src/TorControl/Useful.h @@ -0,0 +1,71 @@ +/* Ricochet - https://ricochet.im/ + * Copyright (C) 2014, John Brooks + * + * 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 +#include + +/* 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 + diff --git a/retroshare-gui/src/gui/AboutWidget.cpp b/retroshare-gui/src/gui/AboutWidget.cpp index d19e4a7bc..1ade24dc7 100644 --- a/retroshare-gui/src/gui/AboutWidget.cpp +++ b/retroshare-gui/src/gui/AboutWidget.cpp @@ -225,9 +225,10 @@ void AWidget::initImages() //p.drawPixmap(QRect(10, 10, width()-10, 60), image); /* Draw RetroShare version */ - p.drawText(QPointF(10, 50), QString("%1 : %2").arg(tr("Retroshare version"), Rshare::retroshareVersion(true))); #ifdef RS_ONLYHIDDENNODE - p.drawText(QPointF(10, 70), QString("Only Hidden Node")); + p.drawText(QPointF(10, 50), QString("%1 : %2 (With embedded Tor)").arg(tr("Retroshare version"), Rshare::retroshareVersion(true))); +#else + p.drawText(QPointF(10, 50), QString("%1 : %2").arg(tr("Retroshare version"), Rshare::retroshareVersion(true))); #endif /* Draw Qt's version number */ diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index 75bb7d7a3..133bfbc4a 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -195,6 +195,10 @@ GenCertDialog::GenCertDialog(bool onlyGenerateIdentity, QWidget *parent) ui.nodeType_CB->setCurrentIndex(1); ui.nodeType_CB->setEnabled(false); #endif +#ifdef RETROTOR + ui.adv_checkbox->setChecked(false); + ui.adv_checkbox->setVisible(true); +#endif initKeyList(); setupState(); @@ -255,10 +259,16 @@ void GenCertDialog::setupState() { bool adv_state = ui.adv_checkbox->isChecked(); +#ifdef RETROTOR + bool retrotor = true ; +#else + bool retrotor = false ; +#endif + if(!adv_state) { ui.reuse_existing_node_CB->setChecked(false) ; - ui.nodeType_CB->setCurrentIndex(0) ; + ui.nodeType_CB->setCurrentIndex(retrotor?1:0) ; ui.keylength_comboBox->setCurrentIndex(0) ; } bool hidden_state = ui.nodeType_CB->currentIndex()==1; @@ -271,8 +281,8 @@ void GenCertDialog::setupState() 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")); - ui.label_nodeType->setVisible(adv_state) ; - ui.nodeType_CB->setVisible(adv_state) ; + ui.label_nodeType->setVisible(adv_state && !retrotor) ; + ui.nodeType_CB->setVisible(adv_state && !retrotor) ; ui.reuse_existing_node_CB->setEnabled(adv_state) ; ui.importIdentity_PB->setVisible(adv_state && !generate_new) ; ui.exportIdentity_PB->setVisible(adv_state && !generate_new) ; @@ -308,13 +318,13 @@ void GenCertDialog::setupState() ui.entropy_bar->setVisible(true); ui.genButton->setVisible(true); - ui.hiddenaddr_input->setVisible(hidden_state); - ui.hiddenaddr_label->setVisible(hidden_state); + ui.hiddenaddr_input->setVisible(hidden_state && !retrotor); + ui.hiddenaddr_label->setVisible(hidden_state && !retrotor); - ui.hiddenport_label->setVisible(hidden_state); - ui.hiddenport_spinBox->setVisible(hidden_state); + ui.hiddenport_label->setVisible(hidden_state && !retrotor); + ui.hiddenport_spinBox->setVisible(hidden_state && !retrotor); - ui.cbUseBob->setVisible(hidden_state); + ui.cbUseBob->setVisible(hidden_state && !retrotor); if(!mAllFieldsOk) { diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index 35399146a..6219575b5 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -83,6 +83,7 @@ #include "statusbar/SoundStatus.h" #include "statusbar/ToasterDisable.h" #include "statusbar/SysTrayStatus.h" +#include "statusbar/torstatus.h" #include #include @@ -244,18 +245,37 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags flags) peerstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowPeer", QVariant(true)).toBool()); statusBar()->addWidget(peerstatus); - natstatus = new NATStatus(); - if(hiddenmode) natstatus->setVisible(false); - else natstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowNAT", QVariant(true)).toBool()); - statusBar()->addWidget(natstatus); - natstatus->getNATStatus(); - - dhtstatus = new DHTStatus(); - if(hiddenmode) dhtstatus->setVisible(false); - else dhtstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowDHT", QVariant(true)).toBool()); - statusBar()->addWidget(dhtstatus); - dhtstatus->getDHTStatus(); - + if(hiddenmode) + { +#ifdef RETROTOR + torstatus = new TorStatus(); + torstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowTor", QVariant(true)).toBool()); + statusBar()->addWidget(torstatus); + torstatus->getTorStatus(); +#else + torstatus = NULL ; +#endif + + natstatus = NULL ; + dhtstatus = NULL ; + } + else + { + torstatus = NULL ; + + natstatus = new NATStatus(); + if(hiddenmode) natstatus->setVisible(false); + else natstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowNAT", QVariant(true)).toBool()); + statusBar()->addWidget(natstatus); + natstatus->getNATStatus(); + + dhtstatus = new DHTStatus(); + if(hiddenmode) dhtstatus->setVisible(false); + else dhtstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowDHT", QVariant(true)).toBool()); + statusBar()->addWidget(dhtstatus); + dhtstatus->getDHTStatus(); + } + hashingstatus = new HashingStatus(); hashingstatus->setVisible(Settings->valueFromGroup("StatusBar", "ShowHashing", QVariant(true)).toBool()); statusBar()->addPermanentWidget(hashingstatus, 1); @@ -710,6 +730,9 @@ void MainWindow::updateStatus() if (ratesstatus) ratesstatus->getRatesStatus(downKb, upKb); + if(torstatus) + torstatus->getTorStatus(); + if(!hiddenmode) { if (natstatus) @@ -1594,6 +1617,10 @@ void MainWindow::setCompactStatusMode(bool compact) dhtstatus->setCompactMode(compact); dhtstatus->getDHTStatus(); } + + if(torstatus) + torstatus->setCompactMode(compact) ; + hashingstatus->setCompactMode(compact); ratesstatus->setCompactMode(compact); //opModeStatus: TODO Show only ??? diff --git a/retroshare-gui/src/gui/MainWindow.h b/retroshare-gui/src/gui/MainWindow.h index a52bf0073..0705cac6e 100644 --- a/retroshare-gui/src/gui/MainWindow.h +++ b/retroshare-gui/src/gui/MainWindow.h @@ -46,6 +46,7 @@ class OpModeStatus; class SoundStatus; class ToasterDisable; class SysTrayStatus; +class TorStatus ; //class ForumsDialog; class GxsChannelDialog ; class GxsForumsDialog ; @@ -293,6 +294,7 @@ private: SoundStatus *soundStatus; ToasterDisable *toasterDisable; SysTrayStatus *sysTrayStatus; + TorStatus *torstatus; /* Status */ std::set m_apStatusObjects; // added objects for status diff --git a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h index e67eeeee8..e442bded9 100644 --- a/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h +++ b/retroshare-gui/src/gui/NetworkDialog/pgpid_item_proxy.h @@ -1,6 +1,8 @@ #ifndef PGPID_ITEM_PROXY_H #define PGPID_ITEM_PROXY_H +#include "util/cxx11retrocompat.h" + #include class pgpid_item_proxy : diff --git a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp index 93028d20c..918939917 100644 --- a/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp +++ b/retroshare-gui/src/gui/chat/ChatLobbyDialog.cpp @@ -46,7 +46,8 @@ #include "util/HandleRichText.h" #include "util/QtVersion.h" -#include +#include "retroshare/rsnotify.h" +#include "util/rstime.h" #include #include @@ -339,7 +340,7 @@ void ChatLobbyDialog::init(const ChatId &/*id*/, const QString &/*title*/) if(rsIdentity->getIdDetails(gxs_id,details)) break ; else - usleep(1000*300) ; + rstime::rs_usleep(1000*300) ; ui.chatWidget->setName(QString::fromUtf8(details.mNickname.c_str())); //ui.chatWidget->addToolsAction(ui.actionChangeNickname); diff --git a/retroshare-gui/src/gui/common/FriendList.cpp b/retroshare-gui/src/gui/common/FriendList.cpp index c251a9575..726bf61ea 100644 --- a/retroshare-gui/src/gui/common/FriendList.cpp +++ b/retroshare-gui/src/gui/common/FriendList.cpp @@ -342,7 +342,8 @@ void FriendList::peerTreeWidgetCustomPopupMenu() // QMenu *lobbyMenu = NULL; - switch (type) { + switch (type) + { case TYPE_GROUP: { bool standard = c->data(COLUMN_DATA, ROLE_STANDARD).toBool(); @@ -448,11 +449,11 @@ void FriendList::peerTreeWidgetCustomPopupMenu() contextMenu->addAction(QIcon(IMAGE_EXPORTFRIEND), tr("Recommend this node to..."), this, SLOT(recommendfriend())); } - contextMenu->addAction(QIcon(IMAGE_CONNECT), tr("Attempt to connect"), this, SLOT(connectfriend())); + if(!rsPeers->isHiddenNode(rsPeers->getOwnId()) || rsPeers->isHiddenNode( RsPeerId(getRsId(c)) )) + contextMenu->addAction(QIcon(IMAGE_CONNECT), tr("Attempt to connect"), this, SLOT(connectfriend())); contextMenu->addAction(QIcon(IMAGE_COPYLINK), tr("Copy certificate link"), this, SLOT(copyFullCertificate())); - //this is a SSL key contextMenu->addAction(QIcon(IMAGE_REMOVEFRIEND), tr("Remove Friend Node"), this, SLOT(removefriend())); diff --git a/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp b/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp index dce1b39f4..5bd3c1789 100644 --- a/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp +++ b/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp @@ -45,11 +45,12 @@ #define IDDIALOG_IDLIST 1 -#define ROLE_ID Qt::UserRole -#define ROLE_SORT_GROUP Qt::UserRole + 1 -#define ROLE_SORT_STANDARD_GROUP Qt::UserRole + 2 -#define ROLE_SORT_NAME Qt::UserRole + 3 -#define ROLE_SORT_STATE Qt::UserRole + 4 +#define ROLE_ID Qt::UserRole +#define ROLE_SORT_GROUP Qt::UserRole + 1 +#define ROLE_SORT_STANDARD_GROUP Qt::UserRole + 2 +#define ROLE_SORT_NAME Qt::UserRole + 3 +#define ROLE_SORT_STATE Qt::UserRole + 4 +#define ROLE_FILTER_REASON Qt::UserRole + 5 #define IMAGE_GROUP16 ":/images/user/group16.png" #define IMAGE_FRIENDINFO ":/images/peerdetails_16x16.png" @@ -109,10 +110,15 @@ FriendSelectionWidget::FriendSelectionWidget(QWidget *parent) mActionSortByState->setCheckable(true); connect(mActionSortByState, SIGNAL(toggled(bool)), this, SLOT(sortByState(bool))); ui->friendList->addContextMenuAction(mActionSortByState); + mActionFilterConnected = new QAction(tr("Filter only connected"), this); + mActionFilterConnected->setCheckable(true); + connect(mActionFilterConnected, SIGNAL(toggled(bool)), this, SLOT(filterConnected(bool))); + ui->friendList->addContextMenuAction(mActionFilterConnected); /* initialize list */ ui->friendList->setColumnCount(COLUMN_COUNT); ui->friendList->headerItem()->setText(COLUMN_NAME, tr("Name")); + ui->friendList->setFilterReasonRole(ROLE_FILTER_REASON); /* sort list by name ascending */ ui->friendList->sortItems(COLUMN_NAME, Qt::AscendingOrder); @@ -216,8 +222,8 @@ static void initSslItem(QTreeWidgetItem *item, const RsPeerDetails &detail, cons item->setData(COLUMN_NAME, ROLE_SORT_GROUP, 1); item->setData(COLUMN_NAME, ROLE_SORT_STANDARD_GROUP, 0); - item->setData(COLUMN_NAME, ROLE_SORT_STATE, (state != (int) RS_STATUS_OFFLINE) ? 0 : 1); item->setData(COLUMN_NAME, ROLE_SORT_NAME, name); + item->setData(COLUMN_NAME, ROLE_SORT_STATE, state); } void FriendSelectionWidget::fillList() @@ -359,8 +365,8 @@ void FriendSelectionWidget::secured_fillList() groupItem->setData(COLUMN_NAME, ROLE_SORT_GROUP, 0); groupItem->setData(COLUMN_NAME, ROLE_SORT_STANDARD_GROUP, (groupInfo->flag & RS_GROUP_FLAG_STANDARD) ? 0 : 1); - groupItem->setData(COLUMN_NAME, ROLE_SORT_STATE, 0); groupItem->setData(COLUMN_NAME, ROLE_SORT_NAME, groupName); + groupItem->setData(COLUMN_NAME, ROLE_SORT_STATE, 0); if (mListModus == MODUS_CHECK) { groupItem->setCheckState(0, Qt::Unchecked); @@ -426,8 +432,8 @@ void FriendSelectionWidget::secured_fillList() gpgItem->setData(COLUMN_NAME, ROLE_SORT_GROUP, 1); gpgItem->setData(COLUMN_NAME, ROLE_SORT_STANDARD_GROUP, 0); - gpgItem->setData(COLUMN_NAME, ROLE_SORT_STATE, (state != (int) RS_STATUS_OFFLINE) ? 0 : 1); gpgItem->setData(COLUMN_NAME, ROLE_SORT_NAME, name); + gpgItem->setData(COLUMN_NAME, ROLE_SORT_STATE, state); if (mListModus == MODUS_CHECK) { gpgItem->setCheckState(0, Qt::Unchecked); @@ -561,9 +567,9 @@ void FriendSelectionWidget::secured_fillList() gxsItem->setData(COLUMN_NAME, ROLE_SORT_GROUP, 1); gxsItem->setData(COLUMN_NAME, ROLE_SORT_STANDARD_GROUP, 0); + gxsItem->setData(COLUMN_NAME, ROLE_SORT_NAME, name); //TODO: online state for gxs items gxsItem->setData(COLUMN_NAME, ROLE_SORT_STATE, 1); - gxsItem->setData(COLUMN_NAME, ROLE_SORT_NAME, name); if (mListModus == MODUS_CHECK) gxsItem->setCheckState(0, Qt::Unchecked); @@ -615,9 +621,9 @@ void FriendSelectionWidget::secured_fillList() gxsItem->setData(COLUMN_NAME, ROLE_SORT_GROUP, 1); gxsItem->setData(COLUMN_NAME, ROLE_SORT_STANDARD_GROUP, 0); + gxsItem->setData(COLUMN_NAME, ROLE_SORT_NAME, name); //TODO: online state for gxs items gxsItem->setData(COLUMN_NAME, ROLE_SORT_STATE, 1); - gxsItem->setData(COLUMN_NAME, ROLE_SORT_NAME, name); if (mListModus == MODUS_CHECK) gxsItem->setCheckState(0, Qt::Unchecked); @@ -650,6 +656,7 @@ void FriendSelectionWidget::secured_fillList() mInFillList = false; ui->friendList->resort(); + filterConnected(isFilterConnected()); emit contentChanged(); } @@ -751,7 +758,7 @@ void FriendSelectionWidget::peerStatusChanged(const QString& peerId, int status) item->setTextColor(COLUMN_NAME, color); item->setIcon(COLUMN_NAME, QIcon(StatusDefs::imageUser(gpgStatus))); - item->setData(COLUMN_NAME, ROLE_SORT_STATE, (gpgStatus != (int) RS_STATUS_OFFLINE) ? 0 : 1); + item->setData(COLUMN_NAME, ROLE_SORT_STATE, gpgStatus); bFoundGPG = true; } @@ -770,7 +777,7 @@ void FriendSelectionWidget::peerStatusChanged(const QString& peerId, int status) item->setTextColor(COLUMN_NAME, color); item->setIcon(COLUMN_NAME, QIcon(StatusDefs::imageUser(status))); - item->setData(COLUMN_NAME, ROLE_SORT_STATE, (status != (int) RS_STATUS_OFFLINE) ? 0 : 1); + item->setData(COLUMN_NAME, ROLE_SORT_STATE, status); bFoundSSL = true; } @@ -799,6 +806,7 @@ void FriendSelectionWidget::peerStatusChanged(const QString& peerId, int status) } ui->friendList->resort(); + filterConnected(isFilterConnected()); } void FriendSelectionWidget::addContextMenuAction(QAction *action) @@ -810,7 +818,7 @@ void FriendSelectionWidget::contextMenuRequested(const QPoint &/*pos*/) { QMenu *contextMenu = new QMenu(this); - if (mListModus == MODUS_CHECK) { + if (mListModus == MODUS_MULTI) { contextMenu->addAction(QIcon(), tr("Mark all"), this, SLOT(selectAll())); contextMenu->addAction(QIcon(), tr("Mark none"), this, SLOT(deselectAll())); } @@ -953,37 +961,9 @@ void FriendSelectionWidget::itemChanged(QTreeWidgetItem *item, int column) void FriendSelectionWidget::filterItems(const QString& text) { - int count = ui->friendList->topLevelItemCount(); - for (int index = 0; index < count; ++index) { - filterItem(ui->friendList->topLevelItem(index), text); - } -} - -bool FriendSelectionWidget::filterItem(QTreeWidgetItem *item, const QString &text) -{ - bool visible = true; - - if (text.isEmpty() == false) { - if (item->text(0).contains(text, Qt::CaseInsensitive) == false) { - visible = false; - } - } - - int visibleChildCount = 0; - int count = item->childCount(); - for (int index = 0; index < count; ++index) { - if (filterItem(item->child(index), text)) { - ++visibleChildCount; - } - } - - if (visible || visibleChildCount) { - item->setHidden(false); - } else { - item->setHidden(true); - } - - return (visible || visibleChildCount); + ui->friendList->filterItems(COLUMN_NAME, text); + ui->friendList->resort(); + filterConnected(isFilterConnected()); } int FriendSelectionWidget::selectedItemCount() @@ -1168,9 +1148,24 @@ void FriendSelectionWidget::sortByState(bool sort) mActionSortByState->setChecked(sort); ui->friendList->resort(); + filterConnected(isFilterConnected()); } bool FriendSelectionWidget::isSortByState() { return mActionSortByState->isChecked(); } + +void FriendSelectionWidget::filterConnected(bool filter) +{ + ui->friendList->filterMinValItems(COLUMN_NAME, filter ? RS_STATUS_AWAY : RS_STATUS_OFFLINE, ROLE_SORT_STATE); + + mActionFilterConnected->setChecked(filter); + + ui->friendList->resort(); +} + +bool FriendSelectionWidget::isFilterConnected() +{ + return mActionFilterConnected->isChecked(); +} diff --git a/retroshare-gui/src/gui/common/FriendSelectionWidget.h b/retroshare-gui/src/gui/common/FriendSelectionWidget.h index c83aa716e..5cb3198f0 100644 --- a/retroshare-gui/src/gui/common/FriendSelectionWidget.h +++ b/retroshare-gui/src/gui/common/FriendSelectionWidget.h @@ -82,6 +82,7 @@ public: void start(); bool isSortByState(); + bool isFilterConnected(); int selectedItemCount(); std::string selectedId(IdType &idType); @@ -130,6 +131,7 @@ signals: public slots: void sortByState(bool sort); + void filterConnected(bool filter); private slots: void groupsChanged(int type); @@ -144,7 +146,6 @@ private slots: private: void fillList(); void secured_fillList(); - bool filterItem(QTreeWidgetItem *item, const QString &text); void selectedIds(IdType idType, std::set &ids, bool onlyDirectSelected); void setSelectedIds(IdType idType, const std::set &ids, bool add); @@ -161,6 +162,7 @@ private: bool mInSslItemChanged; bool mInFillList; QAction *mActionSortByState; + QAction *mActionFilterConnected; /* Color definitions (for standard see qss.default) */ QColor mTextColorOnline; diff --git a/retroshare-gui/src/gui/common/RSTreeWidget.cpp b/retroshare-gui/src/gui/common/RSTreeWidget.cpp index a261bff80..e4bd8be36 100644 --- a/retroshare-gui/src/gui/common/RSTreeWidget.cpp +++ b/retroshare-gui/src/gui/common/RSTreeWidget.cpp @@ -34,6 +34,7 @@ RSTreeWidget::RSTreeWidget(QWidget *parent) : QTreeWidget(parent) { mEnableColumnCustomize = false; mSettingsVersion = 0; // disabled + mFilterReasonRole = -1; // disabled QHeaderView *h = header(); h->setContextMenuPolicy(Qt::CustomContextMenu); @@ -84,6 +85,12 @@ void RSTreeWidget::mousePressEvent(QMouseEvent *event) QTreeWidget::mousePressEvent(event); } +void RSTreeWidget::setFilterReasonRole(int role /*=-1*/) +{ + if (role > Qt::UserRole) + mFilterReasonRole = role; +} + void RSTreeWidget::filterItems(int filterColumn, const QString &text, int role) { int count = topLevelItemCount(); @@ -101,6 +108,13 @@ void RSTreeWidget::filterItems(int filterColumn, const QString &text, int role) bool RSTreeWidget::filterItem(QTreeWidgetItem *item, int filterColumn, const QString &text, int role) { bool itemVisible = true; + //Get who hide this item + int filterReason = 0; + if (mFilterReasonRole >= Qt::UserRole) + filterReason = item->data(filterColumn, mFilterReasonRole).toInt(); + //Remove this filter for last test + if (filterReason & FILTER_REASON_TEXT) + filterReason -= FILTER_REASON_TEXT; if (!text.isEmpty()) { if (!item->data(filterColumn, role).toString().contains(text, Qt::CaseInsensitive)) { @@ -116,11 +130,62 @@ bool RSTreeWidget::filterItem(QTreeWidgetItem *item, int filterColumn, const QSt } } - if (itemVisible || visibleChildCount) { - item->setHidden(false); - } else { - item->setHidden(true); + if (!itemVisible && !visibleChildCount) { + filterReason |= FILTER_REASON_TEXT; } + item->setHidden(filterReason != 0); + //Update hiding reason + if (mFilterReasonRole >= Qt::UserRole) + item->setData(filterColumn, mFilterReasonRole, filterReason); + + return (itemVisible || visibleChildCount); +} + +void RSTreeWidget::filterMinValItems(int filterColumn, const double &value, int role) +{ + int count = topLevelItemCount(); + for (int index = 0; index < count; ++index) { + filterMinValItem(topLevelItem(index), filterColumn, value, role); + } + + QTreeWidgetItem *item = currentItem(); + if (item && item->isHidden()) { + // active item is hidden, deselect it + setCurrentItem(NULL); + } +} + +bool RSTreeWidget::filterMinValItem(QTreeWidgetItem *item, int filterColumn, const double &value, int role) +{ + bool itemVisible = true; + //Get who hide this item + int filterReason = 0; + if (mFilterReasonRole >= Qt::UserRole) + filterReason = item->data(filterColumn, mFilterReasonRole).toInt(); + //Remove this filter for last test + if (filterReason & FILTER_REASON_MINVAL) + filterReason -= FILTER_REASON_MINVAL; + + bool ok = false; + if ((item->data(filterColumn, role).toDouble(&ok) < value) && ok ) { + itemVisible = false; + } + + int visibleChildCount = 0; + int count = item->childCount(); + for (int index = 0; index < count; ++index) { + if (filterMinValItem(item->child(index), filterColumn, value, role)) { + ++visibleChildCount; + } + } + + if (!itemVisible && !visibleChildCount) { + filterReason |= FILTER_REASON_MINVAL; + } + item->setHidden(filterReason != 0); + //Update hiding reason + if (mFilterReasonRole >= Qt::UserRole) + item->setData(filterColumn, mFilterReasonRole, filterReason); return (itemVisible || visibleChildCount); } @@ -294,6 +359,6 @@ void RSTreeWidget::columnVisible() void RSTreeWidget::resort() { if (isSortingEnabled()) { - sortByColumn(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); + sortItems(header()->sortIndicatorSection(), header()->sortIndicatorOrder()); } } diff --git a/retroshare-gui/src/gui/common/RSTreeWidget.h b/retroshare-gui/src/gui/common/RSTreeWidget.h index cffa9975f..2d42088b9 100644 --- a/retroshare-gui/src/gui/common/RSTreeWidget.h +++ b/retroshare-gui/src/gui/common/RSTreeWidget.h @@ -24,6 +24,9 @@ #include +#define FILTER_REASON_TEXT 0x0001 +#define FILTER_REASON_MINVAL 0x0002 + /* Subclassing QTreeWidget */ class RSTreeWidget : public QTreeWidget { @@ -35,7 +38,9 @@ public: QString placeholderText() { return mPlaceholderText; } void setPlaceholderText(const QString &text); + void setFilterReasonRole(int role = -1); void filterItems(int filterColumn, const QString &text, int role = Qt::DisplayRole); + void filterMinValItems(int filterColumn, const double &value, int role = Qt::DisplayRole); void setSettingsVersion(qint32 version); void processSettings(bool load); @@ -58,6 +63,7 @@ signals: private: bool filterItem(QTreeWidgetItem *item, int filterColumn, const QString &text, int role); + bool filterMinValItem(QTreeWidgetItem *item, int filterColumn, const double &value, int role); private slots: void headerContextMenuRequested(const QPoint &pos); @@ -74,6 +80,7 @@ private: QMap mColumnCustomizable; QList mContextMenuActions; QList mContextMenuMenus; + int mFilterReasonRole; }; #endif diff --git a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp index 0eb83b5fc..8c50c719d 100755 --- a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp +++ b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp @@ -435,15 +435,29 @@ void ConnectFriendWizard::initializePage(int id) break; case Page_Conclusion: { + bool peerIsHiddenNode = peerDetails.isHiddenNode ; + bool amIHiddenNode = rsPeers->isHiddenNode(rsPeers->getOwnId()) ; + std::cerr << "Conclusion page id : " << peerDetails.id << "; gpg_id : " << peerDetails.gpg_id << std::endl; ui->_direct_transfer_CB_2 ->setChecked(false) ; //peerDetails.service_perm_flags & RS_NODE_PERM_DIRECT_DL) ; ui->_allow_push_CB_2 ->setChecked(peerDetails.service_perm_flags & RS_NODE_PERM_ALLOW_PUSH) ; - ui->_require_WL_CB_2 ->setChecked(peerDetails.service_perm_flags & RS_NODE_PERM_REQUIRE_WL) ; + + if(!peerIsHiddenNode && !amIHiddenNode) + ui->_require_WL_CB_2 ->setChecked(peerDetails.service_perm_flags & RS_NODE_PERM_REQUIRE_WL) ; + else + { + ui->_require_WL_CB_2 ->setChecked(false) ; + ui->_require_WL_CB_2 ->hide() ; + + ui->_addIPToWhiteList_CB_2->hide(); + ui->_addIPToWhiteList_ComboBox_2->hide(); + } sockaddr_storage addr ; - std::cerr << "Cert IP = " << peerDetails.extAddr << std::endl; + std::cerr << "Cert IP = " << peerDetails.extAddr << std::endl; + if(sockaddr_storage_ipv4_aton(addr,peerDetails.extAddr.c_str()) && sockaddr_storage_isValidNet(addr)) { QString ipstring0 = QString::fromStdString(sockaddr_storage_iptostring(addr)); @@ -577,29 +591,28 @@ void ConnectFriendWizard::initializePage(int id) ui->fr_avatar->setFrameType(AvatarWidget::NORMAL_FRAME); setPixmap(QWizard::LogoPixmap, QPixmap(":/images/user/user_request48.png")); + ui->fr_signGPGCheckBox->setChecked(false); + //set the radio button to sign the GPG key if (peerDetails.accept_connection && !peerDetails.ownsign) { //gpg key connection is already accepted, don't propose to accept it again - ui->fr_signGPGCheckBox->setChecked(false); ui->fr_acceptNoSignGPGCheckBox->hide(); + ui->fr_signGPGCheckBox->show(); ui->fr_acceptNoSignGPGCheckBox->setChecked(false); } if (!peerDetails.accept_connection && peerDetails.ownsign) { //gpg key is already signed, don't propose to sign it again ui->fr_acceptNoSignGPGCheckBox->setChecked(true); ui->fr_signGPGCheckBox->hide(); - ui->fr_signGPGCheckBox->setChecked(false); } if (!peerDetails.accept_connection && !peerDetails.ownsign) { ui->fr_acceptNoSignGPGCheckBox->setChecked(true); ui->fr_signGPGCheckBox->show(); - ui->fr_signGPGCheckBox->setChecked(false); ui->fr_acceptNoSignGPGCheckBox->show(); } if (peerDetails.accept_connection && peerDetails.ownsign) { ui->fr_acceptNoSignGPGCheckBox->setChecked(false); ui->fr_acceptNoSignGPGCheckBox->hide(); - ui->fr_signGPGCheckBox->setChecked(false); ui->fr_signGPGCheckBox->hide(); } diff --git a/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui b/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui index 3df4cf673..a11bb18e7 100644 --- a/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui +++ b/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui @@ -6,8 +6,8 @@ 0 0 - 760 - 538 + 1161 + 1223 diff --git a/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp b/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp index aae2af466..c243006d4 100755 --- a/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp +++ b/retroshare-gui/src/gui/connect/ConnectProgressDialog.cpp @@ -64,6 +64,9 @@ ConnectProgressDialog::ConnectProgressDialog(const RsPeerId& id, QWidget *parent ui->headerFrame->setHeaderText(tr("Connection Assistant")); connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(stopAndClose())); + + mAmIHiddenNode = rsPeers->isHiddenNode(rsPeers->getOwnId()) ; + mIsPeerHiddenNode = rsPeers->isHiddenNode(id) ; } ConnectProgressDialog::~ConnectProgressDialog() @@ -165,15 +168,35 @@ void ConnectProgressDialog::initDialog() ui->NetResult->setText(tr("N/A")); ui->ContactResult->setText(tr("N/A")); +#ifdef RS_USE_BITDHT + if(!mIsPeerHiddenNode && !mAmIHiddenNode) + { ui->DhtResult->setText(tr("DHT Startup")); ui->DhtProgressBar->setValue(0); + } +#else + if(mIsPeerHiddenNode || mAmIHiddenNode) + { + ui->DhtResult->hide(); + ui->DhtLabel->hide(); + ui->DhtProgressBar->hide(); + } +#endif + if(mIsPeerHiddenNode || mAmIHiddenNode) + { + ui->UdpResult->hide(); + ui->UdpProgressBar->hide(); + ui->UdpLabel->hide(); + } + else + { + ui->UdpResult->setText(tr("N/A")); + ui->UdpProgressBar->setValue(0); + } ui->LookupResult->setText(tr("N/A")); ui->LookupProgressBar->setValue(0); - ui->UdpResult->setText(tr("N/A")); - ui->UdpProgressBar->setValue(0); - sayInProgress(); if (rsPeers->isFriend(mId)) @@ -219,7 +242,9 @@ void ConnectProgressDialog::updateStatus() updateNetworkStatus(); updateContactStatus(); +#ifdef RS_USE_BITDHT updateDhtStatus(); +#endif updateLookupStatus(); updateUdpStatus(); @@ -443,6 +468,7 @@ void ConnectProgressDialog::updateLookupStatus() break; } +#ifdef RS_USE_BITDHT time_t now = time(NULL); switch(mDhtStatus) { @@ -520,6 +546,7 @@ void ConnectProgressDialog::updateLookupStatus() mLookupStatus = CONNECT_LOOKUP_ONLINE; break; } +#endif } @@ -572,6 +599,7 @@ void ConnectProgressDialog::updateUdpStatus() ui->UdpProgressBar->setValue(calcProgress(now, mUdpTS, CONNECT_UDP_TYPICAL, CONNECT_UDP_SLOW, CONNECT_UDP_PERIOD)); +#ifdef RS_USE_BITDHT /* now lookup details from Dht */ RsDhtNetPeer status; rsDht->getNetPeerStatus(mId, status); @@ -599,6 +627,7 @@ void ConnectProgressDialog::updateUdpStatus() } } } +#endif } diff --git a/retroshare-gui/src/gui/connect/ConnectProgressDialog.h b/retroshare-gui/src/gui/connect/ConnectProgressDialog.h index 1dcda6730..8620bd93b 100644 --- a/retroshare-gui/src/gui/connect/ConnectProgressDialog.h +++ b/retroshare-gui/src/gui/connect/ConnectProgressDialog.h @@ -97,6 +97,9 @@ private: time_t mUdpTS; uint32_t mUdpStatus; + bool mAmIHiddenNode ; + bool mIsPeerHiddenNode ; + /** Qt Designer generated object */ Ui::ConnectProgressDialog *ui; }; diff --git a/retroshare-gui/src/gui/connect/ConnectProgressDialog.ui b/retroshare-gui/src/gui/connect/ConnectProgressDialog.ui index c9129d8c5..8666655b6 100644 --- a/retroshare-gui/src/gui/connect/ConnectProgressDialog.ui +++ b/retroshare-gui/src/gui/connect/ConnectProgressDialog.ui @@ -6,8 +6,8 @@ 0 0 - 553 - 489 + 623 + 608 @@ -21,7 +21,16 @@ 0 - + + 0 + + + 0 + + + 0 + + 0 @@ -97,7 +106,7 @@ - + 75 @@ -117,7 +126,7 @@ - + 75 @@ -137,7 +146,7 @@ - + 75 @@ -283,10 +292,10 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13pt;">This Widget shows the progress of your connection to your new peer.</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13pt;">It is helpful for problem-solving.</span></p> -<p style="-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:'Lucida Grande'; font-size:13pt;"></p> +<p style="-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:'Lucida Grande'; font-size:13pt;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13pt;">If you are an expert RS user, or trust that RS will do the right thing</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13pt;">you can close it.</span></p></body></html> @@ -331,7 +340,6 @@ p, li { white-space: pre-wrap; } - diff --git a/retroshare-gui/src/gui/icons.qrc b/retroshare-gui/src/gui/icons.qrc index 5fe788ba2..aab1afb40 100644 --- a/retroshare-gui/src/gui/icons.qrc +++ b/retroshare-gui/src/gui/icons.qrc @@ -1,5 +1,6 @@ + icons/onion.png icons/svg/hidden.svg icons/svg/randomness.svg icons/svg/password.svg @@ -227,6 +228,7 @@ icons/tor-logo.png icons/tor-off.png icons/tor-on.png + icons/no-tor.png icons/tor-starting.png icons/tor-stopping.png icons/user-away_64.png diff --git a/retroshare-gui/src/gui/icons/no-tor.png b/retroshare-gui/src/gui/icons/no-tor.png new file mode 100644 index 000000000..ed0c29538 Binary files /dev/null and b/retroshare-gui/src/gui/icons/no-tor.png differ diff --git a/retroshare-gui/src/gui/icons/onion.png b/retroshare-gui/src/gui/icons/onion.png new file mode 100644 index 000000000..256dc2403 Binary files /dev/null and b/retroshare-gui/src/gui/icons/onion.png differ diff --git a/retroshare-gui/src/gui/settings/AppearancePage.ui b/retroshare-gui/src/gui/settings/AppearancePage.ui index d97b102fd..1813e8d3f 100755 --- a/retroshare-gui/src/gui/settings/AppearancePage.ui +++ b/retroshare-gui/src/gui/settings/AppearancePage.ui @@ -366,13 +366,6 @@ - - - - Show DHT Status - - - @@ -380,13 +373,6 @@ - - - - Show NAT Status - - - @@ -418,17 +404,31 @@ - - + + - Show SysTray on Status Bar + Show NAT Status + + + + + + + Show Operating Mode Status - + - Show Operating Mode Status + Show DHT Status + + + + + + + Show SysTray on Status Bar diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index 4a2930929..6a019990a 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -59,7 +59,26 @@ /// /// \brief hiddenServiceIncomingTab index of hidden serice incoming tab /// -const static uint32_t hiddenServiceIncomingTab = 2; +/// + +// Tabs numbers *after* non relevant tabs are removed. So do not use them to add/remove tabs!! +#ifdef RETROTOR +static const uint32_t TAB_HIDDEN_SERVICE_OUTGOING = 0; +static const uint32_t TAB_HIDDEN_SERVICE_INCOMING = 1; + +static const uint32_t TAB_NETWORK = 0; +static const uint32_t TAB_HIDDEN_SERVICE = 1; +static const uint32_t TAB_IP_FILTERS = 99; // This is a trick: these tabs do not exist, so enabling/disabling them has no effect +static const uint32_t TAB_RELAYS = 99; +#else +const static uint32_t TAB_HIDDEN_SERVICE_OUTGOING = 0; +const static uint32_t TAB_HIDDEN_SERVICE_INCOMING = 2; + +const static uint32_t TAB_NETWORK = 0; +const static uint32_t TAB_IP_FILTERS = 1; +const static uint32_t TAB_HIDDEN_SERVICE = 2; +const static uint32_t TAB_RELAYS = 3; +#endif //#define SERVER_DEBUG 1 @@ -71,6 +90,26 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) manager = NULL ; +#ifdef RETROTOR + // Here we use absolute numbers instead of consts defined above, because the consts correspond to the tab number *after* this tab removal. + + ui.tabWidget->removeTab(3) ; // remove relays. Not useful in Tor mode. + ui.tabWidget->removeTab(1) ; // remove IP filters. Not useful in Tor mode. + + ui.hiddenServiceTab->removeTab(1) ; // remove the Automatic I2P/BOB tab + ui.hiddenpage_proxyAddress_i2p->hide() ; + ui.hiddenpage_proxyLabel_i2p->hide() ; + ui.hiddenpage_proxyPort_i2p->hide() ; + ui.label_i2p_outgoing->hide() ; + ui.iconlabel_i2p_outgoing->hide() ; + ui.plainTextEdit->hide() ; + ui.hiddenpage_configuration->hide() ; + ui.l_hiddenpage_configuration->hide() ; + ui.hiddenpageInHelpPlainTextEdit->hide() ; + + ui.hiddenpage_outHeader->setText(tr("Tor has been automatically configured by Retroshare. You shouldn't need to change anything here.")) ; + ui.hiddenpage_inHeader->setText(tr("Tor has been automatically configured by Retroshare. You shouldn't need to change anything here.")) ; +#endif ui.filteredIpsTable->setHorizontalHeaderItem(COLUMN_RANGE,new QTableWidgetItem(tr("IP Range"))) ; ui.filteredIpsTable->setHorizontalHeaderItem(COLUMN_STATUS,new QTableWidgetItem(tr("Status"))) ; ui.filteredIpsTable->setHorizontalHeaderItem(COLUMN_ORIGIN,new QTableWidgetItem(tr("Origin"))) ; @@ -98,7 +137,7 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) for(std::list::const_iterator it(ip_servers.begin());it!=ip_servers.end();++it) ui.IPServersLV->addItem(QString::fromStdString(*it)) ; - ui.hiddenServiceTab->setTabEnabled(hiddenServiceIncomingTab, false); + ui.hiddenServiceTab->setTabEnabled(TAB_HIDDEN_SERVICE_INCOMING, false); ui.gbBob->setEnabled(false); ui.swBobAdvanced->setCurrentIndex(0); @@ -196,7 +235,6 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) QObject::connect(ui.enableCheckBox,SIGNAL(toggled(bool)),this,SLOT(updateRelayMode())); QObject::connect(ui.serverCheckBox,SIGNAL(toggled(bool)),this,SLOT(updateRelayMode())); - } void ServerPage::saveAndTestInProxy() @@ -300,8 +338,8 @@ void ServerPage::load() if (mIsHiddenNode) { mHiddenType = detail.hiddenType; - ui.tabWidget->setTabEnabled(1,false) ; // ip filter - ui.tabWidget->setTabEnabled(3,false) ; // relay + ui.tabWidget->setTabEnabled(TAB_IP_FILTERS,false) ; // ip filter + ui.tabWidget->setTabEnabled(TAB_RELAYS,false) ; // relay loadHiddenNode(); return; } @@ -460,6 +498,9 @@ void ServerPage::toggleIpFiltering(bool b) void ServerPage::loadFilteredIps() { + if(rsBanList == NULL) + return ; + if(rsBanList->ipFilteringEnabled()) { whileBlocking(ui.denyAll_CB)->setChecked(true) ; @@ -854,6 +895,8 @@ void ServerPage::toggleUPnP() settingChangeable = true; } + // Shouldn't we use readOnly instead of enabled?? + if (settingChangeable) { ui.localAddress->setEnabled(false); @@ -876,7 +919,7 @@ void ServerPage::saveAddresses() saveCommon(); - if(ui.tabWidget->currentIndex() == 2) // hidden services tab + if(ui.tabWidget->currentIndex() == TAB_HIDDEN_SERVICE) // hidden services tab updateOutProxyIndicator(); if (mIsHiddenNode) { @@ -1055,7 +1098,7 @@ void ServerPage::loadHiddenNode() ui.label_dynDNS->setVisible(false); ui.dynDNS ->setVisible(false); - ui.hiddenServiceTab->setTabEnabled(hiddenServiceIncomingTab, true); + ui.hiddenServiceTab->setTabEnabled(TAB_HIDDEN_SERVICE_INCOMING, true); /* Addresses must be set here - otherwise can't edit it */ /* set local address */ diff --git a/retroshare-gui/src/gui/settings/ServerPage.ui b/retroshare-gui/src/gui/settings/ServerPage.ui index 666e69bab..36e114a8f 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.ui +++ b/retroshare-gui/src/gui/settings/ServerPage.ui @@ -6,8 +6,8 @@ 0 0 - 1220 - 896 + 1283 + 917 @@ -26,7 +26,7 @@ - 0 + 2 diff --git a/retroshare-gui/src/gui/settings/rsharesettings.h b/retroshare-gui/src/gui/settings/rsharesettings.h index 21b1faae9..99e33b5f2 100644 --- a/retroshare-gui/src/gui/settings/rsharesettings.h +++ b/retroshare-gui/src/gui/settings/rsharesettings.h @@ -187,6 +187,10 @@ public: /** Sets whether the bandwidth graph is always on top. */ void setBWGraphAlwaysOnTop(bool alwaysOnTop); +#ifdef RETROTOR + void setHiddenServiceKey() ; +#endif + uint getNewsFeedFlags(); void setNewsFeedFlags(uint flags); diff --git a/retroshare-gui/src/gui/statistics/DhtWindow.cpp b/retroshare-gui/src/gui/statistics/DhtWindow.cpp index a6406743d..5684593fd 100644 --- a/retroshare-gui/src/gui/statistics/DhtWindow.cpp +++ b/retroshare-gui/src/gui/statistics/DhtWindow.cpp @@ -279,6 +279,7 @@ void DhtWindow::updateNetStatus() void DhtWindow::updateNetPeers() { +#ifdef RS_USE_BITDHT //QTreeWidget *peerTreeWidget = ui.peerTreeWidget; std::list peerIds; @@ -364,7 +365,6 @@ void DhtWindow::updateNetPeers() QHeaderView * _header = ui.peerTreeWidget->header () ; _header->resizeSection ( PTW_COL_RSNAME, 170 ); - /* update the data */ RsDhtNetPeer status; rsDht->getNetPeerStatus(*it, status); @@ -518,6 +518,7 @@ void DhtWindow::updateNetPeers() //peerSummaryLabel->setText(connstr); +#endif } diff --git a/retroshare-gui/src/gui/statusbar/torstatus.cpp b/retroshare-gui/src/gui/statusbar/torstatus.cpp new file mode 100644 index 000000000..399243ac4 --- /dev/null +++ b/retroshare-gui/src/gui/statusbar/torstatus.cpp @@ -0,0 +1,138 @@ +/**************************************************************** + * RetroShare is distributed under the following license: + * + * Copyright (C) 2009 RetroShare Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ +#include "torstatus.h" + +#include +#include +#include +#include + +#include "retroshare/rsconfig.h" +#include "util/misc.h" + +#ifdef RETROTOR +#include "TorControl/TorManager.h" +#include "TorControl/TorControl.h" +#endif + +#include + +TorStatus::TorStatus(QWidget *parent) + : QWidget(parent) +{ + QHBoxLayout *hbox = new QHBoxLayout(); + hbox->setMargin(0); + hbox->setSpacing(6); + + statusTor = new QLabel("" + tr("Tor") + ":", this ); + statusTor->setToolTip(tr("

This version of Retroshare uses Tor to connect to your friends.

")) ; + hbox->addWidget(statusTor); + + torstatusLabel = new QLabel( this ); + torstatusLabel->setPixmap(QPixmap(":/icons/no-tor.png")); + hbox->addWidget(torstatusLabel); + + _compactMode = false; + + setLayout( hbox ); +} + +void TorStatus::getTorStatus() +{ + statusTor->setVisible(!_compactMode); + QString text = _compactMode?statusTor->text():""; + + /* check local network state. We cannot make sure that Tor is running yet. */ + uint32_t netState = rsConfig -> getNetState(); + bool online ; + + switch(netState) + { + default: + case RSNET_NETSTATE_BAD_UNKNOWN: + case RSNET_NETSTATE_BAD_OFFLINE: online = false ; + break ; + + case RSNET_NETSTATE_WARNING_RESTART: + + case RSNET_NETSTATE_BAD_NATSYM: + case RSNET_NETSTATE_BAD_NODHT_NAT: + case RSNET_NETSTATE_WARNING_NATTED: + case RSNET_NETSTATE_WARNING_NODHT: + case RSNET_NETSTATE_GOOD: + case RSNET_NETSTATE_ADV_FORWARD: online = true ; + break ; + } + + /* now the extra bit .... switch on check boxes */ + + int S = QFontMetricsF(torstatusLabel->font()).height(); + +#ifdef RETROTOR + // get Tor status + int tor_control_status = Tor::TorManager::instance()->control()->status(); + int torstatus = Tor::TorManager::instance()->control()->torStatus(); + + QString tor_control_status_str,torstatus_str ; + bool tor_control_ok ; + + switch(tor_control_status) + { + default: + case Tor::TorControl::Error : tor_control_ok = false ; tor_control_status_str = "Error" ; break ; + case Tor::TorControl::NotConnected: tor_control_ok = false ; tor_control_status_str = "Not connected" ; break ; + case Tor::TorControl::Connecting: tor_control_ok = false ; tor_control_status_str = "Connecting" ; break ; + case Tor::TorControl::Authenticating: tor_control_ok = false ; tor_control_status_str = "Authenticating" ; break ; + case Tor::TorControl::Connected: tor_control_ok = true ; tor_control_status_str = "Connected" ; 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 ; + } + +#define MIN_RS_NET_SIZE 10 + + if(torstatus == Tor::TorControl::TorOffline || !online || !tor_control_ok) + { + // RED - some issue. + torstatusLabel->setPixmap(QPixmap(":/icons/tor-stopping.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); + torstatusLabel->setToolTip( text + tr("Tor is currently offline")); + } + else if(torstatus == Tor::TorControl::TorReady && online && tor_control_ok) + { + torstatusLabel->setPixmap(QPixmap(":/icons/tor-on.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); + torstatusLabel->setToolTip( text + tr("Tor is OK")); + } + else // torstatus == Tor::TorControl::TorUnknown + { + // GRAY. + torstatusLabel->setPixmap(QPixmap(":/icons/no-tor.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); + torstatusLabel->setToolTip( text + tr("No tor configuration")); + } +#else + torstatusLabel->setPixmap(QPixmap(":/icons/tor-stopping.png").scaledToHeight(S,Qt::SmoothTransformation)); + torstatusLabel->setToolTip( text + tr("Tor is currently offline")); +#endif +} diff --git a/retroshare-gui/src/gui/statusbar/torstatus.h b/retroshare-gui/src/gui/statusbar/torstatus.h new file mode 100644 index 000000000..6728b7cbc --- /dev/null +++ b/retroshare-gui/src/gui/statusbar/torstatus.h @@ -0,0 +1,41 @@ +/**************************************************************** + * RetroShare is distributed under the following license: + * + * Copyright (C) 2009 RetroShare Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ****************************************************************/ + +#pragma once + +#include + +class QLabel; + +class TorStatus : public QWidget +{ + Q_OBJECT +public: + TorStatus(QWidget *parent = 0); + + void getTorStatus( ); + void setCompactMode(bool compact) {_compactMode = compact; } + +private: + QLabel *torstatusLabel, *statusTor; + bool _compactMode; +}; + diff --git a/retroshare-gui/src/main.cpp b/retroshare-gui/src/main.cpp index 8dfa43c48..54979f983 100644 --- a/retroshare-gui/src/main.cpp +++ b/retroshare-gui/src/main.cpp @@ -45,8 +45,16 @@ #include "idle/idle.h" #include "lang/languagesupport.h" #include "util/RsGxsUpdateBroadcast.h" +#include "util/rsdir.h" +#include "util/rstime.h" + +#ifdef RETROTOR +#include "TorControl/TorManager.h" +#include "TorControl/TorControlWindow.h" +#endif #include "retroshare/rsidentity.h" +#include "retroshare/rspeers.h" #ifdef SIGFPE_DEBUG #include @@ -274,12 +282,9 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); RshareSettings::Create (); /* Setup The GUI Stuff */ - Rshare rshare(args, argc, argv, - QString::fromUtf8(RsAccounts::ConfigDirectory().c_str())); + Rshare rshare(args, argc, argv, QString::fromUtf8(RsAccounts::ConfigDirectory().c_str())); /* Start RetroShare */ - QSplashScreen splashScreen(QPixmap(":/images/logo/logo_splash.png")/* , Qt::WindowStaysOnTopHint*/); - QString sDefaultGXSIdToCreate = ""; switch (initResult) { case RS_INIT_OK: @@ -306,19 +311,20 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); if (genCert) { GenCertDialog gd(false); - if (gd.exec () == QDialog::Rejected) { + + if (gd.exec () == QDialog::Rejected) return 1; - } + sDefaultGXSIdToCreate = gd.getGXSNickname(); } - splashScreen.show(); + //splashScreen.show(); } break; case RS_INIT_HAVE_ACCOUNT: { - splashScreen.show(); - splashScreen.showMessage(rshare.translate("SplashScreen", "Load profile"), Qt::AlignHCenter | Qt::AlignBottom); + //splashScreen.show(); + //splashScreen.showMessage(rshare.translate("SplashScreen", "Load profile"), Qt::AlignHCenter | Qt::AlignBottom); RsPeerId preferredId; RsAccounts::GetPreferredAccountId(preferredId); @@ -340,8 +346,59 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); SoundManager::create(); +#ifdef RETROTOR + // Now that we know the Tor service running, and we know the SSL id, we can make sure it provides a viable hidden service + + QString tor_hidden_service_dir = QString::fromStdString(RsAccounts::AccountDirectory()) + QString("/hidden_service/") ; + + Tor::TorManager *torManager = Tor::TorManager::instance(); + torManager->setDataDirectory(Rshare::dataDirectory() + QString("/tor/")); + torManager->setHiddenServiceDirectory(tor_hidden_service_dir); // re-set it, because now it's changed to the specific location that is run + + RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir.toUtf8())) ; + + torManager->setupHiddenService(); + + if(! torManager->start() || torManager->hasError()) + { + QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+torManager->errorMessage()) ; + return 1 ; + } + + { + TorControlDialog tcd(torManager) ; + QString error_msg ; + tcd.show(); + + while(tcd.checkForTor(error_msg) != TorControlDialog::TOR_STATUS_OK || tcd.checkForHiddenService() != TorControlDialog::HIDDEN_SERVICE_STATUS_OK) // runs until some status is reached: either tor works, or it fails. + { + QCoreApplication::processEvents(); + rstime::rs_usleep(0.2*1000*1000) ; + + if(!error_msg.isNull()) + { + QMessageBox::critical(NULL,QObject::tr("Cannot start Tor"),QObject::tr("Sorry but Tor cannot be started on your system!\n\nThe error reported is:\"")+error_msg+"\"") ; + return 1; + } + } + + tcd.hide(); + + if(tcd.checkForHiddenService() != TorControlDialog::HIDDEN_SERVICE_STATUS_OK) + { + QMessageBox::critical(NULL,QObject::tr("Cannot start a hidden tor service!"),QObject::tr("It was not possible to start a hidden service.")) ; + return 1 ; + } + } +#endif + + QSplashScreen splashScreen(QPixmap(":/images/logo/logo_splash.png")/* , Qt::WindowStaysOnTopHint*/); + + splashScreen.show(); splashScreen.showMessage(rshare.translate("SplashScreen", "Load configuration"), Qt::AlignHCenter | Qt::AlignBottom); + QCoreApplication::processEvents(); + /* stop Retroshare if startup fails */ if (!RsControl::instance()->StartupRetroShare()) { @@ -349,10 +406,38 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); return 1; } +#ifdef RETROTOR + // Tor works with viable hidden service. Let's use it! + + QString service_id ; + QString onion_address ; + uint16_t service_port ; + uint16_t service_target_port ; + uint16_t proxy_server_port ; + QHostAddress service_target_address ; + QHostAddress proxy_server_address ; + + torManager->getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port); + torManager->getProxyServerInfo(proxy_server_address,proxy_server_port) ; + + std::cerr << "Got hidden service info: " << std::endl; + std::cerr << " onion address : " << onion_address.toStdString() << std::endl; + std::cerr << " service_id : " << service_id.toStdString() << std::endl; + std::cerr << " service port : " << service_port << std::endl; + std::cerr << " target port : " << service_target_port << std::endl; + std::cerr << " target address : " << service_target_address.toString().toStdString() << std::endl; + + std::cerr << "Setting proxy server to " << service_target_address.toString().toStdString() << ":" << service_target_port << std::endl; + + rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address.toString().toStdString(), service_target_port); + rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address.toStdString(), service_port); + rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address.toString().toStdString(),proxy_server_port) ; +#endif Rshare::initPlugins(); splashScreen.showMessage(rshare.translate("SplashScreen", "Create interface"), Qt::AlignHCenter | Qt::AlignBottom); + QCoreApplication::processEvents(); // forces splashscreen to show up RsharePeerSettings::Create(); diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index d3a4c05c1..2fa5aafca 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -1,9 +1,10 @@ !include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri") TEMPLATE = app -QT += network xml +QT += network xml CONFIG += qt gui uic qrc resources idle bitdht CONFIG += link_prl +CONFIG += console TARGET = retroshare DEFINES += TARGET=\\\"$${TARGET}\\\" @@ -20,6 +21,13 @@ profiling { QMAKE_LFLAGS *= -pg } +retrotor { + DEFINES *= RETROTOR + + FORMS += TorControl/TorControlWindow.ui + SOURCES += TorControl/TorControlWindow.cpp + HEADERS += TorControl/TorControlWindow.h +} #QMAKE_CFLAGS += -fmudflap #LIBS *= /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflap.a /usr/lib/gcc/x86_64-linux-gnu/4.4/libmudflapth.a @@ -325,6 +333,47 @@ INCLUDEPATH += $$PWD/../../libresapi/src PRE_TARGETDEPS *= $$OUT_PWD/../../libresapi/src/lib/libresapi.a LIBS += $$OUT_PWD/../../libresapi/src/lib/libresapi.a +retrotor { +HEADERS += TorControl/AddOnionCommand.h \ + TorControl/AuthenticateCommand.h \ + TorControl/GetConfCommand.h \ + TorControl/HiddenService.h \ + TorControl/ProtocolInfoCommand.h \ + TorControl/SetConfCommand.h \ + TorControl/TorControlCommand.h \ + TorControl/TorControl.h \ + TorControl/TorControlSocket.h \ + TorControl/TorManager.h \ + TorControl/TorProcess.h \ + TorControl/TorProcess_p.h \ + TorControl/TorSocket.h \ + TorControl/Useful.h \ + TorControl/CryptoKey.h \ + TorControl/PendingOperation.h \ + TorControl/SecureRNG.h \ + TorControl/Settings.h \ + TorControl/StrUtil.h \ + TorControl/TorProcess_p.h + +SOURCES += TorControl/AddOnionCommand.cpp \ + TorControl/AuthenticateCommand.cpp \ + TorControl/GetConfCommand.cpp \ + TorControl/HiddenService.cpp \ + TorControl/ProtocolInfoCommand.cpp \ + TorControl/SetConfCommand.cpp \ + TorControl/TorControlCommand.cpp \ + TorControl/TorControl.cpp \ + TorControl/TorControlSocket.cpp \ + TorControl/TorManager.cpp \ + TorControl/TorProcess.cpp \ + TorControl/TorSocket.cpp \ + TorControl/CryptoKey.cpp \ + TorControl/PendingOperation.cpp \ + TorControl/SecureRNG.cpp \ + TorControl/Settings.cpp \ + TorControl/StrUtil.cpp +} + # Input HEADERS += rshare.h \ retroshare-gui/configpage.h \ @@ -532,6 +581,7 @@ HEADERS += rshare.h \ gui/statusbar/peerstatus.h \ gui/statusbar/natstatus.h \ gui/statusbar/dhtstatus.h \ + gui/statusbar/torstatus.h \ gui/statusbar/ratesstatus.h \ gui/statusbar/hashingstatus.h \ gui/statusbar/discstatus.h \ @@ -874,6 +924,7 @@ SOURCES += main.cpp \ gui/statusbar/peerstatus.cpp \ gui/statusbar/natstatus.cpp \ gui/statusbar/dhtstatus.cpp \ + gui/statusbar/torstatus.cpp \ gui/statusbar/ratesstatus.cpp \ gui/statusbar/hashingstatus.cpp \ gui/statusbar/discstatus.cpp \ diff --git a/retroshare-gui/src/util/HandleRichText.cpp b/retroshare-gui/src/util/HandleRichText.cpp index 312946f08..2b3e75b35 100644 --- a/retroshare-gui/src/util/HandleRichText.cpp +++ b/retroshare-gui/src/util/HandleRichText.cpp @@ -36,7 +36,7 @@ #include "gui/RetroShareLink.h" #include "util/ObjectPainter.h" #include "util/imageutil.h" -#include "util/rsscopetimer.h" +#include "util/rstime.h" #include @@ -1153,7 +1153,7 @@ bool RsHtml::makeEmbeddedImage(const QString &fileName, QString &embeddedImage, /** Converts image to embedded image HTML fragment **/ bool RsHtml::makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels, const int maxBytes) { - RsScopeTimer s("Embed image"); + rstime::RsScopeTimer s("Embed image"); QImage opt; return ImageUtil::optimizeSize(embeddedImage, originalImage, opt, maxPixels, maxBytes); } diff --git a/retroshare-gui/src/util/imageutil.cpp b/retroshare-gui/src/util/imageutil.cpp index ca2716b09..d32da5a97 100644 --- a/retroshare-gui/src/util/imageutil.cpp +++ b/retroshare-gui/src/util/imageutil.cpp @@ -1,6 +1,6 @@ #include "imageutil.h" #include "util/misc.h" -#include "util/rsscopetimer.h" +#include "util/rstime.h" #include #include @@ -113,7 +113,7 @@ bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &opti int ImageUtil::checkSize(QString &embeddedImage, const QImage &img, int maxBytes) { - RsScopeTimer st("Check size"); + rstime::RsScopeTimer st("Check size"); QByteArray bytearray; QBuffer buffer(&bytearray); @@ -166,7 +166,7 @@ void ImageUtil::quantization(const QImage &img, QVector &palette) int bits = 4; // bits/pixel int samplesize = 100000; //only take this many color samples - RsScopeTimer st("Quantization"); + rstime::RsScopeTimer st("Quantization"); QSet colors; //collect color information diff --git a/retroshare-nogui/src/retroshare.cc b/retroshare-nogui/src/retroshare.cc index ef02bb623..65d8c6168 100644 --- a/retroshare-nogui/src/retroshare.cc +++ b/retroshare-nogui/src/retroshare.cc @@ -30,7 +30,8 @@ #include "notifytxt.h" #include -#include +#include "util/argstream.h" +#include "util/rstime.h" #include #ifdef WINDOWS_SYS #include @@ -105,7 +106,7 @@ int main(int argc, char **argv) while(ctrl_mod.processShouldExit() == false) { - usleep(1000*1000); + rstime::rs_usleep(1000*1000); if(!tac.isRunning() && !already) { @@ -224,7 +225,7 @@ int main(int argc, char **argv) #endif } - usleep(1000); + rstime::rs_usleep(1000); } return 1; diff --git a/retroshare.pri b/retroshare.pri index 90901d8ae..e657b3cf7 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -3,6 +3,12 @@ CONFIG *= retroshare_gui no_retroshare_gui:CONFIG -= retroshare_gui +# To build the RetroTor executable, just uncomment the following option. +# RetroTor is a version of RS that automatically configures Tor for its own usage +# using only hidden nodes. It will not start if Tor is not working. + +# CONFIG *= retrotor + # To disable RetroShare-nogui append the following # assignation to qmake command line "CONFIG+=no_retroshare_nogui" CONFIG *= retroshare_nogui @@ -220,6 +226,10 @@ rs_autologin { warning("You have enabled RetroShare auto-login, this is discouraged. The usage of auto-login on some linux distributions may allow someone having access to your session to steal the SSL keys of your node location and therefore compromise your security") } +retrotor { + CONFIG *= rs_onlyhiddennode +} + rs_onlyhiddennode { DEFINES *= RS_ONLYHIDDENNODE warning("QMAKE: You have enabled only hidden node.") diff --git a/supportlibs/pegmarkdown/Makefile.orig b/supportlibs/pegmarkdown/Makefile.orig deleted file mode 100644 index 1e8c7a003..000000000 --- a/supportlibs/pegmarkdown/Makefile.orig +++ /dev/null @@ -1,42 +0,0 @@ -uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') -ifneq (,$(findstring MINGW,$(uname_S))) - X = .exe -endif - -export X - -PROGRAM=markdown$(X) -CFLAGS ?= -Wall -O3 -ansi -D_GNU_SOURCE # -flto for newer GCC versions -OBJS=markdown_parser.o markdown_output.o markdown_lib.o utility_functions.o parsing_functions.o odf.o -PEGDIR=peg-0.1.9 -LEG=$(PEGDIR)/leg$(X) -PKG_CONFIG = pkg-config - -ALL : $(PROGRAM) - -$(LEG): $(PEGDIR) - CC=gcc make -C $(PEGDIR) - -%.o : %.c markdown_peg.h - $(CC) -c `$(PKG_CONFIG) --cflags glib-2.0` $(CFLAGS) -o $@ $< - -$(PROGRAM) : markdown.c $(OBJS) - $(CC) `$(PKG_CONFIG) --cflags glib-2.0` $(CFLAGS) -o $@ $< $(OBJS) `$(PKG_CONFIG) --libs glib-2.0` - -markdown_parser.c : markdown_parser.leg $(LEG) markdown_peg.h parsing_functions.c utility_functions.c - $(LEG) -o $@ $< - -.PHONY: clean test - -clean: - rm -f markdown_parser.c $(PROGRAM) $(OBJS) - -distclean: clean - make -C $(PEGDIR) clean -\ -test: $(PROGRAM) - cd MarkdownTest_1.0.3; \ - ./MarkdownTest.pl --script=../$(PROGRAM) --tidy - -leak-check: $(PROGRAM) - valgrind --leak-check=full ./markdown README