diff --git a/.gitmodules b/.gitmodules index 2f69bb7b2..5199bdb4d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "supportlibs/libsam3"] path = supportlibs/libsam3 url = https://github.com/i2p/libsam3.git +[submodule "supportlibs/jni.hpp"] + path = supportlibs/jni.hpp + url = https://github.com/RetroShare/jni.hpp.git diff --git a/build_scripts/Android/Dockerfile b/build_scripts/Android/Dockerfile index 2e4b8c762..490a247c9 100644 --- a/build_scripts/Android/Dockerfile +++ b/build_scripts/Android/Dockerfile @@ -2,7 +2,7 @@ ## image name must match gitlab repository name, you can play just with the tag ## the part after : # export CI_IMAGE_NAME="registry.gitlab.com/retroshare/retroshare:android_arm_base" -# docker build --squash -t "${CI_REGISTRY_IMAGE}" \ +# docker build --squash --tag "${CI_IMAGE_NAME}" \ # --build-arg QT_INSTALLER_JWT_TOKEN="your qt JWT token goes here" . # # To build Android ARMv8 (64 bit) package pass also @@ -25,14 +25,16 @@ FROM ubuntu:20.04 ENV DEBIAN_FRONTEND=noninteractive +ENV APT_UNAT="--assume-yes --quiet" -RUN apt-get update && apt-get clean -RUN apt-get install -y -qq \ +RUN apt-get update $APT_UNAT && apt-get upgrade --show-upgraded $APT_UNAT && \ + apt-get clean $APT_UNAT +RUN apt-get install --no-install-recommends $APT_UNAT \ bash build-essential bzip2 cmake curl chrpath doxygen \ git p7zip python qt5-default qttools5-dev tclsh unzip wget zip # Dependencies to create Android pkg -RUN apt-get install -y -qq \ +RUN apt-get install --no-install-recommends $APT_UNAT \ openjdk-8-jre openjdk-8-jdk openjdk-8-jdk-headless gradle ARG FRESHCLONE=0 @@ -74,11 +76,11 @@ ARG QT_INSTALLER_JWT_TOKEN RUN $PREPARE_TOOLCHAIN install_qt_android # Avoid Qt account details leak into the image RUN rm -f /root/.local/share/Qt/qtaccount.ini -# Shrink image by removing unneded Qt components +# Shrink image by removing unneeded Qt components RUN rm -r \ $NATIVE_LIBS_TOOLCHAIN_PATH/Qt/Docs/ \ $NATIVE_LIBS_TOOLCHAIN_PATH/Qt/Examples/ \ - $NATIVE_LIBS_TOOLCHAIN_PATH/Qt/Tools/ + $NATIVE_LIBS_TOOLCHAIN_PATH/Qt/Tools/ RUN mkdir /jsonapi-generator-build WORKDIR /jsonapi-generator-build/ diff --git a/build_scripts/Android/prepare-toolchain-clang.sh b/build_scripts/Android/prepare-toolchain-clang.sh index 25fefccba..510b04855 100755 --- a/build_scripts/Android/prepare-toolchain-clang.sh +++ b/build_scripts/Android/prepare-toolchain-clang.sh @@ -112,6 +112,9 @@ define_default_value MVPTREE_SOURCE_VERSION origin/master define_default_value REPORT_DIR "$(pwd)/$(basename ${NATIVE_LIBS_TOOLCHAIN_PATH})_build_report/" +define_default_value RS_SRC_DIR "$(realpath $(dirname $BASH_SOURCE)/../../)" + + cArch="" eABI="" cmakeABI="" @@ -829,6 +832,15 @@ build_phash() popd } +task_register fetch_jni_hpp +fetch_jni_hpp() +{ + local rDir="supportlibs/jni.hpp/" + + [ "$(ls "${RS_SRC_DIR}/${rDir}" | wc -l)" -gt "4" ] || + git -C ${RS_SRC_DIR} submodule update --init ${rDir} +} + task_register build_mvptree build_mvptree() { @@ -862,6 +874,7 @@ build_default_toolchain() task_run build_xapian || return $? task_run build_miniupnpc || return $? task_run build_phash || return $? + task_run fetch_jni_hpp || return $? task_run deduplicate_includes || return $? task_run get_native_libs_toolchain_path || return $? } diff --git a/build_scripts/GitlabCI/Android.Dockerfile b/build_scripts/GitlabCI/Android.Dockerfile index a0844972d..bc4bd0755 100644 --- a/build_scripts/GitlabCI/Android.Dockerfile +++ b/build_scripts/GitlabCI/Android.Dockerfile @@ -1,7 +1,9 @@ ARG ANDROID_NDK_ARCH=arm FROM registry.gitlab.com/retroshare/retroshare:android_${ANDROID_NDK_ARCH}_base -RUN apt-get update -y && apt-get upgrade -y +ENV APT_UNAT="--assume-yes --quiet" + +RUN apt-get update $APT_UNAT && apt-get upgrade $APT_UNAT --show-upgraded ARG REPO_URL=https://gitlab.com/RetroShare/RetroShare.git ARG REPO_BRANCH=master diff --git a/build_scripts/OBS b/build_scripts/OBS index b260e5834..df16cb915 160000 --- a/build_scripts/OBS +++ b/build_scripts/OBS @@ -1 +1 @@ -Subproject commit b260e58346b1eec782bdf88a7e8f3c9d36fd3ecb +Subproject commit df16cb915465d058c75277678799ce4dadeae287 diff --git a/jsonapi-generator/README.adoc b/jsonapi-generator/README.adoc index 64284b85c..cea59a117 100644 --- a/jsonapi-generator/README.adoc +++ b/jsonapi-generator/README.adoc @@ -1,447 +1,10 @@ -// SPDX-FileCopyrightText: (C) 2004-2019 Retroshare Team +// SPDX-FileCopyrightText: (C) 2021 Retroshare Team // SPDX-License-Identifier: CC0-1.0 -RetroShare JSON API -=================== += Moved -:Cxx: C++ +JSON API generator is now part of `libretroshare` look under `src/jsonapi/` +directory in `libretroshare` repository. -== How to use RetroShare JSON API - -Look for methods marked with +@jsonapi+ doxygen custom command into -+libretroshare/src/retroshare+. The method path is composed by service instance -pointer name like +rsGxsChannels+ for +RsGxsChannels+, and the method name like -+createGroup+ and pass the input paramethers as a JSON object. - -.Service instance pointer in rsgxschannels.h -[source,cpp] --------------------------------------------------------------------------------- -/** - * Pointer to global instance of RsGxsChannels service implementation - * @jsonapi{development} - */ -extern RsGxsChannels* rsGxsChannels; --------------------------------------------------------------------------------- - -.Method declaration in rsgxschannels.h -[source,cpp] --------------------------------------------------------------------------------- - /** - * @brief Request channel creation. - * The action is performed asyncronously, so it could fail in a subsequent - * phase even after returning true. - * @jsonapi{development} - * @param[out] token Storage for RsTokenService token to track request - * status. - * @param[in] group Channel data (name, description...) - * @return false on error, true otherwise - */ - virtual bool createGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; --------------------------------------------------------------------------------- - -.paramethers.json -[source,json] --------------------------------------------------------------------------------- -{ - "group":{ - "mMeta":{ - "mGroupName":"JSON test group", - "mGroupFlags":4, - "mSignFlags":520 - }, - "mDescription":"JSON test group description" - }, - "caller_data":"Here can go any kind of JSON data (even objects) that the caller want to get back together with the response" -} --------------------------------------------------------------------------------- - -.Calling the JSON API with curl on the terminal -[source,bash] --------------------------------------------------------------------------------- -curl -u $API_USER --data @paramethers.json http://127.0.0.1:9092/rsGxsChannels/createGroup --------------------------------------------------------------------------------- - -.JSON API call result -[source,json] --------------------------------------------------------------------------------- -{ - "caller_data": "Here can go any kind of JSON data (even objects) that the caller want to get back together with the response", - "retval": true, - "token": 3 -} --------------------------------------------------------------------------------- - -Even if it is less efficient because of URL encoding HTTP +GET+ method is -supported too, so in cases where the client cannot use +POST+ she can still use -+GET+ taking care of encoding the JSON data. With +curl+ this can be done at -least in two different ways. - -.Calling the JSON API with GET method with curl on the terminal -[source,bash] --------------------------------------------------------------------------------- -curl -u $API_USER --get --data-urlencode jsonData@paramethers.json \ - http://127.0.0.1:9092/rsGxsChannels/createGroup --------------------------------------------------------------------------------- - -Letting +curl+ do the encoding is much more elegant but it is semantically -equivalent to the following. - -.Calling the JSON API with GET method and pre-encoded data with curl on the terminal --------------------------------------------------------------------------------- -curl -u $API_USER http://127.0.0.1:9092/rsGxsChannels/createGroup?jsonData=%7B%0A%20%20%20%20%22group%22%3A%7B%0A%20%20%20%20%20%20%20%20%22mMeta%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupName%22%3A%22JSON%20test%20group%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupFlags%22%3A4%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mSignFlags%22%3A520%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22mDescription%22%3A%22JSON%20test%20group%20description%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22caller_data%22%3A%22Here%20can%20go%20any%20kind%20of%20JSON%20data%20%28even%20objects%29%20that%20the%20caller%20want%20to%20get%20back%20together%20with%20the%20response%22%0A%7D --------------------------------------------------------------------------------- - -Note that using +GET+ method +?jsonData=+ and then the JSON data URL encoded are -added after the path in the HTTP request. - - -== JSON API authentication - -Most of JSON API methods require authentication as they give access to -RetroShare user data, and we don't want any application running on the system -eventually by other users be able to access private data indiscriminately. -JSON API support HTTP Basic as authentication scheme, this is enough as JSON API -server is intented for usage on the same system (127.0.0.1) not over an -untrusted network. -If you need to use JSON API over an untrusted network consider using a reverse -proxy with HTTPS such as NGINX in front of JSON API server. -If RetroShare login has been effectuated through the JSON API you can use your -location SSLID as username and your PGP password as credential for the JSON API, -but we suggests you use specific meaningful and human readable credentials for -each JSON API client so the human user can have better control over which client -can access the JSON API. - -.NewToken.json -[source,json] --------------------------------------------------------------------------------- -{ - "token": "myNewUser:myNewPassword" -} --------------------------------------------------------------------------------- - -.An authenticated client can authorize new tokens like this --------------------------------------------------------------------------------- -curl -u $API_USER --data @NewToken.json http://127.0.0.1:9092/jsonApiServer/authorizeToken --------------------------------------------------------------------------------- - -.An unauthenticated JSON API client can request access with --------------------------------------------------------------------------------- -curl --data @NewToken.json http://127.0.0.1:9092/jsonApiServer/requestNewTokenAutorization --------------------------------------------------------------------------------- - -When an unauthenticated client request his token to be authorized, JSON API -server will try to ask confirmation to the human user if possible through -+mNewAccessRequestCallback+, if it is not possible or the user didn't authorized -the token +false+ is returned. - - -== Offer new RetroShare services through JSON API - -To offer a retroshare service through the JSON API, first of all one need find -the global pointer to the service instance and document it in doxygen syntax, -plus marking with the custom doxygen command +@jsonapi{RS_VERSION}+ where -+RS_VERSION+ is the retroshare version in which this service became available -with the current semantic (major changes to the service semantic, changes the -meaning of the service itself, so the version should be updated in the -documentation in that case). - -.Service instance pointer in rsgxschannels.h -[source,cpp] --------------------------------------------------------------------------------- -/** - * Pointer to global instance of RsGxsChannels service implementation - * @jsonapi{development} - */ -extern RsGxsChannels* rsGxsChannels; --------------------------------------------------------------------------------- - - -Once the service instance itself is known to the JSON API you need to document -in doxygen syntax and mark with the custom doxygen command -+@jsonapi{RS_VERSION}+ the methods of the service that you want to make -available through JSON API. - -.Offering RsGxsChannels::getChannelDownloadDirectory in rsgxschannels.h -[source,cpp] --------------------------------------------------------------------------------- - /** - * Get download directory for the given channel - * @jsonapi{development} - * @param[in] channelId id of the channel - * @param[out] directory reference to string where to store the path - * @return false on error, true otherwise - */ - virtual bool getChannelDownloadDirectory( const RsGxsGroupId& channelId, - std::string& directory ) = 0; --------------------------------------------------------------------------------- - -For each paramether you must specify if it is used as input +@param[in]+ as -output +@param[out]+ or both +@param[inout]+. Paramethers and return value -types must be of a type supported by +RsTypeSerializer+ which already support -most basic types (+bool+, +std::string+...), +RsSerializable+ and containers of -them like +std::vector+. Paramethers passed by value and by -reference of those types are both supported, while passing by pointer is not -supported. If your paramether or return +class+/+struct+ type is not supported -yet by +RsTypeSerializer+ most convenient approach is to make it derive from -+RsSerializable+ and implement +serial_process+ method like I did with -+RsGxsChannelGroup+. - -.Deriving RsGxsChannelGroup from RsSerializable in rsgxschannels.h -[source,cpp] --------------------------------------------------------------------------------- -struct RsGxsChannelGroup : RsSerializable -{ - RsGroupMetaData mMeta; - std::string mDescription; - RsGxsImage mImage; - - bool mAutoDownload; - - /// @see RsSerializable - virtual void serial_process( RsGenericSerializer::SerializeJob j, - RsGenericSerializer::SerializeContext& ctx ) - { - RS_SERIAL_PROCESS(mMeta); - RS_SERIAL_PROCESS(mDescription); - RS_SERIAL_PROCESS(mImage); - RS_SERIAL_PROCESS(mAutoDownload); - } -}; --------------------------------------------------------------------------------- - -You can do the same recursively for any member of your +struct+ that is not yet -supported by +RsTypeSerializer+. - -Some Retroshare {Cxx} API functions are asyncronous, historically RetroShare -didn't follow a policy on how to expose asyncronous API so differents services -and some times even differents method of the same service follow differents -asyncronous patterns, thus making automatic generation of JSON API wrappers for -those methods impractical. Instead of dealing with all those differents patterns -I have chosed to support only one new pattern taking advantage of modern {Cxx}11 -and restbed features. On the {Cxx}11 side lambdas and +std::function+s are used, -on the restbed side Server Side Events are used to send asyncronous results. - -Lets see an example so it will be much esier to understand. - -.RsGxsChannels::turtleSearchRequest asyncronous API -[source,cpp] --------------------------------------------------------------------------------- - /** - * @brief Request remote channels search - * @jsonapi{development} - * @param[in] matchString string to look for in the search - * @param multiCallback function that will be called each time a search - * result is received - * @param[in] maxWait maximum wait time in seconds for search results - * @return false on error, true otherwise - */ - virtual bool turtleSearchRequest( - const std::string& matchString, - const std::function& multiCallback, - std::time_t maxWait = 300 ) = 0; --------------------------------------------------------------------------------- - -+RsGxsChannels::turtleSearchRequest(...)+ is an asyncronous method because it -send a channel search request on turtle network and then everytime a result is -received from the network +multiCallback+ is called and the result is passed as -parameter. To be supported by the automatic JSON API wrappers generator an -asyncronous method need a parameter of type +std::function+ called -+callback+ if the callback will be called only once or +multiCallback+ if the -callback is expected to be called more then once like in this case. -A second mandatory parameter is +maxWait+ of type +std::time_t+ it indicates the -maximum amount of time in seconds for which the caller is willing to wait for -results, in case the timeout is reached the callback will not be called anymore. - -[IMPORTANT] -================================================================================ -+callback+ and +multiCallback+ parameters documentation must *not* specify -+[in]+, +[out]+, +[inout]+, in Doxygen documentation as this would fool the -automatic wrapper generator, and ultimately break the compilation. -================================================================================ - -.RsFiles::turtleSearchRequest asyncronous JSON API usage example -[source,bash] --------------------------------------------------------------------------------- -$ cat turtle_search.json -{ - "matchString":"linux" -} -$ curl --data @turtle_search.json http://127.0.0.1:9092/rsFiles/turtleSearchRequest -data: {"retval":true} - -data: {"results":[{"size":157631,"hash":"69709b4d01025584a8def5cd78ebbd1a3cf3fd05","name":"kill_bill_linux_1024x768.jpg"},{"size":192560,"hash":"000000000000000000009a93e5be8486c496f46c","name":"coffee_box_linux2.jpg"},{"size":455087,"hash":"9a93e5be8486c496f46c00000000000000000000","name":"Linux.png"},{"size":182004,"hash":"e8845280912ebf3779e400000000000000000000","name":"Linux_2_6.png"}]} - -data: {"results":[{"size":668,"hash":"e8845280912ebf3779e400000000000000000000","name":"linux.png"},{"size":70,"hash":"e8845280912ebf3779e400000000000000000000","name":"kali-linux-2016.2-amd64.txt.sha1sum"},{"size":3076767744,"hash":"e8845280912ebf3779e400000000000000000000","name":"kali-linux-2016.2-amd64.iso"},{"size":2780872,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.bin"},{"size":917504,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.lzma"},{"size":2278404096,"hash":"e8845280912ebf3779e400000000000000000000","name":"gentoo-linux-livedvd-amd64-multilib-20160704.iso"},{"size":151770333,"hash":"e8845280912ebf3779e400000000000000000000","name":"flashtool-0.9.23.0-linux.tar.7z"},{"size":2847372,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.elf"},{"size":1310720,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.gz"},{"size":987809,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux-lzma.elf"}]} - --------------------------------------------------------------------------------- - -By default JSON API methods requires client authentication and their wrappers -are automatically generated by +json-api-generator+. -In some cases methods need do be accessible without authentication such as -+rsLoginHelper/getLocations+ so in the doxygen documentaion they have the custom -command +@jsonapi{RS_VERSION,unauthenticated}+. -Other methods such as +/rsControl/rsGlobalShutDown+ need special care so they -are marked with the custom doxygen command +@jsonapi{RS_VERSION,manualwrapper}+ -and their wrappers are not automatically generated but written manually into -+JsonApiServer::JsonApiServer(...)+. - -== Quirks - -=== 64 bits integers handling - -While JSON doesn't have problems representing 64 bits integers JavaScript, Dart -and other languages are not capable to handle those numbers natively. -To overcome this limitation JSON API output 64 bit integers as an object with -two keys, one as proper integer and one as string representation. - -.JSON API 64 bit integer output example -[source,json] --------------------------------------------------------------------------------- -"lobby_id": { "xint64": 6215642878098695544, "xstr64": "6215642878098695544" } --------------------------------------------------------------------------------- - -So from languages that have proper 64bit integers support like Python or C++ one -better read from `xint64` which is represented as a JSON integer, from languages -where there is no proper 64bit integers support like JavaScript one can read from -`xstr64` which is represented as JSON string (note that the first is not wrapped -in "" while the latter is). - -When one input a 64bit integer into the JSON API it first try to parse it as if -it was sent the old way for retrocompatibility. - -.JSON API 64 bit integer deprecated format input example -[source,json] --------------------------------------------------------------------------------- -"lobby_id":6215642878098695544 --------------------------------------------------------------------------------- - -This way is *DEPRECATED* and may disappear in the future, it is TEMPORALLY kept -only for retrocompatibiliy with old clients. - -If retrocompatible parsing attempt fail then it try to parse with the new way -with proper JSON integer format. - -.JSON API 64 bit integer new proper integer format input example -[source,json] --------------------------------------------------------------------------------- -lobby_id": { "xint64": 6215642878098695544 } --------------------------------------------------------------------------------- - -If this fails then it try to parse with the new way with JSON string format. - -.JSON API 64 bit integer new string format input example -[source,json] --------------------------------------------------------------------------------- -"lobby_id": { "xstr64": "6215642878098695544" } --------------------------------------------------------------------------------- - -[WARNING] -================================================================================ -Clients written in languages without proper 64bit integers support must -use *ONLY* the string format otherwise they will send approximated values and -get unexpected results from the JSON API, because parsing will success but the -value will not be exactly the one you believe you sent. -================================================================================ - - -== A bit of history - -=== First writings about this - -The previous attempt of exposing a RetroShare JSON API is called +libresapi+ and -unfortunatley it requires a bunch of boilerplate code when we want to expose -something present in the {Cxx} API in the JSON API. - -As an example here you can see the libresapi that exposes part of the retroshare -chat {Cxx} API and lot of boilerplate code just to convert {Cxx} objects to JSON - -https://github.com/RetroShare/RetroShare/blob/v0.6.4/libresapi/src/api/ChatHandler.cpp#L44 - -To avoid the {Cxx} to JSON and back conversion boilerplate code I have worked out -an extension to our {Cxx} serialization code so it is capable to serialize and -deserialize to JSON you can see it in this pull request - -https://github.com/RetroShare/RetroShare/pull/1155 - -So first step toward having a good API is to take advantage of the fact that RS -is now capable of converting C++ objects from and to JSON. - -The current API is accessible via HTTP and unix socket, there is no -authentication in both of them, so anyone having access to the HTTP server or to -the unix socket can access the API without extra restrictions. -Expecially for the HTTP API this is a big risk because also if the http server -listen on 127.0.0.1 every application on the machine (even rogue javascript -running on your web browser) can access that and for example on android it is -not safe at all (because of that I implemented the unix socket access so at -least in android API was reasonably safe) because of this. - -A second step to improve the API would be to implement some kind of API -authentication mechanism (it would be nice that the mechanism is handled at API -level and not at transport level so we can use it for any API trasport not just -HTTP for example) - -The HTTP server used by libresapi is libmicrohttpd server that is very minimal, -it doesn't provide HTTPS nor modern HTTP goodies, like server notifications, -websockets etc. because the lack of support we have a token polling mechanism in -libresapi to avoid polling for every thing but it is still ugly, so if we can -completely get rid of polling in the API that would be really nice. -I have done a crawl to look for a replacement and briefly looked at - -- https://www.gnu.org/software/libmicrohttpd/ -- http://wolkykim.github.io/libasyncd/ -- https://github.com/corvusoft/restbed -- https://code.facebook.com/posts/1503205539947302/introducing-proxygen-facebook-s-c-http-framework/ -- https://github.com/cmouse/yahttp - -taking in account a few metrics like modern HTTP goodies support, license, -platform support, external dependencies and documentation it seemed to me that -restbed is the more appropriate. - -Another source of boilerplate code into libresapi is the mapping between JSON -API requests and C++ API methods as an example you can look at this - -https://github.com/RetroShare/RetroShare/blob/v0.6.4/libresapi/src/api/ChatHandler.cpp#L158 - -and this - -https://github.com/RetroShare/RetroShare/blob/v0.6.4/libresapi/src/api/ApiServer.cpp#L253 - -The abstract logic of this thing is, when libreasapi get a request like -+/chat/initiate_distant_chat+ then call -+ChatHandler::handleInitiateDistantChatConnexion+ which in turn is just a -wrapper of +RsMsgs::initiateDistantChatConnexion+ all this process is basically -implemented as boilerplate code and would be unnecessary in a smarter design of -the API because almost all the information needed is already present in the -C++ API +libretroshare/src/retroshare+. - -So a third step to improve the JSON API would be to remove this source of -boilerplate code by automatizing the mapping between C++ and JSON API call. - -This may result a little tricky as language parsing or other adevanced things -may be required. - -Hope this dive is useful for you + -Cheers + -G10h4ck - -=== Second writings about this - -I have been investigating a bit more about: -[verse, G10h4ck] -________________________________________________________________________________ -So a third step to improve the JSON API would be to remove this source of -boilerplate code by automatizing the mapping between C++ and JSON API call -________________________________________________________________________________ - -After spending some hours investigating this topic the most reasonable approach -seems to: - -1. Properly document headers in +libretroshare/src/retroshare/+ in doxygen syntax -specifying wihich params are input and/or output (doxygen sysntax for this is -+@param[in/out/inout]+) this will be the API documentation too. - -2. At compile time use doxygen to generate XML description of the headers and use -the XML to generate the JSON api server stub. -http://www.stack.nl/~dimitri/doxygen/manual/customize.html#xmlgenerator - -3. Enjoy +This directory and all it's content is kept as is only for retro-compatibility +with the old, deprecated `qmake` build system. diff --git a/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl b/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl deleted file mode 100644 index 29f53ad48..000000000 --- a/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * RetroShare JSON API * - * * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License version 3 as * - * published by the Free Software Foundation. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -registerHandler( "$%apiPath%$", - [this](const std::shared_ptr session) -{ - const std::multimap headers - { - { "Connection", "keep-alive" }, - { "Content-Type", "text/event-stream" } - }; - session->yield(rb::OK, headers); - - size_t reqSize = session->get_request()->get_header("Content-Length", 0); - session->fetch( reqSize, [this]( - const std::shared_ptr session, - const rb::Bytes& body ) - { - INITIALIZE_API_CALL_JSON_CONTEXT; - - if( !checkRsServicePtrReady( - $%instanceName%$, "$%instanceName%$", cAns, session ) ) - return; - -$%paramsDeclaration%$ - -$%inputParamsDeserialization%$ - - const std::weak_ptr weakService(mService); - const std::weak_ptr weakSession(session); - $%callbackName%$ = [weakService, weakSession]($%callbackParams%$) - { - auto session = weakSession.lock(); - if(!session || session->is_closed()) return; - - auto lService = weakService.lock(); - if(!lService || lService->is_down()) return; - -$%callbackParamsSerialization%$ - - std::stringstream sStream; - sStream << "data: " << compactJSON << ctx.mJson << "\n\n"; - const std::string message = sStream.str(); - - lService->schedule( [weakSession, message]() - { - auto session = weakSession.lock(); - if(!session || session->is_closed()) return; - session->yield(message); - $%sessionEarlyClose%$ - } ); - }; - -$%functionCall%$ - -$%outputParamsSerialization%$ - - // return them to the API caller - std::stringstream message; - message << "data: " << compactJSON << cAns.mJson << "\n\n"; - session->yield(message.str()); - $%sessionDelayedClose%$ - } ); -}, $%requiresAuth%$ ); diff --git a/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl b/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl new file mode 120000 index 000000000..1ae671997 --- /dev/null +++ b/jsonapi-generator/src/async-method-wrapper-template.cpp.tmpl @@ -0,0 +1 @@ +../../libretroshare/src/jsonapi/async-method-wrapper-template.cpp.tmpl \ No newline at end of file diff --git a/jsonapi-generator/src/jsonapi-generator-doxygen.conf b/jsonapi-generator/src/jsonapi-generator-doxygen.conf deleted file mode 100644 index 0edd1de3e..000000000 --- a/jsonapi-generator/src/jsonapi-generator-doxygen.conf +++ /dev/null @@ -1,230 +0,0 @@ -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = "libretroshare" - -ALIASES += jsonapi{1}="\xmlonly\endxmlonly" -ALIASES += jsonapi{2}="\xmlonly\endxmlonly" - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by putting a % sign in front of the word or -# globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - - -# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = YES - - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO, these classes will be included in the various overviews. This option -# has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = NO - - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING -# Note: If this tag is empty the current directory is searched. - -#INPUT = - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. -# -# Note that for custom extensions or not directly supported extensions you also -# need to set EXTENSION_MAPPING for the extension otherwise the files are not -# read by doxygen. -# -# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, -# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, -# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. - -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.idl \ - *.ddl \ - *.odl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.cs \ - *.d \ - *.php \ - *.php4 \ - *.php5 \ - *.phtml \ - *.inc \ - *.m \ - *.markdown \ - *.md \ - *.mm \ - *.dox \ - *.py \ - *.pyw \ - *.f90 \ - *.f95 \ - *.f03 \ - *.f08 \ - *.f \ - *.for \ - *.tcl \ - *.vhd \ - *.vhdl \ - *.ucf \ - *.qsf - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = NO - -# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. -# The default value is: YES. - -GENERATE_LATEX = NO - -# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that -# captures the structure of the code including all documentation. -# The default value is: NO. - -GENERATE_XML = YES - - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all -# C-preprocessor directives found in the sources and include files. -# The default value is: YES. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names -# in the source code. If set to NO, only conditional compilation will be -# performed. Macro expansion can be done in a controlled way by setting -# EXPAND_ONLY_PREDEF to YES. -# The default value is: NO. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then -# the macro expansion is limited to the macros specified with the PREDEFINED and -# EXPAND_AS_DEFINED tags. -# The default value is: NO. -# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. - -EXPAND_ONLY_PREDEF = NO - diff --git a/jsonapi-generator/src/jsonapi-generator-doxygen.conf b/jsonapi-generator/src/jsonapi-generator-doxygen.conf new file mode 120000 index 000000000..9aff33c39 --- /dev/null +++ b/jsonapi-generator/src/jsonapi-generator-doxygen.conf @@ -0,0 +1 @@ +../../libretroshare/src/jsonapi/jsonapi-generator-doxygen.conf \ No newline at end of file diff --git a/jsonapi-generator/src/method-wrapper-template.cpp.tmpl b/jsonapi-generator/src/method-wrapper-template.cpp.tmpl deleted file mode 100644 index a3927fba9..000000000 --- a/jsonapi-generator/src/method-wrapper-template.cpp.tmpl +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * RetroShare JSON API * - * * - * Copyright (C) 2018-2019 Gioacchino Mazzurco * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with this program. If not, see . * - * * - *******************************************************************************/ - -registerHandler( "$%apiPath%$", - [](const std::shared_ptr session) -{ - size_t reqSize = session->get_request()->get_header("Content-Length", 0); - session->fetch( reqSize, []( - const std::shared_ptr session, - const rb::Bytes& body ) - { - INITIALIZE_API_CALL_JSON_CONTEXT; - - if( !checkRsServicePtrReady( - $%instanceName%$, "$%instanceName%$", cAns, session ) ) - return; - -$%paramsDeclaration%$ - - // deserialize input parameters from JSON -$%inputParamsDeserialization%$ - - // call retroshare C++ API -$%functionCall%$ - - // serialize out parameters and return value to JSON -$%outputParamsSerialization%$ - - // return them to the API caller - DEFAULT_API_CALL_JSON_RETURN(rb::OK); - } ); -}, $%requiresAuth%$ ); - diff --git a/jsonapi-generator/src/method-wrapper-template.cpp.tmpl b/jsonapi-generator/src/method-wrapper-template.cpp.tmpl new file mode 120000 index 000000000..717044ed2 --- /dev/null +++ b/jsonapi-generator/src/method-wrapper-template.cpp.tmpl @@ -0,0 +1 @@ +../../libretroshare/src/jsonapi/method-wrapper-template.cpp.tmpl \ No newline at end of file diff --git a/libbitdht/CMakeLists.txt b/libbitdht/CMakeLists.txt new file mode 100644 index 000000000..af352651c --- /dev/null +++ b/libbitdht/CMakeLists.txt @@ -0,0 +1,19 @@ +# RetroShare decentralized communication platform +# +# Copyright (C) 2021 Gioacchino Mazzurco +# Copyright (C) 2021 Asociación Civil Altermundi +# +# SPDX-License-Identifier: CC0-1.0 + +cmake_minimum_required (VERSION 2.8.12) +project(bitdht) + +file( + GLOB BITDHT_SOURCES + src/bitdht/*.c src/bitdht/*.cc src/udp/*.cc src/util/*.cc ) + +add_library(${PROJECT_NAME} ${BITDHT_SOURCES}) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/libretroshare/CMakeLists.txt b/libretroshare/CMakeLists.txt new file mode 100644 index 000000000..93c8b04a5 --- /dev/null +++ b/libretroshare/CMakeLists.txt @@ -0,0 +1,411 @@ +# RetroShare decentralized communication platform +# +# Copyright (C) 2021 Gioacchino Mazzurco +# Copyright (C) 2021 Asociación Civil Altermundi +# +# SPDX-License-Identifier: CC0-1.0 + +cmake_minimum_required (VERSION 3.18.0) +project(retroshare) + +include(CMakeDependentOption) + +set(FETCHCONTENT_QUIET OFF) +include(FetchContent) + +# sqlcipher +option( + RS_SQLCIPHER + "SQLCipher encryption for GXS database" + ON ) + +# rs_gxs_send_all +option( + RS_GXS_SEND_ALL + "GXS distribute all available messages on request, indipendently from \ + local sync timer" + ON ) + +# bitdht +option( + RS_BITDHT + "Use bitdht (BitTorrent DHT own implementation) to look for online peers" + ON ) + +# use_dht_stunner +cmake_dependent_option( + RS_BITDHT_STUNNER + "Use bitdht (BitTorrent DHT own implementation) for NAT type discovery and \ + attempt the STUN (Session Traversal Utilities for NAT)" + ON + "RS_BITDHT" + OFF ) + +# use_dht_stunner_ext_ip +cmake_dependent_option( + RS_BITDHT_STUNNER_EXT_IP + "Use bitdht (BitTorrent DHT own implementation) stunner to figure out our \ + external IP. As this purely relying on random DHT peers that answer our \ + request, it can easily be abused. Therefore, it is turned off by default." + OFF + "RS_BITDHT_STUNNER" + OFF ) + +# rs_jsonapi +option( + RS_JSON_API + "Use restbed to expose libretroshare as JSON API via HTTP" + OFF ) + +# rs_deep_forums_index +option( + RS_FORUM_DEEP_INDEX + "Xapian based full text index and search of GXS forums" + OFF ) + +# rs_broadcast_discovery +option( + RS_BRODCAST_DISCOVERY + "Local area network peer discovery via udp-discovery-cpp" + ON ) + +# rs_dh_init_check +option( + RS_DH_PRIME_INIT_CHECK + "Check Diffie Hellman prime at each startup. This is not necessary and on \ + all Android mobile phones tested this take at least one minute at startup \ + which is untolerable for most phone users." + ON ) + +option( + RS_MINIUPNPC + "Forward ports in NAT router via miniupnpc" + ON ) + +cmake_dependent_option( + RS_LIBUPNP + "Forward ports in NAT router via libupnp (unstable)" + OFF + "NOT RS_MINIUPNPC" + OFF ) + +option( + RS_LIBRETROSHARE_STATIC + "Build RetroShare static library" + ON ) + +cmake_dependent_option( + RS_LIBRETROSHARE_SHARED + "Build RetroShare shared library" + OFF + "NOT RS_LIBRETROSHARE_STATIC" + OFF ) + +# rs_deprecatedwarning +option( + RS_WARN_DEPRECATED + "Print warning about RetroShare deprecated components usage during build" + ON ) + +# rs_cppwarning +option( + RS_WARN_LESS + "Silence a few at the moment very common warnings about RetroShare \ + components during build" + OFF ) + +# rs_v07_changes +option( + RS_V07_BREAKING_CHANGES + "Enable retro-compatibility breaking changes planned for RetroShare 0.7.0" + OFF ) + +set( + RS_DATA_DIR + "${CMAKE_INSTALL_PREFIX}/share/retroshare" + CACHE STRING + "Path where to install RetroShare system wide data" ) + +################################################################################ + +find_package(Git REQUIRED) + +#function(check_submodule sPath) +# if(NOT EXISTS "${sPath}/.git" ) +# message("Initializing submodule ${sPath}") +# execute_process( +# COMMAND "${GIT_EXECUTABLE}" submodule update --init +# WORKING_DIRECTORY "${sPath}" +# COMMAND_ECHO STDERR +# COMMAND_ERROR_IS_FATAL ANY) +# endif() +#endfunction() + +################################################################################ + +include(src/CMakeLists.txt) +list(TRANSFORM RS_SOURCES PREPEND src/) +list(TRANSFORM RS_PUBLIC_HEADERS PREPEND src/) + +if(RS_LIBRETROSHARE_STATIC) + add_library(${PROJECT_NAME} STATIC ${RS_SOURCES}) +endif(RS_LIBRETROSHARE_STATIC) + +if(RS_LIBRETROSHARE_SHARED) + add_library(${PROJECT_NAME} SHARED ${RS_SOURCES}) + + ## Ensure statically linked libraries such as openpgpsdk are compiled with + ## PIC Which is needed for shared library + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif(RS_LIBRETROSHARE_SHARED) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src ) + + +find_package(OpenSSL REQUIRED) +target_include_directories(${PROJECT_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR}) +target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto) + +################################################################################ + +set(OPENPGPSDK_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../openpgpsdk/") +if(EXISTS "${OPENPGPSDK_DEVEL_DIR}/.git" ) + message( + CHECK_PASS + "openpgpsdk submodule found at ${OPENPGPSDK_DEVEL_DIR} using it" ) + add_subdirectory(${OPENPGPSDK_DEVEL_DIR} ${CMAKE_BINARY_DIR}/openpgpsdk) +else() + FetchContent_Declare( + openpgpsdk + GIT_REPOSITORY "https://gitlab.com/RetroShare/openpgpsdk.git" + GIT_TAG "origin/master" + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + TIMEOUT 10 + ) + FetchContent_MakeAvailable(openpgpsdk) +endif() + +target_link_libraries(${PROJECT_NAME} PRIVATE openpgpsdk) + +################################################################################ + +if(RS_BITDHT) + set(BITDHT_DEVEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../libbitdht/") + if(EXISTS "${BITDHT_DEVEL_DIR}/.git" ) + message( + CHECK_PASS + "BitDHT submodule found at ${BITDHT_DEVEL_DIR} using it" ) + add_subdirectory(${BITDHT_DEVEL_DIR} ${CMAKE_BINARY_DIR}/bitdht) + else() + FetchContent_Declare( + bitdht + GIT_REPOSITORY "https://gitlab.com/RetroShare/bitdht.git" + GIT_TAG "origin/master" + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + TIMEOUT 10 + ) + FetchContent_MakeAvailable(bitdht) + endif() + + add_compile_definitions(RS_USE_BITDHT) + target_link_libraries(${PROJECT_NAME} PRIVATE bitdht) + + if(RS_BITDHT_STUNNER) + add_compile_definitions(RS_USE_DHT_STUNNER) + + if(RS_BITDHT_STUNNER_EXT_IP) + # TODO: Refactor this define to use proper naming + add_compile_definitions(ALLOW_DHT_STUNNER) + endif(RS_BITDHT_STUNNER_EXT_IP) + endif(RS_BITDHT_STUNNER) +endif(RS_BITDHT) + +################################################################################ + +if(RS_JSON_API) + find_package(Doxygen REQUIRED) + find_package(Python3 REQUIRED) + + ## TODO: execute at build time instead that at cofiguration time see + ## add_custom_command or add_custom_target + + set( + JSON_API_GENERATOR_WORK_DIR + "${CMAKE_BINARY_DIR}/jsonapi-generator.workdir/" ) + + set( + JSON_API_GENERATOR_DOXYFILE + "${JSON_API_GENERATOR_WORK_DIR}/jsonapi-generator-doxygen.conf" ) + + set( + JSONAPI_GENERATOR_OUTPUT_DIR + "${JSON_API_GENERATOR_WORK_DIR}/src/" ) + + set( + JSONAPI_GENERATOR_SOURCE_DIR + "${CMAKE_CURRENT_SOURCE_DIR}/src/jsonapi/" ) + + set( + JSONAPI_GENERATOR_EXECUTABLE + "${JSONAPI_GENERATOR_SOURCE_DIR}/jsonapi-generator.py" ) + + file( + COPY "src/jsonapi/jsonapi-generator-doxygen.conf" + DESTINATION "${JSON_API_GENERATOR_WORK_DIR}" ) + + file( + APPEND + "${JSON_API_GENERATOR_DOXYFILE}" + "OUTPUT_DIRECTORY=${JSONAPI_GENERATOR_OUTPUT_DIR}\n" + "INPUT=${CMAKE_CURRENT_SOURCE_DIR}" ) + + add_custom_command( + OUTPUT + "${JSONAPI_GENERATOR_OUTPUT_DIR}/jsonapi-includes.inl" + "${JSONAPI_GENERATOR_OUTPUT_DIR}/jsonapi-wrappers.inl" + COMMAND ${DOXYGEN_EXECUTABLE} ${JSON_API_GENERATOR_DOXYFILE} + COMMAND + ${Python3_EXECUTABLE} ${JSONAPI_GENERATOR_EXECUTABLE} + ${JSONAPI_GENERATOR_SOURCE_DIR} ${JSONAPI_GENERATOR_OUTPUT_DIR} + MAIN_DEPENDENCY "${JSONAPI_GENERATOR_EXECUTABLE}" + DEPENDS ${JSON_API_GENERATOR_DOXYFILE} ${RS_PUBLIC_HEADERS} ) + + target_sources( + ${PROJECT_NAME} PRIVATE + "${JSONAPI_GENERATOR_OUTPUT_DIR}/jsonapi-includes.inl" + "${JSONAPI_GENERATOR_OUTPUT_DIR}/jsonapi-wrappers.inl" ) + + include_directories(${JSONAPI_GENERATOR_OUTPUT_DIR}) + + set(BUILD_TESTS OFF CACHE BOOL "Do not build restbed tests") + set(BUILD_SSL OFF CACHE BOOL "Do not build restbed SSL support") + + FetchContent_Declare( + restbed + GIT_REPOSITORY "https://github.com/Corvusoft/restbed.git" + GIT_TAG "4.8" + GIT_SUBMODULES dependency/asio dependency/catch + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + TIMEOUT 10 + ) + FetchContent_MakeAvailable(restbed) + + target_link_libraries(${PROJECT_NAME} PRIVATE restbed) + + ## TODO: work around target_include_directories should be added upstream + include_directories(${restbed_SOURCE_DIR}/source/) + + add_compile_definitions(RS_JSONAPI) +endif(RS_JSON_API) + +################################################################################ + +if(RS_FORUM_DEEP_INDEX) + find_package(Xapian REQUIRED) + target_link_libraries(${PROJECT_NAME} PRIVATE ${XAPIAN_LIBRARIES}) + + add_compile_definitions(RS_DEEP_FORUMS_INDEX) +endif(RS_FORUM_DEEP_INDEX) + +################################################################################ + +## TODO: Check if https://github.com/rbock/sqlpp11 or +## https://github.com/rbock/sqlpp17 may improve GXS code +if(RS_SQLCIPHER) + find_library(RS_SQL_LIB "sqlcipher" REQUIRED) + find_path( + RS_SQL_LIB_INCLUDE "sqlcipher/sqlite3.h" + PATH_SUFFIXES "include" "includes" + REQUIRED ) + target_include_directories( + ${PROJECT_NAME} + PRIVATE "${RS_SQL_LIB_INCLUDE}/sqlcipher" ) + target_link_libraries(${PROJECT_NAME} PRIVATE ${RS_SQL_LIB}) +else() + add_compile_definitions(NO_SQLCIPHER) + find_package(SQLite3 REQUIRED) + target_link_libraries(${PROJECT_NAME} PRIVATE SQLite::SQLite3) +endif() + +add_compile_definitions( + SQLITE_HAS_CODEC + RS_ENABLE_GXS + GXS_ENABLE_SYNC_MSGS + RS_USE_GXS_DISTANT_SYNC + RS_GXS_TRANS + V07_NON_BACKWARD_COMPATIBLE_CHANGE_001 + V07_NON_BACKWARD_COMPATIBLE_CHANGE_002 + V07_NON_BACKWARD_COMPATIBLE_CHANGE_003 ) + +if(RS_V07_BREAKING_CHANGES) + add_compile_definitions( + V07_NON_BACKWARD_COMPATIBLE_CHANGE_004 + V07_NON_BACKWARD_COMPATIBLE_CHANGE_UNNAMED ) +endif() + +if(RS_DH_PRIME_INIT_CHECK) + add_compile_definitions(RS_DISABLE_DIFFIE_HELLMAN_INIT_CHECK) +endif(RS_DH_PRIME_INIT_CHECK) + +if(RS_MINIUPNPC) + add_compile_definitions(RS_USE_LIBMINIUPNPC) +endif(RS_MINIUPNPC) + +if(RS_LIBUPNP) + message(FATAL_ERROR "UPnP support via libupnp is currently not supported") + #add_compile_definitions(RS_USE_LIBUPNP) +endif(RS_LIBUPNP) + +if(RS_GXS_SEND_ALL) + add_compile_definitions(RS_GXS_SEND_ALL) +endif(RS_GXS_SEND_ALL) + +if(RS_BRODCAST_DISCOVERY) + ## TODO: upstream option to disable tests building + set(BUILD_EXAMPLE OFF CACHE BOOL "Do not build udp-discovery-cpp examples") + set(BUILD_TOOL OFF CACHE BOOL "Do not build udp-discovery-tool application") + FetchContent_Declare( + udp-discovery-cpp + GIT_REPOSITORY "https://github.com/truvorskameikin/udp-discovery-cpp.git" + GIT_TAG "origin/master" + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + TIMEOUT 10 + ) + FetchContent_MakeAvailable(udp-discovery-cpp) + + target_link_libraries(${PROJECT_NAME} PRIVATE udp-discovery-cpp) + + ## TODO: Temporary work around target_include_directories should be added + ## upstream + include_directories(${udp-discovery-cpp_SOURCE_DIR}) +endif(RS_BRODCAST_DISCOVERY) + +if(NOT RS_WARN_DEPRECATED) + add_compile_definitions(RS_NO_WARN_DEPRECATED) + target_compile_options( + ${PROJECT_NAME} PRIVATE + -Wno-deprecated -Wno-deprecated-declarations ) +endif(NOT RS_WARN_DEPRECATED) + +if(RS_WARN_LESS) + add_compile_definitions(RS_NO_WARN_CPP) + + target_compile_options( + ${PROJECT_NAME} PRIVATE + -Wno-cpp -Wno-inconsistent-missing-override ) +endif(RS_WARN_LESS) + +################################################################################ + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_compile_definitions(RS_DATA_DIR="${RS_DATA_DIR}") +endif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + +## Useful to debug CMake +#set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/libretroshare/src/CMakeLists.txt b/libretroshare/src/CMakeLists.txt new file mode 100644 index 000000000..c33799d7c --- /dev/null +++ b/libretroshare/src/CMakeLists.txt @@ -0,0 +1,409 @@ +# RetroShare decentralized communication platform +# +# Copyright (C) 2021 Gioacchino Mazzurco +# Copyright (C) 2021 Asociación Civil Altermundi +# +# SPDX-License-Identifier: CC0-1.0 + +list( + APPEND RS_PUBLIC_HEADERS + retroshare/rsexpr.h + retroshare/rsgxsdistsync.h + retroshare/rsiface.h + retroshare/rsrtt.h + retroshare/rsbanlist.h + retroshare/rsconfig.h + retroshare/rsdisc.h + retroshare/rsflags.h + retroshare/rsgrouter.h + retroshare/rsgxsflags.h + retroshare/rsgxsservice.h + retroshare/rsgxstrans.h + retroshare/rsgxstunnel.h + retroshare/rsids.h + retroshare/rsnotify.h + retroshare/rsphoto.h + retroshare/rsplugin.h + retroshare/rsreputations.h + retroshare/rsservicecontrol.h + retroshare/rstokenservice.h + retroshare/rsturtle.h + retroshare/rsgossipdiscovery.h + retroshare/rsgxscommon.h + retroshare/rsposted.h + retroshare/rsstatus.h + retroshare/rsversion.h + retroshare/rsgxsifacehelper.h + retroshare/rshistory.h + retroshare/rsidentity.h + retroshare/rsmsgs.h + retroshare/rsgxschannels.h + retroshare/rsgxscircles.h + retroshare/rsgxsiface.h + retroshare/rsgxsifacetypes.h + retroshare/rstypes.h + retroshare/rsgxsforums.h + retroshare/rsevents.h + retroshare/rsfiles.h + retroshare/rsinit.h + retroshare/rspeers.h ) + +list( + APPEND RS_SOURCES + chat/distantchat.cc + chat/p3chatservice.cc + chat/rschatitems.cc + chat/distributedchat.cc + crypto/chacha20.cpp + crypto/hashstream.cc + crypto/rsaes.cc + crypto/rscrypto.cpp ) + +if(RS_BITDHT) + list( + APPEND RS_PUBLIC_HEADERS + retroshare/rsdht.h ) + + list( + APPEND RS_SOURCES + dht/connectstatebox.cc + dht/p3bitdht.cc + dht/p3bitdht_interface.cc + dht/p3bitdht_peernet.cc + dht/p3bitdht_peers.cc + dht/p3bitdht_relay.cc ) +endif(RS_BITDHT) + +list( + APPEND RS_SOURCES + file_sharing/filelist_io.cc + file_sharing/rsfilelistitems.cc + file_sharing/file_tree.cc + file_sharing/directory_updater.cc + file_sharing/p3filelists.cc + file_sharing/hash_cache.cc + file_sharing/dir_hierarchy.cc + file_sharing/directory_storage.cc + ft/ftchunkmap.cc + ft/ftfilecreator.cc + ft/ftfileprovider.cc + ft/ftfilesearch.cc + ft/ftturtlefiletransferitem.cc + ft/fttransfermodule.cc + ft/ftcontroller.cc + ft/ftdatamultiplex.cc + ft/ftextralist.cc + ft/ftserver.cc ) + +list( + APPEND RS_SOURCES + grouter/groutermatrix.cc + grouter/grouteritems.cc + grouter/p3grouter.cc ) + +list( + APPEND RS_SOURCES + gxs/rsgxsdata.cc + gxs/rsgxsrequesttypes.cc + gxs/gxssecurity.cc + gxs/gxstokenqueue.cc + gxs/rsdataservice.cc + gxs/rsgxsdataaccess.cc + gxs/rsgxsnetutils.cc + gxs/rsgxsnettunnel.cc + gxs/rsgxsutil.cc + gxs/rsnxsobserver.cpp + gxs/rsgenexchange.cc + gxs/rsgxsnetservice.cc ) + +list( + APPEND RS_SOURCES + gxstrans/p3gxstransitems.cc + gxstrans/p3gxstrans.cc ) + +list( + APPEND RS_SOURCES + gxstunnel/rsgxstunnelitems.cc + gxstunnel/p3gxstunnel.cc ) + +if(RS_JSON_API) + list( + APPEND RS_PUBLIC_HEADERS + retroshare/rsjsonapi.h ) + + list( + APPEND RS_SOURCES + jsonapi/jsonapi.cpp ) +endif(RS_JSON_API) + +list( + APPEND RS_SOURCES + pgp/pgpkeyutil.cc + pgp/pgpauxutils.cc + pgp/pgphandler.cc + pgp/rscertificate.cc ) + +#./plugins/dlfcn_win32.cc +#./plugins/dlfcn_win32.h +#./plugins/pluginmanager.h +#./plugins/rscacheservice.h +#./plugins/rspqiservice.h +#./plugins/pluginmanager.cc + +list( + APPEND RS_SOURCES + pqi/pqibin.cc + pqi/pqiipset.cc + pqi/pqiloopback.cc + pqi/pqimonitor.cc + pqi/pqipersongrp.cc + pqi/pqiqos.cc + pqi/pqiqosstreamer.cc + pqi/pqisslproxy.cc + pqi/pqistore.cc + pqi/authgpg.cc + pqi/p3cfgmgr.cc + pqi/p3notify.cc + pqi/p3servicecontrol.cc + pqi/pqinetstatebox.cc + pqi/pqiperson.cc + pqi/pqiservice.cc + pqi/pqissllistener.cc + pqi/pqissludp.cc + pqi/pqithreadstreamer.cc + pqi/sslfns.cc + pqi/authssl.cc + pqi/p3historymgr.cc + pqi/p3linkmgr.cc + pqi/pqihandler.cc + pqi/pqistreamer.cc + pqi/p3netmgr.cc + pqi/p3peermgr.cc + pqi/pqinetwork.cc + pqi/pqissl.cc + pqi/pqisslpersongrp.cc ) + +#./pqi/pqissli2psam3.cpp +#./pqi/pqissli2psam3.h + +list( + APPEND RS_SOURCES + rsitems/rsbanlistitems.cc + rsitems/rsbwctrlitems.cc + rsitems/rsconfigitems.cc + rsitems/rsfiletransferitems.cc + rsitems/rsgxscommentitems.cc + rsitems/rsgxsforumitems.cc + rsitems/rsgxsiditems.cc + rsitems/rsgxsrecognitems.cc + rsitems/rsgxsreputationitems.cc + rsitems/rsgxsupdateitems.cc + rsitems/rshistoryitems.cc + rsitems/rsrttitems.cc + rsitems/rsserviceinfoitems.cc ) + +#retroshare/rswiki.h +#./rsitems/rswikiitems.cc +#./rsitems/rswikiitems.h +#./rsitems/rswireitems.h + +list( + APPEND RS_SOURCES + rsitems/rsgxschannelitems.cc + rsitems/rsgxscircleitems.cc + rsitems/rsgxsitems.cc + rsitems/rsmsgitems.cc ) + +#./rsitems/rsphotoitems.cc +#./rsitems/rsphotoitems.h +#./rsitems/rsposteditems.cc +#./rsitems/rsposteditems.h +#./rsitems/rswireitems.cc +#retroshare/rswire.h + +list( + APPEND RS_SOURCES + rsitems/rsnxsitems.cc ) + +list( + APPEND RS_SOURCES + rsserver/p3status.cc + rsserver/p3face-config.cc + rsserver/p3face-info.cc + rsserver/p3history.cc + rsserver/p3serverconfig.cc + rsserver/rsloginhandler.cc + rsserver/p3face-server.cc + rsserver/p3msgs.cc + rsserver/p3peers.cc + rsserver/rsaccounts.cc + rsserver/rsinit.cc ) + +list( + APPEND RS_SOURCES + serialiser/rsbaseserial.cc + serialiser/rsserializable.cc + serialiser/rstlvaddrs.cc + serialiser/rstlvbanlist.cc + serialiser/rstlvbase.cc + serialiser/rstlvbinary.cc + serialiser/rstlvfileitem.cc + serialiser/rstlvgenericmap.inl + serialiser/rstlvgenericparam.cc + serialiser/rstlvidset.cc + serialiser/rstlvimage.cc + serialiser/rstlvitem.cc + serialiser/rstlvkeys.cc + serialiser/rstlvkeyvalue.cc + serialiser/rstlvstring.cc + serialiser/rsserializer.cc + serialiser/rstypeserializer.cc + serialiser/rsserial.cc ) + +# ./services/autoproxy +#./services/autoproxy/p3i2psam3.cpp +#./services/autoproxy/p3i2psam3.h + +list( + APPEND RS_SOURCES + services/autoproxy/rsautoproxymonitor.cc + services/p3bwctrl.cc + services/p3heartbeat.cc + services/p3service.cc + services/p3serviceinfo.cc + services/p3statusservice.cc + services/p3banlist.cc + services/p3rtt.cc + services/rseventsservice.cc + services/p3gxscircles.cc + services/p3gxscommon.cc + services/p3gxsreputation.cc + services/p3msgservice.cc + services/p3idservice.cc + services/p3gxschannels.cc + services/p3gxsforums.cc ) + +#./services/p3wiki.cc +#./services/p3wiki.h +#./services/p3wire.cc +#./services/p3wire.h + +#./services/p3photoservice.cc +#./services/p3photoservice.h +#./services/p3postbase.cc +#./services/p3postbase.h +#./services/p3posted.cc +#./services/p3posted.h + +if(RS_BRODCAST_DISCOVERY) + list( + APPEND RS_PUBLIC_HEADERS + retroshare/rsbroadcastdiscovery.h ) + + list( + APPEND RS_SOURCES + services/broadcastdiscoveryservice.cc ) +endif(RS_BRODCAST_DISCOVERY) + +list( + APPEND RS_SOURCES + tcponudp/tcppacket.cc + tcponudp/tcpstream.cc + tcponudp/tou.cc + tcponudp/udppeer.cc + tcponudp/bss_tou.cc + tcponudp/udprelay.cc ) + +if(RS_BITDHT_STUNNER) + list( + APPEND RS_SOURCES + tcponudp/udpstunner.cc ) +endif(RS_BITDHT_STUNNER) + +list( + APPEND RS_SOURCES + turtle/rsturtleitem.cc + turtle/p3turtle.cc ) + +list( + APPEND RS_SOURCES +# util/contentvalue.cc +# util/exampletst.c +# util/rsdbbind.cc +# util/rsdiscspace.cc + util/rsexpr.cc + util/rsprint.cc +# util/rsrecogn.cc +# util/rssharedptr.h +# util/rstickevent.cc + util/rstime.cc + util/smallobject.cc +# util/retrodb.cc + util/rsbase64.cc + util/rsjson.cc +# util/rskbdinput.cc + util/rsrandom.cc + util/rsstring.cc + util/rsurl.cc + util/folderiterator.cc + util/rsdir.cc + util/rsfile.cc + util/dnsresolver.cc + util/extaddrfinder.cc + util/rsdebug.cc + util/rsdnsutils.cc + util/rsnet.cc + util/rsnet_ss.cc + util/rsthreads.cc ) + +# util/i2pcommon.cpp +# util/i2pcommon.h + +if(RS_FORUM_DEEP_INDEX) + list( + APPEND RS_SOURCES + deep_search/commonutils.cpp + deep_search/forumsindex.cpp ) +endif(RS_FORUM_DEEP_INDEX) + + +#./deep_search/filesflacindexer.hpp +#./deep_search/filesoggindexer.hpp +#./deep_search/filestaglibindexer.hpp +#./deep_search/filesindex.cpp +#./deep_search/filesindex.hpp +#./deep_search/channelsindex.cpp +#./deep_search/channelsindex.hpp + +list( + APPEND RS_SOURCES + gossipdiscovery/gossipdiscoveryitems.cc + gossipdiscovery/p3gossipdiscovery.cc ) + +if(RS_MINIUPNPC) +list( + APPEND RS_SOURCES + rs_upnp/upnphandler_miniupnp.cc ) +endif(RS_MINIUPNPC) + +#./rs_upnp/UPnPBase.cpp +#./rs_upnp/upnphandler_libupnp.cc +#./rs_upnp/upnptest.cc +#./rs_upnp/upnputil.cc + +#./rs_android/LocalArray.h +#./rs_android/README-ifaddrs-android.adoc +#./rs_android/ScopedFd.h +#./rs_android/androidcoutcerrcatcher.hpp +#./rs_android/errorconditionwrap.cpp +#./rs_android/ifaddrs-android.h +#./rs_android/org +#./rs_android/org/retroshare +#./rs_android/org/retroshare/service +#./rs_android/org/retroshare/service/AssetHelper.java +#./rs_android/org/retroshare/service/ErrorConditionWrap.java +#./rs_android/org/retroshare/service/RetroShareServiceAndroid.java +#./rs_android/retroshareserviceandroid.cpp +#./rs_android/retroshareserviceandroid.hpp +#./rs_android/rsjni.cpp +#./rs_android/rsjni.hpp diff --git a/libretroshare/src/deep_search/forumsindex.cpp b/libretroshare/src/deep_search/forumsindex.cpp index acc7aed9a..a04f2ecac 100644 --- a/libretroshare/src/deep_search/forumsindex.cpp +++ b/libretroshare/src/deep_search/forumsindex.cpp @@ -46,7 +46,9 @@ std::error_condition DeepForumsIndex::search( // End of prefix configuration. // And parse the query. - Xapian::Query query = queryparser.parse_query(queryStr); + using XQP = Xapian::QueryParser; + Xapian::Query query = queryparser.parse_query( + queryStr, XQP::FLAG_WILDCARD | XQP::FLAG_DEFAULT ); // Use an Enquire object on the database to run the query. Xapian::Enquire enquire(db); diff --git a/libretroshare/src/friend_server/fsclient.cc b/libretroshare/src/friend_server/fsclient.cc index adc294d0c..ccdd9a019 100644 --- a/libretroshare/src/friend_server/fsclient.cc +++ b/libretroshare/src/friend_server/fsclient.cc @@ -90,7 +90,7 @@ bool FsClient::sendItem(const std::string& address,uint16_t port,RsItem *item,st RsDbg() << "Sending item to friend server at \"" << address << ":" << port ; - int CreateSocket = 0,n = 0; + int CreateSocket = 0; char dataReceived[1024]; struct sockaddr_in ipOfServer; @@ -130,7 +130,7 @@ bool FsClient::sendItem(const std::string& address,uint16_t port,RsItem *item,st // FsSerializer().serialise(item,data,&size); // write(CreateSocket,data,size); // shouldn't we use the pqistreamer in R/W mode instead? - RsFdBinInterface *bio = new RsFdBinInterface(CreateSocket); // deleted by ~pqistreamer() + RsFdBinInterface *bio = new RsFdBinInterface(CreateSocket,true); // deleted by ~pqistreamer() pqithreadstreamer p(this,rss,RsPeerId(),bio,BIN_FLAGS_READABLE | BIN_FLAGS_WRITEABLE | BIN_FLAGS_NO_CLOSE); p.start(); diff --git a/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc b/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc index 732967f44..948fa3ccc 100644 --- a/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc +++ b/libretroshare/src/gossipdiscovery/p3gossipdiscovery.cc @@ -107,7 +107,7 @@ p3discovery2::p3discovery2( addSerialType(new RsDiscSerialiser()); // Add self into PGP FriendList. - mFriendList[AuthGPG::getAuthGPG()->getGPGOwnId()] = DiscPgpInfo(); + mFriendList[AuthPGP::getPgpOwnId()] = DiscPgpInfo(); } @@ -219,7 +219,7 @@ void p3discovery2::removeFriend(const RsPeerId &sslId) std::cerr << std::endl; #endif /* pgp peer without any ssl entries -> check if they are still a real friend */ - if (!(AuthGPG::getAuthGPG()->isGPGAccepted(pgpId))) + if (!(AuthPGP::isPGPAccepted(pgpId))) { #ifdef P3DISC_DEBUG std::cerr << "p3discovery2::addFriend() pgpId is no longer a friend, removing"; @@ -604,8 +604,8 @@ void p3discovery2::updatePgpFriendList() std::list::iterator lit; std::map::iterator it; - RsPgpId ownPgpId = AuthGPG::getAuthGPG()->getGPGOwnId(); - AuthGPG::getAuthGPG()->getGPGAcceptedList(pgpList); + RsPgpId ownPgpId = AuthPGP::getPgpOwnId(); + AuthPGP::getPgpAcceptedList(pgpList); pgpList.push_back(ownPgpId); // convert to set for ordering. @@ -723,7 +723,7 @@ void p3discovery2::processPGPList(const RsPeerId &fromId, const RsDiscPgpListIte std::set::const_iterator fit; for(fit = item->pgpIdSet.ids.begin(); fit != item->pgpIdSet.ids.end(); ++fit) { - if (!AuthGPG::getAuthGPG()->isGPGId(*fit)) + if (!AuthPGP::isPGPId(*fit)) { #ifdef P3DISC_DEBUG std::cerr << "p3discovery2::processPGPList() requesting certificate for PgpId: " << *fit; @@ -1058,11 +1058,11 @@ void p3discovery2::recvPGPCertificateRequest( const RsPeerId& fromId, const RsDi return; } - RsPgpId ownPgpId = AuthGPG::getAuthGPG()->getGPGOwnId(); + RsPgpId ownPgpId = AuthPGP::getPgpOwnId(); for(const RsPgpId& pgpId : item->pgpIdSet.ids) if (pgpId == ownPgpId) sendPGPCertificate(pgpId, fromId); - else if(ps.vs_disc != RS_VS_DISC_OFF && AuthGPG::getAuthGPG()->isGPGAccepted(pgpId)) + else if(ps.vs_disc != RS_VS_DISC_OFF && AuthPGP::isPGPAccepted(pgpId)) sendPGPCertificate(pgpId, fromId); else std::cerr << "(WW) not sending certificate " << pgpId << " asked by friend " << fromId << " because this either this cert is not a friend, or discovery is off" << std::endl; @@ -1078,7 +1078,7 @@ void p3discovery2::sendPGPCertificate(const RsPgpId &aboutId, const RsPeerId &to unsigned char *bin_data; size_t bin_len; - if(!AuthGPG::getAuthGPG()->exportPublicKey(aboutId,bin_data,bin_len,false,true)) + if(!AuthPGP::exportPublicKey(aboutId,bin_data,bin_len,false,true)) { std::cerr << "(EE) cannot export public key " << aboutId << " requested by peer " << toId << std::endl; return ; @@ -1098,7 +1098,7 @@ void p3discovery2::recvPGPCertificate(const RsPeerId& fromId, RsDiscPgpKeyItem* std::string cert_name; std::list cert_signers; - if(!AuthGPG::getAuthGPG()->getGPGDetailsFromBinaryBlock( (unsigned char*)item->bin_data,item->bin_len, cert_pgp_id, cert_name, cert_signers )) + if(!AuthPGP::getPgpDetailsFromBinaryBlock( (unsigned char*)item->bin_data,item->bin_len, cert_pgp_id, cert_name, cert_signers )) { std::cerr << "(EE) cannot parse own PGP key sent by " << fromId << std::endl; return; @@ -1147,7 +1147,7 @@ void p3discovery2::recvPGPCertificate(const RsPeerId& fromId, RsDiscPgpKeyItem* // otherwise the connection should already be accepted. This only happens when the short invite peer sends its own PGP key. if(det.skip_pgp_signature_validation) - AuthGPG::getAuthGPG()->AllowConnection(det.gpg_id,true); + AuthPGP::AllowConnection(det.gpg_id,true); } /************* from pqiServiceMonitor *******************/ diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index da8973eb1..fb1b4c295 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -41,6 +41,7 @@ #include "rsserver/p3face.h" #include "retroshare/rsevents.h" #include "util/radix64.h" +#include "util/cxx17retrocompat.h" #define PUB_GRP_MASK 0x000f #define RESTR_GRP_MASK 0x00f0 @@ -2285,8 +2286,9 @@ bool RsGenExchange::processGrpMask(const RsGxsGroupId& grpId, ContentValue &grpC void RsGenExchange::publishMsgs() { + bool atLeastOneMessageCreatedSuccessfully = false; - RS_STACK_MUTEX(mGenMtx) ; + RS_STACK_MUTEX(mGenMtx); rstime_t now = time(NULL); @@ -2463,6 +2465,8 @@ void RsGenExchange::publishMsgs() // add to published to allow acknowledgement mMsgNotify.insert(std::make_pair(mit->first, std::make_pair(grpId, msgId))); mDataAccess->updatePublicRequestStatus(mit->first, RsTokenService::COMPLETE); + + atLeastOneMessageCreatedSuccessfully = true; } else { @@ -2496,6 +2500,8 @@ void RsGenExchange::publishMsgs() mNotifications.push_back(ch); } + + if(atLeastOneMessageCreatedSuccessfully) mNetService->requestPull(); } RsGenExchange::ServiceCreate_Return RsGenExchange::service_CreateGroup(RsGxsGrpItem* /* grpItem */, @@ -2723,7 +2729,8 @@ bool RsGenExchange::checkKeys(const RsTlvSecurityKeySet& keySet) void RsGenExchange::publishGrps() { - std::list groups_to_subscribe ; + bool atLeastOneGroupCreatedSuccessfully = false; + std::list groups_to_subscribe; { RS_STACK_MUTEX(mGenMtx) ; @@ -2954,6 +2961,8 @@ void RsGenExchange::publishGrps() // add to published to allow acknowledgement toNotify.insert(std::make_pair(token, GrpNote(true,ggps.mIsUpdate,grpId))); + + atLeastOneGroupCreatedSuccessfully = true; } } @@ -2972,9 +2981,14 @@ void RsGenExchange::publishGrps() // This is done off-mutex to avoid possible cross deadlocks with the net service. - if(mNetService!=NULL) - for(std::list::const_iterator it(groups_to_subscribe.begin());it!=groups_to_subscribe.end();++it) - mNetService->subscribeStatusChanged((*it),true) ; + if(mNetService != nullptr) + { + for(auto& grpId : std::as_const(groups_to_subscribe)) + mNetService->subscribeStatusChanged(grpId, true); + + if(atLeastOneGroupCreatedSuccessfully) + mNetService->requestPull(); + } } uint32_t RsGenExchange::generatePublicToken() diff --git a/libretroshare/src/gxs/rsgxsnetservice.cc b/libretroshare/src/gxs/rsgxsnetservice.cc index e664b580c..9d9437991 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.cc +++ b/libretroshare/src/gxs/rsgxsnetservice.cc @@ -35,7 +35,7 @@ // | // +----------- sharePublishKeys() // | -// +----------- syncWithPeers() +// +----------- checkUpdatesFromPeers() // | | // | +--if AutoSync--- send global UpdateTS of each peer to itself => the peer knows the last // | | time current peer has received an updated from himself @@ -127,14 +127,14 @@ // (Set at server side to be mGrpServerUpdateItem->grpUpdateTS) // // Only updated in processCompletedIncomingTransaction() from Grp list transaction. -// Used in syncWithPeers() sending in RsNxsSyncGrp once to all peers: peer will send data if +// Used in checkUpdatesFromPeers() sending in RsNxsSyncGrp once to all peers: peer will send data if // has something new. All time comparisons are in the friends' clock time. // // mClientMsgUpdateMap: map< RsPeerId, map > // // Last msg list modification time sent by that peer Id // Updated in processCompletedIncomingTransaction() from Grp list trans. -// Used in syncWithPeers() sending in RsNxsSyncGrp once to all peers. +// Used in checkUpdatesFromPeers() sending in RsNxsSyncGrp once to all peers. // Set at server to be mServerMsgUpdateMap[grpId]->msgUpdateTS // // mGrpServerUpdateItem: TimeStamp Last group local modification timestamp over all groups @@ -150,7 +150,7 @@ // // tick() tick() // | | -// +---- SyncWithPeers +-- recvNxsItemQueue() +// +---- checkUpdatesFromPeers() +-- recvNxsItemQueue() // | | // +---------------- Send global UpdateTS of each peer to itself => the peer knows +---------> +------ handleRecvSyncGroup( RsNxsSyncGrp*) // | the last msg sent (stored in mClientGrpUpdateMap[peer_id]), | | - parse all subscribed groups. For each, send a RsNxsSyncGrpItem with publish TS @@ -457,7 +457,7 @@ int RsGxsNetService::tick() if((elapsed) < now) { - syncWithPeers(); + checkUpdatesFromPeers(); syncGrpStatistics(); checkDistantSyncState(); @@ -570,39 +570,40 @@ RsGxsGroupId RsGxsNetService::hashGrpId(const RsGxsGroupId& gid,const RsPeerId& return RsGxsGroupId( RsDirUtil::sha1sum(tmpmem,SIZE).toByteArray() ); } -void RsGxsNetService::syncWithPeers() +std::error_condition RsGxsNetService::checkUpdatesFromPeers( + std::set peers ) { #ifdef NXS_NET_DEBUG_0 - GXSNETDEBUG___ << "RsGxsNetService::syncWithPeers() this=" << (void*)this << ". serviceInfo=" << mServiceInfo << std::endl; + RS_DBG("this=", (void*)this, ". serviceInfo=", mServiceInfo); #endif - static RsNxsSerialiser ser(mServType) ; // this is used to estimate bandwidth. - - RS_STACK_MUTEX(mNxsMutex) ; - - std::set peers; - mNetMgr->getOnlineList(mServiceInfo.mServiceType, peers); - - if(mAllowDistSync && mGxsNetTunnel != NULL) + /* If specific peers are passed as paramether ask only to them */ + if(peers.empty()) { - // Grab all online virtual peers of distant tunnels for the current service. + mNetMgr->getOnlineList(mServiceInfo.mServiceType, peers); - std::list vpids ; - mGxsNetTunnel->getVirtualPeers(vpids); + if(mAllowDistSync && mGxsNetTunnel != nullptr) + { + /* Grab all online virtual peers of distant tunnels for the current + * service. */ - for(auto it(vpids.begin());it!=vpids.end();++it) - peers.insert(RsPeerId(*it)) ; + std::list vpids ; + mGxsNetTunnel->getVirtualPeers(vpids); + + for(auto it(vpids.begin());it!=vpids.end();++it) + peers.insert(RsPeerId(*it)) ; + } } - if (peers.empty()) { - // nothing to do - return; - } + // Still empty? Reports there are no available peers + if (peers.empty()) return std::errc::network_down; + + + RS_STACK_MUTEX(mNxsMutex); // for now just grps for(auto sit = peers.begin(); sit != peers.end(); ++sit) { - const RsPeerId peerId = *sit; ClientGrpMap::const_iterator cit = mClientGrpUpdateMap.find(peerId); @@ -624,8 +625,7 @@ void RsGxsNetService::syncWithPeers() generic_sendItem(grp); } - if(!mAllowMsgSync) - return ; + if(!mAllowMsgSync) return std::error_condition(); #ifndef GXS_DISABLE_SYNC_MSGS @@ -743,10 +743,12 @@ void RsGxsNetService::syncWithPeers() } } -#endif +#endif // ndef GXS_DISABLE_SYNC_MSGS + + return std::error_condition(); } -void RsGxsNetService::generic_sendItem(RsNxsItem *si) +void RsGxsNetService::generic_sendItem(rs_owner_ptr si) { // check if the item is to be sent to a distant peer or not @@ -1022,32 +1024,32 @@ void RsGxsNetService::locked_resetClientTS(const RsGxsGroupId& grpId) it->second.msgUpdateInfos.erase(grpId) ; } -void RsGxsNetService::subscribeStatusChanged(const RsGxsGroupId& grpId,bool subscribed) +void RsGxsNetService::subscribeStatusChanged( + const RsGxsGroupId& grpId, bool subscribed ) { - RS_STACK_MUTEX(mNxsMutex) ; - - if(!subscribed) - return ; + if(!subscribed) return; // When we subscribe, we reset the time stamps, so that the entire group list // gets requested once again, for a proper update. + RS_STACK_MUTEX(mNxsMutex); + #ifdef NXS_NET_DEBUG_0 - GXSNETDEBUG__G(grpId) << "Changing subscribe status for grp " << grpId << " to " << subscribed << ": reseting all server msg time stamps for this group, and server global TS." << std::endl; - std::map::iterator it = mServerMsgUpdateMap.find(grpId) ; + RS_DBG( "Changing subscribe status for grp", grpId, " to ", subscribed, + ": reseting all server msg time stamps for this group, and " + "server global TS." ); #endif - RsGxsServerMsgUpdate& item(mServerMsgUpdateMap[grpId]) ; + RsGxsServerMsgUpdate& item(mServerMsgUpdateMap[grpId]); + item.msgUpdateTS = static_cast(time(nullptr)); - item.msgUpdateTS = time(NULL) ; + /* We also update mGrpServerUpdateItem so as to trigger a new grp list + * exchange with friends (friends will send their known ClientTS which + * will be lower than our own grpUpdateTS, triggering our sending of the + * new subscribed grp list. */ + mGrpServerUpdate.grpUpdateTS = static_cast(time(nullptr)); - // We also update mGrpServerUpdateItem so as to trigger a new grp list exchange with friends (friends will send their known ClientTS which - // will be lower than our own grpUpdateTS, triggering our sending of the new subscribed grp list. - - mGrpServerUpdate.grpUpdateTS = time(NULL) ; - - if(subscribed) - locked_resetClientTS(grpId) ; + locked_resetClientTS(grpId); } bool RsGxsNetService::fragmentMsg(RsNxsMsg& msg, MsgFragments& msgFragments) const @@ -1718,13 +1720,25 @@ RsItem *RsGxsNetService::generic_recvItem() void RsGxsNetService::recvNxsItemQueue() { - RsItem *item ; + RsItem* item; - while(NULL != (item=generic_recvItem())) - { + while(nullptr != (item=generic_recvItem())) + { #ifdef NXS_NET_DEBUG_1 - GXSNETDEBUG_P_(item->PeerId()) << "Received RsGxsNetService Item:" << (void*)item << " type=" << std::hex << item->PacketId() << std::dec << std::endl ; + RS_DBG( "Received RsGxsNetService Item: ", (void*)item, " type=", + item->PacketId() ); #endif + /* Handle pull request and other new items here to not mess with all the + * old nested code and items hell */ + switch(static_cast(item->PacketSubType())) + { + case RsNxsSubtype::PULL_REQUEST: + std::unique_ptr pullItem( + static_cast(item) ); + handlePullRequest(std::move(pullItem)); + continue; + } + // RsNxsItem needs dynamic_cast, since they have derived siblings. // RsNxsItem *ni = dynamic_cast(item) ; @@ -4690,7 +4704,7 @@ bool RsGxsNetService::checkPermissionsForFriendGroup(const RsPeerId& sslId,const if(!grpMeta.mInternalCircle.isNull()) { RsGroupInfo ginfo ; - RsPgpId pgpId = mPgpUtils->getPGPId(sslId) ; + RsPgpId pgpId = mPgpUtils->getPgpId(sslId) ; #ifdef NXS_NET_DEBUG_4 GXSNETDEBUG_PG(sslId,grpMeta.mGroupId) << " Group internal circle: " << grpMeta.mInternalCircle << ", We're owner. Sending to everyone in the group." << std::endl; @@ -5075,6 +5089,46 @@ void RsGxsNetService::handleRecvPublishKeys(RsNxsGroupPublishKeyItem *item) } } +std::error_condition RsGxsNetService::requestPull(std::set peers) +{ + /* If specific peers are passed as paramether ask only to them */ + if(peers.empty()) + { + mNetMgr->getOnlineList(mServiceInfo.mServiceType, peers); + + if(mAllowDistSync && mGxsNetTunnel != nullptr) + { + /* Grab all online virtual peers of distant tunnels for the current + * service. */ + + std::list vpids ; + mGxsNetTunnel->getVirtualPeers(vpids); + + for(auto it(vpids.begin());it!=vpids.end();++it) + peers.insert(RsPeerId(*it)) ; + } + } + + // Still empty? Reports there are no available peers + if (peers.empty()) return std::errc::network_down; + + for(auto& peerId: std::as_const(peers)) + { + auto item = new RsNxsPullRequestItem( + static_cast(mServType) ); + item->PeerId(peerId); + generic_sendItem(item); + } + + return std::error_condition(); +} + +void RsGxsNetService::handlePullRequest( + std::unique_ptr item ) +{ + checkUpdatesFromPeers(std::set{item->PeerId()}); +} + bool RsGxsNetService::getGroupServerUpdateTS(const RsGxsGroupId& gid,rstime_t& group_server_update_TS, rstime_t& msg_server_update_TS) { RS_STACK_MUTEX(mNxsMutex) ; diff --git a/libretroshare/src/gxs/rsgxsnetservice.h b/libretroshare/src/gxs/rsgxsnetservice.h index a4b448003..0baee2a2f 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.h +++ b/libretroshare/src/gxs/rsgxsnetservice.h @@ -250,6 +250,15 @@ public: void threadTick() override; /// @see RsTickingThread + + /// @see RsNetworkExchangeService + std::error_condition checkUpdatesFromPeers( + std::set peers = std::set() ) override; + + /// @see RsNetworkExchangeService + std::error_condition requestPull( + std::set peers = std::set() ) override; + private: /*! @@ -423,6 +432,8 @@ private: */ void handleRecvPublishKeys(RsNxsGroupPublishKeyItem*) ; + void handlePullRequest(std::unique_ptr item); + /** E: item handlers **/ @@ -459,7 +470,7 @@ private: void locked_pushMsgRespFromList(std::list& itemL, const RsPeerId& sslId, const RsGxsGroupId &grp_id, const uint32_t& transN); void checkDistantSyncState(); - void syncWithPeers(); + void syncGrpStatistics(); void addGroupItemToList(NxsTransaction*& tr, const RsGxsGroupId& grpId, uint32_t& transN, @@ -559,7 +570,7 @@ private: void cleanRejectedMessages(); void processObserverNotifications(); - void generic_sendItem(RsNxsItem *si); + void generic_sendItem(rs_owner_ptr si); RsItem *generic_recvItem(); private: diff --git a/libretroshare/src/gxs/rsgxsnetutils.cc b/libretroshare/src/gxs/rsgxsnetutils.cc index b74abcb0f..6d759da7e 100644 --- a/libretroshare/src/gxs/rsgxsnetutils.cc +++ b/libretroshare/src/gxs/rsgxsnetutils.cc @@ -223,7 +223,7 @@ bool GrpCircleVetting::canSend( { if(mCircles->isLoaded(circleId)) { - const RsPgpId& pgpId = mPgpUtils->getPGPId(peerId); + const RsPgpId& pgpId = mPgpUtils->getPgpId(peerId); return mCircles->canSend(circleId, pgpId,should_encrypt); } @@ -302,7 +302,7 @@ bool MsgCircleIdsRequestVetting::cleared() if(filtered_out_msgs>0) std::cerr << "(WW) " << filtered_out_msgs << " messages not sent because they are signed by author(s) not member of that circle " << mCircleId << std::endl; - RsPgpId pgpId = mPgpUtils->getPGPId(mPeerId); + RsPgpId pgpId = mPgpUtils->getPgpId(mPeerId); bool can_send_res = mCircles->canSend(mCircleId, pgpId,mShouldEncrypt); if(mShouldEncrypt) // that means the circle is external diff --git a/libretroshare/src/gxs/rsnxs.h b/libretroshare/src/gxs/rsnxs.h index 2f9bb25a6..2197d0e7a 100644 --- a/libretroshare/src/gxs/rsnxs.h +++ b/libretroshare/src/gxs/rsnxs.h @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -325,4 +325,20 @@ public: return RsReputationLevel::NEUTRAL; } } + + /** + * @brief Check if new stuff is available from peers + * @param peers peers to check, if empty all available peers are checked + */ + virtual std::error_condition checkUpdatesFromPeers( + std::set peers = std::set() ) = 0; + + /** + * @brief request online peers to pull updates from our node ASAP + * @param peers peers to which request pull from, if empty all available + * peers are requested to pull + * @return success or error details + */ + virtual std::error_condition requestPull( + std::set peers = std::set() ) = 0; }; diff --git a/libretroshare/src/jsonapi/README.adoc b/libretroshare/src/jsonapi/README.adoc new file mode 100644 index 000000000..64284b85c --- /dev/null +++ b/libretroshare/src/jsonapi/README.adoc @@ -0,0 +1,447 @@ +// SPDX-FileCopyrightText: (C) 2004-2019 Retroshare Team +// SPDX-License-Identifier: CC0-1.0 + +RetroShare JSON API +=================== + +:Cxx: C++ + +== How to use RetroShare JSON API + +Look for methods marked with +@jsonapi+ doxygen custom command into ++libretroshare/src/retroshare+. The method path is composed by service instance +pointer name like +rsGxsChannels+ for +RsGxsChannels+, and the method name like ++createGroup+ and pass the input paramethers as a JSON object. + +.Service instance pointer in rsgxschannels.h +[source,cpp] +-------------------------------------------------------------------------------- +/** + * Pointer to global instance of RsGxsChannels service implementation + * @jsonapi{development} + */ +extern RsGxsChannels* rsGxsChannels; +-------------------------------------------------------------------------------- + +.Method declaration in rsgxschannels.h +[source,cpp] +-------------------------------------------------------------------------------- + /** + * @brief Request channel creation. + * The action is performed asyncronously, so it could fail in a subsequent + * phase even after returning true. + * @jsonapi{development} + * @param[out] token Storage for RsTokenService token to track request + * status. + * @param[in] group Channel data (name, description...) + * @return false on error, true otherwise + */ + virtual bool createGroup(uint32_t& token, RsGxsChannelGroup& group) = 0; +-------------------------------------------------------------------------------- + +.paramethers.json +[source,json] +-------------------------------------------------------------------------------- +{ + "group":{ + "mMeta":{ + "mGroupName":"JSON test group", + "mGroupFlags":4, + "mSignFlags":520 + }, + "mDescription":"JSON test group description" + }, + "caller_data":"Here can go any kind of JSON data (even objects) that the caller want to get back together with the response" +} +-------------------------------------------------------------------------------- + +.Calling the JSON API with curl on the terminal +[source,bash] +-------------------------------------------------------------------------------- +curl -u $API_USER --data @paramethers.json http://127.0.0.1:9092/rsGxsChannels/createGroup +-------------------------------------------------------------------------------- + +.JSON API call result +[source,json] +-------------------------------------------------------------------------------- +{ + "caller_data": "Here can go any kind of JSON data (even objects) that the caller want to get back together with the response", + "retval": true, + "token": 3 +} +-------------------------------------------------------------------------------- + +Even if it is less efficient because of URL encoding HTTP +GET+ method is +supported too, so in cases where the client cannot use +POST+ she can still use ++GET+ taking care of encoding the JSON data. With +curl+ this can be done at +least in two different ways. + +.Calling the JSON API with GET method with curl on the terminal +[source,bash] +-------------------------------------------------------------------------------- +curl -u $API_USER --get --data-urlencode jsonData@paramethers.json \ + http://127.0.0.1:9092/rsGxsChannels/createGroup +-------------------------------------------------------------------------------- + +Letting +curl+ do the encoding is much more elegant but it is semantically +equivalent to the following. + +.Calling the JSON API with GET method and pre-encoded data with curl on the terminal +-------------------------------------------------------------------------------- +curl -u $API_USER http://127.0.0.1:9092/rsGxsChannels/createGroup?jsonData=%7B%0A%20%20%20%20%22group%22%3A%7B%0A%20%20%20%20%20%20%20%20%22mMeta%22%3A%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupName%22%3A%22JSON%20test%20group%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mGroupFlags%22%3A4%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mSignFlags%22%3A520%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22mDescription%22%3A%22JSON%20test%20group%20description%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22caller_data%22%3A%22Here%20can%20go%20any%20kind%20of%20JSON%20data%20%28even%20objects%29%20that%20the%20caller%20want%20to%20get%20back%20together%20with%20the%20response%22%0A%7D +-------------------------------------------------------------------------------- + +Note that using +GET+ method +?jsonData=+ and then the JSON data URL encoded are +added after the path in the HTTP request. + + +== JSON API authentication + +Most of JSON API methods require authentication as they give access to +RetroShare user data, and we don't want any application running on the system +eventually by other users be able to access private data indiscriminately. +JSON API support HTTP Basic as authentication scheme, this is enough as JSON API +server is intented for usage on the same system (127.0.0.1) not over an +untrusted network. +If you need to use JSON API over an untrusted network consider using a reverse +proxy with HTTPS such as NGINX in front of JSON API server. +If RetroShare login has been effectuated through the JSON API you can use your +location SSLID as username and your PGP password as credential for the JSON API, +but we suggests you use specific meaningful and human readable credentials for +each JSON API client so the human user can have better control over which client +can access the JSON API. + +.NewToken.json +[source,json] +-------------------------------------------------------------------------------- +{ + "token": "myNewUser:myNewPassword" +} +-------------------------------------------------------------------------------- + +.An authenticated client can authorize new tokens like this +-------------------------------------------------------------------------------- +curl -u $API_USER --data @NewToken.json http://127.0.0.1:9092/jsonApiServer/authorizeToken +-------------------------------------------------------------------------------- + +.An unauthenticated JSON API client can request access with +-------------------------------------------------------------------------------- +curl --data @NewToken.json http://127.0.0.1:9092/jsonApiServer/requestNewTokenAutorization +-------------------------------------------------------------------------------- + +When an unauthenticated client request his token to be authorized, JSON API +server will try to ask confirmation to the human user if possible through ++mNewAccessRequestCallback+, if it is not possible or the user didn't authorized +the token +false+ is returned. + + +== Offer new RetroShare services through JSON API + +To offer a retroshare service through the JSON API, first of all one need find +the global pointer to the service instance and document it in doxygen syntax, +plus marking with the custom doxygen command +@jsonapi{RS_VERSION}+ where ++RS_VERSION+ is the retroshare version in which this service became available +with the current semantic (major changes to the service semantic, changes the +meaning of the service itself, so the version should be updated in the +documentation in that case). + +.Service instance pointer in rsgxschannels.h +[source,cpp] +-------------------------------------------------------------------------------- +/** + * Pointer to global instance of RsGxsChannels service implementation + * @jsonapi{development} + */ +extern RsGxsChannels* rsGxsChannels; +-------------------------------------------------------------------------------- + + +Once the service instance itself is known to the JSON API you need to document +in doxygen syntax and mark with the custom doxygen command ++@jsonapi{RS_VERSION}+ the methods of the service that you want to make +available through JSON API. + +.Offering RsGxsChannels::getChannelDownloadDirectory in rsgxschannels.h +[source,cpp] +-------------------------------------------------------------------------------- + /** + * Get download directory for the given channel + * @jsonapi{development} + * @param[in] channelId id of the channel + * @param[out] directory reference to string where to store the path + * @return false on error, true otherwise + */ + virtual bool getChannelDownloadDirectory( const RsGxsGroupId& channelId, + std::string& directory ) = 0; +-------------------------------------------------------------------------------- + +For each paramether you must specify if it is used as input +@param[in]+ as +output +@param[out]+ or both +@param[inout]+. Paramethers and return value +types must be of a type supported by +RsTypeSerializer+ which already support +most basic types (+bool+, +std::string+...), +RsSerializable+ and containers of +them like +std::vector+. Paramethers passed by value and by +reference of those types are both supported, while passing by pointer is not +supported. If your paramether or return +class+/+struct+ type is not supported +yet by +RsTypeSerializer+ most convenient approach is to make it derive from ++RsSerializable+ and implement +serial_process+ method like I did with ++RsGxsChannelGroup+. + +.Deriving RsGxsChannelGroup from RsSerializable in rsgxschannels.h +[source,cpp] +-------------------------------------------------------------------------------- +struct RsGxsChannelGroup : RsSerializable +{ + RsGroupMetaData mMeta; + std::string mDescription; + RsGxsImage mImage; + + bool mAutoDownload; + + /// @see RsSerializable + virtual void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx ) + { + RS_SERIAL_PROCESS(mMeta); + RS_SERIAL_PROCESS(mDescription); + RS_SERIAL_PROCESS(mImage); + RS_SERIAL_PROCESS(mAutoDownload); + } +}; +-------------------------------------------------------------------------------- + +You can do the same recursively for any member of your +struct+ that is not yet +supported by +RsTypeSerializer+. + +Some Retroshare {Cxx} API functions are asyncronous, historically RetroShare +didn't follow a policy on how to expose asyncronous API so differents services +and some times even differents method of the same service follow differents +asyncronous patterns, thus making automatic generation of JSON API wrappers for +those methods impractical. Instead of dealing with all those differents patterns +I have chosed to support only one new pattern taking advantage of modern {Cxx}11 +and restbed features. On the {Cxx}11 side lambdas and +std::function+s are used, +on the restbed side Server Side Events are used to send asyncronous results. + +Lets see an example so it will be much esier to understand. + +.RsGxsChannels::turtleSearchRequest asyncronous API +[source,cpp] +-------------------------------------------------------------------------------- + /** + * @brief Request remote channels search + * @jsonapi{development} + * @param[in] matchString string to look for in the search + * @param multiCallback function that will be called each time a search + * result is received + * @param[in] maxWait maximum wait time in seconds for search results + * @return false on error, true otherwise + */ + virtual bool turtleSearchRequest( + const std::string& matchString, + const std::function& multiCallback, + std::time_t maxWait = 300 ) = 0; +-------------------------------------------------------------------------------- + ++RsGxsChannels::turtleSearchRequest(...)+ is an asyncronous method because it +send a channel search request on turtle network and then everytime a result is +received from the network +multiCallback+ is called and the result is passed as +parameter. To be supported by the automatic JSON API wrappers generator an +asyncronous method need a parameter of type +std::function+ called ++callback+ if the callback will be called only once or +multiCallback+ if the +callback is expected to be called more then once like in this case. +A second mandatory parameter is +maxWait+ of type +std::time_t+ it indicates the +maximum amount of time in seconds for which the caller is willing to wait for +results, in case the timeout is reached the callback will not be called anymore. + +[IMPORTANT] +================================================================================ ++callback+ and +multiCallback+ parameters documentation must *not* specify ++[in]+, +[out]+, +[inout]+, in Doxygen documentation as this would fool the +automatic wrapper generator, and ultimately break the compilation. +================================================================================ + +.RsFiles::turtleSearchRequest asyncronous JSON API usage example +[source,bash] +-------------------------------------------------------------------------------- +$ cat turtle_search.json +{ + "matchString":"linux" +} +$ curl --data @turtle_search.json http://127.0.0.1:9092/rsFiles/turtleSearchRequest +data: {"retval":true} + +data: {"results":[{"size":157631,"hash":"69709b4d01025584a8def5cd78ebbd1a3cf3fd05","name":"kill_bill_linux_1024x768.jpg"},{"size":192560,"hash":"000000000000000000009a93e5be8486c496f46c","name":"coffee_box_linux2.jpg"},{"size":455087,"hash":"9a93e5be8486c496f46c00000000000000000000","name":"Linux.png"},{"size":182004,"hash":"e8845280912ebf3779e400000000000000000000","name":"Linux_2_6.png"}]} + +data: {"results":[{"size":668,"hash":"e8845280912ebf3779e400000000000000000000","name":"linux.png"},{"size":70,"hash":"e8845280912ebf3779e400000000000000000000","name":"kali-linux-2016.2-amd64.txt.sha1sum"},{"size":3076767744,"hash":"e8845280912ebf3779e400000000000000000000","name":"kali-linux-2016.2-amd64.iso"},{"size":2780872,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.bin"},{"size":917504,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.lzma"},{"size":2278404096,"hash":"e8845280912ebf3779e400000000000000000000","name":"gentoo-linux-livedvd-amd64-multilib-20160704.iso"},{"size":151770333,"hash":"e8845280912ebf3779e400000000000000000000","name":"flashtool-0.9.23.0-linux.tar.7z"},{"size":2847372,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.elf"},{"size":1310720,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux.gz"},{"size":987809,"hash":"e8845280912ebf3779e400000000000000000000","name":"openwrt-ar71xx-generic-vmlinux-lzma.elf"}]} + +-------------------------------------------------------------------------------- + +By default JSON API methods requires client authentication and their wrappers +are automatically generated by +json-api-generator+. +In some cases methods need do be accessible without authentication such as ++rsLoginHelper/getLocations+ so in the doxygen documentaion they have the custom +command +@jsonapi{RS_VERSION,unauthenticated}+. +Other methods such as +/rsControl/rsGlobalShutDown+ need special care so they +are marked with the custom doxygen command +@jsonapi{RS_VERSION,manualwrapper}+ +and their wrappers are not automatically generated but written manually into ++JsonApiServer::JsonApiServer(...)+. + +== Quirks + +=== 64 bits integers handling + +While JSON doesn't have problems representing 64 bits integers JavaScript, Dart +and other languages are not capable to handle those numbers natively. +To overcome this limitation JSON API output 64 bit integers as an object with +two keys, one as proper integer and one as string representation. + +.JSON API 64 bit integer output example +[source,json] +-------------------------------------------------------------------------------- +"lobby_id": { "xint64": 6215642878098695544, "xstr64": "6215642878098695544" } +-------------------------------------------------------------------------------- + +So from languages that have proper 64bit integers support like Python or C++ one +better read from `xint64` which is represented as a JSON integer, from languages +where there is no proper 64bit integers support like JavaScript one can read from +`xstr64` which is represented as JSON string (note that the first is not wrapped +in "" while the latter is). + +When one input a 64bit integer into the JSON API it first try to parse it as if +it was sent the old way for retrocompatibility. + +.JSON API 64 bit integer deprecated format input example +[source,json] +-------------------------------------------------------------------------------- +"lobby_id":6215642878098695544 +-------------------------------------------------------------------------------- + +This way is *DEPRECATED* and may disappear in the future, it is TEMPORALLY kept +only for retrocompatibiliy with old clients. + +If retrocompatible parsing attempt fail then it try to parse with the new way +with proper JSON integer format. + +.JSON API 64 bit integer new proper integer format input example +[source,json] +-------------------------------------------------------------------------------- +lobby_id": { "xint64": 6215642878098695544 } +-------------------------------------------------------------------------------- + +If this fails then it try to parse with the new way with JSON string format. + +.JSON API 64 bit integer new string format input example +[source,json] +-------------------------------------------------------------------------------- +"lobby_id": { "xstr64": "6215642878098695544" } +-------------------------------------------------------------------------------- + +[WARNING] +================================================================================ +Clients written in languages without proper 64bit integers support must +use *ONLY* the string format otherwise they will send approximated values and +get unexpected results from the JSON API, because parsing will success but the +value will not be exactly the one you believe you sent. +================================================================================ + + +== A bit of history + +=== First writings about this + +The previous attempt of exposing a RetroShare JSON API is called +libresapi+ and +unfortunatley it requires a bunch of boilerplate code when we want to expose +something present in the {Cxx} API in the JSON API. + +As an example here you can see the libresapi that exposes part of the retroshare +chat {Cxx} API and lot of boilerplate code just to convert {Cxx} objects to JSON + +https://github.com/RetroShare/RetroShare/blob/v0.6.4/libresapi/src/api/ChatHandler.cpp#L44 + +To avoid the {Cxx} to JSON and back conversion boilerplate code I have worked out +an extension to our {Cxx} serialization code so it is capable to serialize and +deserialize to JSON you can see it in this pull request + +https://github.com/RetroShare/RetroShare/pull/1155 + +So first step toward having a good API is to take advantage of the fact that RS +is now capable of converting C++ objects from and to JSON. + +The current API is accessible via HTTP and unix socket, there is no +authentication in both of them, so anyone having access to the HTTP server or to +the unix socket can access the API without extra restrictions. +Expecially for the HTTP API this is a big risk because also if the http server +listen on 127.0.0.1 every application on the machine (even rogue javascript +running on your web browser) can access that and for example on android it is +not safe at all (because of that I implemented the unix socket access so at +least in android API was reasonably safe) because of this. + +A second step to improve the API would be to implement some kind of API +authentication mechanism (it would be nice that the mechanism is handled at API +level and not at transport level so we can use it for any API trasport not just +HTTP for example) + +The HTTP server used by libresapi is libmicrohttpd server that is very minimal, +it doesn't provide HTTPS nor modern HTTP goodies, like server notifications, +websockets etc. because the lack of support we have a token polling mechanism in +libresapi to avoid polling for every thing but it is still ugly, so if we can +completely get rid of polling in the API that would be really nice. +I have done a crawl to look for a replacement and briefly looked at + +- https://www.gnu.org/software/libmicrohttpd/ +- http://wolkykim.github.io/libasyncd/ +- https://github.com/corvusoft/restbed +- https://code.facebook.com/posts/1503205539947302/introducing-proxygen-facebook-s-c-http-framework/ +- https://github.com/cmouse/yahttp + +taking in account a few metrics like modern HTTP goodies support, license, +platform support, external dependencies and documentation it seemed to me that +restbed is the more appropriate. + +Another source of boilerplate code into libresapi is the mapping between JSON +API requests and C++ API methods as an example you can look at this + +https://github.com/RetroShare/RetroShare/blob/v0.6.4/libresapi/src/api/ChatHandler.cpp#L158 + +and this + +https://github.com/RetroShare/RetroShare/blob/v0.6.4/libresapi/src/api/ApiServer.cpp#L253 + +The abstract logic of this thing is, when libreasapi get a request like ++/chat/initiate_distant_chat+ then call ++ChatHandler::handleInitiateDistantChatConnexion+ which in turn is just a +wrapper of +RsMsgs::initiateDistantChatConnexion+ all this process is basically +implemented as boilerplate code and would be unnecessary in a smarter design of +the API because almost all the information needed is already present in the +C++ API +libretroshare/src/retroshare+. + +So a third step to improve the JSON API would be to remove this source of +boilerplate code by automatizing the mapping between C++ and JSON API call. + +This may result a little tricky as language parsing or other adevanced things +may be required. + +Hope this dive is useful for you + +Cheers + +G10h4ck + +=== Second writings about this + +I have been investigating a bit more about: +[verse, G10h4ck] +________________________________________________________________________________ +So a third step to improve the JSON API would be to remove this source of +boilerplate code by automatizing the mapping between C++ and JSON API call +________________________________________________________________________________ + +After spending some hours investigating this topic the most reasonable approach +seems to: + +1. Properly document headers in +libretroshare/src/retroshare/+ in doxygen syntax +specifying wihich params are input and/or output (doxygen sysntax for this is ++@param[in/out/inout]+) this will be the API documentation too. + +2. At compile time use doxygen to generate XML description of the headers and use +the XML to generate the JSON api server stub. +http://www.stack.nl/~dimitri/doxygen/manual/customize.html#xmlgenerator + +3. Enjoy diff --git a/libretroshare/src/jsonapi/async-method-wrapper-template.cpp.tmpl b/libretroshare/src/jsonapi/async-method-wrapper-template.cpp.tmpl new file mode 100644 index 000000000..29f53ad48 --- /dev/null +++ b/libretroshare/src/jsonapi/async-method-wrapper-template.cpp.tmpl @@ -0,0 +1,80 @@ +/******************************************************************************* + * RetroShare JSON API * + * * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License version 3 as * + * published by the Free Software Foundation. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +registerHandler( "$%apiPath%$", + [this](const std::shared_ptr session) +{ + const std::multimap headers + { + { "Connection", "keep-alive" }, + { "Content-Type", "text/event-stream" } + }; + session->yield(rb::OK, headers); + + size_t reqSize = session->get_request()->get_header("Content-Length", 0); + session->fetch( reqSize, [this]( + const std::shared_ptr session, + const rb::Bytes& body ) + { + INITIALIZE_API_CALL_JSON_CONTEXT; + + if( !checkRsServicePtrReady( + $%instanceName%$, "$%instanceName%$", cAns, session ) ) + return; + +$%paramsDeclaration%$ + +$%inputParamsDeserialization%$ + + const std::weak_ptr weakService(mService); + const std::weak_ptr weakSession(session); + $%callbackName%$ = [weakService, weakSession]($%callbackParams%$) + { + auto session = weakSession.lock(); + if(!session || session->is_closed()) return; + + auto lService = weakService.lock(); + if(!lService || lService->is_down()) return; + +$%callbackParamsSerialization%$ + + std::stringstream sStream; + sStream << "data: " << compactJSON << ctx.mJson << "\n\n"; + const std::string message = sStream.str(); + + lService->schedule( [weakSession, message]() + { + auto session = weakSession.lock(); + if(!session || session->is_closed()) return; + session->yield(message); + $%sessionEarlyClose%$ + } ); + }; + +$%functionCall%$ + +$%outputParamsSerialization%$ + + // return them to the API caller + std::stringstream message; + message << "data: " << compactJSON << cAns.mJson << "\n\n"; + session->yield(message.str()); + $%sessionDelayedClose%$ + } ); +}, $%requiresAuth%$ ); diff --git a/libretroshare/src/jsonapi/jsonapi-generator-doxygen.conf b/libretroshare/src/jsonapi/jsonapi-generator-doxygen.conf new file mode 100644 index 000000000..0edd1de3e --- /dev/null +++ b/libretroshare/src/jsonapi/jsonapi-generator-doxygen.conf @@ -0,0 +1,230 @@ +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = "libretroshare" + +ALIASES += jsonapi{1}="\xmlonly\endxmlonly" +ALIASES += jsonapi{2}="\xmlonly\endxmlonly" + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +#INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = NO + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = YES + + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + diff --git a/libretroshare/src/jsonapi/jsonapi-generator.py b/libretroshare/src/jsonapi/jsonapi-generator.py new file mode 100755 index 000000000..accf9a0cd --- /dev/null +++ b/libretroshare/src/jsonapi/jsonapi-generator.py @@ -0,0 +1,365 @@ +#!/usr/bin/python3 + +# RetroShare JSON API generator +# +# Copyright (C) 2019 selankon +# Copyright (C) 2021 Gioacchino Mazzurco +# Copyright (C) 2021 Asociación Civil Altermundi +# +# This program is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the +# Free Software Foundation, version 3. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see +# +# SPDX-FileCopyrightText: Retroshare Team +# SPDX-License-Identifier: AGPL-3.0-only + + +# Original idea and implementation by G10h4ck (jsonapi-generator.cpp) +# Initial python reimplementation by Sehraf +# +# This python 3 script has superseded the original C++/Qt implementation this +# and is now used at build time in without depending on Qt. + +import os +import sys + +import xml.etree.ElementTree as ET +from string import Template + + +class MethodParam: + _type = '' + _name = '' + _defval = '' + _in = False + _out = False + _isMultiCallback = False + _isSingleCallback = False + + +class TemplateOwn(Template): + delimiter = '$%' + pattern = ''' + \$%(?: + (?P\$\%) | # Escape sequence of two delimiters + (?P[_a-z][_a-z0-9]*)%\$ | # delimiter and a Python identifier + {(?P[_a-z][_a-z0-9]*)} | # delimiter and a braced identifier + (?P) # Other ill-formed delimiter exprs + ) + ''' + + +def getText(e): + return "".join(e.itertext()) + + +def processFile(file): + try: + dom1 = ET.parse(file).getroot() + except FileNotFoundError: + print('Can\'t open:', file) + + headerFileInfo = dom1[0].findall('location')[0].attrib['file'] + headerRelPath = os.path.dirname(headerFileInfo).split('/')[-1] + '/' + os.path.basename(headerFileInfo) + + for sectDef in dom1.findall('.//memberdef'): + if sectDef.attrib['kind'] != 'variable' or sectDef.find('.//jsonapi') == None: + continue + + instanceName = sectDef.find('name').text + typeName = sectDef.find('type/ref').text + + typeFilePath = sectDef.find('type/ref').attrib['refid'] + + try: + dom2 = ET.parse(doxPrefix + typeFilePath + '.xml').getroot() + except FileNotFoundError: + print('Can\'t open:', doxPrefix + typeFilePath + '.xml') + + for member in dom2.findall('.//member'): + refid = member.attrib['refid'] + methodName = member.find('name').text + + requiresAuth = True + + defFilePath = refid.split('_')[0] + '.xml' + defFile = defFilePath + + print('Looking for', typeName, methodName, 'into', typeFilePath) + + try: + defDoc = ET.parse(doxPrefix + defFilePath).getroot() + except FileNotFoundError: + print('Can\'t open:', doxPrefix + defFilePath) + + memberdef = None + for tmpMBD in defDoc.findall('.//memberdef'): + tmpId = tmpMBD.attrib['id'] + tmpKind = tmpMBD.attrib['kind'] + tmpJsonApiTagList = tmpMBD.findall('.//jsonapi') + + if len(tmpJsonApiTagList) != 0 and tmpId == refid and tmpKind == 'function': + tmpJsonApiTag = tmpJsonApiTagList[0] + + tmpAccessValue = None + if 'access' in tmpJsonApiTag.attrib: + tmpAccessValue = tmpJsonApiTag.attrib['access'] + + requiresAuth = 'unauthenticated' != tmpAccessValue; + + if 'manualwrapper' != tmpAccessValue: + memberdef = tmpMBD + + break + + if memberdef == None: + continue + + apiPath = '/' + instanceName + '/' + methodName + + retvalType = getText(memberdef.find('type')) + # Apparently some xml declarations include new lines ('\n') and/or multiple spaces + # Strip them using python magic + retvalType = ' '.join(retvalType.split()) + + paramsMap = {} + orderedParamNames = [] + + hasInput = False + hasOutput = False + hasSingleCallback = False + hasMultiCallback = False + callbackName = '' + callbackParams = '' + + for tmpPE in memberdef.findall('param'): + mp = MethodParam() + + pName = getText(tmpPE.find('declname')) + tmpDefval = tmpPE.find('defval') + mp._defval = getText(tmpDefval) if tmpDefval != None else '' + pType = getText(tmpPE.find('type')) + + if pType.startswith('const '): pType = pType[6:] + if pType.startswith('std::function'): + if pType.endswith('&'): pType = pType[:-1] + if pName.startswith('multiCallback'): + mp._isMultiCallback = True + hasMultiCallback = True + elif pName.startswith('callback'): + mp._isSingleCallback = True + hasSingleCallback = True + callbackName = pName + callbackParams = pType + else: + pType = pType.replace('&', '').replace(' ', '') + + # Apparently some xml declarations include new lines ('\n') and/or multiple spaces + # Strip them using python magic + pType = ' '.join(pType.split()) + mp._defval = ' '.join(mp._defval.split()) + + mp._type = pType + mp._name = pName + + paramsMap[pName] = mp + orderedParamNames.append(pName) + + for tmpPN in memberdef.findall('.//parametername'): + tmpParam = paramsMap[tmpPN.text] + tmpD = tmpPN.attrib['direction'] if 'direction' in tmpPN.attrib else '' + + if 'in' in tmpD: + tmpParam._in = True + hasInput = True + if 'out' in tmpD: + tmpParam._out = True + hasOutput = True + + # Params sanity check + for pmKey in paramsMap: + pm = paramsMap[pmKey] + if not (pm._isMultiCallback or pm._isSingleCallback or pm._in or pm._out): + print('ERROR', 'Parameter:', pm._name, 'of:', apiPath, + 'declared in:', headerRelPath, + 'miss doxygen parameter direction attribute!', + defFile) + sys.exit() + + functionCall = '\t\t' + if retvalType != 'void': + functionCall += retvalType + ' retval = ' + hasOutput = True + functionCall += instanceName + '->' + methodName + '(' + functionCall += ', '.join(orderedParamNames) + ');\n' + + print(instanceName, apiPath, retvalType, typeName, methodName) + for pn in orderedParamNames: + mp = paramsMap[pn] + print('\t', mp._type, mp._name, mp._in, mp._out) + + inputParamsDeserialization = '' + if hasInput: + inputParamsDeserialization += '\t\t{\n' + inputParamsDeserialization += '\t\t\tRsGenericSerializer::SerializeContext& ctx(cReq);\n' + inputParamsDeserialization += '\t\t\tRsGenericSerializer::SerializeJob j(RsGenericSerializer::FROM_JSON);\n'; + + outputParamsSerialization = '' + if hasOutput: + outputParamsSerialization += '\t\t{\n' + outputParamsSerialization += '\t\t\tRsGenericSerializer::SerializeContext& ctx(cAns);\n' + outputParamsSerialization += '\t\t\tRsGenericSerializer::SerializeJob j(RsGenericSerializer::TO_JSON);\n'; + + paramsDeclaration = '' + for pn in orderedParamNames: + mp = paramsMap[pn] + paramsDeclaration += '\t\t' + mp._type + ' ' + mp._name + if mp._defval != '': + paramsDeclaration += ' = ' + mp._defval + paramsDeclaration += ';\n' + if mp._in: + inputParamsDeserialization += '\t\t\tRS_SERIAL_PROCESS(' + inputParamsDeserialization += mp._name + ');\n' + if mp._out: + outputParamsSerialization += '\t\t\tRS_SERIAL_PROCESS(' + outputParamsSerialization += mp._name + ');\n' + + if hasInput: + inputParamsDeserialization += '\t\t}\n' + if retvalType != 'void': + outputParamsSerialization += '\t\t\tRS_SERIAL_PROCESS(retval);\n' + if hasOutput: + outputParamsSerialization += '\t\t}\n' + + captureVars = '' + + sessionEarlyClose = '' + if hasSingleCallback: + sessionEarlyClose = 'session->close();' + + sessionDelayedClose = '' + if hasMultiCallback: + sessionDelayedClose = """ + RsThread::async( [=]() + { + std::this_thread::sleep_for( + std::chrono::seconds(maxWait+120) ); + auto lService = weakService.lock(); + if(!lService || lService->is_down()) return; + lService->schedule( [=]() + { + auto session = weakSession.lock(); + if(session && session->is_open()) + session->close(); + } ); + } ); +""" + captureVars = 'this' + + callbackParamsSerialization = '' + + if hasSingleCallback or hasMultiCallback or (callbackParams.find('(') + 2 < callbackParams.find(')')): + cbs = '' + + callbackParams = callbackParams.split('(')[1] + callbackParams = callbackParams.split(')')[0] + + cbs += '\t\t\tRsGenericSerializer::SerializeContext ctx;\n' + + for cbPar in callbackParams.split(','): + isConst = cbPar.startswith('const ') + pSep = ' ' + isRef = '&' in cbPar + if isRef: pSep = '&' + sepIndex = cbPar.rfind(pSep) + 1 + cpt = cbPar[0:sepIndex][6:] + cpn = cbPar[sepIndex:] + + cbs += '\t\t\tRsTypeSerializer::serial_process(' + cbs += 'RsGenericSerializer::TO_JSON, ctx, ' + if isConst: + cbs += 'const_cast<' + cbs += cpt + cbs += '>(' + cbs += cpn + if isConst: cbs += ')' + cbs += ', "' + cbs += cpn + cbs += '" );\n' + + callbackParamsSerialization += cbs + + substitutionsMap = dict() + substitutionsMap['paramsDeclaration'] = paramsDeclaration + substitutionsMap['inputParamsDeserialization'] = inputParamsDeserialization + substitutionsMap['outputParamsSerialization'] = outputParamsSerialization + substitutionsMap['instanceName'] = instanceName + substitutionsMap['functionCall'] = functionCall + substitutionsMap['apiPath'] = apiPath + substitutionsMap['sessionEarlyClose'] = sessionEarlyClose + substitutionsMap['sessionDelayedClose'] = sessionDelayedClose + substitutionsMap['captureVars'] = captureVars + substitutionsMap['callbackName'] = callbackName + substitutionsMap['callbackParams'] = callbackParams + substitutionsMap['callbackParamsSerialization'] = callbackParamsSerialization + substitutionsMap['requiresAuth'] = 'true' if requiresAuth else 'false' + + # print(substitutionsMap) + + templFilePath = sourcePath + if hasMultiCallback or hasSingleCallback: + templFilePath += '/async-method-wrapper-template.cpp.tmpl' + else: + templFilePath += '/method-wrapper-template.cpp.tmpl' + + templFile = open(templFilePath, 'r') + wrapperDef = TemplateOwn(templFile.read()) + + tmp = wrapperDef.substitute(substitutionsMap) + wrappersDefFile.write(tmp) + + cppApiIncludesSet.add('#include "' + headerRelPath + '"\n') + + +if len(sys.argv) != 3: + print('Usage:', sys.argv[0], 'SOURCE_PATH OUTPUT_PATH Got:', sys.argv[:]) + sys.exit(-1) + +sourcePath = str(sys.argv[1]) +outputPath = str(sys.argv[2]) +doxPrefix = outputPath + '/xml/' + +try: + wrappersDefFile = open(outputPath + '/jsonapi-wrappers.inl', 'w') +except FileNotFoundError: + print('Can\'t open:', outputPath + '/jsonapi-wrappers.inl') + +try: + cppApiIncludesFile = open(outputPath + '/jsonapi-includes.inl', 'w'); +except FileNotFoundError: + print('Can\'t open:', outputPath + '/jsonapi-includes.inl') + +cppApiIncludesSet = set() + +filesIterator = None +try: + filesIterator = os.listdir(doxPrefix) +except FileNotFoundError: + print("Doxygen xml output dir not found: ", doxPrefix) + os.exit(-1) + +for file in filesIterator: + if file.endswith("8h.xml"): + processFile(os.path.join(doxPrefix, file)) + + +for incl in cppApiIncludesSet: + cppApiIncludesFile.write(incl) diff --git a/libretroshare/src/jsonapi/jsonapi.cpp b/libretroshare/src/jsonapi/jsonapi.cpp index 4b003f225..2f2ded30c 100644 --- a/libretroshare/src/jsonapi/jsonapi.cpp +++ b/libretroshare/src/jsonapi/jsonapi.cpp @@ -796,8 +796,9 @@ void JsonApiServer::run() } catch(std::exception& e) { - RsErr() << __PRETTY_FUNCTION__ << " Failure starting JSON API server: " - << e.what() << std::endl; + /* TODO: find a way to report back programmatically if failed listening + * port */ + RS_ERR("Failure starting JSON API server: ", e.what()); print_stacktrace(); return; } diff --git a/libretroshare/src/jsonapi/method-wrapper-template.cpp.tmpl b/libretroshare/src/jsonapi/method-wrapper-template.cpp.tmpl new file mode 100644 index 000000000..a3927fba9 --- /dev/null +++ b/libretroshare/src/jsonapi/method-wrapper-template.cpp.tmpl @@ -0,0 +1,50 @@ +/******************************************************************************* + * RetroShare JSON API * + * * + * Copyright (C) 2018-2019 Gioacchino Mazzurco * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +registerHandler( "$%apiPath%$", + [](const std::shared_ptr session) +{ + size_t reqSize = session->get_request()->get_header("Content-Length", 0); + session->fetch( reqSize, []( + const std::shared_ptr session, + const rb::Bytes& body ) + { + INITIALIZE_API_CALL_JSON_CONTEXT; + + if( !checkRsServicePtrReady( + $%instanceName%$, "$%instanceName%$", cAns, session ) ) + return; + +$%paramsDeclaration%$ + + // deserialize input parameters from JSON +$%inputParamsDeserialization%$ + + // call retroshare C++ API +$%functionCall%$ + + // serialize out parameters and return value to JSON +$%outputParamsSerialization%$ + + // return them to the API caller + DEFAULT_API_CALL_JSON_RETURN(rb::OK); + } ); +}, $%requiresAuth%$ ); + diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index e79a5e82e..bc4e6beb5 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: (C) 2004-2019 Retroshare Team +# SPDX-FileCopyrightText: (C) 2004-2021 Retroshare Team # SPDX-License-Identifier: CC0-1.0 !include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri") @@ -9,13 +9,15 @@ libretroshare_shared { } else { CONFIG += staticlib } -CONFIG -= qt + TARGET = retroshare TARGET_PRL = libretroshare DESTDIR = lib !include("use_libretroshare.pri"):error("Including") +QMAKE_CXXFLAGS += -fPIC + # treat warnings as error for better removing #QMAKE_CFLAGS += -Werror #QMAKE_CXXFLAGS += -Werror @@ -206,7 +208,7 @@ linux-* { LIBS *= -ldl DEFINES *= PLUGIN_DIR=\"\\\"$${PLUGIN_DIR}\\\"\" - DEFINES *= DATA_DIR=\"\\\"$${DATA_DIR}\\\"\" + DEFINES *= RS_DATA_DIR=\"\\\"$${RS_DATA_DIR}\\\"\" } linux-g++ { @@ -290,7 +292,7 @@ mac { #LIBS += -lsqlite3 DEFINES *= PLUGIN_DIR=\"\\\"$${PLUGIN_DIR}\\\"\" - DEFINES *= DATA_DIR=\"\\\"$${DATA_DIR}\\\"\" + DEFINES *= RS_DATA_DIR=\"\\\"$${RS_DATA_DIR}\\\"\" } ################################# FreeBSD ########################################## @@ -364,6 +366,7 @@ HEADERS += chat/distantchat.h \ HEADERS += pqi/authssl.h \ pqi/authgpg.h \ pgp/pgphandler.h \ + pgp/openpgpsdkhandler.h \ pgp/pgpkeyutil.h \ pqi/pqifdbin.h \ pqi/rstcpsocket.h \ @@ -447,7 +450,7 @@ HEADERS += rsitems/rsitem.h \ serialiser/rstlvkeyvalue.h \ serialiser/rstlvgenericparam.h \ serialiser/rstlvgenericmap.h \ - serialiser/rstlvgenericmap.inl \ + serialiser/rstlvgenericmap.inl \ serialiser/rstlvlist.h \ serialiser/rstlvmaps.h \ serialiser/rstlvbanlist.h \ @@ -489,6 +492,7 @@ HEADERS += util/folderiterator.h \ util/rsmemory.h \ util/smallobject.h \ util/rsdir.h \ + util/rsfile.h \ util/argstream.h \ util/rsdiscspace.h \ util/rsnet.h \ @@ -514,7 +518,8 @@ HEADERS += util/folderiterator.h \ util/cxx11retrocompat.h \ util/cxx14retrocompat.h \ util/cxx17retrocompat.h \ - util/rsurl.h + util/cxx23retrocompat.h \ + util/rsurl.h SOURCES += ft/ftchunkmap.cc \ ft/ftcontroller.cc \ @@ -541,6 +546,7 @@ SOURCES += chat/distantchat.cc \ SOURCES += pqi/authgpg.cc \ pqi/authssl.cc \ pgp/pgphandler.cc \ + pgp/openpgpsdkhandler.cc \ pgp/pgpkeyutil.cc \ pgp/rscertificate.cc \ pgp/pgpauxutils.cc \ @@ -644,6 +650,7 @@ SOURCES += util/folderiterator.cc \ util/rsexpr.cc \ util/smallobject.cc \ util/rsdir.cc \ + util/rsfile.cc \ util/rsdiscspace.cc \ util/rsnet.cc \ util/rsnet_ss.cc \ @@ -725,6 +732,41 @@ SOURCES += rsitems/rsnxsitems.cc \ gxs/rsgxsrequesttypes.cc \ gxs/rsnxsobserver.cpp +# Tor +HEADERS += retroshare/rstor.h + +HEADERS += tor/AddOnionCommand.h \ + tor/AuthenticateCommand.h \ + tor/CryptoKey.h \ + tor/GetConfCommand.h \ + tor/HiddenService.h \ + tor/PendingOperation.h \ + tor/ProtocolInfoCommand.h \ + tor/TorTypes.h \ + tor/SetConfCommand.h \ + tor/StrUtil.h \ + tor/bytearray.h \ + tor/TorControl.h \ + tor/TorControlCommand.h \ + tor/TorControlSocket.h \ + tor/TorManager.h \ + tor/TorProcess.h + +SOURCES += tor/AddOnionCommand.cpp \ + tor/AuthenticateCommand.cpp \ + tor/GetConfCommand.cpp \ + tor/HiddenService.cpp \ + tor/ProtocolInfoCommand.cpp \ + tor/SetConfCommand.cpp \ + tor/TorControlCommand.cpp \ + tor/TorControl.cpp \ + tor/TorControlSocket.cpp \ + tor/TorManager.cpp \ + tor/TorProcess.cpp \ + tor/CryptoKey.cpp \ + tor/PendingOperation.cpp \ + tor/StrUtil.cpp + # gxs tunnels HEADERS += gxstunnel/p3gxstunnel.h \ gxstunnel/rsgxstunnelitems.h \ @@ -1103,6 +1145,8 @@ test_bitdht { ################################# Android ##################################### android-* { + lessThan(ANDROID_API_VERSION, 24) { + ## TODO: This probably disable largefile support and maybe is not necessary with ## __ANDROID_API__ >= 24 hence should be made conditional or moved to a ## compatibility header @@ -1110,12 +1154,26 @@ android-* { DEFINES *= "fseeko64=fseeko" DEFINES *= "ftello64=ftello" +## @See: rs_android/README-ifaddrs-android.adoc + HEADERS += \ + rs_android/ifaddrs-android.h \ + rs_android/LocalArray.h \ + rs_android/ScopedFd.h + } + ## Static library are very susceptible to order in command line sLibs = bz2 $$RS_UPNP_LIB $$RS_SQL_LIB ssl crypto LIBS += $$linkStaticLibs(sLibs) PRE_TARGETDEPS += $$pretargetStaticLibs(sLibs) - HEADERS += util/androiddebug.h + HEADERS += \ + rs_android/androidcoutcerrcatcher.hpp \ + rs_android/retroshareserviceandroid.hpp \ + rs_android/rsjni.hpp + + SOURCES += rs_android/rsjni.cpp \ + rs_android/retroshareserviceandroid.cpp \ + rs_android/errorconditionwrap.cpp } diff --git a/libretroshare/src/pgp/openpgpsdkhandler.cc b/libretroshare/src/pgp/openpgpsdkhandler.cc new file mode 100644 index 000000000..c619ca4c5 --- /dev/null +++ b/libretroshare/src/pgp/openpgpsdkhandler.cc @@ -0,0 +1,1815 @@ +/******************************************************************************* + * libretroshare/src/pgp: pgphandler.cc * + * * + * libretroshare: retroshare core library * + * * + * Copyright 2018 Cyril Soler * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include + +#ifdef WINDOWS_SYS +#include +#include "util/rsstring.h" +#include "util/rswin.h" +#endif + +extern "C" { +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +} +#include "openpgpsdkhandler.h" + +#include "util/rsdir.h" +#include "util/rsdiscspace.h" +#include "util/rsmemory.h" +#include "pgp/pgpkeyutil.h" +#include "retroshare/rspeers.h" + +static const uint32_t PGP_CERTIFICATE_LIMIT_MAX_NAME_SIZE = 64 ; +static const uint32_t PGP_CERTIFICATE_LIMIT_MAX_EMAIL_SIZE = 64 ; +static const uint32_t PGP_CERTIFICATE_LIMIT_MAX_PASSWD_SIZE = 1024 ; + +//#define DEBUG_PGPHANDLER 1 +//#define PGPHANDLER_DSA_SUPPORT + +ops_keyring_t *OpenPGPSDKHandler::allocateOPSKeyring() +{ + ops_keyring_t *kr = (ops_keyring_t*)rs_malloc(sizeof(ops_keyring_t)) ; + + if(kr == NULL) + return NULL ; + + kr->nkeys = 0 ; + kr->nkeys_allocated = 0 ; + kr->keys = 0 ; + + return kr ; +} + +ops_parse_cb_return_t cb_get_passphrase(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo)// __attribute__((unused))) +{ + const ops_parser_content_union_t *content=&content_->content; + bool prev_was_bad = false ; + + switch(content_->tag) + { + case OPS_PARSER_CMD_GET_SK_PASSPHRASE_PREV_WAS_BAD: prev_was_bad = true ; + /* fallthrough */ + case OPS_PARSER_CMD_GET_SK_PASSPHRASE: + { + std::string passwd; + std::string uid_hint ; + + if(cbinfo->cryptinfo.keydata->nuids > 0) + uid_hint = std::string((const char *)cbinfo->cryptinfo.keydata->uids[0].user_id) ; + uid_hint += "(" + RsPgpId(cbinfo->cryptinfo.keydata->key_id).toStdString()+")" ; + + bool cancelled = false ; + passwd = PGPHandler::passphraseCallback()(NULL,"",uid_hint.c_str(),NULL,prev_was_bad,&cancelled) ; + + if(cancelled) + *(unsigned char *)cbinfo->arg = 1; + + *(content->secret_key_passphrase.passphrase)= (char *)ops_mallocz(passwd.length()+1) ; + memcpy(*(content->secret_key_passphrase.passphrase),passwd.c_str(),passwd.length()) ; + return OPS_KEEP_MEMORY; + } + break; + + default: + break; + } + + return OPS_RELEASE_MEMORY; +} + +OpenPGPSDKHandler::OpenPGPSDKHandler(const std::string& pubring, const std::string& secring,const std::string& trustdb,const std::string& pgp_lock_filename) + : PGPHandler(pubring,secring,trustdb,pgp_lock_filename) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + // Allocate public and secret keyrings. + // + _pubring = allocateOPSKeyring() ; + _secring = allocateOPSKeyring() ; + + // Check that the file exists. If not, create a void keyring. + + FILE *ftest ; + ftest = RsDirUtil::rs_fopen(pubring.c_str(),"rb") ; + bool pubring_exist = (ftest != NULL) ; + if(ftest != NULL) + fclose(ftest) ; + ftest = RsDirUtil::rs_fopen(secring.c_str(),"rb") ; + bool secring_exist = (ftest != NULL) ; + if(ftest != NULL) + fclose(ftest) ; + + // Read public and secret keyrings from supplied files. + // + if(pubring_exist) + { + if(ops_false == ops_keyring_read_from_file(_pubring, false, pubring.c_str())) + throw std::runtime_error("OpenPGPSDKHandler::readKeyRing(): cannot read pubring. File corrupted.") ; + } + else + RsErr() << "pubring file \"" << pubring << "\" not found. Creating a void keyring." ; + + const ops_keydata_t *keydata ; + int i=0 ; + while( (keydata = ops_keyring_get_key_by_index(_pubring,i)) != NULL ) + { + PGPCertificateInfo& cert(_public_keyring_map[ RsPgpId(keydata->key_id) ]) ; + + // Init all certificates. + + initCertificateInfo(cert,keydata,i) ; + + // Validate signatures. + + validateAndUpdateSignatures(cert,keydata) ; + + ++i ; + } + _pubring_last_update_time = time(NULL) ; + _pubring_changed = false; + + RsErr() << "Pubring read successfully." ; + + if(secring_exist) + { + if(ops_false == ops_keyring_read_from_file(_secring, false, secring.c_str())) + throw std::runtime_error("OpenPGPSDKHandler::readKeyRing(): cannot read secring. File corrupted.") ; + } + else + RsErr() << "secring file \"" << secring << "\" not found. Creating a void keyring." ; + + i=0 ; + while( (keydata = ops_keyring_get_key_by_index(_secring,i)) != NULL ) + { + initCertificateInfo(_secret_keyring_map[ RsPgpId(keydata->key_id) ],keydata,i) ; + ++i ; + } + _secring_last_update_time = time(NULL) ; + + RsErr() << "Secring read successfully." ; + + locked_readPrivateTrustDatabase() ; + _trustdb_last_update_time = time(NULL) ; +} + +void OpenPGPSDKHandler::initCertificateInfo(PGPCertificateInfo& cert,const ops_keydata_t *keydata,uint32_t index) +{ + // Parse certificate name + // + + if(keydata->uids != NULL) + { + std::string namestring( (char *)keydata->uids[0].user_id ) ; + + cert._name = "" ; + uint32_t i=0; + while(i < namestring.length() && namestring[i] != '(' && namestring[i] != '<') { cert._name += namestring[i] ; ++i ;} + + // trim right spaces + std::string::size_type found = cert._name.find_last_not_of(' '); + if (found != std::string::npos) + cert._name.erase(found + 1); + else + cert._name.clear(); // all whitespace + + std::string& next = (namestring[i] == '(')?cert._comment:cert._email ; + ++i ; + next = "" ; + while(i < namestring.length() && namestring[i] != ')' && namestring[i] != '>') { next += namestring[i] ; ++i ;} + + while(i < namestring.length() && namestring[i] != '(' && namestring[i] != '<') { next += namestring[i] ; ++i ;} + + if(i< namestring.length()) + { + std::string& next2 = (namestring[i] == '(')?cert._comment:cert._email ; + ++i ; + next2 = "" ; + while(i < namestring.length() && namestring[i] != ')' && namestring[i] != '>') { next2 += namestring[i] ; ++i ;} + } + } + + cert._trustLvl = 1 ; // to be setup accordingly + cert._validLvl = 1 ; // to be setup accordingly + cert._key_index = index ; + cert._flags = 0 ; + cert._time_stamp = 0 ;// "never" by default. Will be updated by trust database, and effective key usage. + + switch(keydata->key.pkey.algorithm) + { + case OPS_PKA_RSA: cert._type = PGPCertificateInfo::PGP_CERTIFICATE_TYPE_RSA ; + break ; + case OPS_PKA_DSA: cert._type = PGPCertificateInfo::PGP_CERTIFICATE_TYPE_DSA ; + cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM ; + break ; + default: cert._type = PGPCertificateInfo::PGP_CERTIFICATE_TYPE_UNKNOWN ; + cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM ; + break ; + } + + ops_fingerprint_t f ; + ops_fingerprint(&f,&keydata->key.pkey) ; + + cert._fpr = PGPFingerprintType(f.fingerprint) ; +} + +bool OpenPGPSDKHandler::validateAndUpdateSignatures(PGPCertificateInfo& cert,const ops_keydata_t *keydata) +{ + ops_validate_result_t* result=(ops_validate_result_t*)ops_mallocz(sizeof *result); + ops_boolean_t res = ops_validate_key_signatures(result,keydata,_pubring,cb_get_passphrase) ; + + if(res == ops_false) + { + static ops_boolean_t already = 0 ; + if(!already) + { + RsErr() << "(WW) Error in OpenPGPSDKHandler::validateAndUpdateSignatures(). Validation failed for at least some signatures." ; + already = 1 ; + } + } + + bool ret = false ; + + // Parse signers. + // + + if(result != NULL) + for(size_t i=0;ivalid_count;++i) + { + RsPgpId signer_id(result->valid_sigs[i].signer_id); + + if(cert.signers.find(signer_id) == cert.signers.end()) + { + cert.signers.insert(signer_id) ; + ret = true ; + } + } + + ops_validate_result_free(result) ; + + return ret ; +} + +OpenPGPSDKHandler::~OpenPGPSDKHandler() +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. +#ifdef DEBUG_PGPHANDLER + RsErr() << "Freeing OpenPGPSDKHandler. Deleting keyrings." ; +#endif + + // no need to free the the _map_ elements. They will be freed by the following calls: + // + ops_keyring_free(_pubring) ; + ops_keyring_free(_secring) ; + + free(_pubring) ; + free(_secring) ; +} + +void OpenPGPSDKHandler::printOPSKeys() const +{ + RsErr() << "Public keyring list from OPS:" ; + ops_keyring_list(_pubring) ; +} + +bool OpenPGPSDKHandler::haveSecretKey(const RsPgpId& id) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + return locked_getSecretKey(id) != NULL ; +} + +bool OpenPGPSDKHandler::availableGPGCertificatesWithPrivateKeys(std::list& ids) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + // go through secret keyring, and check that we have the pubkey as well. + // + + const ops_keydata_t *keydata = NULL ; + int i=0 ; + + while( (keydata = ops_keyring_get_key_by_index(_secring,i++)) != NULL ) + if(ops_keyring_find_key_by_id(_pubring,keydata->key_id) != NULL) // check that the key is in the pubring as well + { +#ifdef PGPHANDLER_DSA_SUPPORT + if(keydata->key.pkey.algorithm == OPS_PKA_RSA || keydata->key.pkey.algorithm == OPS_PKA_DSA) +#else + if(keydata->key.pkey.algorithm == OPS_PKA_RSA) +#endif + ids.push_back(RsPgpId(keydata->key_id)) ; +#ifdef DEBUG_PGPHANDLER + else + RsErr() << "Skipping keypair " << RsPgpId(keydata->key_id).toStdString() << ", unsupported algorithm: " << keydata->key.pkey.algorithm ; +#endif + } + + return true ; +} + +bool OpenPGPSDKHandler::GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passphrase, RsPgpId& pgpId, const int keynumbits, std::string& errString) +{ + // Some basic checks + + if(!RsDiscSpace::checkForDiscSpace(RS_PGP_DIRECTORY)) + { + errString = std::string("(EE) low disc space in pgp directory. Can't write safely to keyring.") ; + return false ; + } + if(name.length() > PGP_CERTIFICATE_LIMIT_MAX_NAME_SIZE) + { + errString = std::string("(EE) name in certificate exceeds the maximum allowed name size") ; + return false ; + } + if(email.length() > PGP_CERTIFICATE_LIMIT_MAX_EMAIL_SIZE) + { + errString = std::string("(EE) email in certificate exceeds the maximum allowed email size") ; + return false ; + } + if(passphrase.length() > PGP_CERTIFICATE_LIMIT_MAX_PASSWD_SIZE) + { + errString = std::string("(EE) passphrase in certificate exceeds the maximum allowed passphrase size") ; + return false ; + } + if(keynumbits % 1024 != 0) + { + errString = std::string("(EE) RSA key length is not a multiple of 1024") ; + return false ; + } + + // Now the real thing + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + + // 1 - generate keypair - RSA-2048 + // + ops_user_id_t uid ; + char *s = strdup((name + " (Generated by RetroShare) <" + email + ">" ).c_str()) ; + uid.user_id = (unsigned char *)s ; + unsigned long int e = 65537 ; // some prime number + + ops_keydata_t *key = ops_rsa_create_selfsigned_keypair(keynumbits, e, &uid) ; + + free(s) ; + + if(!key) + return false ; + + // 2 - save the private key encrypted to a temporary memory buffer, so as to read an encrypted key to memory + + ops_create_info_t *cinfo = NULL ; + ops_memory_t *buf = NULL ; + ops_setup_memory_write(&cinfo, &buf, 0); + + if(!ops_write_transferable_secret_key(key,(unsigned char *)passphrase.c_str(),passphrase.length(),ops_false,cinfo)) + { + errString = std::string("(EE) Cannot encode secret key to memory!!") ; + return false ; + } + + // 3 - read the memory chunk into an encrypted keyring + + ops_keyring_t *tmp_secring = allocateOPSKeyring() ; + + if(! ops_keyring_read_from_mem(tmp_secring, ops_false, buf)) + { + errString = std::string("(EE) Cannot re-read key from memory!!") ; + return false ; + } + ops_teardown_memory_write(cinfo,buf); // cleanup memory + + // 4 - copy the encrypted private key to the private keyring + + pgpId = RsPgpId(tmp_secring->keys[0].key_id) ; + addNewKeyToOPSKeyring(_secring,tmp_secring->keys[0]) ; + initCertificateInfo(_secret_keyring_map[ pgpId ],&tmp_secring->keys[0],_secring->nkeys-1) ; + +#ifdef DEBUG_PGPHANDLER + RsErr() << "Added new secret key with id " << pgpId.toStdString() << " to secret keyring." ; +#endif + ops_keyring_free(tmp_secring) ; + free(tmp_secring) ; + + // 5 - add key to secret keyring on disk. + + cinfo = NULL ; + std::string secring_path_tmp = _secring_path + ".tmp" ; + + if(RsDirUtil::fileExists(_secring_path) && !RsDirUtil::copyFile(_secring_path,secring_path_tmp)) + { + errString= std::string("Cannot copy secret keyring !! Disk full? Out of disk quota?") ; + return false ; + } + int fd=ops_setup_file_append(&cinfo, secring_path_tmp.c_str()); + + if(!ops_write_transferable_secret_key(key,(unsigned char *)passphrase.c_str(),passphrase.length(),ops_false,cinfo)) + { + errString= std::string("Cannot encode secret key to disk!! Disk full? Out of disk quota?") ; + return false ; + } + ops_teardown_file_write(cinfo,fd) ; + + if(!RsDirUtil::renameFile(secring_path_tmp,_secring_path)) + { + errString= std::string("Cannot rename tmp secret key file ") + secring_path_tmp + " into " + _secring_path +". Disk error?" ; + return false ; + } + + // 6 - copy the public key to the public keyring on disk + + cinfo = NULL ; + std::string pubring_path_tmp = _pubring_path + ".tmp" ; + + if(RsDirUtil::fileExists(_pubring_path) && !RsDirUtil::copyFile(_pubring_path,pubring_path_tmp)) + { + errString= std::string("Cannot encode secret key to disk!! Disk full? Out of disk quota?") ; + return false ; + } + fd=ops_setup_file_append(&cinfo, pubring_path_tmp.c_str()); + + if(!ops_write_transferable_public_key(key, ops_false, cinfo)) + { + errString=std::string("Cannot encode secret key to memory!!") ; + return false ; + } + ops_teardown_file_write(cinfo,fd) ; + + if(!RsDirUtil::renameFile(pubring_path_tmp,_pubring_path)) + { + errString= std::string("Cannot rename tmp public key file ") + pubring_path_tmp + " into " + _pubring_path +". Disk error?" ; + return false ; + } + // 7 - clean + ops_keydata_free(key) ; + + // 8 - re-read the key from the public keyring, and add it to memory. + + _pubring_last_update_time = 0 ; // force update pubring from disk. + locked_syncPublicKeyring() ; + +#ifdef DEBUG_PGPHANDLER + RsErr() << "Added new public key with id " << pgpId.toStdString() << " to public keyring." ; +#endif + + // 9 - Update some flags. + + privateTrustCertificate(pgpId,PGPCertificateInfo::PGP_CERTIFICATE_TRUST_ULTIMATE) ; + + return true ; +} + +std::string OpenPGPSDKHandler::makeRadixEncodedPGPKey(const ops_keydata_t *key,bool include_signatures) +{ + ops_create_info_t* cinfo; + ops_memory_t *buf = NULL ; + ops_setup_memory_write(&cinfo, &buf, 0); + ops_boolean_t armoured = ops_true ; + + if(key->type == OPS_PTAG_CT_PUBLIC_KEY) + { + if(ops_write_transferable_public_key_from_packet_data(key,armoured,cinfo) != ops_true) + return "ERROR: This key cannot be processed by RetroShare because\nDSA certificates are not yet handled." ; + } + else if(key->type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + { + if(ops_write_transferable_secret_key_from_packet_data(key,armoured,cinfo) != ops_true) + return "ERROR: This key cannot be processed by RetroShare because\nDSA certificates are not yet handled." ; + } + else + { + ops_create_info_delete(cinfo); + RsErr() << "Unhandled key type " << key->type ; + return "ERROR: Cannot write key. Unhandled key type. " ; + } + + ops_writer_close(cinfo) ; + + std::string res((char *)ops_memory_get_data(buf),ops_memory_get_length(buf)) ; + ops_teardown_memory_write(cinfo,buf); + + if(!include_signatures) + { + std::string tmp ; + if(PGPKeyManagement::createMinimalKey(res,tmp) ) + res = tmp ; + } + + return res ; +} + +const ops_keydata_t *OpenPGPSDKHandler::locked_getSecretKey(const RsPgpId& id) const +{ + std::map::const_iterator res = _secret_keyring_map.find(id) ; + + if(res == _secret_keyring_map.end()) + return NULL ; + else + return ops_keyring_get_key_by_index(_secring,res->second._key_index) ; +} +const ops_keydata_t *OpenPGPSDKHandler::locked_getPublicKey(const RsPgpId& id,bool stamp_the_key) const +{ + std::map::const_iterator res = _public_keyring_map.find(id) ; + + if(res == _public_keyring_map.end()) + return NULL ; + else + { + if(stamp_the_key) // Should we stamp the key as used? + { + static rstime_t last_update_db_because_of_stamp = 0 ; + rstime_t now = time(NULL) ; + + res->second._time_stamp = now ; + + if(now > last_update_db_because_of_stamp + 3600) // only update database once every hour. No need to do it more often. + { + _trustdb_changed = true ; + last_update_db_because_of_stamp = now ; + } + } + return ops_keyring_get_key_by_index(_pubring,res->second._key_index) ; + } +} + +std::string OpenPGPSDKHandler::SaveCertificateToString(const RsPgpId& id,bool include_signatures) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + const ops_keydata_t *key = locked_getPublicKey(id,false) ; + + if(key == NULL) + { + RsErr() << "Cannot output key " << id.toStdString() << ": not found in keyring." ; + return "" ; + } + + return makeRadixEncodedPGPKey(key,include_signatures) ; +} + +bool OpenPGPSDKHandler::exportPublicKey( const RsPgpId& id, unsigned char*& mem_block, size_t& mem_size, bool armoured, bool include_signatures ) const +{ + mem_block = nullptr; mem_size = 0; // clear just in case + + if(armoured) + { + RsErr() << __PRETTY_FUNCTION__ << " should not be used with " + << "armoured=true, because there's a bug in the armoured export" + << " of OPS" ; + print_stacktrace(); + return false; + } + + RS_STACK_MUTEX(pgphandlerMtx); + const ops_keydata_t* key = locked_getPublicKey(id,false); + + if(!key) + { + RsErr() << __PRETTY_FUNCTION__ << " key id: " << id + << " not found in keyring." ; + return false; + } + + ops_create_info_t* cinfo; + ops_memory_t *buf = nullptr; + ops_setup_memory_write(&cinfo, &buf, 0); + + if(ops_write_transferable_public_key_from_packet_data( + key, armoured, cinfo ) != ops_true) + { + RsErr() << __PRETTY_FUNCTION__ << " This key id " << id + << " cannot be processed by RetroShare because DSA certificates" + << " support is not implemented yet." ; + return false; + } + + ops_writer_close(cinfo); + + mem_size = ops_memory_get_length(buf); + mem_block = reinterpret_cast(malloc(mem_size)); + memcpy(mem_block,ops_memory_get_data(buf),mem_size); + + ops_teardown_memory_write(cinfo,buf); + + if(!include_signatures) + { + size_t new_size; + PGPKeyManagement::findLengthOfMinimalKey(mem_block, mem_size, new_size); + mem_size = new_size; + } + + return true; +} + +bool OpenPGPSDKHandler::exportGPGKeyPair(const std::string& filename,const RsPgpId& exported_key_id) const +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + const ops_keydata_t *pubkey = locked_getPublicKey(exported_key_id,false) ; + + if(pubkey == NULL) + { + RsErr() << "Cannot output key " << exported_key_id.toStdString() << ": not found in public keyring." ; + return false ; + } + const ops_keydata_t *seckey = locked_getSecretKey(exported_key_id) ; + + if(seckey == NULL) + { + RsErr() << "Cannot output key " << exported_key_id.toStdString() << ": not found in secret keyring." ; + return false ; + } + + FILE *f = RsDirUtil::rs_fopen(filename.c_str(),"w") ; + if(f == NULL) + { + RsErr() << "Cannot output key " << exported_key_id.toStdString() << ": file " << filename << " cannot be written. Please check for permissions, quotas, disk space." ; + return false ; + } + + fprintf(f,"%s\n", makeRadixEncodedPGPKey(pubkey,true).c_str()) ; + fprintf(f,"%s\n", makeRadixEncodedPGPKey(seckey,true).c_str()) ; + + fclose(f) ; + return true ; +} + +bool OpenPGPSDKHandler::exportGPGKeyPairToString( std::string& data, const RsPgpId& exportedKeyId, bool includeSignatures, std::string& errorMsg ) const +{ + RS_STACK_MUTEX(pgphandlerMtx); + + const ops_keydata_t *pubkey = locked_getPublicKey(exportedKeyId,false); + + if(!pubkey) + { + errorMsg = "Cannot output key " + exportedKeyId.toStdString() + + ": not found in public keyring."; + return false; + } + const ops_keydata_t *seckey = locked_getSecretKey(exportedKeyId); + + if(!seckey) + { + errorMsg = "Cannot output key " + exportedKeyId.toStdString() + + ": not found in secret keyring."; + return false; + } + + data = makeRadixEncodedPGPKey(pubkey, includeSignatures); + data += "\n"; + data += makeRadixEncodedPGPKey(seckey, includeSignatures); + data += "\n"; + return true; +} + +bool OpenPGPSDKHandler::getGPGDetailsFromBinaryBlock(const unsigned char *mem_block,size_t mem_size,RsPgpId& key_id, std::string& name, std::list& signers) const +{ + ops_keyring_t *tmp_keyring = allocateOPSKeyring(); + ops_memory_t *mem = ops_memory_new() ; + ops_memory_add(mem,mem_block,mem_size); + + if(!ops_keyring_read_from_mem(tmp_keyring,ops_false,mem)) + { + ops_keyring_free(tmp_keyring) ; + free(tmp_keyring) ; + ops_memory_release(mem) ; + free(mem) ; + + RsErr() << "Could not read key. Format error?" ; + //error_string = std::string("Could not read key. Format error?") ; + return false ; + } + ops_memory_release(mem) ; + free(mem) ; + //error_string.clear() ; + + if(tmp_keyring->nkeys != 1) + { + RsErr() << "No or incomplete/invalid key in supplied pgp block." ; + return false ; + } + if(tmp_keyring->keys[0].uids == NULL) + { + RsErr() << "No uid in supplied key." ; + return false ; + } + + key_id = RsPgpId(tmp_keyring->keys[0].key_id) ; + name = std::string((char *)tmp_keyring->keys[0].uids[0].user_id) ; + + // now parse signatures. + // + ops_validate_result_t* result=(ops_validate_result_t*)ops_mallocz(sizeof *result); + ops_boolean_t res ; + + { + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + res = ops_validate_key_signatures(result,&tmp_keyring->keys[0],_pubring,cb_get_passphrase) ; + } + + if(res == ops_false) + RsErr() << "(WW) Error in OpenPGPSDKHandler::validateAndUpdateSignatures(). Validation failed for at least some signatures." ; + + // also add self-signature if any (there should be!). + // + res = ops_validate_key_signatures(result,&tmp_keyring->keys[0],tmp_keyring,cb_get_passphrase) ; + + if(res == ops_false) + RsErr() << "(WW) Error in OpenPGPSDKHandler::validateAndUpdateSignatures(). Validation failed for at least some signatures." ; + + // Parse signers. + // + + std::set signers_set ; // Use a set to remove duplicates. + + if(result != NULL) + for(size_t i=0;ivalid_count;++i) + signers_set.insert(RsPgpId(result->valid_sigs[i].signer_id)) ; + + ops_validate_result_free(result) ; + + ops_keyring_free(tmp_keyring) ; + free(tmp_keyring) ; + + // write to the output variable + + signers.clear() ; + + for(std::set::const_iterator it(signers_set.begin());it!=signers_set.end();++it) + signers.push_back(*it) ; + + return true ; +} + +bool OpenPGPSDKHandler::importGPGKeyPair(const std::string& filename,RsPgpId& imported_key_id,std::string& import_error) +{ + import_error = "" ; + + // 1 - Test for file existance + // + FILE *ftest = RsDirUtil::rs_fopen(filename.c_str(),"r") ; + + if(ftest == NULL) + { + import_error = "Cannot open file " + filename + " for read. Please check access permissions." ; + return false ; + } + + fclose(ftest) ; + + // 2 - Read keyring from supplied file. + // + ops_keyring_t *tmp_keyring = allocateOPSKeyring(); + + if(ops_false == ops_keyring_read_from_file(tmp_keyring, ops_true, filename.c_str())) + { + import_error = "OpenPGPSDKHandler::readKeyRing(): cannot read key file. File corrupted?" ; + free(tmp_keyring); + return false ; + } + + return checkAndImportKeyPair(tmp_keyring, imported_key_id, import_error); +} + +bool OpenPGPSDKHandler::importGPGKeyPairFromString(const std::string &data, RsPgpId &imported_key_id, std::string &import_error) +{ + import_error = "" ; + + ops_memory_t* mem = ops_memory_new(); + ops_memory_add(mem, (unsigned char*)data.data(), data.length()); + + ops_keyring_t *tmp_keyring = allocateOPSKeyring(); + + if(ops_false == ops_keyring_read_from_mem(tmp_keyring, ops_true, mem)) + { + import_error = "OpenPGPSDKHandler::importGPGKeyPairFromString(): cannot parse key data" ; + free(tmp_keyring); + return false ; + } + return checkAndImportKeyPair(tmp_keyring, imported_key_id, import_error); +} + +bool OpenPGPSDKHandler::checkAndImportKeyPair(ops_keyring_t *tmp_keyring, RsPgpId &imported_key_id, std::string &import_error) +{ + if(tmp_keyring == 0) + { + import_error = "OpenPGPSDKHandler::checkAndImportKey(): keyring is null" ; + return false; + } + + if(tmp_keyring->nkeys != 2) + { + import_error = "OpenPGPSDKHandler::importKeyPair(): file does not contain a valid keypair." ; + if(tmp_keyring->nkeys > 2) + import_error += "\nMake sure that your key is a RSA key (DSA is not yet supported) and does not contain subkeys (not supported yet)."; + return false ; + } + + // 3 - Test that keyring contains a valid keypair. + // + const ops_keydata_t *pubkey = NULL ; + const ops_keydata_t *seckey = NULL ; + + if(tmp_keyring->keys[0].type == OPS_PTAG_CT_PUBLIC_KEY) + pubkey = &tmp_keyring->keys[0] ; + else if(tmp_keyring->keys[0].type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + seckey = &tmp_keyring->keys[0] ; + else + { + import_error = "Unrecognised key type in key file for key #0. Giving up." ; + RsErr() << "Unrecognised key type " << tmp_keyring->keys[0].type << " in key file for key #0. Giving up." ; + return false ; + } + if(tmp_keyring->keys[1].type == OPS_PTAG_CT_PUBLIC_KEY) + pubkey = &tmp_keyring->keys[1] ; + else if(tmp_keyring->keys[1].type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) + seckey = &tmp_keyring->keys[1] ; + else + { + import_error = "Unrecognised key type in key file for key #1. Giving up." ; + RsErr() << "Unrecognised key type " << tmp_keyring->keys[1].type << " in key file for key #1. Giving up." ; + return false ; + } + + if(pubkey == nullptr || seckey == nullptr || pubkey == seckey) + { + import_error = "File does not contain a public and a private key. Sorry." ; + return false ; + } + if(memcmp( pubkey->fingerprint.fingerprint, + seckey->fingerprint.fingerprint, + RsPgpFingerprint::SIZE_IN_BYTES ) != 0) + { + import_error = "Public and private keys do nt have the same fingerprint. Sorry!" ; + return false ; + } + if(pubkey->key.pkey.version != 4) + { + import_error = "Public key is not version 4. Rejected!" ; + return false ; + } + + // 4 - now check self-signature for this keypair. For this we build a dummy keyring containing only the key. + // + ops_validate_result_t *result=(ops_validate_result_t*)ops_mallocz(sizeof *result); + + ops_keyring_t dummy_keyring ; + dummy_keyring.nkeys=1 ; + dummy_keyring.nkeys_allocated=1 ; + dummy_keyring.keys=const_cast(pubkey) ; + + ops_validate_key_signatures(result, const_cast(pubkey), &dummy_keyring, cb_get_passphrase) ; + + // Check that signatures contain at least one certification from the user id. + // + bool found = false ; + + for(uint32_t i=0;ivalid_count;++i) + if(!memcmp( + static_cast(result->valid_sigs[i].signer_id), + pubkey->key_id, + RsPgpId::SIZE_IN_BYTES )) + { + found = true ; + break ; + } + + if(!found) + { + import_error = "Cannot validate self signature for the imported key. Sorry." ; + return false ; + } + ops_validate_result_free(result); + + if(!RsDiscSpace::checkForDiscSpace(RS_PGP_DIRECTORY)) + { + import_error = std::string("(EE) low disc space in pgp directory. Can't write safely to keyring.") ; + return false ; + } + // 5 - All test passed. Adding key to keyring. + // + { + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + imported_key_id = RsPgpId(pubkey->key_id) ; + + if(locked_getSecretKey(imported_key_id) == NULL) + { + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + + ops_create_info_t *cinfo = NULL ; + + // Make a copy of the secret keyring + // + std::string secring_path_tmp = _secring_path + ".tmp" ; + if(RsDirUtil::fileExists(_secring_path) && !RsDirUtil::copyFile(_secring_path,secring_path_tmp)) + { + import_error = "(EE) Cannot write secret key to disk!! Disk full? Out of disk quota. Keyring will be left untouched." ; + return false ; + } + + // Append the new key + + int fd=ops_setup_file_append(&cinfo, secring_path_tmp.c_str()); + + if(!ops_write_transferable_secret_key_from_packet_data(seckey,ops_false,cinfo)) + { + import_error = "(EE) Cannot encode secret key to disk!! Disk full? Out of disk quota?" ; + return false ; + } + ops_teardown_file_write(cinfo,fd) ; + + // Rename the new keyring to overwrite the old one. + // + if(!RsDirUtil::renameFile(secring_path_tmp,_secring_path)) + { + import_error = " (EE) Cannot move temp file " + secring_path_tmp + ". Bad write permissions?" ; + return false ; + } + + addNewKeyToOPSKeyring(_secring,*seckey) ; + initCertificateInfo(_secret_keyring_map[ imported_key_id ],seckey,_secring->nkeys-1) ; + } + else + import_error = "Private key already exists! Not importing it again." ; + + if(locked_addOrMergeKey(_pubring,_public_keyring_map,pubkey)) + _pubring_changed = true ; + } + + // 6 - clean + // + ops_keyring_free(tmp_keyring) ; + free(tmp_keyring); + + // write public key to disk + syncDatabase(); + + return true ; +} + +void OpenPGPSDKHandler::addNewKeyToOPSKeyring(ops_keyring_t *kr,const ops_keydata_t& key) +{ + if(kr->nkeys >= kr->nkeys_allocated) + { + kr->keys = (ops_keydata_t *)realloc(kr->keys,(kr->nkeys+1)*sizeof(ops_keydata_t)) ; + kr->nkeys_allocated = kr->nkeys+1; + } + memset(&kr->keys[kr->nkeys],0,sizeof(ops_keydata_t)) ; + ops_keydata_copy(&kr->keys[kr->nkeys],&key) ; + kr->nkeys++ ; +} + +bool OpenPGPSDKHandler::LoadCertificateFromBinaryData(const unsigned char *data,uint32_t data_len,RsPgpId& id,std::string& error_string) +{ + return LoadCertificate(data,data_len,ops_false,id,error_string); +} + +bool OpenPGPSDKHandler::LoadCertificateFromString(const std::string& pgp_cert,RsPgpId& id,std::string& error_string) +{ + return LoadCertificate((unsigned char*)(pgp_cert.c_str()),pgp_cert.length(),ops_true,id,error_string); +} + +bool OpenPGPSDKHandler::LoadCertificate(const unsigned char *data,uint32_t data_len,bool armoured,RsPgpId& id,std::string& error_string) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. +#ifdef DEBUG_PGPHANDLER + RsErr() << "Reading new key from string: " ; +#endif + + ops_keyring_t *tmp_keyring = allocateOPSKeyring(); + ops_memory_t *mem = ops_memory_new() ; + ops_memory_add(mem,data,data_len) ; + + if(!ops_keyring_read_from_mem(tmp_keyring,armoured,mem)) + { + ops_keyring_free(tmp_keyring) ; + free(tmp_keyring) ; + ops_memory_release(mem) ; + free(mem) ; + + RsErr() << "Could not read key. Format error?" ; + error_string = std::string("Could not read key. Format error?") ; + return false ; + } + ops_memory_release(mem) ; + free(mem) ; + error_string.clear() ; + + // Check that there is exactly one key in this data packet. + // + if(tmp_keyring->nkeys != 1) + { + RsErr() << "Loaded certificate contains more than one PGP key. This is not allowed." ; + error_string = "Loaded certificate contains more than one PGP key. This is not allowed." ; + return false ; + } + + const ops_keydata_t *keydata = ops_keyring_get_key_by_index(tmp_keyring,0); + + // Check that the key is a version 4 key + // + if(keydata->key.pkey.version != 4) + { + error_string = "Public key is not version 4. Rejected!" ; + RsErr() << "Received a key with unhandled version number (" << keydata->key.pkey.version << ")" ; + return false ; + } + + // Check that the key is correctly self-signed. + // + ops_validate_result_t* result=(ops_validate_result_t*)ops_mallocz(sizeof *result); + + ops_validate_key_signatures(result,keydata,tmp_keyring,cb_get_passphrase) ; + + bool found = false ; + + for(uint32_t i=0;ivalid_count;++i) + if(!memcmp( + static_cast(result->valid_sigs[i].signer_id), + keydata->key_id, + RsPgpId::SIZE_IN_BYTES )) + { + found = true ; + break ; + } + + if(!found) + { + error_string = "This key is not self-signed. This is required by Retroshare." ; + RsErr() << "This key is not self-signed. This is required by Retroshare." ; + ops_validate_result_free(result); + return false ; + } + ops_validate_result_free(result); + +#ifdef DEBUG_PGPHANDLER + RsErr() << " Key read correctly: " ; + ops_keyring_list(tmp_keyring) ; +#endif + + int i=0 ; + + while( (keydata = ops_keyring_get_key_by_index(tmp_keyring,i++)) != NULL ) + if(locked_addOrMergeKey(_pubring,_public_keyring_map,keydata)) + { + _pubring_changed = true ; +#ifdef DEBUG_PGPHANDLER + RsErr() << " Added the key in the main public keyring." ; +#endif + } + else + RsErr() << "Key already in public keyring." ; + + if(tmp_keyring->nkeys > 0) + id = RsPgpId(tmp_keyring->keys[0].key_id) ; + else + return false ; + + ops_keyring_free(tmp_keyring) ; + free(tmp_keyring) ; + + _pubring_changed = true ; + + return true ; +} + +bool OpenPGPSDKHandler::locked_addOrMergeKey(ops_keyring_t *keyring,std::map& kmap,const ops_keydata_t *keydata) +{ + bool ret = false ; + RsPgpId id(keydata->key_id) ; + +#ifdef DEBUG_PGPHANDLER + RsErr() << "AddOrMergeKey():" ; + RsErr() << " id: " << id.toStdString() ; +#endif + + // See if the key is already in the keyring + const ops_keydata_t *existing_key = NULL; + std::map::const_iterator res = kmap.find(id) ; + + // Checks that + // - the key is referenced by keyid + // - the map is initialized + // - the fingerprint matches! + // + if(res == kmap.end() || (existing_key = ops_keyring_get_key_by_index(keyring,res->second._key_index)) == NULL) + { +#ifdef DEBUG_PGPHANDLER + RsErr() << " Key is new. Adding it to keyring" ; +#endif + addNewKeyToOPSKeyring(keyring,*keydata) ; // the key is new. + initCertificateInfo(kmap[id],keydata,keyring->nkeys-1) ; + existing_key = &(keyring->keys[keyring->nkeys-1]) ; + ret = true ; + } + else + { + if(memcmp( existing_key->fingerprint.fingerprint, + keydata->fingerprint.fingerprint, + RsPgpFingerprint::SIZE_IN_BYTES )) + { + RsErr() << "(EE) attempt to merge key with identical id, but different fingerprint!" ; + return false ; + } + +#ifdef DEBUG_PGPHANDLER + RsErr() << " Key exists. Merging signatures." ; +#endif + ret = mergeKeySignatures(const_cast(existing_key),keydata) ; + + if(ret) + initCertificateInfo(kmap[id],existing_key,res->second._key_index) ; + } + + if(ret) + { + validateAndUpdateSignatures(kmap[id],existing_key) ; + kmap[id]._time_stamp = time(NULL) ; + } + + return ret ; +} + +bool OpenPGPSDKHandler::encryptTextToFile(const RsPgpId& key_id,const std::string& text,const std::string& outfile) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + const ops_keydata_t *public_key = locked_getPublicKey(key_id,true) ; + + if(public_key == NULL) + { + RsErr() << "Cannot get public key of id " << key_id.toStdString() ; + return false ; + } + + if(public_key->type != OPS_PTAG_CT_PUBLIC_KEY) + { + RsErr() << "OpenPGPSDKHandler::encryptTextToFile(): ERROR: supplied id did not return a public key!" ; + return false ; + } + + std::string outfile_tmp = outfile + ".tmp" ; + + ops_create_info_t *info; + int fd = ops_setup_file_write(&info, outfile_tmp.c_str(), ops_true); + + if (fd < 0) + { + RsErr() << "OpenPGPSDKHandler::encryptTextToFile(): ERROR: Cannot write to " << outfile_tmp ; + return false ; + } + + if(!ops_encrypt_stream(info, public_key, NULL, ops_false, ops_true)) + { + RsErr() << "OpenPGPSDKHandler::encryptTextToFile(): ERROR: encryption failed." ; + return false ; + } + + ops_write(text.c_str(), text.length(), info); + ops_teardown_file_write(info, fd); + + if(!RsDirUtil::renameFile(outfile_tmp,outfile)) + { + RsErr() << "OpenPGPSDKHandler::encryptTextToFile(): ERROR: Cannot rename " + outfile_tmp + " to " + outfile + ". Disk error?" ; + return false ; + } + + return true ; +} + +bool OpenPGPSDKHandler::encryptDataBin(const RsPgpId& key_id,const void *data, const uint32_t len, unsigned char *encrypted_data, unsigned int *encrypted_data_len) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + const ops_keydata_t *public_key = locked_getPublicKey(key_id,true) ; + + if(public_key == NULL) + { + RsErr() << "Cannot get public key of id " << key_id.toStdString() ; + return false ; + } + + if(public_key->type != OPS_PTAG_CT_PUBLIC_KEY) + { + RsErr() << "OpenPGPSDKHandler::encryptTextToFile(): ERROR: supplied id did not return a public key!" ; + return false ; + } + if(public_key->key.pkey.algorithm != OPS_PKA_RSA) + { + RsErr() << "OpenPGPSDKHandler::encryptTextToFile(): ERROR: supplied key id " << key_id.toStdString() << " is not an RSA key (DSA for instance, is not supported)!" ; + return false ; + } + ops_create_info_t *info; + ops_memory_t *buf = NULL ; + ops_setup_memory_write(&info, &buf, 0); + bool res = true; + + if(!ops_encrypt_stream(info, public_key, NULL, ops_false, ops_false)) + { + RsErr() << "Encryption failed." ; + res = false ; + } + + ops_write(data,len,info); + ops_writer_close(info); + ops_create_info_delete(info); + + int tlen = ops_memory_get_length(buf) ; + + if( (int)*encrypted_data_len >= tlen) + { + if(res) + { + memcpy(encrypted_data,ops_memory_get_data(buf),tlen) ; + *encrypted_data_len = tlen ; + res = true ; + } + } + else + { + RsErr() << "Not enough room to fit encrypted data. Size given=" << *encrypted_data_len << ", required=" << tlen ; + res = false ; + } + + ops_memory_release(buf) ; + free(buf) ; + + return res ; +} + +bool OpenPGPSDKHandler::decryptDataBin(const RsPgpId& /*key_id*/,const void *encrypted_data, const uint32_t encrypted_len, unsigned char *data, unsigned int *data_len) +{ + int out_length ; + unsigned char *out ; + ops_boolean_t res = ops_decrypt_memory((const unsigned char *)encrypted_data,encrypted_len,&out,&out_length,_secring,ops_false,cb_get_passphrase) ; + + if(*data_len < (unsigned int)out_length) + { + RsErr() << "Not enough room to store decrypted data! Please give more."; + return false ; + } + + *data_len = (unsigned int)out_length ; + memcpy(data,out,out_length) ; + free(out) ; + + return (bool)res ; +} + +bool OpenPGPSDKHandler::decryptTextFromFile(const RsPgpId&,std::string& text,const std::string& inputfile) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + unsigned char *out_buf = NULL ; + std::string buf ; + + FILE *f = RsDirUtil::rs_fopen(inputfile.c_str(),"rb") ; + + if (f == NULL) + { + RsErr() << "Cannot open file " << inputfile << " for read." ; + return false; + } + + int c ; + while( (c = fgetc(f))!= EOF) + buf += (unsigned char)c; + + fclose(f) ; + +#ifdef DEBUG_PGPHANDLER + RsErr() << "OpenPGPSDKHandler::decryptTextFromFile: read a file of length " << std::dec << buf.length() ; + RsErr() << "buf=\"" << buf << "\"" ; +#endif + + int out_length ; + ops_boolean_t res = ops_decrypt_memory((const unsigned char *)buf.c_str(),buf.length(),&out_buf,&out_length,_secring,ops_true,cb_get_passphrase) ; + + text = std::string((char *)out_buf,out_length) ; + free (out_buf); + return (bool)res ; +} + +bool OpenPGPSDKHandler::SignDataBin(const RsPgpId& id,const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,bool use_raw_signature, std::string reason /* = "" */) +{ + // need to find the key and to decrypt it. + ops_keydata_t *key = nullptr; + { + RS_STACK_MUTEX(pgphandlerMtx); // lock access to PGP memory structures. + const ops_keydata_t *test_key = locked_getSecretKey(id); + if(!test_key) + { + RsErr("Cannot sign: no secret key with id ", id.toStdString() ); + return false ; + } + // Copy key as it may take time for user to respond. + key = ops_keydata_new(); + ops_keydata_copy(key, test_key); + } + + std::string uid_hint ; + if(key->nuids > 0) + uid_hint = std::string((const char *)key->uids[0].user_id) ; + uid_hint += "(" + RsPgpId(key->key_id).toStdString()+")" ; + +#ifdef DEBUG_PGPHANDLER + ops_fingerprint_t f ; + ops_fingerprint(&f,&key->key.pkey) ; + + PGPFingerprintType fp(f.fingerprint) ; +#endif + + bool last_passwd_was_wrong = false ; + ops_secret_key_t *secret_key = nullptr ; + + for(int i=0;i<3;++i) + { + bool cancelled =false; + // Need to be outside of mutex to not block GUI. + std::string passphrase = _passphrase_callback(NULL,reason.c_str(),uid_hint.c_str(),"Please enter password for encrypting your key : ",last_passwd_was_wrong,&cancelled) ;//TODO reason + + secret_key = ops_decrypt_secret_key_from_data(key,passphrase.c_str()) ; + + if(cancelled) + { + RsErr() << "Key entering cancelled" ; + ops_keydata_free(key); + return false ; + } + if(secret_key) + break ; + + RsErr() << "Key decryption went wrong. Wrong password?" ; + last_passwd_was_wrong = true ; + } + // No more need of key, free it. + ops_keydata_free(key); + + if(!secret_key) + { + RsErr() << "Could not obtain secret key. Signature cancelled." ; + return false ; + } + + // then do the signature. + + RS_STACK_MUTEX(pgphandlerMtx); // lock access to PGP memory structures. + + ops_boolean_t not_raw = !use_raw_signature ; +#ifdef V07_NON_BACKWARD_COMPATIBLE_CHANGE_002 + ops_memory_t *memres = ops_sign_buf(data,len,OPS_SIG_BINARY,OPS_HASH_SHA256,secret_key,ops_false,ops_false,not_raw,not_raw) ; +#else + ops_memory_t *memres = ops_sign_buf(data,len,OPS_SIG_BINARY,OPS_HASH_SHA1,secret_key,ops_false,ops_false,not_raw,not_raw) ; +#endif + + if(!memres) + return false ; + + bool res ; + uint32_t slen = (uint32_t)ops_memory_get_length(memres); + + if(*signlen >= slen) + { + *signlen = slen ; + + memcpy(sign,ops_memory_get_data(memres),*signlen) ; + res = true ; + } + else + { + RsErr() << "(EE) memory chunk is not large enough for signature packet. Requred size: " << slen << " bytes." ; + res = false ; + } + + ops_memory_release(memres) ; + free(memres) ; + ops_secret_key_free(secret_key) ; + free(secret_key) ; + +#ifdef DEBUG_PGPHANDLER + RsErr() << "Signed with fingerprint " << fp.toStdString() << ", length " << std::dec << *signlen << ", literal data length = " << len ; + RsErr() << "Signature body: " ; + hexdump( (unsigned char *)data, len) ; + RsErr() ; + RsErr() << "Data: " ; + hexdump( (unsigned char *)sign,*signlen) ; + RsErr() ; +#endif + return res ; +} + +bool OpenPGPSDKHandler::privateSignCertificate(const RsPgpId& ownId,const RsPgpId& id_of_key_to_sign) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + ops_keydata_t *key_to_sign = const_cast(locked_getPublicKey(id_of_key_to_sign,true)) ; + + if(key_to_sign == NULL) + { + RsErr() << "Cannot sign: no public key with id " << id_of_key_to_sign.toStdString() ; + return false ; + } + + // 1 - get decrypted secret key + // + const ops_keydata_t *skey = locked_getSecretKey(ownId) ; + + if(!skey) + { + RsErr() << "Cannot sign: no secret key with id " << ownId.toStdString() ; + return false ; + } + const ops_keydata_t *pkey = locked_getPublicKey(ownId,true) ; + + if(!pkey) + { + RsErr() << "Cannot sign: no public key with id " << ownId.toStdString() ; + return false ; + } + + bool cancelled = false; + std::string passphrase = _passphrase_callback(NULL,"",RsPgpId(skey->key_id).toStdString().c_str(),"Please enter passwd for encrypting your key : ",false,&cancelled) ; + + ops_secret_key_t *secret_key = ops_decrypt_secret_key_from_data(skey,passphrase.c_str()) ; + + if(cancelled) + { + RsErr() << "Key cancelled by used." ; + return false ; + } + if(!secret_key) + { + RsErr() << "Key decryption went wrong. Wrong passwd?" ; + return false ; + } + + // 2 - then do the signature. + + if(!ops_sign_key(key_to_sign,pkey->key_id,secret_key)) + { + RsErr() << "Key signature went wrong. Wrong passwd?" ; + return false ; + } + + // 3 - free memory + // + ops_secret_key_free(secret_key) ; + free(secret_key) ; + + _pubring_changed = true ; + + // 4 - update signatures. + // + PGPCertificateInfo& cert(_public_keyring_map[ id_of_key_to_sign ]) ; + validateAndUpdateSignatures(cert,key_to_sign) ; + cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE ; + + return true ; +} + +bool OpenPGPSDKHandler::getKeyFingerprint(const RsPgpId& id, RsPgpFingerprint& fp) const +{ + RS_STACK_MUTEX(pgphandlerMtx); + + const ops_keydata_t *key = locked_getPublicKey(id,false) ; + + if(!key) return false; + + ops_fingerprint_t f ; + ops_fingerprint(&f,&key->key.pkey) ; + + fp = RsPgpFingerprint::fromBufferUnsafe(f.fingerprint); + + return true ; +} + +bool OpenPGPSDKHandler::VerifySignBin(const void *literal_data, uint32_t literal_data_length, unsigned char *sign, unsigned int sign_len, const PGPFingerprintType& key_fingerprint) +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + + RsPgpId id = RsPgpId(key_fingerprint.toByteArray() + PGPFingerprintType::SIZE_IN_BYTES - RsPgpId::SIZE_IN_BYTES) ; + const ops_keydata_t *key = locked_getPublicKey(id,true) ; + + if(key == NULL) + { + RsErr() << "No key returned by fingerprint " << key_fingerprint.toStdString() << ", and ID " << id.toStdString() << ", signature verification failed!" ; + return false ; + } + + // Check that fingerprint is the same. + const ops_public_key_t *pkey = &key->key.pkey ; + ops_fingerprint_t fp ; + ops_fingerprint(&fp,pkey) ; + + if(key_fingerprint != PGPFingerprintType(fp.fingerprint)) + { + RsErr() << "Key fingerprint does not match " << key_fingerprint.toStdString() << ", for ID " << id.toStdString() << ", signature verification failed!" ; + return false ; + } + +#ifdef DEBUG_PGPHANDLER + RsErr() << "Verifying signature from fingerprint " << key_fingerprint.toStdString() << ", length " << std::dec << sign_len << ", literal data length = " << literal_data_length ; + RsErr() << "Signature body: " ; + hexdump( (unsigned char *)sign,sign_len) ; + RsErr() ; + RsErr() << "Signed data: " ; + hexdump( (unsigned char *)literal_data, literal_data_length) ; + RsErr() ; +#endif + + return ops_validate_detached_signature(literal_data,literal_data_length,sign,sign_len,key) ; +} + +// Lexicographic order on signature packets +// +bool operator<(const ops_packet_t& p1,const ops_packet_t& p2) +{ + if(p1.length < p2.length) + return true ; + if(p1.length > p2.length) + return false ; + + for(uint32_t i=0;i p2.raw[i]) + return false ; + } + return false ; +} + +bool OpenPGPSDKHandler::mergeKeySignatures(ops_keydata_t *dst,const ops_keydata_t *src) +{ + // First sort all signatures into lists to see which is new, which is not new + +#ifdef DEBUG_PGPHANDLER + RsErr() << "Merging signatures for key " << RsPgpId(dst->key_id).toStdString() ; +#endif + std::set dst_packets ; + + for(uint32_t i=0;inpackets;++i) dst_packets.insert(dst->packets[i]) ; + + std::set to_add ; + + for(uint32_t i=0;inpackets;++i) + if(dst_packets.find(src->packets[i]) == dst_packets.end()) + { + uint8_t tag ; + uint32_t length ; + unsigned char *tmp_data = src->packets[i].raw ; // put it in a tmp variable because read_packetHeader() will modify it!! + + PGPKeyParser::read_packetHeader(tmp_data,tag,length) ; + + if(tag == PGPKeyParser::PGP_PACKET_TAG_SIGNATURE) + to_add.insert(src->packets[i]) ; +#ifdef DEBUG_PGPHANDLER + else + RsErr() << " Packet with tag 0x" << std::hex << (int)(src->packets[i].raw[0]) << std::dec << " not merged, because it is not a signature." ; +#endif + } + + for(std::set::const_iterator it(to_add.begin());it!=to_add.end();++it) + { +#ifdef DEBUG_PGPHANDLER + RsErr() << " Adding packet with tag 0x" << std::hex << (int)(*it).raw[0] << std::dec ; +#endif + ops_add_packet_to_keydata(dst,&*it) ; + } + return to_add.size() > 0 ; +} + +bool OpenPGPSDKHandler::syncDatabase() +{ + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + +#ifdef DEBUG_PGPHANDLER + RsErr() << "Sync-ing keyrings." ; +#endif + locked_syncPublicKeyring() ; + //locked_syncSecretKeyring() ; + + // Now sync the trust database as well. + // + locked_syncTrustDatabase() ; + +#ifdef DEBUG_PGPHANDLER + RsErr() << "Done. " ; +#endif + return true ; +} + +bool OpenPGPSDKHandler::locked_syncPublicKeyring() +{ + struct stat64 buf ; +#ifdef WINDOWS_SYS + std::wstring wfullname; + librs::util::ConvertUtf8ToUtf16(_pubring_path, wfullname); + if(-1 == _wstati64(wfullname.c_str(), &buf)) +#else + if(-1 == stat64(_pubring_path.c_str(), &buf)) +#endif + { + RsErr() << "OpenPGPSDKHandler::syncDatabase(): can't stat file " << _pubring_path << ". Can't sync public keyring." ; + buf.st_mtime = 0; + } + + if(_pubring_last_update_time < buf.st_mtime) + { + RsErr() << "Detected change on disk of public keyring. Merging!" << std::endl ; + + locked_mergeKeyringFromDisk(_pubring,_public_keyring_map,_pubring_path) ; + _pubring_last_update_time = buf.st_mtime ; + } + + // Now check if the pubring was locally modified, which needs saving it again + if(_pubring_changed && RsDiscSpace::checkForDiscSpace(RS_PGP_DIRECTORY)) + { + std::string tmp_keyring_file = _pubring_path + ".tmp" ; + + RsErr() << "Local changes in public keyring. Writing to disk..." ; + if(!ops_write_keyring_to_file(_pubring,ops_false,tmp_keyring_file.c_str(),ops_true)) + { + RsErr() << "Cannot write public keyring tmp file. Disk full? Disk quota exceeded?" ; + return false ; + } + if(!RsDirUtil::renameFile(tmp_keyring_file,_pubring_path)) + { + RsErr() << "Cannot rename tmp pubring file " << tmp_keyring_file << " into actual pubring file " << _pubring_path << ". Check writing permissions?!?" ; + return false ; + } + + RsErr() << "Done." ; + _pubring_last_update_time = time(NULL) ; // should we get this value from the disk instead?? + _pubring_changed = false ; + } + return true ; +} + +void OpenPGPSDKHandler::locked_mergeKeyringFromDisk(ops_keyring_t *keyring, + std::map& kmap, + const std::string& keyring_file) +{ +#ifdef DEBUG_PGPHANDLER + RsErr() << "Merging keyring " << keyring_file << " from disk to memory." ; +#endif + + // 1 - load keyring into a temporary keyring list. + ops_keyring_t *tmp_keyring = OpenPGPSDKHandler::allocateOPSKeyring() ; + + if(ops_false == ops_keyring_read_from_file(tmp_keyring, false, keyring_file.c_str())) + { + RsErr() << "OpenPGPSDKHandler::locked_mergeKeyringFromDisk(): cannot read keyring. File corrupted?" ; + ops_keyring_free(tmp_keyring) ; + return ; + } + + // 2 - load new keys and merge existing key signatures + + for(int i=0;inkeys;++i) + locked_addOrMergeKey(keyring,kmap,&tmp_keyring->keys[i]) ;// we dont' account for the return value. This is disk merging, not local changes. + + // 4 - clean + ops_keyring_free(tmp_keyring) ; +} + +bool OpenPGPSDKHandler::removeKeysFromPGPKeyring(const std::set& keys_to_remove,std::string& backup_file,uint32_t& error_code) +{ + // 1 - lock everything. + // + RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. + RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. + + error_code = PGP_KEYRING_REMOVAL_ERROR_NO_ERROR ; + + for(std::set::const_iterator it(keys_to_remove.begin());it!=keys_to_remove.end();++it) + if(locked_getSecretKey(*it) != NULL) + { + RsErr() << "(EE) OpenPGPSDKHandler:: can't remove key " << (*it).toStdString() << " since its shared by a secret key! Operation cancelled." ; + error_code = PGP_KEYRING_REMOVAL_ERROR_CANT_REMOVE_SECRET_KEYS ; + return false ; + } + + // 2 - sync everything. + // + locked_syncPublicKeyring() ; + + // 3 - make a backup of the public keyring + // + char template_name[_pubring_path.length()+8] ; + sprintf(template_name,"%s.XXXXXX",_pubring_path.c_str()) ; + +#if defined __USE_XOPEN_EXTENDED || defined __USE_XOPEN2K8 + int fd_keyring_backup(mkstemp(template_name)); + if (fd_keyring_backup == -1) +#else + if(mktemp(template_name) == NULL) +#endif + { + RsErr() << "OpenPGPSDKHandler::removeKeysFromPGPKeyring(): cannot create keyring backup file. Giving up." ; + error_code = PGP_KEYRING_REMOVAL_ERROR_CANNOT_CREATE_BACKUP ; + return false ; + } +#if defined __USE_XOPEN_EXTENDED || defined __USE_XOPEN2K8 + close(fd_keyring_backup); // TODO: keep the file open and use the fd +#endif + + if(!ops_write_keyring_to_file(_pubring,ops_false,template_name,ops_true)) + { + RsErr() << "OpenPGPSDKHandler::removeKeysFromPGPKeyring(): cannot write keyring backup file. Giving up." ; + error_code = PGP_KEYRING_REMOVAL_ERROR_CANNOT_WRITE_BACKUP ; + return false ; + } + backup_file = std::string(template_name,_pubring_path.length()+7) ; + + RsErr() << "Keyring was backed up to file " << backup_file ; + + // Remove keys from the keyring, and update the keyring map. + // + for(std::set::const_iterator it(keys_to_remove.begin());it!=keys_to_remove.end();++it) + { + if(locked_getSecretKey(*it) != NULL) + { + RsErr() << "(EE) OpenPGPSDKHandler:: can't remove key " << (*it).toStdString() << " since its shared by a secret key!" ; + continue ; + } + + std::map::iterator res = _public_keyring_map.find(*it) ; + + if(res == _public_keyring_map.end()) + { + RsErr() << "(EE) OpenPGPSDKHandler:: can't remove key " << (*it).toStdString() << " from keyring: key not found." ; + continue ; + } + + if(res->second._key_index >= (unsigned int)_pubring->nkeys || RsPgpId(_pubring->keys[res->second._key_index].key_id) != *it) + { + RsErr() << "(EE) OpenPGPSDKHandler:: can't remove key " << (*it).toStdString() << ". Inconsistency found." ; + error_code = PGP_KEYRING_REMOVAL_ERROR_DATA_INCONSISTENCY ; + return false ; + } + + // Move the last key to the freed place. This deletes the key in place. + // + ops_keyring_remove_key(_pubring,res->second._key_index) ; + + // Erase the info from the keyring map. + // + _public_keyring_map.erase(res) ; + + // now update all indices back. This internal look is very costly, but it avoids deleting the wrong keys, since the keyring structure is + // changed by ops_keyring_remove_key and therefore indices don't point to the correct location anymore. + + int i=0 ; + const ops_keydata_t *keydata ; + while( (keydata = ops_keyring_get_key_by_index(_pubring,i)) != NULL ) + { + PGPCertificateInfo& cert(_public_keyring_map[ RsPgpId(keydata->key_id) ]) ; + cert._key_index = i ; + ++i ; + } + } + + // Everything went well, sync back the keyring on disk + + _pubring_changed = true ; + _trustdb_changed = true ; + + locked_syncPublicKeyring() ; + locked_syncTrustDatabase() ; + + return true ; +} diff --git a/libretroshare/src/pgp/openpgpsdkhandler.h b/libretroshare/src/pgp/openpgpsdkhandler.h new file mode 100644 index 000000000..8aff1459b --- /dev/null +++ b/libretroshare/src/pgp/openpgpsdkhandler.h @@ -0,0 +1,116 @@ +/******************************************************************************* + * libretroshare/src/pgp: pgphandler.h * + * * + * libretroshare: retroshare core library * + * * + * Copyright 2018 Cyril Soler * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#include +#include +#include +#include +#include + +#include "util/rsthreads.h" +#include "pgp/pgphandler.h" +#include "retroshare/rstypes.h" + +extern "C" { + // we should make sure later on to get rid of these structures in the .h + #include "openpgpsdk/keyring.h" +} + +/// This class offer an abstract pgp handler to be used in RetroShare. +class OpenPGPSDKHandler: public PGPHandler +{ +public: + OpenPGPSDKHandler( const std::string& path_to_public_keyring, + const std::string& path_to_secret_keyring, + const std::string& path_to_trust_database, + const std::string& pgp_lock_file) ; + + virtual ~OpenPGPSDKHandler() ; + + //================================================================================================// + // Implemented API from PGPHandler // + //================================================================================================// + + //virtual std::string makeRadixEncodedPGPKey(uint32_t key_index,bool include_signatures) override; + virtual bool removeKeysFromPGPKeyring(const std::set& key_ids,std::string& backup_file,uint32_t& error_code) override; + virtual bool availableGPGCertificatesWithPrivateKeys(std::list& ids) override; + virtual bool GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passphrase, RsPgpId& pgpId, const int keynumbits, std::string& errString) override; + + virtual std::string SaveCertificateToString(const RsPgpId& id,bool include_signatures) const override; + virtual bool exportPublicKey( const RsPgpId& id, unsigned char*& mem_block, size_t& mem_size, bool armoured, bool include_signatures ) const override; + + virtual bool exportGPGKeyPair(const std::string& filename,const RsPgpId& exported_key_id) const override; + virtual bool exportGPGKeyPairToString( std::string& data, const RsPgpId& exportedKeyId, bool includeSignatures, std::string& errorMsg ) const override; + virtual bool getGPGDetailsFromBinaryBlock(const unsigned char *mem_block,size_t mem_size,RsPgpId& key_id, std::string& name, std::list& signers) const override; + virtual bool importGPGKeyPair(const std::string& filename,RsPgpId& imported_key_id,std::string& import_error) override; + virtual bool importGPGKeyPairFromString(const std::string &data, RsPgpId &imported_key_id, std::string &import_error) override; + virtual bool LoadCertificateFromBinaryData(const unsigned char *data,uint32_t data_len,RsPgpId& id,std::string& error_string) override; + virtual bool LoadCertificateFromString(const std::string& pgp_cert,RsPgpId& id,std::string& error_string) override; + virtual bool encryptTextToFile(const RsPgpId& key_id,const std::string& text,const std::string& outfile) override; + virtual bool encryptDataBin(const RsPgpId& key_id,const void *data, const uint32_t len, unsigned char *encrypted_data, unsigned int *encrypted_data_len) override; + virtual bool decryptDataBin(const RsPgpId& /*key_id*/,const void *encrypted_data, const uint32_t encrypted_len, unsigned char *data, unsigned int *data_len) override; + virtual bool decryptTextFromFile(const RsPgpId&,std::string& text,const std::string& inputfile) override; + virtual bool SignDataBin(const RsPgpId& id,const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,bool use_raw_signature, std::string reason /* = "" */) override; + virtual bool privateSignCertificate(const RsPgpId& ownId,const RsPgpId& id_of_key_to_sign) override; + virtual bool VerifySignBin(const void *literal_data, uint32_t literal_data_length, unsigned char *sign, unsigned int sign_len, const PGPFingerprintType& key_fingerprint) override; + virtual bool getKeyFingerprint(const RsPgpId& id, RsPgpFingerprint& fp) const override; + virtual bool haveSecretKey(const RsPgpId& id) const override; + virtual bool syncDatabase() override; + private: + bool locked_syncPublicKeyring() ; + + void initCertificateInfo(PGPCertificateInfo& cert,const ops_keydata_t *keydata,uint32_t i) ; + bool LoadCertificate(const unsigned char *data,uint32_t data_len,bool armoured,RsPgpId& id,std::string& error_string) ; + + // Returns true if the signatures have been updated + // + bool validateAndUpdateSignatures(PGPCertificateInfo& cert,const ops_keydata_t *keydata) ; + + /** Check public/private key and import them into the keyring + * @param keyring keyring with the new public/private key pair. Will be freed by the function. + * @param imported_key_id PGP id of the imported key + * @param import_error human readbale error message + * @returns true on success + * */ + bool checkAndImportKeyPair(ops_keyring_t *keyring, RsPgpId& imported_key_id,std::string& import_error); + + const ops_keydata_t *locked_getPublicKey(const RsPgpId&,bool stamp_the_key) const; + const ops_keydata_t *locked_getSecretKey(const RsPgpId&) const ; + + void locked_mergeKeyringFromDisk(ops_keyring_t *keyring, std::map& kmap, const std::string& keyring_file) ; + bool locked_addOrMergeKey(ops_keyring_t *keyring,std::map& kmap,const ops_keydata_t *keydata) ; + + // Members. + // + ops_keyring_t *_pubring ; + ops_keyring_t *_secring ; + + void printOPSKeys() const; + + // Helper functions. + // + static std::string makeRadixEncodedPGPKey(const ops_keydata_t *key,bool include_signatures) ; + static ops_keyring_t *allocateOPSKeyring() ; + static void addNewKeyToOPSKeyring(ops_keyring_t*, const ops_keydata_t&) ; + static bool mergeKeySignatures(ops_keydata_t *dst,const ops_keydata_t *src) ; // returns true if signature lists are different +}; diff --git a/libretroshare/src/pgp/pgpauxutils.cc b/libretroshare/src/pgp/pgpauxutils.cc index d7229e848..c87985fab 100644 --- a/libretroshare/src/pgp/pgpauxutils.cc +++ b/libretroshare/src/pgp/pgpauxutils.cc @@ -34,17 +34,17 @@ PgpAuxUtilsImpl::PgpAuxUtilsImpl() const RsPgpId& PgpAuxUtilsImpl::getPGPOwnId() { - return AuthGPG::getAuthGPG()->getGPGOwnId(); + return AuthPGP::getPgpOwnId(); } -RsPgpId PgpAuxUtilsImpl::getPGPId(const RsPeerId& sslid) +RsPgpId PgpAuxUtilsImpl::getPgpId(const RsPeerId& sslid) { return rsPeers->getGPGId(sslid); } bool PgpAuxUtilsImpl::getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) const { - return AuthGPG::getAuthGPG()->getKeyFingerprint(id, fp); + return AuthPGP::getKeyFingerprint(id, fp); } bool PgpAuxUtilsImpl::VerifySignBin(const void *data, @@ -54,17 +54,17 @@ bool PgpAuxUtilsImpl::VerifySignBin(const void *data, const PGPFingerprintType& withfingerprint) { - return AuthGPG::getAuthGPG()->VerifySignBin(data, len, sign, signlen, withfingerprint); + return AuthPGP::VerifySignBin(data, len, sign, signlen, withfingerprint); } -bool PgpAuxUtilsImpl::getGPGAllList(std::list &ids) +bool PgpAuxUtilsImpl::getPgpAllList(std::list &ids) { - return AuthGPG::getAuthGPG()->getGPGAllList(ids); + return AuthPGP::getPgpAllList(ids); } bool PgpAuxUtilsImpl::parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const { - return AuthGPG::getAuthGPG()->parseSignature(sign,signlen,issuer); + return AuthPGP::parseSignature(sign,signlen,issuer); } diff --git a/libretroshare/src/pgp/pgpauxutils.h b/libretroshare/src/pgp/pgpauxutils.h index 4b188e3ae..aa897c0e1 100644 --- a/libretroshare/src/pgp/pgpauxutils.h +++ b/libretroshare/src/pgp/pgpauxutils.h @@ -35,8 +35,8 @@ class PgpAuxUtils virtual ~PgpAuxUtils(){} virtual const RsPgpId &getPGPOwnId() = 0; - virtual RsPgpId getPGPId(const RsPeerId& sslid) = 0; - virtual bool getGPGAllList(std::list &ids) = 0; + virtual RsPgpId getPgpId(const RsPeerId& sslid) = 0; + virtual bool getPgpAllList(std::list &ids) = 0; virtual bool getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) const = 0; virtual bool parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const =0; @@ -49,12 +49,12 @@ public: PgpAuxUtilsImpl(); virtual const RsPgpId &getPGPOwnId(); - virtual RsPgpId getPGPId(const RsPeerId& sslid); + virtual RsPgpId getPgpId(const RsPeerId& sslid); virtual bool parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const ; virtual bool getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) const; virtual bool VerifySignBin(const void *data, uint32_t len, unsigned char *sign, unsigned int signlen, const PGPFingerprintType& withfingerprint); - virtual bool getGPGAllList(std::list &ids); + virtual bool getPgpAllList(std::list &ids); }; diff --git a/libretroshare/src/pgp/pgphandler.cc b/libretroshare/src/pgp/pgphandler.cc index b0ffd31cd..2d3625f24 100644 --- a/libretroshare/src/pgp/pgphandler.cc +++ b/libretroshare/src/pgp/pgphandler.cc @@ -33,15 +33,6 @@ #include "util/rswin.h" #endif -extern "C" { -#include -#include -#include -#include -#include -#include -#include -} #include "pgphandler.h" #include "retroshare/rsiface.h" // For rsicontrol. #include "retroshare/rspeers.h" // For rsicontrol. @@ -59,56 +50,6 @@ static const uint32_t PGP_CERTIFICATE_LIMIT_MAX_PASSWD_SIZE = 1024 ; PassphraseCallback PGPHandler::_passphrase_callback = NULL ; -ops_keyring_t *PGPHandler::allocateOPSKeyring() -{ - ops_keyring_t *kr = (ops_keyring_t*)rs_malloc(sizeof(ops_keyring_t)) ; - - if(kr == NULL) - return NULL ; - - kr->nkeys = 0 ; - kr->nkeys_allocated = 0 ; - kr->keys = 0 ; - - return kr ; -} - -ops_parse_cb_return_t cb_get_passphrase(const ops_parser_content_t *content_,ops_parse_cb_info_t *cbinfo)// __attribute__((unused))) -{ - const ops_parser_content_union_t *content=&content_->content; - bool prev_was_bad = false ; - - switch(content_->tag) - { - case OPS_PARSER_CMD_GET_SK_PASSPHRASE_PREV_WAS_BAD: prev_was_bad = true ; - /* fallthrough */ - case OPS_PARSER_CMD_GET_SK_PASSPHRASE: - { - std::string passwd; - std::string uid_hint ; - - if(cbinfo->cryptinfo.keydata->nuids > 0) - uid_hint = std::string((const char *)cbinfo->cryptinfo.keydata->uids[0].user_id) ; - uid_hint += "(" + RsPgpId(cbinfo->cryptinfo.keydata->key_id).toStdString()+")" ; - - bool cancelled = false ; - passwd = PGPHandler::passphraseCallback()(NULL,"",uid_hint.c_str(),NULL,prev_was_bad,&cancelled) ; - - if(cancelled) - *(unsigned char *)cbinfo->arg = 1; - - *(content->secret_key_passphrase.passphrase)= (char *)ops_mallocz(passwd.length()+1) ; - memcpy(*(content->secret_key_passphrase.passphrase),passwd.c_str(),passwd.length()) ; - return OPS_KEEP_MEMORY; - } - break; - - default: - break; - } - - return OPS_RELEASE_MEMORY; -} void PGPHandler::setPassphraseCallback(PassphraseCallback cb) { _passphrase_callback = cb ; @@ -117,242 +58,47 @@ void PGPHandler::setPassphraseCallback(PassphraseCallback cb) PGPHandler::PGPHandler(const std::string& pubring, const std::string& secring,const std::string& trustdb,const std::string& pgp_lock_filename) : pgphandlerMtx(std::string("PGPHandler")), _pubring_path(pubring),_secring_path(secring),_trustdb_path(trustdb),_pgp_lock_filename(pgp_lock_filename) { - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - _pubring_changed = false ; - _trustdb_changed = false ; - - RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. - - if(_passphrase_callback == NULL) - std::cerr << "WARNING: before created a PGPHandler, you need to init the passphrase callback using PGPHandler::setPassphraseCallback()" << std::endl; - - // Allocate public and secret keyrings. - // - _pubring = allocateOPSKeyring() ; - _secring = allocateOPSKeyring() ; - - // Check that the file exists. If not, create a void keyring. - - FILE *ftest ; - ftest = RsDirUtil::rs_fopen(pubring.c_str(),"rb") ; - bool pubring_exist = (ftest != NULL) ; - if(ftest != NULL) - fclose(ftest) ; - ftest = RsDirUtil::rs_fopen(secring.c_str(),"rb") ; - bool secring_exist = (ftest != NULL) ; - if(ftest != NULL) - fclose(ftest) ; - - // Read public and secret keyrings from supplied files. - // - if(pubring_exist) - { - if(ops_false == ops_keyring_read_from_file(_pubring, false, pubring.c_str())) - throw std::runtime_error("PGPHandler::readKeyRing(): cannot read pubring. File corrupted.") ; - } - else - std::cerr << "pubring file \"" << pubring << "\" not found. Creating a void keyring." << std::endl; - - const ops_keydata_t *keydata ; - int i=0 ; - while( (keydata = ops_keyring_get_key_by_index(_pubring,i)) != NULL ) - { - PGPCertificateInfo& cert(_public_keyring_map[ RsPgpId(keydata->key_id) ]) ; - - // Init all certificates. - - initCertificateInfo(cert,keydata,i) ; - - // Validate signatures. - - validateAndUpdateSignatures(cert,keydata) ; - - ++i ; - } - _pubring_last_update_time = time(NULL) ; - std::cerr << "Pubring read successfully." << std::endl; - - if(secring_exist) - { - if(ops_false == ops_keyring_read_from_file(_secring, false, secring.c_str())) - throw std::runtime_error("PGPHandler::readKeyRing(): cannot read secring. File corrupted.") ; - } - else - std::cerr << "secring file \"" << secring << "\" not found. Creating a void keyring." << std::endl; - - i=0 ; - while( (keydata = ops_keyring_get_key_by_index(_secring,i)) != NULL ) - { - initCertificateInfo(_secret_keyring_map[ RsPgpId(keydata->key_id) ],keydata,i) ; - ++i ; - } - _secring_last_update_time = time(NULL) ; - - std::cerr << "Secring read successfully." << std::endl; - - locked_readPrivateTrustDatabase() ; - _trustdb_last_update_time = time(NULL) ; -} - -void PGPHandler::initCertificateInfo(PGPCertificateInfo& cert,const ops_keydata_t *keydata,uint32_t index) -{ - // Parse certificate name - // - - if(keydata->uids != NULL) - { - std::string namestring( (char *)keydata->uids[0].user_id ) ; - - cert._name = "" ; - uint32_t i=0; - while(i < namestring.length() && namestring[i] != '(' && namestring[i] != '<') { cert._name += namestring[i] ; ++i ;} - - // trim right spaces - std::string::size_type found = cert._name.find_last_not_of(' '); - if (found != std::string::npos) - cert._name.erase(found + 1); - else - cert._name.clear(); // all whitespace - - std::string& next = (namestring[i] == '(')?cert._comment:cert._email ; - ++i ; - next = "" ; - while(i < namestring.length() && namestring[i] != ')' && namestring[i] != '>') { next += namestring[i] ; ++i ;} - - while(i < namestring.length() && namestring[i] != '(' && namestring[i] != '<') { next += namestring[i] ; ++i ;} - - if(i< namestring.length()) - { - std::string& next2 = (namestring[i] == '(')?cert._comment:cert._email ; - ++i ; - next2 = "" ; - while(i < namestring.length() && namestring[i] != ')' && namestring[i] != '>') { next2 += namestring[i] ; ++i ;} - } - } - - cert._trustLvl = 1 ; // to be setup accordingly - cert._validLvl = 1 ; // to be setup accordingly - cert._key_index = index ; - cert._flags = 0 ; - cert._time_stamp = 0 ;// "never" by default. Will be updated by trust database, and effective key usage. - - switch(keydata->key.pkey.algorithm) - { - case OPS_PKA_RSA: cert._type = PGPCertificateInfo::PGP_CERTIFICATE_TYPE_RSA ; - break ; - case OPS_PKA_DSA: cert._type = PGPCertificateInfo::PGP_CERTIFICATE_TYPE_DSA ; - cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM ; - break ; - default: cert._type = PGPCertificateInfo::PGP_CERTIFICATE_TYPE_UNKNOWN ; - cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM ; - break ; - } - - ops_fingerprint_t f ; - ops_fingerprint(&f,&keydata->key.pkey) ; - - cert._fpr = PGPFingerprintType(f.fingerprint) ; -} - -bool PGPHandler::validateAndUpdateSignatures(PGPCertificateInfo& cert,const ops_keydata_t *keydata) -{ - ops_validate_result_t* result=(ops_validate_result_t*)ops_mallocz(sizeof *result); - ops_boolean_t res = ops_validate_key_signatures(result,keydata,_pubring,cb_get_passphrase) ; - - if(res == ops_false) - { - static ops_boolean_t already = 0 ; - if(!already) - { - std::cerr << "(WW) Error in PGPHandler::validateAndUpdateSignatures(). Validation failed for at least some signatures." << std::endl; - already = 1 ; - } - } - - bool ret = false ; - - // Parse signers. - // - - if(result != NULL) - for(size_t i=0;ivalid_count;++i) - { - RsPgpId signer_id(result->valid_sigs[i].signer_id); - - if(cert.signers.find(signer_id) == cert.signers.end()) - { - cert.signers.insert(signer_id) ; - ret = true ; - } - } - - ops_validate_result_free(result) ; - - return ret ; } PGPHandler::~PGPHandler() { - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. -#ifdef DEBUG_PGPHANDLER - std::cerr << "Freeing PGPHandler. Deleting keyrings." << std::endl; -#endif - - // no need to free the the _map_ elements. They will be freed by the following calls: - // - ops_keyring_free(_pubring) ; - ops_keyring_free(_secring) ; - - free(_pubring) ; - free(_secring) ; } bool PGPHandler::printKeys() const { #ifdef DEBUG_PGPHANDLER - std::cerr << "Printing details of all " << std::dec << _public_keyring_map.size() << " keys: " << std::endl; + RsErr() << "Printing details of all " << std::dec << _public_keyring_map.size() << " keys: " ; #endif for(std::map::const_iterator it(_public_keyring_map.begin()); it != _public_keyring_map.end(); ++it) { - std::cerr << "PGP Key: " << it->first.toStdString() << std::endl; + RsErr() << "PGP Key: " << it->first.toStdString() ; - std::cerr << "\tName : " << it->second._name << std::endl; - std::cerr << "\tEmail : " << it->second._email << std::endl; - std::cerr << "\tOwnSign : " << (it->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE) << std::endl; - std::cerr << "\tAccept Connect: " << (it->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION) << std::endl; - std::cerr << "\ttrustLvl : " << it->second._trustLvl << std::endl; - std::cerr << "\tvalidLvl : " << it->second._validLvl << std::endl; - std::cerr << "\tUse time stamp: " << it->second._time_stamp << std::endl; - std::cerr << "\tfingerprint : " << it->second._fpr.toStdString() << std::endl; - std::cerr << "\tSigners : " << it->second.signers.size() << std::endl; + RsErr() << "\tName : " << it->second._name ; + RsErr() << "\tEmail : " << it->second._email ; + RsErr() << "\tOwnSign : " << (it->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE) ; + RsErr() << "\tAccept Connect: " << (it->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION) ; + RsErr() << "\ttrustLvl : " << it->second._trustLvl ; + RsErr() << "\tvalidLvl : " << it->second._validLvl ; + RsErr() << "\tUse time stamp: " << it->second._time_stamp ; + RsErr() << "\tfingerprint : " << it->second._fpr.toStdString() ; + RsErr() << "\tSigners : " << it->second.signers.size() ; std::set::const_iterator sit; for(sit = it->second.signers.begin(); sit != it->second.signers.end(); ++sit) { - std::cerr << "\t\tSigner ID:" << (*sit).toStdString() << ", Name: " ; + RsErr() << "\t\tSigner ID:" << (*sit).toStdString() << ", Name: " ; const PGPCertificateInfo *info = PGPHandler::getCertificateInfo(*sit) ; if(info != NULL) - std::cerr << info->_name ; + RsErr() << info->_name ; - std::cerr << std::endl ; + RsErr() << std::endl ; } } - std::cerr << "Public keyring list from OPS:" << std::endl; - ops_keyring_list(_pubring) ; - return true ; } -bool PGPHandler::haveSecretKey(const RsPgpId& id) const -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - return locked_getSecretKey(id) != NULL ; -} - const PGPCertificateInfo *PGPHandler::getCertificateInfo(const RsPgpId& id) const { RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. @@ -365,1193 +111,13 @@ const PGPCertificateInfo *PGPHandler::getCertificateInfo(const RsPgpId& id) cons return NULL ; } -bool PGPHandler::availableGPGCertificatesWithPrivateKeys(std::list& ids) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - // go through secret keyring, and check that we have the pubkey as well. - // - - const ops_keydata_t *keydata = NULL ; - int i=0 ; - - while( (keydata = ops_keyring_get_key_by_index(_secring,i++)) != NULL ) - if(ops_keyring_find_key_by_id(_pubring,keydata->key_id) != NULL) // check that the key is in the pubring as well - { -#ifdef PGPHANDLER_DSA_SUPPORT - if(keydata->key.pkey.algorithm == OPS_PKA_RSA || keydata->key.pkey.algorithm == OPS_PKA_DSA) -#else - if(keydata->key.pkey.algorithm == OPS_PKA_RSA) -#endif - ids.push_back(RsPgpId(keydata->key_id)) ; -#ifdef DEBUG_PGPHANDLER - else - std::cerr << "Skipping keypair " << RsPgpId(keydata->key_id).toStdString() << ", unsupported algorithm: " << keydata->key.pkey.algorithm << std::endl; -#endif - } - - return true ; -} - -bool PGPHandler::GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passphrase, RsPgpId& pgpId, const int keynumbits, std::string& errString) -{ - // Some basic checks - - if(!RsDiscSpace::checkForDiscSpace(RS_PGP_DIRECTORY)) - { - errString = std::string("(EE) low disc space in pgp directory. Can't write safely to keyring.") ; - return false ; - } - if(name.length() > PGP_CERTIFICATE_LIMIT_MAX_NAME_SIZE) - { - errString = std::string("(EE) name in certificate exceeds the maximum allowed name size") ; - return false ; - } - if(email.length() > PGP_CERTIFICATE_LIMIT_MAX_EMAIL_SIZE) - { - errString = std::string("(EE) email in certificate exceeds the maximum allowed email size") ; - return false ; - } - if(passphrase.length() > PGP_CERTIFICATE_LIMIT_MAX_PASSWD_SIZE) - { - errString = std::string("(EE) passphrase in certificate exceeds the maximum allowed passphrase size") ; - return false ; - } - if(keynumbits % 1024 != 0) - { - errString = std::string("(EE) RSA key length is not a multiple of 1024") ; - return false ; - } - - // Now the real thing - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. - - // 1 - generate keypair - RSA-2048 - // - ops_user_id_t uid ; - char *s = strdup((name + " (Generated by RetroShare) <" + email + ">" ).c_str()) ; - uid.user_id = (unsigned char *)s ; - unsigned long int e = 65537 ; // some prime number - - ops_keydata_t *key = ops_rsa_create_selfsigned_keypair(keynumbits, e, &uid) ; - - free(s) ; - - if(!key) - return false ; - - // 2 - save the private key encrypted to a temporary memory buffer, so as to read an encrypted key to memory - - ops_create_info_t *cinfo = NULL ; - ops_memory_t *buf = NULL ; - ops_setup_memory_write(&cinfo, &buf, 0); - - if(!ops_write_transferable_secret_key(key,(unsigned char *)passphrase.c_str(),passphrase.length(),ops_false,cinfo)) - { - errString = std::string("(EE) Cannot encode secret key to memory!!") ; - return false ; - } - - // 3 - read the memory chunk into an encrypted keyring - - ops_keyring_t *tmp_secring = allocateOPSKeyring() ; - - if(! ops_keyring_read_from_mem(tmp_secring, ops_false, buf)) - { - errString = std::string("(EE) Cannot re-read key from memory!!") ; - return false ; - } - ops_teardown_memory_write(cinfo,buf); // cleanup memory - - // 4 - copy the encrypted private key to the private keyring - - pgpId = RsPgpId(tmp_secring->keys[0].key_id) ; - addNewKeyToOPSKeyring(_secring,tmp_secring->keys[0]) ; - initCertificateInfo(_secret_keyring_map[ pgpId ],&tmp_secring->keys[0],_secring->nkeys-1) ; - -#ifdef DEBUG_PGPHANDLER - std::cerr << "Added new secret key with id " << pgpId.toStdString() << " to secret keyring." << std::endl; -#endif - ops_keyring_free(tmp_secring) ; - free(tmp_secring) ; - - // 5 - add key to secret keyring on disk. - - cinfo = NULL ; - std::string secring_path_tmp = _secring_path + ".tmp" ; - - if(RsDirUtil::fileExists(_secring_path) && !RsDirUtil::copyFile(_secring_path,secring_path_tmp)) - { - errString= std::string("Cannot copy secret keyring !! Disk full? Out of disk quota?") ; - return false ; - } - int fd=ops_setup_file_append(&cinfo, secring_path_tmp.c_str()); - - if(!ops_write_transferable_secret_key(key,(unsigned char *)passphrase.c_str(),passphrase.length(),ops_false,cinfo)) - { - errString= std::string("Cannot encode secret key to disk!! Disk full? Out of disk quota?") ; - return false ; - } - ops_teardown_file_write(cinfo,fd) ; - - if(!RsDirUtil::renameFile(secring_path_tmp,_secring_path)) - { - errString= std::string("Cannot rename tmp secret key file ") + secring_path_tmp + " into " + _secring_path +". Disk error?" ; - return false ; - } - - // 6 - copy the public key to the public keyring on disk - - cinfo = NULL ; - std::string pubring_path_tmp = _pubring_path + ".tmp" ; - - if(RsDirUtil::fileExists(_pubring_path) && !RsDirUtil::copyFile(_pubring_path,pubring_path_tmp)) - { - errString= std::string("Cannot encode secret key to disk!! Disk full? Out of disk quota?") ; - return false ; - } - fd=ops_setup_file_append(&cinfo, pubring_path_tmp.c_str()); - - if(!ops_write_transferable_public_key(key, ops_false, cinfo)) - { - errString=std::string("Cannot encode secret key to memory!!") ; - return false ; - } - ops_teardown_file_write(cinfo,fd) ; - - if(!RsDirUtil::renameFile(pubring_path_tmp,_pubring_path)) - { - errString= std::string("Cannot rename tmp public key file ") + pubring_path_tmp + " into " + _pubring_path +". Disk error?" ; - return false ; - } - // 7 - clean - ops_keydata_free(key) ; - - // 8 - re-read the key from the public keyring, and add it to memory. - - _pubring_last_update_time = 0 ; // force update pubring from disk. - locked_syncPublicKeyring() ; - -#ifdef DEBUG_PGPHANDLER - std::cerr << "Added new public key with id " << pgpId.toStdString() << " to public keyring." << std::endl; -#endif - - // 9 - Update some flags. - - privateTrustCertificate(pgpId,PGPCertificateInfo::PGP_CERTIFICATE_TRUST_ULTIMATE) ; - - return true ; -} - -std::string PGPHandler::makeRadixEncodedPGPKey(const ops_keydata_t *key,bool include_signatures) -{ - ops_create_info_t* cinfo; - ops_memory_t *buf = NULL ; - ops_setup_memory_write(&cinfo, &buf, 0); - ops_boolean_t armoured = ops_true ; - - if(key->type == OPS_PTAG_CT_PUBLIC_KEY) - { - if(ops_write_transferable_public_key_from_packet_data(key,armoured,cinfo) != ops_true) - return "ERROR: This key cannot be processed by RetroShare because\nDSA certificates are not yet handled." ; - } - else if(key->type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) - { - if(ops_write_transferable_secret_key_from_packet_data(key,armoured,cinfo) != ops_true) - return "ERROR: This key cannot be processed by RetroShare because\nDSA certificates are not yet handled." ; - } - else - { - ops_create_info_delete(cinfo); - std::cerr << "Unhandled key type " << key->type << std::endl; - return "ERROR: Cannot write key. Unhandled key type. " ; - } - - ops_writer_close(cinfo) ; - - std::string res((char *)ops_memory_get_data(buf),ops_memory_get_length(buf)) ; - ops_teardown_memory_write(cinfo,buf); - - if(!include_signatures) - { - std::string tmp ; - if(PGPKeyManagement::createMinimalKey(res,tmp) ) - res = tmp ; - } - - return res ; -} - -const ops_keydata_t *PGPHandler::locked_getSecretKey(const RsPgpId& id) const -{ - std::map::const_iterator res = _secret_keyring_map.find(id) ; - - if(res == _secret_keyring_map.end()) - return NULL ; - else - return ops_keyring_get_key_by_index(_secring,res->second._key_index) ; -} -const ops_keydata_t *PGPHandler::locked_getPublicKey(const RsPgpId& id,bool stamp_the_key) const -{ - std::map::const_iterator res = _public_keyring_map.find(id) ; - - if(res == _public_keyring_map.end()) - return NULL ; - else - { - if(stamp_the_key) // Should we stamp the key as used? - { - static rstime_t last_update_db_because_of_stamp = 0 ; - rstime_t now = time(NULL) ; - - res->second._time_stamp = now ; - - if(now > last_update_db_because_of_stamp + 3600) // only update database once every hour. No need to do it more often. - { - _trustdb_changed = true ; - last_update_db_because_of_stamp = now ; - } - } - return ops_keyring_get_key_by_index(_pubring,res->second._key_index) ; - } -} - -std::string PGPHandler::SaveCertificateToString(const RsPgpId& id,bool include_signatures) const -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - const ops_keydata_t *key = locked_getPublicKey(id,false) ; - - if(key == NULL) - { - std::cerr << "Cannot output key " << id.toStdString() << ": not found in keyring." << std::endl; - return "" ; - } - - return makeRadixEncodedPGPKey(key,include_signatures) ; -} - -bool PGPHandler::exportPublicKey( - const RsPgpId& id, - unsigned char*& mem_block, size_t& mem_size, - bool armoured, bool include_signatures ) const -{ - mem_block = nullptr; mem_size = 0; // clear just in case - - if(armoured) - { - RsErr() << __PRETTY_FUNCTION__ << " should not be used with " - << "armoured=true, because there's a bug in the armoured export" - << " of OPS" << std::endl; - print_stacktrace(); - return false; - } - - RS_STACK_MUTEX(pgphandlerMtx); - const ops_keydata_t* key = locked_getPublicKey(id,false); - - if(!key) - { - RsErr() << __PRETTY_FUNCTION__ << " key id: " << id - << " not found in keyring." << std::endl; - return false; - } - - ops_create_info_t* cinfo; - ops_memory_t *buf = nullptr; - ops_setup_memory_write(&cinfo, &buf, 0); - - if(ops_write_transferable_public_key_from_packet_data( - key, armoured, cinfo ) != ops_true) - { - RsErr() << __PRETTY_FUNCTION__ << " This key id " << id - << " cannot be processed by RetroShare because DSA certificates" - << " support is not implemented yet." << std::endl; - return false; - } - - ops_writer_close(cinfo); - - mem_size = ops_memory_get_length(buf); - mem_block = reinterpret_cast(malloc(mem_size)); - memcpy(mem_block,ops_memory_get_data(buf),mem_size); - - ops_teardown_memory_write(cinfo,buf); - - if(!include_signatures) - { - size_t new_size; - PGPKeyManagement::findLengthOfMinimalKey(mem_block, mem_size, new_size); - mem_size = new_size; - } - - return true; -} - -bool PGPHandler::exportGPGKeyPair(const std::string& filename,const RsPgpId& exported_key_id) const -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - const ops_keydata_t *pubkey = locked_getPublicKey(exported_key_id,false) ; - - if(pubkey == NULL) - { - std::cerr << "Cannot output key " << exported_key_id.toStdString() << ": not found in public keyring." << std::endl; - return false ; - } - const ops_keydata_t *seckey = locked_getSecretKey(exported_key_id) ; - - if(seckey == NULL) - { - std::cerr << "Cannot output key " << exported_key_id.toStdString() << ": not found in secret keyring." << std::endl; - return false ; - } - - FILE *f = RsDirUtil::rs_fopen(filename.c_str(),"w") ; - if(f == NULL) - { - std::cerr << "Cannot output key " << exported_key_id.toStdString() << ": file " << filename << " cannot be written. Please check for permissions, quotas, disk space." << std::endl; - return false ; - } - - fprintf(f,"%s\n", makeRadixEncodedPGPKey(pubkey,true).c_str()) ; - fprintf(f,"%s\n", makeRadixEncodedPGPKey(seckey,true).c_str()) ; - - fclose(f) ; - return true ; -} - -bool PGPHandler::exportGPGKeyPairToString( - std::string& data, const RsPgpId& exportedKeyId, - bool includeSignatures, std::string& errorMsg ) const -{ - RS_STACK_MUTEX(pgphandlerMtx); - - const ops_keydata_t *pubkey = locked_getPublicKey(exportedKeyId,false); - - if(!pubkey) - { - errorMsg = "Cannot output key " + exportedKeyId.toStdString() + - ": not found in public keyring."; - return false; - } - const ops_keydata_t *seckey = locked_getSecretKey(exportedKeyId); - - if(!seckey) - { - errorMsg = "Cannot output key " + exportedKeyId.toStdString() + - ": not found in secret keyring."; - return false; - } - - data = makeRadixEncodedPGPKey(pubkey, includeSignatures); - data += "\n"; - data += makeRadixEncodedPGPKey(seckey, includeSignatures); - data += "\n"; - return true; -} - -bool PGPHandler::getGPGDetailsFromBinaryBlock(const unsigned char *mem_block,size_t mem_size,RsPgpId& key_id, std::string& name, std::list& signers) const -{ - ops_keyring_t *tmp_keyring = allocateOPSKeyring(); - ops_memory_t *mem = ops_memory_new() ; - ops_memory_add(mem,mem_block,mem_size); - - if(!ops_keyring_read_from_mem(tmp_keyring,ops_false,mem)) - { - ops_keyring_free(tmp_keyring) ; - free(tmp_keyring) ; - ops_memory_release(mem) ; - free(mem) ; - - std::cerr << "Could not read key. Format error?" << std::endl; - //error_string = std::string("Could not read key. Format error?") ; - return false ; - } - ops_memory_release(mem) ; - free(mem) ; - //error_string.clear() ; - - if(tmp_keyring->nkeys != 1) - { - std::cerr << "No or incomplete/invalid key in supplied pgp block." << std::endl; - return false ; - } - if(tmp_keyring->keys[0].uids == NULL) - { - std::cerr << "No uid in supplied key." << std::endl; - return false ; - } - - key_id = RsPgpId(tmp_keyring->keys[0].key_id) ; - name = std::string((char *)tmp_keyring->keys[0].uids[0].user_id) ; - - // now parse signatures. - // - ops_validate_result_t* result=(ops_validate_result_t*)ops_mallocz(sizeof *result); - ops_boolean_t res ; - - { - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - res = ops_validate_key_signatures(result,&tmp_keyring->keys[0],_pubring,cb_get_passphrase) ; - } - - if(res == ops_false) - std::cerr << "(WW) Error in PGPHandler::validateAndUpdateSignatures(). Validation failed for at least some signatures." << std::endl; - - // also add self-signature if any (there should be!). - // - res = ops_validate_key_signatures(result,&tmp_keyring->keys[0],tmp_keyring,cb_get_passphrase) ; - - if(res == ops_false) - std::cerr << "(WW) Error in PGPHandler::validateAndUpdateSignatures(). Validation failed for at least some signatures." << std::endl; - - // Parse signers. - // - - std::set signers_set ; // Use a set to remove duplicates. - - if(result != NULL) - for(size_t i=0;ivalid_count;++i) - signers_set.insert(RsPgpId(result->valid_sigs[i].signer_id)) ; - - ops_validate_result_free(result) ; - - ops_keyring_free(tmp_keyring) ; - free(tmp_keyring) ; - - // write to the output variable - - signers.clear() ; - - for(std::set::const_iterator it(signers_set.begin());it!=signers_set.end();++it) - signers.push_back(*it) ; - - return true ; -} - -bool PGPHandler::importGPGKeyPair(const std::string& filename,RsPgpId& imported_key_id,std::string& import_error) -{ - import_error = "" ; - - // 1 - Test for file existance - // - FILE *ftest = RsDirUtil::rs_fopen(filename.c_str(),"r") ; - - if(ftest == NULL) - { - import_error = "Cannot open file " + filename + " for read. Please check access permissions." ; - return false ; - } - - fclose(ftest) ; - - // 2 - Read keyring from supplied file. - // - ops_keyring_t *tmp_keyring = allocateOPSKeyring(); - - if(ops_false == ops_keyring_read_from_file(tmp_keyring, ops_true, filename.c_str())) - { - import_error = "PGPHandler::readKeyRing(): cannot read key file. File corrupted?" ; - free(tmp_keyring); - return false ; - } - - return checkAndImportKeyPair(tmp_keyring, imported_key_id, import_error); -} - -bool PGPHandler::importGPGKeyPairFromString(const std::string &data, RsPgpId &imported_key_id, std::string &import_error) -{ - import_error = "" ; - - ops_memory_t* mem = ops_memory_new(); - ops_memory_add(mem, (unsigned char*)data.data(), data.length()); - - ops_keyring_t *tmp_keyring = allocateOPSKeyring(); - - if(ops_false == ops_keyring_read_from_mem(tmp_keyring, ops_true, mem)) - { - import_error = "PGPHandler::importGPGKeyPairFromString(): cannot parse key data" ; - free(tmp_keyring); - return false ; - } - return checkAndImportKeyPair(tmp_keyring, imported_key_id, import_error); -} - -bool PGPHandler::checkAndImportKeyPair(ops_keyring_t *tmp_keyring, RsPgpId &imported_key_id, std::string &import_error) -{ - if(tmp_keyring == 0) - { - import_error = "PGPHandler::checkAndImportKey(): keyring is null" ; - return false; - } - - if(tmp_keyring->nkeys != 2) - { - import_error = "PGPHandler::importKeyPair(): file does not contain a valid keypair." ; - if(tmp_keyring->nkeys > 2) - import_error += "\nMake sure that your key is a RSA key (DSA is not yet supported) and does not contain subkeys (not supported yet)."; - return false ; - } - - // 3 - Test that keyring contains a valid keypair. - // - const ops_keydata_t *pubkey = NULL ; - const ops_keydata_t *seckey = NULL ; - - if(tmp_keyring->keys[0].type == OPS_PTAG_CT_PUBLIC_KEY) - pubkey = &tmp_keyring->keys[0] ; - else if(tmp_keyring->keys[0].type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) - seckey = &tmp_keyring->keys[0] ; - else - { - import_error = "Unrecognised key type in key file for key #0. Giving up." ; - std::cerr << "Unrecognised key type " << tmp_keyring->keys[0].type << " in key file for key #0. Giving up." << std::endl; - return false ; - } - if(tmp_keyring->keys[1].type == OPS_PTAG_CT_PUBLIC_KEY) - pubkey = &tmp_keyring->keys[1] ; - else if(tmp_keyring->keys[1].type == OPS_PTAG_CT_ENCRYPTED_SECRET_KEY) - seckey = &tmp_keyring->keys[1] ; - else - { - import_error = "Unrecognised key type in key file for key #1. Giving up." ; - std::cerr << "Unrecognised key type " << tmp_keyring->keys[1].type << " in key file for key #1. Giving up." << std::endl; - return false ; - } - - if(pubkey == nullptr || seckey == nullptr || pubkey == seckey) - { - import_error = "File does not contain a public and a private key. Sorry." ; - return false ; - } - if(memcmp( pubkey->fingerprint.fingerprint, - seckey->fingerprint.fingerprint, - RsPgpFingerprint::SIZE_IN_BYTES ) != 0) - { - import_error = "Public and private keys do nt have the same fingerprint. Sorry!" ; - return false ; - } - if(pubkey->key.pkey.version != 4) - { - import_error = "Public key is not version 4. Rejected!" ; - return false ; - } - - // 4 - now check self-signature for this keypair. For this we build a dummy keyring containing only the key. - // - ops_validate_result_t *result=(ops_validate_result_t*)ops_mallocz(sizeof *result); - - ops_keyring_t dummy_keyring ; - dummy_keyring.nkeys=1 ; - dummy_keyring.nkeys_allocated=1 ; - dummy_keyring.keys=const_cast(pubkey) ; - - ops_validate_key_signatures(result, const_cast(pubkey), &dummy_keyring, cb_get_passphrase) ; - - // Check that signatures contain at least one certification from the user id. - // - bool found = false ; - - for(uint32_t i=0;ivalid_count;++i) - if(!memcmp( - static_cast(result->valid_sigs[i].signer_id), - pubkey->key_id, - RsPgpId::SIZE_IN_BYTES )) - { - found = true ; - break ; - } - - if(!found) - { - import_error = "Cannot validate self signature for the imported key. Sorry." ; - return false ; - } - ops_validate_result_free(result); - - if(!RsDiscSpace::checkForDiscSpace(RS_PGP_DIRECTORY)) - { - import_error = std::string("(EE) low disc space in pgp directory. Can't write safely to keyring.") ; - return false ; - } - // 5 - All test passed. Adding key to keyring. - // - { - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - imported_key_id = RsPgpId(pubkey->key_id) ; - - if(locked_getSecretKey(imported_key_id) == NULL) - { - RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. - - ops_create_info_t *cinfo = NULL ; - - // Make a copy of the secret keyring - // - std::string secring_path_tmp = _secring_path + ".tmp" ; - if(RsDirUtil::fileExists(_secring_path) && !RsDirUtil::copyFile(_secring_path,secring_path_tmp)) - { - import_error = "(EE) Cannot write secret key to disk!! Disk full? Out of disk quota. Keyring will be left untouched." ; - return false ; - } - - // Append the new key - - int fd=ops_setup_file_append(&cinfo, secring_path_tmp.c_str()); - - if(!ops_write_transferable_secret_key_from_packet_data(seckey,ops_false,cinfo)) - { - import_error = "(EE) Cannot encode secret key to disk!! Disk full? Out of disk quota?" ; - return false ; - } - ops_teardown_file_write(cinfo,fd) ; - - // Rename the new keyring to overwrite the old one. - // - if(!RsDirUtil::renameFile(secring_path_tmp,_secring_path)) - { - import_error = " (EE) Cannot move temp file " + secring_path_tmp + ". Bad write permissions?" ; - return false ; - } - - addNewKeyToOPSKeyring(_secring,*seckey) ; - initCertificateInfo(_secret_keyring_map[ imported_key_id ],seckey,_secring->nkeys-1) ; - } - else - import_error = "Private key already exists! Not importing it again." ; - - if(locked_addOrMergeKey(_pubring,_public_keyring_map,pubkey)) - _pubring_changed = true ; - } - - // 6 - clean - // - ops_keyring_free(tmp_keyring) ; - free(tmp_keyring); - - // write public key to disk - syncDatabase(); - - return true ; -} - -void PGPHandler::addNewKeyToOPSKeyring(ops_keyring_t *kr,const ops_keydata_t& key) -{ - if(kr->nkeys >= kr->nkeys_allocated) - { - kr->keys = (ops_keydata_t *)realloc(kr->keys,(kr->nkeys+1)*sizeof(ops_keydata_t)) ; - kr->nkeys_allocated = kr->nkeys+1; - } - memset(&kr->keys[kr->nkeys],0,sizeof(ops_keydata_t)) ; - ops_keydata_copy(&kr->keys[kr->nkeys],&key) ; - kr->nkeys++ ; -} - -bool PGPHandler::LoadCertificateFromBinaryData(const unsigned char *data,uint32_t data_len,RsPgpId& id,std::string& error_string) -{ - return LoadCertificate(data,data_len,ops_false,id,error_string); -} - -bool PGPHandler::LoadCertificateFromString(const std::string& pgp_cert,RsPgpId& id,std::string& error_string) -{ - return LoadCertificate((unsigned char*)(pgp_cert.c_str()),pgp_cert.length(),ops_true,id,error_string); -} - -bool PGPHandler::LoadCertificate(const unsigned char *data,uint32_t data_len,bool armoured,RsPgpId& id,std::string& error_string) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. -#ifdef DEBUG_PGPHANDLER - std::cerr << "Reading new key from string: " << std::endl; -#endif - - ops_keyring_t *tmp_keyring = allocateOPSKeyring(); - ops_memory_t *mem = ops_memory_new() ; - ops_memory_add(mem,data,data_len) ; - - if(!ops_keyring_read_from_mem(tmp_keyring,armoured,mem)) - { - ops_keyring_free(tmp_keyring) ; - free(tmp_keyring) ; - ops_memory_release(mem) ; - free(mem) ; - - std::cerr << "Could not read key. Format error?" << std::endl; - error_string = std::string("Could not read key. Format error?") ; - return false ; - } - ops_memory_release(mem) ; - free(mem) ; - error_string.clear() ; - - // Check that there is exactly one key in this data packet. - // - if(tmp_keyring->nkeys != 1) - { - std::cerr << "Loaded certificate contains more than one PGP key. This is not allowed." << std::endl; - error_string = "Loaded certificate contains more than one PGP key. This is not allowed." ; - return false ; - } - - const ops_keydata_t *keydata = ops_keyring_get_key_by_index(tmp_keyring,0); - - // Check that the key is a version 4 key - // - if(keydata->key.pkey.version != 4) - { - error_string = "Public key is not version 4. Rejected!" ; - std::cerr << "Received a key with unhandled version number (" << keydata->key.pkey.version << ")" << std::endl; - return false ; - } - - // Check that the key is correctly self-signed. - // - ops_validate_result_t* result=(ops_validate_result_t*)ops_mallocz(sizeof *result); - - ops_validate_key_signatures(result,keydata,tmp_keyring,cb_get_passphrase) ; - - bool found = false ; - - for(uint32_t i=0;ivalid_count;++i) - if(!memcmp( - static_cast(result->valid_sigs[i].signer_id), - keydata->key_id, - RsPgpId::SIZE_IN_BYTES )) - { - found = true ; - break ; - } - - if(!found) - { - error_string = "This key is not self-signed. This is required by Retroshare." ; - std::cerr << "This key is not self-signed. This is required by Retroshare." << std::endl; - ops_validate_result_free(result); - return false ; - } - ops_validate_result_free(result); - -#ifdef DEBUG_PGPHANDLER - std::cerr << " Key read correctly: " << std::endl; - ops_keyring_list(tmp_keyring) ; -#endif - - int i=0 ; - - while( (keydata = ops_keyring_get_key_by_index(tmp_keyring,i++)) != NULL ) - if(locked_addOrMergeKey(_pubring,_public_keyring_map,keydata)) - { - _pubring_changed = true ; -#ifdef DEBUG_PGPHANDLER - std::cerr << " Added the key in the main public keyring." << std::endl; -#endif - } - else - std::cerr << "Key already in public keyring." << std::endl; - - if(tmp_keyring->nkeys > 0) - id = RsPgpId(tmp_keyring->keys[0].key_id) ; - else - return false ; - - ops_keyring_free(tmp_keyring) ; - free(tmp_keyring) ; - - _pubring_changed = true ; - - return true ; -} - -bool PGPHandler::locked_addOrMergeKey(ops_keyring_t *keyring,std::map& kmap,const ops_keydata_t *keydata) -{ - bool ret = false ; - RsPgpId id(keydata->key_id) ; - -#ifdef DEBUG_PGPHANDLER - std::cerr << "AddOrMergeKey():" << std::endl; - std::cerr << " id: " << id.toStdString() << std::endl; -#endif - - // See if the key is already in the keyring - const ops_keydata_t *existing_key = NULL; - std::map::const_iterator res = kmap.find(id) ; - - // Checks that - // - the key is referenced by keyid - // - the map is initialized - // - the fingerprint matches! - // - if(res == kmap.end() || (existing_key = ops_keyring_get_key_by_index(keyring,res->second._key_index)) == NULL) - { -#ifdef DEBUG_PGPHANDLER - std::cerr << " Key is new. Adding it to keyring" << std::endl; -#endif - addNewKeyToOPSKeyring(keyring,*keydata) ; // the key is new. - initCertificateInfo(kmap[id],keydata,keyring->nkeys-1) ; - existing_key = &(keyring->keys[keyring->nkeys-1]) ; - ret = true ; - } - else - { - if(memcmp( existing_key->fingerprint.fingerprint, - keydata->fingerprint.fingerprint, - RsPgpFingerprint::SIZE_IN_BYTES )) - { - std::cerr << "(EE) attempt to merge key with identical id, but different fingerprint!" << std::endl; - return false ; - } - -#ifdef DEBUG_PGPHANDLER - std::cerr << " Key exists. Merging signatures." << std::endl; -#endif - ret = mergeKeySignatures(const_cast(existing_key),keydata) ; - - if(ret) - initCertificateInfo(kmap[id],existing_key,res->second._key_index) ; - } - - if(ret) - { - validateAndUpdateSignatures(kmap[id],existing_key) ; - kmap[id]._time_stamp = time(NULL) ; - } - - return ret ; -} - -bool PGPHandler::encryptTextToFile(const RsPgpId& key_id,const std::string& text,const std::string& outfile) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - const ops_keydata_t *public_key = locked_getPublicKey(key_id,true) ; - - if(public_key == NULL) - { - std::cerr << "Cannot get public key of id " << key_id.toStdString() << std::endl; - return false ; - } - - if(public_key->type != OPS_PTAG_CT_PUBLIC_KEY) - { - std::cerr << "PGPHandler::encryptTextToFile(): ERROR: supplied id did not return a public key!" << std::endl; - return false ; - } - - std::string outfile_tmp = outfile + ".tmp" ; - - ops_create_info_t *info; - int fd = ops_setup_file_write(&info, outfile_tmp.c_str(), ops_true); - - if (fd < 0) - { - std::cerr << "PGPHandler::encryptTextToFile(): ERROR: Cannot write to " << outfile_tmp << std::endl; - return false ; - } - - if(!ops_encrypt_stream(info, public_key, NULL, ops_false, ops_true)) - { - std::cerr << "PGPHandler::encryptTextToFile(): ERROR: encryption failed." << std::endl; - return false ; - } - - ops_write(text.c_str(), text.length(), info); - ops_teardown_file_write(info, fd); - - if(!RsDirUtil::renameFile(outfile_tmp,outfile)) - { - std::cerr << "PGPHandler::encryptTextToFile(): ERROR: Cannot rename " + outfile_tmp + " to " + outfile + ". Disk error?" << std::endl; - return false ; - } - - return true ; -} - -bool PGPHandler::encryptDataBin(const RsPgpId& key_id,const void *data, const uint32_t len, unsigned char *encrypted_data, unsigned int *encrypted_data_len) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - const ops_keydata_t *public_key = locked_getPublicKey(key_id,true) ; - - if(public_key == NULL) - { - std::cerr << "Cannot get public key of id " << key_id.toStdString() << std::endl; - return false ; - } - - if(public_key->type != OPS_PTAG_CT_PUBLIC_KEY) - { - std::cerr << "PGPHandler::encryptTextToFile(): ERROR: supplied id did not return a public key!" << std::endl; - return false ; - } - if(public_key->key.pkey.algorithm != OPS_PKA_RSA) - { - std::cerr << "PGPHandler::encryptTextToFile(): ERROR: supplied key id " << key_id.toStdString() << " is not an RSA key (DSA for instance, is not supported)!" << std::endl; - return false ; - } - ops_create_info_t *info; - ops_memory_t *buf = NULL ; - ops_setup_memory_write(&info, &buf, 0); - bool res = true; - - if(!ops_encrypt_stream(info, public_key, NULL, ops_false, ops_false)) - { - std::cerr << "Encryption failed." << std::endl; - res = false ; - } - - ops_write(data,len,info); - ops_writer_close(info); - ops_create_info_delete(info); - - int tlen = ops_memory_get_length(buf) ; - - if( (int)*encrypted_data_len >= tlen) - { - if(res) - { - memcpy(encrypted_data,ops_memory_get_data(buf),tlen) ; - *encrypted_data_len = tlen ; - res = true ; - } - } - else - { - std::cerr << "Not enough room to fit encrypted data. Size given=" << *encrypted_data_len << ", required=" << tlen << std::endl; - res = false ; - } - - ops_memory_release(buf) ; - free(buf) ; - - return res ; -} - -bool PGPHandler::decryptDataBin(const RsPgpId& /*key_id*/,const void *encrypted_data, const uint32_t encrypted_len, unsigned char *data, unsigned int *data_len) -{ - int out_length ; - unsigned char *out ; - ops_boolean_t res = ops_decrypt_memory((const unsigned char *)encrypted_data,encrypted_len,&out,&out_length,_secring,ops_false,cb_get_passphrase) ; - - if(*data_len < (unsigned int)out_length) - { - std::cerr << "Not enough room to store decrypted data! Please give more."<< std::endl; - return false ; - } - - *data_len = (unsigned int)out_length ; - memcpy(data,out,out_length) ; - free(out) ; - - return (bool)res ; -} - -bool PGPHandler::decryptTextFromFile(const RsPgpId&,std::string& text,const std::string& inputfile) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - unsigned char *out_buf = NULL ; - std::string buf ; - - FILE *f = RsDirUtil::rs_fopen(inputfile.c_str(),"rb") ; - - if (f == NULL) - { - std::cerr << "Cannot open file " << inputfile << " for read." << std::endl; - return false; - } - - int c ; - while( (c = fgetc(f))!= EOF) - buf += (unsigned char)c; - - fclose(f) ; - -#ifdef DEBUG_PGPHANDLER - std::cerr << "PGPHandler::decryptTextFromFile: read a file of length " << std::dec << buf.length() << std::endl; - std::cerr << "buf=\"" << buf << "\"" << std::endl; -#endif - - int out_length ; - ops_boolean_t res = ops_decrypt_memory((const unsigned char *)buf.c_str(),buf.length(),&out_buf,&out_length,_secring,ops_true,cb_get_passphrase) ; - - text = std::string((char *)out_buf,out_length) ; - free (out_buf); - return (bool)res ; -} - -bool PGPHandler::SignDataBin(const RsPgpId& id,const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,bool use_raw_signature, std::string reason /* = "" */) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - // need to find the key and to decrypt it. - - const ops_keydata_t *key = locked_getSecretKey(id) ; - - if(!key) - { - std::cerr << "Cannot sign: no secret key with id " << id.toStdString() << std::endl; - return false ; - } - - std::string uid_hint ; - if(key->nuids > 0) - uid_hint = std::string((const char *)key->uids[0].user_id) ; - uid_hint += "(" + RsPgpId(key->key_id).toStdString()+")" ; - -#ifdef DEBUG_PGPHANDLER - ops_fingerprint_t f ; - ops_fingerprint(&f,&key->key.pkey) ; - - PGPFingerprintType fp(f.fingerprint) ; -#endif - - bool last_passwd_was_wrong = false ; -ops_secret_key_t *secret_key = NULL ; - - for(int i=0;i<3;++i) - { - bool cancelled =false; - std::string passphrase = _passphrase_callback(NULL,reason.c_str(),uid_hint.c_str(),"Please enter passwd for encrypting your key : ",last_passwd_was_wrong,&cancelled) ;//TODO reason - - secret_key = ops_decrypt_secret_key_from_data(key,passphrase.c_str()) ; - - if(cancelled) - { - std::cerr << "Key entering cancelled" << std::endl; - return false ; - } - if(secret_key) - break ; - - std::cerr << "Key decryption went wrong. Wrong passwd?" << std::endl; - last_passwd_was_wrong = true ; - } - if(!secret_key) - { - std::cerr << "Could not obtain secret key. Signature cancelled." << std::endl; - return false ; - } - - // then do the signature. - - ops_boolean_t not_raw = !use_raw_signature ; -#ifdef V07_NON_BACKWARD_COMPATIBLE_CHANGE_002 - ops_memory_t *memres = ops_sign_buf(data,len,OPS_SIG_BINARY,OPS_HASH_SHA256,secret_key,ops_false,ops_false,not_raw,not_raw) ; -#else - ops_memory_t *memres = ops_sign_buf(data,len,OPS_SIG_BINARY,OPS_HASH_SHA1,secret_key,ops_false,ops_false,not_raw,not_raw) ; -#endif - - if(!memres) - return false ; - - bool res ; - uint32_t slen = (uint32_t)ops_memory_get_length(memres); - - if(*signlen >= slen) - { - *signlen = slen ; - - memcpy(sign,ops_memory_get_data(memres),*signlen) ; - res = true ; - } - else - { - std::cerr << "(EE) memory chunk is not large enough for signature packet. Requred size: " << slen << " bytes." << std::endl; - res = false ; - } - - ops_memory_release(memres) ; - free(memres) ; - ops_secret_key_free(secret_key) ; - free(secret_key) ; - -#ifdef DEBUG_PGPHANDLER - std::cerr << "Signed with fingerprint " << fp.toStdString() << ", length " << std::dec << *signlen << ", literal data length = " << len << std::endl; - std::cerr << "Signature body: " << std::endl; - hexdump( (unsigned char *)data, len) ; - std::cerr << std::endl; - std::cerr << "Data: " << std::endl; - hexdump( (unsigned char *)sign,*signlen) ; - std::cerr << std::endl; -#endif - return res ; -} - -bool PGPHandler::privateSignCertificate(const RsPgpId& ownId,const RsPgpId& id_of_key_to_sign) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - ops_keydata_t *key_to_sign = const_cast(locked_getPublicKey(id_of_key_to_sign,true)) ; - - if(key_to_sign == NULL) - { - std::cerr << "Cannot sign: no public key with id " << id_of_key_to_sign.toStdString() << std::endl; - return false ; - } - - // 1 - get decrypted secret key - // - const ops_keydata_t *skey = locked_getSecretKey(ownId) ; - - if(!skey) - { - std::cerr << "Cannot sign: no secret key with id " << ownId.toStdString() << std::endl; - return false ; - } - const ops_keydata_t *pkey = locked_getPublicKey(ownId,true) ; - - if(!pkey) - { - std::cerr << "Cannot sign: no public key with id " << ownId.toStdString() << std::endl; - return false ; - } - - bool cancelled = false; - std::string passphrase = _passphrase_callback(NULL,"",RsPgpId(skey->key_id).toStdString().c_str(),"Please enter passwd for encrypting your key : ",false,&cancelled) ; - - ops_secret_key_t *secret_key = ops_decrypt_secret_key_from_data(skey,passphrase.c_str()) ; - - if(cancelled) - { - std::cerr << "Key cancelled by used." << std::endl; - return false ; - } - if(!secret_key) - { - std::cerr << "Key decryption went wrong. Wrong passwd?" << std::endl; - return false ; - } - - // 2 - then do the signature. - - if(!ops_sign_key(key_to_sign,pkey->key_id,secret_key)) - { - std::cerr << "Key signature went wrong. Wrong passwd?" << std::endl; - return false ; - } - - // 3 - free memory - // - ops_secret_key_free(secret_key) ; - free(secret_key) ; - - _pubring_changed = true ; - - // 4 - update signatures. - // - PGPCertificateInfo& cert(_public_keyring_map[ id_of_key_to_sign ]) ; - validateAndUpdateSignatures(cert,key_to_sign) ; - cert._flags |= PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE ; - - return true ; -} - void PGPHandler::updateOwnSignatureFlag(const RsPgpId& own_id) { RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. if(_public_keyring_map.find(own_id)==_public_keyring_map.end()) { - std::cerr << __func__ << ": key with id=" << own_id.toStdString() << " not in keyring." << std::endl; + RsErr() << __func__ << ": key with id=" << own_id.toStdString() << " not in keyring." ; // return now, because the following operation would add an entry to _public_keyring_map return; } @@ -1569,7 +135,7 @@ void PGPHandler::updateOwnSignatureFlag(const RsPgpId& cert_id,const RsPgpId& ow if(it == _public_keyring_map.end()) { - std::cerr << "updateOwnSignatureFlag: Cannot get certificate for string " << cert_id.toStdString() << ". This is probably a bug." << std::endl; + RsErr() << "updateOwnSignatureFlag: Cannot get certificate for string " << cert_id.toStdString() << ". This is probably a bug." ; return ; } @@ -1599,59 +165,6 @@ void PGPHandler::locked_updateOwnSignatureFlag(PGPCertificateInfo& cert,const Rs RsPgpFingerprint::SIZE_IN_BYTES - RsPgpId::SIZE_IN_BYTES ); } -bool PGPHandler::getKeyFingerprint(const RsPgpId& id, RsPgpFingerprint& fp) const -{ - RS_STACK_MUTEX(pgphandlerMtx); - - const ops_keydata_t *key = locked_getPublicKey(id,false) ; - - if(!key) return false; - - ops_fingerprint_t f ; - ops_fingerprint(&f,&key->key.pkey) ; - - fp = RsPgpFingerprint::fromBufferUnsafe(f.fingerprint); - - return true ; -} - -bool PGPHandler::VerifySignBin(const void *literal_data, uint32_t literal_data_length, unsigned char *sign, unsigned int sign_len, const PGPFingerprintType& key_fingerprint) -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - - RsPgpId id = RsPgpId(key_fingerprint.toByteArray() + PGPFingerprintType::SIZE_IN_BYTES - RsPgpId::SIZE_IN_BYTES) ; - const ops_keydata_t *key = locked_getPublicKey(id,true) ; - - if(key == NULL) - { - std::cerr << "No key returned by fingerprint " << key_fingerprint.toStdString() << ", and ID " << id.toStdString() << ", signature verification failed!" << std::endl; - return false ; - } - - // Check that fingerprint is the same. - const ops_public_key_t *pkey = &key->key.pkey ; - ops_fingerprint_t fp ; - ops_fingerprint(&fp,pkey) ; - - if(key_fingerprint != PGPFingerprintType(fp.fingerprint)) - { - std::cerr << "Key fingerprint does not match " << key_fingerprint.toStdString() << ", for ID " << id.toStdString() << ", signature verification failed!" << std::endl; - return false ; - } - -#ifdef DEBUG_PGPHANDLER - std::cerr << "Verifying signature from fingerprint " << key_fingerprint.toStdString() << ", length " << std::dec << sign_len << ", literal data length = " << literal_data_length << std::endl; - std::cerr << "Signature body: " << std::endl; - hexdump( (unsigned char *)sign,sign_len) ; - std::cerr << std::endl; - std::cerr << "Signed data: " << std::endl; - hexdump( (unsigned char *)literal_data, literal_data_length) ; - std::cerr << std::endl; -#endif - - return ops_validate_detached_signature(literal_data,literal_data_length,sign,sign_len,key) ; -} - void PGPHandler::setAcceptConnexion(const RsPgpId& id,bool b) { RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. @@ -1699,65 +212,6 @@ bool PGPHandler::isGPGAccepted(const RsPgpId &id) return (res != _public_keyring_map.end()) && (res->second._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION) ; } -// Lexicographic order on signature packets -// -bool operator<(const ops_packet_t& p1,const ops_packet_t& p2) -{ - if(p1.length < p2.length) - return true ; - if(p1.length > p2.length) - return false ; - - for(uint32_t i=0;i p2.raw[i]) - return false ; - } - return false ; -} - -bool PGPHandler::mergeKeySignatures(ops_keydata_t *dst,const ops_keydata_t *src) -{ - // First sort all signatures into lists to see which is new, which is not new - -#ifdef DEBUG_PGPHANDLER - std::cerr << "Merging signatures for key " << RsPgpId(dst->key_id).toStdString() << std::endl; -#endif - std::set dst_packets ; - - for(uint32_t i=0;inpackets;++i) dst_packets.insert(dst->packets[i]) ; - - std::set to_add ; - - for(uint32_t i=0;inpackets;++i) - if(dst_packets.find(src->packets[i]) == dst_packets.end()) - { - uint8_t tag ; - uint32_t length ; - unsigned char *tmp_data = src->packets[i].raw ; // put it in a tmp variable because read_packetHeader() will modify it!! - - PGPKeyParser::read_packetHeader(tmp_data,tag,length) ; - - if(tag == PGPKeyParser::PGP_PACKET_TAG_SIGNATURE) - to_add.insert(src->packets[i]) ; -#ifdef DEBUG_PGPHANDLER - else - std::cerr << " Packet with tag 0x" << std::hex << (int)(src->packets[i].raw[0]) << std::dec << " not merged, because it is not a signature." << std::endl; -#endif - } - - for(std::set::const_iterator it(to_add.begin());it!=to_add.end();++it) - { -#ifdef DEBUG_PGPHANDLER - std::cerr << " Adding packet with tag 0x" << std::hex << (int)(*it).raw[0] << std::dec << std::endl; -#endif - ops_add_packet_to_keydata(dst,&*it) ; - } - return to_add.size() > 0 ; -} - bool PGPHandler::parseSignature(unsigned char *sign, unsigned int signlen,RsPgpId& issuer_id) { PGPSignatureInfo info ; @@ -1780,7 +234,7 @@ bool PGPHandler::privateTrustCertificate(const RsPgpId& id,int trustlvl) { if(trustlvl < 0 || trustlvl >= 6 || trustlvl == 1) { - std::cerr << "Invalid trust level " << trustlvl << " passed to privateTrustCertificate." << std::endl; + RsErr() << "Invalid trust level " << trustlvl << " passed to privateTrustCertificate." ; return false ; } @@ -1788,7 +242,7 @@ bool PGPHandler::privateTrustCertificate(const RsPgpId& id,int trustlvl) if(it == _public_keyring_map.end()) { - std::cerr << "(EE) Key id " << id.toStdString() << " not in the keyring. Can't setup trust level." << std::endl; + RsErr() << "(EE) Key id " << id.toStdString() << " not in the keyring. Can't setup trust level." ; return false ; } @@ -1812,12 +266,12 @@ void PGPHandler::locked_readPrivateTrustDatabase() { FILE *fdb = RsDirUtil::rs_fopen(_trustdb_path.c_str(),"rb") ; #ifdef DEBUG_PGPHANDLER - std::cerr << "PGPHandler: Reading private trust database." << std::endl; + RsErr() << "PGPHandler: Reading private trust database." ; #endif if(fdb == NULL) { - std::cerr << " private trust database not found. No trust info loaded." << std::endl ; + RsErr() << " private trust database not found. No trust info loaded." << std::endl ; return ; } std::map::iterator it ; @@ -1830,12 +284,12 @@ void PGPHandler::locked_readPrivateTrustDatabase() if(it == _public_keyring_map.end()) { - std::cerr << " (WW) Trust packet found for unknown key id " << RsPgpId(trustpacket.user_id).toStdString() << std::endl; + RsErr() << " (WW) Trust packet found for unknown key id " << RsPgpId(trustpacket.user_id).toStdString() ; continue ; } if(trustpacket.trust_level > 6) { - std::cerr << " (WW) Trust packet found with unexpected trust level " << trustpacket.trust_level << std::endl; + RsErr() << " (WW) Trust packet found with unexpected trust level " << trustpacket.trust_level ; continue ; } @@ -1848,19 +302,19 @@ void PGPHandler::locked_readPrivateTrustDatabase() fclose(fdb) ; - std::cerr << "PGPHandler: Successfully read " << std::hex << n_packets << std::dec << " trust packets." << std::endl; + RsErr() << "PGPHandler: Successfully read " << std::hex << n_packets << std::dec << " trust packets." ; } bool PGPHandler::locked_writePrivateTrustDatabase() { FILE *fdb = RsDirUtil::rs_fopen((_trustdb_path+".tmp").c_str(),"wb") ; #ifdef DEBUG_PGPHANDLER - std::cerr << "PGPHandler: Reading private trust database." << std::endl; + RsErr() << "PGPHandler: Reading private trust database." ; #endif if(fdb == NULL) { - std::cerr << " (EE) Can't open private trust database file " << _trustdb_path << " for write. Giving up!" << std::endl ; + RsErr() << " (EE) Can't open private trust database file " << _trustdb_path << " for write. Giving up!" << std::endl ; return false; } PrivateTrustPacket trustpacket ; @@ -1878,7 +332,7 @@ bool PGPHandler::locked_writePrivateTrustDatabase() if(fwrite((void*)&trustpacket,sizeof(PrivateTrustPacket),1,fdb) != 1) { - std::cerr << " (EE) Cannot write to trust database " << _trustdb_path << ". Disc full, or quota exceeded ? Leaving database untouched." << std::endl; + RsErr() << " (EE) Cannot write to trust database " << _trustdb_path << ". Disc full, or quota exceeded ? Leaving database untouched." ; fclose(fdb) ; return false; } @@ -1888,81 +342,13 @@ bool PGPHandler::locked_writePrivateTrustDatabase() if(!RsDirUtil::renameFile(_trustdb_path+".tmp",_trustdb_path)) { - std::cerr << " (EE) Cannot move temp file " << _trustdb_path+".tmp" << ". Bad write permissions?" << std::endl; + RsErr() << " (EE) Cannot move temp file " << _trustdb_path+".tmp" << ". Bad write permissions?" ; return false ; } else return true ; } -bool PGPHandler::syncDatabase() -{ - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. - -#ifdef DEBUG_PGPHANDLER - std::cerr << "Sync-ing keyrings." << std::endl; -#endif - locked_syncPublicKeyring() ; - //locked_syncSecretKeyring() ; - - // Now sync the trust database as well. - // - locked_syncTrustDatabase() ; - -#ifdef DEBUG_PGPHANDLER - std::cerr << "Done. " << std::endl; -#endif - return true ; -} - -bool PGPHandler::locked_syncPublicKeyring() -{ - struct stat64 buf ; -#ifdef WINDOWS_SYS - std::wstring wfullname; - librs::util::ConvertUtf8ToUtf16(_pubring_path, wfullname); - if(-1 == _wstati64(wfullname.c_str(), &buf)) -#else - if(-1 == stat64(_pubring_path.c_str(), &buf)) -#endif - { - std::cerr << "PGPHandler::syncDatabase(): can't stat file " << _pubring_path << ". Can't sync public keyring." << std::endl; - buf.st_mtime = 0; - } - - if(_pubring_last_update_time < buf.st_mtime) - { - std::cerr << "Detected change on disk of public keyring. Merging!" << std::endl ; - - locked_mergeKeyringFromDisk(_pubring,_public_keyring_map,_pubring_path) ; - _pubring_last_update_time = buf.st_mtime ; - } - - // Now check if the pubring was locally modified, which needs saving it again - if(_pubring_changed && RsDiscSpace::checkForDiscSpace(RS_PGP_DIRECTORY)) - { - std::string tmp_keyring_file = _pubring_path + ".tmp" ; - - std::cerr << "Local changes in public keyring. Writing to disk..." << std::endl; - if(!ops_write_keyring_to_file(_pubring,ops_false,tmp_keyring_file.c_str(),ops_true)) - { - std::cerr << "Cannot write public keyring tmp file. Disk full? Disk quota exceeded?" << std::endl; - return false ; - } - if(!RsDirUtil::renameFile(tmp_keyring_file,_pubring_path)) - { - std::cerr << "Cannot rename tmp pubring file " << tmp_keyring_file << " into actual pubring file " << _pubring_path << ". Check writing permissions?!?" << std::endl; - return false ; - } - - std::cerr << "Done." << std::endl; - _pubring_last_update_time = time(NULL) ; // should we get this value from the disk instead?? - _pubring_changed = false ; - } - return true ; -} - bool PGPHandler::locked_syncTrustDatabase() { struct stat64 buf ; @@ -1974,14 +360,14 @@ bool PGPHandler::locked_syncTrustDatabase() if(-1 == stat64(_trustdb_path.c_str(), &buf)) #endif { - std::cerr << "PGPHandler::syncDatabase(): can't stat file " << _trustdb_path << ". Will force write it." << std::endl; + RsErr() << "PGPHandler::syncDatabase(): can't stat file " << _trustdb_path << ". Will force write it." ; _trustdb_changed = true ; // we force write of trust database if it does not exist. buf.st_mtime = 0; } if(_trustdb_last_update_time < buf.st_mtime) { - std::cerr << "Detected change on disk of trust database. " << std::endl ; + RsErr() << "Detected change on disk of trust database. " << std::endl ; locked_readPrivateTrustDatabase(); _trustdb_last_update_time = time(NULL) ; @@ -1989,149 +375,18 @@ bool PGPHandler::locked_syncTrustDatabase() if(_trustdb_changed) { - std::cerr << "Local changes in trust database. Writing to disk..." << std::endl; + RsErr() << "Local changes in trust database. Writing to disk..." ; if(!locked_writePrivateTrustDatabase()) - std::cerr << "Cannot write trust database. Disk full? Disk quota exceeded?" << std::endl; + RsErr() << "Cannot write trust database. Disk full? Disk quota exceeded?" ; else { - std::cerr << "Done." << std::endl; + RsErr() << "Done." ; _trustdb_last_update_time = time(NULL) ; _trustdb_changed = false ; } } return true ; } -void PGPHandler::locked_mergeKeyringFromDisk( ops_keyring_t *keyring, - std::map& kmap, - const std::string& keyring_file) -{ -#ifdef DEBUG_PGPHANDLER - std::cerr << "Merging keyring " << keyring_file << " from disk to memory." << std::endl; -#endif - // 1 - load keyring into a temporary keyring list. - ops_keyring_t *tmp_keyring = PGPHandler::allocateOPSKeyring() ; - if(ops_false == ops_keyring_read_from_file(tmp_keyring, false, keyring_file.c_str())) - { - std::cerr << "PGPHandler::locked_mergeKeyringFromDisk(): cannot read keyring. File corrupted?" ; - ops_keyring_free(tmp_keyring) ; - return ; - } - // 2 - load new keys and merge existing key signatures - - for(int i=0;inkeys;++i) - locked_addOrMergeKey(keyring,kmap,&tmp_keyring->keys[i]) ;// we dont' account for the return value. This is disk merging, not local changes. - - // 4 - clean - ops_keyring_free(tmp_keyring) ; -} - -bool PGPHandler::removeKeysFromPGPKeyring(const std::set& keys_to_remove,std::string& backup_file,uint32_t& error_code) -{ - // 1 - lock everything. - // - RsStackMutex mtx(pgphandlerMtx) ; // lock access to PGP memory structures. - RsStackFileLock flck(_pgp_lock_filename) ; // lock access to PGP directory. - - error_code = PGP_KEYRING_REMOVAL_ERROR_NO_ERROR ; - - for(std::set::const_iterator it(keys_to_remove.begin());it!=keys_to_remove.end();++it) - if(locked_getSecretKey(*it) != NULL) - { - std::cerr << "(EE) PGPHandler:: can't remove key " << (*it).toStdString() << " since its shared by a secret key! Operation cancelled." << std::endl; - error_code = PGP_KEYRING_REMOVAL_ERROR_CANT_REMOVE_SECRET_KEYS ; - return false ; - } - - // 2 - sync everything. - // - locked_syncPublicKeyring() ; - - // 3 - make a backup of the public keyring - // - char template_name[_pubring_path.length()+8] ; - sprintf(template_name,"%s.XXXXXX",_pubring_path.c_str()) ; - -#if defined __USE_XOPEN_EXTENDED || defined __USE_XOPEN2K8 - int fd_keyring_backup(mkstemp(template_name)); - if (fd_keyring_backup == -1) -#else - if(mktemp(template_name) == NULL) -#endif - { - std::cerr << "PGPHandler::removeKeysFromPGPKeyring(): cannot create keyring backup file. Giving up." << std::endl; - error_code = PGP_KEYRING_REMOVAL_ERROR_CANNOT_CREATE_BACKUP ; - return false ; - } -#if defined __USE_XOPEN_EXTENDED || defined __USE_XOPEN2K8 - close(fd_keyring_backup); // TODO: keep the file open and use the fd -#endif - - if(!ops_write_keyring_to_file(_pubring,ops_false,template_name,ops_true)) - { - std::cerr << "PGPHandler::removeKeysFromPGPKeyring(): cannot write keyring backup file. Giving up." << std::endl; - error_code = PGP_KEYRING_REMOVAL_ERROR_CANNOT_WRITE_BACKUP ; - return false ; - } - backup_file = std::string(template_name,_pubring_path.length()+7) ; - - std::cerr << "Keyring was backed up to file " << backup_file << std::endl; - - // Remove keys from the keyring, and update the keyring map. - // - for(std::set::const_iterator it(keys_to_remove.begin());it!=keys_to_remove.end();++it) - { - if(locked_getSecretKey(*it) != NULL) - { - std::cerr << "(EE) PGPHandler:: can't remove key " << (*it).toStdString() << " since its shared by a secret key!" << std::endl; - continue ; - } - - std::map::iterator res = _public_keyring_map.find(*it) ; - - if(res == _public_keyring_map.end()) - { - std::cerr << "(EE) PGPHandler:: can't remove key " << (*it).toStdString() << " from keyring: key not found." << std::endl; - continue ; - } - - if(res->second._key_index >= (unsigned int)_pubring->nkeys || RsPgpId(_pubring->keys[res->second._key_index].key_id) != *it) - { - std::cerr << "(EE) PGPHandler:: can't remove key " << (*it).toStdString() << ". Inconsistency found." << std::endl; - error_code = PGP_KEYRING_REMOVAL_ERROR_DATA_INCONSISTENCY ; - return false ; - } - - // Move the last key to the freed place. This deletes the key in place. - // - ops_keyring_remove_key(_pubring,res->second._key_index) ; - - // Erase the info from the keyring map. - // - _public_keyring_map.erase(res) ; - - // now update all indices back. This internal look is very costly, but it avoids deleting the wrong keys, since the keyring structure is - // changed by ops_keyring_remove_key and therefore indices don't point to the correct location anymore. - - int i=0 ; - const ops_keydata_t *keydata ; - while( (keydata = ops_keyring_get_key_by_index(_pubring,i)) != NULL ) - { - PGPCertificateInfo& cert(_public_keyring_map[ RsPgpId(keydata->key_id) ]) ; - cert._key_index = i ; - ++i ; - } - } - - // Everything went well, sync back the keyring on disk - - _pubring_changed = true ; - _trustdb_changed = true ; - - locked_syncPublicKeyring() ; - locked_syncTrustDatabase() ; - - return true ; -} diff --git a/libretroshare/src/pgp/pgphandler.h b/libretroshare/src/pgp/pgphandler.h index 5d7eb82f8..19323b142 100644 --- a/libretroshare/src/pgp/pgphandler.h +++ b/libretroshare/src/pgp/pgphandler.h @@ -29,12 +29,6 @@ #include #include -extern "C" { -#include -#include -#include -} - typedef std::string (*PassphraseCallback)(void *data, const char *uid_title, const char *uid_hint, const char *passphrase_info, int prev_was_bad,bool *cancelled) ; class PGPCertificateInfo @@ -56,9 +50,11 @@ class PGPCertificateInfo mutable rstime_t _time_stamp ; // last time the key was used (received, used for signature verification, etc) PGPFingerprintType _fpr; /* fingerprint */ - // RsPgpId _key_id ; - uint32_t _key_index ; // index to array of keys in the public keyring + // Index to array of keys in the public keyring. Dependign on the specific implementation + // of how the keyring is stored, this may be used differently. + + uint32_t _key_index ; static const uint32_t PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION = 0x0001 ; static const uint32_t PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE = 0x0002 ; @@ -80,53 +76,87 @@ class PGPCertificateInfo class PGPHandler { public: - PGPHandler( const std::string& path_to_public_keyring, + PGPHandler( const std::string& path_to_public_keyring, const std::string& path_to_secret_keyring, const std::string& path_to_trust_database, const std::string& pgp_lock_file) ; virtual ~PGPHandler() ; - /** + //=======================================================================================// + // Methods that needs to be derived depending on how PGP is implemented // + //=======================================================================================// + + // Removes the given keys from the keyring. Also backup the keyring to a file which name is automatically generated + // and given pack for proper display. + // + virtual bool removeKeysFromPGPKeyring(const std::set& key_ids,std::string& backup_file,uint32_t& error_code) =0; + //virtual std::string makeRadixEncodedPGPKey(uint32_t key_index,bool include_signatures) =0; + + virtual bool availableGPGCertificatesWithPrivateKeys(std::list& ids)=0; + virtual bool GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId& pgpId, const int keynumbits, std::string& errString) =0; + + virtual std::string SaveCertificateToString(const RsPgpId& id,bool include_signatures) const=0; + + /** The caller is in charge of freeing `mem` once finished */ + virtual bool exportPublicKey( const RsPgpId& id, unsigned char*& mem_block, size_t& mem_size, bool armoured, bool include_signatures ) const =0; + + virtual bool exportGPGKeyPair(const std::string& filename,const RsPgpId& exported_key_id) const=0; + virtual bool exportGPGKeyPairToString( std::string& data, const RsPgpId& exportedKeyId, bool includeSignatures, std::string& errorMsg ) const =0; + + // Gets info about the key. Who are the signers, what's the owner's name, etc. + // + virtual bool getGPGDetailsFromBinaryBlock(const unsigned char *mem,size_t mem_size,RsPgpId& key_id, std::string& name, std::list& signers) const =0; + + virtual bool importGPGKeyPair(const std::string& filename,RsPgpId& imported_id,std::string& import_error) =0; + /** * @param ids list of gpg certificate ids (note, not the actual certificates) */ + + virtual bool importGPGKeyPairFromString(const std::string& data,RsPgpId& imported_id,std::string& import_error) =0; + + virtual bool LoadCertificateFromString(const std::string& pem, RsPgpId& gpg_id, std::string& error_string)=0; + virtual bool LoadCertificateFromBinaryData(const unsigned char *bin_data,uint32_t bin_data_len, RsPgpId& gpg_id, std::string& error_string)=0; + + virtual bool encryptTextToFile(const RsPgpId& key_id,const std::string& text,const std::string& outfile) =0; + virtual bool decryptTextFromFile(const RsPgpId& key_id,std::string& text,const std::string& encrypted_inputfile) =0; + + // The client should supply a memory chunk to store the data. The length will be updated to the real length of the data. + // + virtual bool encryptDataBin(const RsPgpId& key_id,const void *data, const uint32_t len , unsigned char *encrypted_data, unsigned int *encrypted_data_len) =0; + virtual bool decryptDataBin(const RsPgpId& key_id,const void *encrypted_data, const uint32_t encrypted_len , unsigned char *data, unsigned int *data_len) =0; + + virtual bool SignDataBin(const RsPgpId& id, const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, bool make_raw_signature=false, std::string reason = "") =0; + virtual bool privateSignCertificate(const RsPgpId& own_id,const RsPgpId& id_of_key_to_sign) =0; + virtual bool VerifySignBin(const void *data, uint32_t data_len, unsigned char *sign, unsigned int sign_len, const PGPFingerprintType& withfingerprint) =0; + /** + * @brief Get PGP fingerprint for the given key + * @param id PGP 64bit key id + * @param fp storage for the retrived key fingerpring, the contained value + * is meaningfull only if true is returned + * @return true if the key was found, false if not + */ + virtual bool getKeyFingerprint(const RsPgpId& id, RsPgpFingerprint& fp) const=0; + + virtual bool haveSecretKey(const RsPgpId& id) const =0; + + // Syncs the keyrings and trust database between memory and disk. The algorithm is: + // 1 - lock the keyrings + // 2 - compare file modification dates with last writing date + // - if file is modified, load it, and merge with memory + // 3 - look into memory modification flags + // - if flag says keyring has changed, write to disk + // + virtual bool syncDatabase() =0; + + + //=======================================================================================// + // Common methods to PGPHandler // + //=======================================================================================// + bool getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&) = NULL) const ; - bool haveSecretKey(const RsPgpId& id) const ; - bool importGPGKeyPair(const std::string& filename,RsPgpId& imported_id,std::string& import_error) ; - bool importGPGKeyPairFromString(const std::string& data,RsPgpId& imported_id,std::string& import_error) ; - bool exportGPGKeyPair(const std::string& filename,const RsPgpId& exported_id) const ; - bool exportGPGKeyPairToString( - std::string& data, const RsPgpId& exportedKeyId, - bool includeSignatures, std::string& errorMsg ) const; - - bool availableGPGCertificatesWithPrivateKeys(std::list& ids); - bool GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId& pgpId, const int keynumbits, std::string& errString) ; - - bool LoadCertificateFromString(const std::string& pem, RsPgpId& gpg_id, std::string& error_string); - bool LoadCertificateFromBinaryData(const unsigned char *bin_data,uint32_t bin_data_len, RsPgpId& gpg_id, std::string& error_string); - - std::string SaveCertificateToString(const RsPgpId& id,bool include_signatures) const ; - - /** The caller is in charge of freeing `mem` once finished */ - bool exportPublicKey( const RsPgpId& id, - unsigned char*& mem, size_t& mem_size, - bool armoured, bool include_signatures) const; - - bool parseSignature(unsigned char *sign, unsigned int signlen,RsPgpId& issuer_id) ; - bool SignDataBin(const RsPgpId& id, const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, bool make_raw_signature=false, std::string reason = "") ; - bool VerifySignBin(const void *data, uint32_t data_len, unsigned char *sign, unsigned int sign_len, const PGPFingerprintType& withfingerprint) ; - bool privateSignCertificate(const RsPgpId& own_id,const RsPgpId& id_of_key_to_sign) ; - - // The client should supply a memory chunk to store the data. The length will be updated to the real length of the data. - // - bool encryptDataBin(const RsPgpId& key_id,const void *data, const uint32_t len - , unsigned char *encrypted_data, unsigned int *encrypted_data_len) ; - bool decryptDataBin(const RsPgpId& key_id,const void *encrypted_data, const uint32_t encrypted_len - , unsigned char *data, unsigned int *data_len) ; - - bool encryptTextToFile(const RsPgpId& key_id,const std::string& text,const std::string& outfile) ; - bool decryptTextFromFile(const RsPgpId& key_id,std::string& text,const std::string& encrypted_inputfile) ; + bool parseSignature(unsigned char *sign, unsigned int signlen,RsPgpId& issuer_id) ; void setAcceptConnexion(const RsPgpId&,bool) ; @@ -135,11 +165,6 @@ public: void locked_updateOwnSignatureFlag(PGPCertificateInfo&, const RsPgpId&, PGPCertificateInfo&, const RsPgpId&) ; - // Removes the given keys from the keyring. Also backup the keyring to a file which name is automatically generated - // and given pack for proper display. - // - bool removeKeysFromPGPKeyring(const std::set& key_ids,std::string& backup_file,uint32_t& error_code) ; - //bool isKeySupported(const RsPgpId& id) const ; bool privateTrustCertificate(const RsPgpId& id,int valid_level) ; @@ -174,66 +199,18 @@ public: */ static RsPgpId pgpIdFromFingerprint(const RsPgpFingerprint& f); - /** - * @brief Get PGP fingerprint for the given key - * @param id PGP 64bit key id - * @param fp storage for the retrived key fingerpring, the contained value - * is meaningfull only if true is returned - * @return true if the key was found, false if not - */ - bool getKeyFingerprint(const RsPgpId& id, RsPgpFingerprint& fp) const; - - // Gets info about the key. Who are the signers, what's the owner's name, etc. - // - bool getGPGDetailsFromBinaryBlock(const unsigned char *mem,size_t mem_size,RsPgpId& key_id, std::string& name, std::list& signers) const ; - // Debug stuff. virtual bool printKeys() const ; - // Syncs the keyrings and trust database between memory and disk. The algorithm is: - // 1 - lock the keyrings - // 2 - compare file modification dates with last writing date - // - if file is modified, load it, and merge with memory - // 3 - look into memory modification flags - // - if flag says keyring has changed, write to disk - // - bool syncDatabase() ; - - private: - bool LoadCertificate(const unsigned char *bin_data,uint32_t bin_data_len, bool armoured, RsPgpId& gpg_id, std::string& error_string); - void initCertificateInfo(PGPCertificateInfo& cert,const ops_keydata_t *keydata,uint32_t i) ; - - // Returns true if the signatures have been updated - // - bool validateAndUpdateSignatures(PGPCertificateInfo& cert,const ops_keydata_t *keydata) ; - - /** Check public/private key and import them into the keyring - * @param keyring keyring with the new public/private key pair. Will be freed by the function. - * @param imported_key_id PGP id of the imported key - * @param import_error human readbale error message - * @returns true on success - * */ - bool checkAndImportKeyPair(ops_keyring_t *keyring, RsPgpId& imported_key_id,std::string& import_error); - - const ops_keydata_t *locked_getPublicKey(const RsPgpId&,bool stamp_the_key) const; - const ops_keydata_t *locked_getSecretKey(const RsPgpId&) const ; - + protected: void locked_readPrivateTrustDatabase() ; bool locked_writePrivateTrustDatabase() ; - - bool locked_syncPublicKeyring() ; - bool locked_syncTrustDatabase() ; - - void locked_mergeKeyringFromDisk(ops_keyring_t *keyring, std::map& kmap, const std::string& keyring_file) ; - bool locked_addOrMergeKey(ops_keyring_t *keyring,std::map& kmap,const ops_keydata_t *keydata) ; + bool locked_syncTrustDatabase() ; // Members. // mutable RsMutex pgphandlerMtx ; - ops_keyring_t *_pubring ; - ops_keyring_t *_secring ; - std::map _public_keyring_map ; // used for fast access to keys. Gives the index in the keyring. std::map _secret_keyring_map ; @@ -249,11 +226,5 @@ public: rstime_t _secring_last_update_time ; rstime_t _trustdb_last_update_time ; - // Helper functions. - // - static std::string makeRadixEncodedPGPKey(const ops_keydata_t *key,bool include_signatures) ; - static ops_keyring_t *allocateOPSKeyring() ; - static void addNewKeyToOPSKeyring(ops_keyring_t*, const ops_keydata_t&) ; static PassphraseCallback _passphrase_callback ; - static bool mergeKeySignatures(ops_keydata_t *dst,const ops_keydata_t *src) ; // returns true if signature lists are different }; diff --git a/libretroshare/src/pgp/rscertificate.cc b/libretroshare/src/pgp/rscertificate.cc index 3f01b3d52..41448fd64 100644 --- a/libretroshare/src/pgp/rscertificate.cc +++ b/libretroshare/src/pgp/rscertificate.cc @@ -553,26 +553,24 @@ unsigned short RsCertificate::loc_port_us() const return (int)ipv4_internal_ip_and_port[4]*256 + (int)ipv4_internal_ip_and_port[5] ; } -bool RsCertificate::cleanCertificate( const std::string& input, std::string& output, Format& format, uint32_t& error_code, bool check_content ) +bool RsCertificate::cleanCertificate( const std::string& input, std::string& output, Format& format, uint32_t& error_code, bool check_content, RsPeerDetails& details) { if(cleanRadix64(input,output,error_code)) { - RsPeerDetails details; - if(rsPeers->parseShortInvite(output,details,error_code)) - { - format = RS_CERTIFICATE_SHORT_RADIX; - return true; - } + if(rsPeers->parseShortInvite(output,details,error_code)) + { + format = RS_CERTIFICATE_SHORT_RADIX; + return true; + } + //Clear details. As parseShortInvite may make it dirty. + details = RsPeerDetails(); format = RS_CERTIFICATE_RADIX; if(!check_content) return true; - uint32_t errCode; - auto crt = RsCertificate::fromString(input, errCode); - error_code = static_cast(errCode); - return crt != nullptr; + return rsPeers->loadDetailsFromStringCert(input,details,error_code); } return false; diff --git a/libretroshare/src/pgp/rscertificate.h b/libretroshare/src/pgp/rscertificate.h index 17a7e4cb8..28e3c2bae 100644 --- a/libretroshare/src/pgp/rscertificate.h +++ b/libretroshare/src/pgp/rscertificate.h @@ -101,7 +101,7 @@ public: static bool cleanCertificate( const std::string& input, std::string& output, - RsCertificate::Format& format, uint32_t& error_code, bool check_content); + RsCertificate::Format& format, uint32_t& error_code, bool check_content, RsPeerDetails& details); const std::set& locators() const { return mLocators; } diff --git a/libretroshare/src/pqi/authgpg.cc b/libretroshare/src/pqi/authgpg.cc index b41084cf0..f7a6fb6c3 100644 --- a/libretroshare/src/pqi/authgpg.cc +++ b/libretroshare/src/pqi/authgpg.cc @@ -46,7 +46,7 @@ //const rstime_t STORE_KEY_TIMEOUT = 1 * 60 * 60; //store key is call around every hour -AuthGPG *AuthGPG::_instance = NULL ; +AuthPGP *AuthPGP::_instance = NULL ; void cleanupZombies(int numkill); // function to cleanup zombies under OSX. @@ -54,34 +54,46 @@ void cleanupZombies(int numkill); // function to cleanup zombies under OSX. /* Function to sign X509_REQ via GPGme. */ -bool AuthGPG::decryptTextFromFile(std::string& text,const std::string& inputfile) +int AuthPGP::availablePgpCertificatesWithPrivateKeys(std::list& pgpIds) { - return PGPHandler::decryptTextFromFile(mOwnGpgId,text,inputfile) ; + return instance()->mPgpHandler->availableGPGCertificatesWithPrivateKeys(pgpIds); +} +bool AuthPGP::getPgpDetailsFromBinaryBlock(const unsigned char *mem,size_t mem_size,RsPgpId& key_id, std::string& name, std::list& signers) +{ + return instance()->mPgpHandler->getGPGDetailsFromBinaryBlock(mem,mem_size,key_id,name,signers); +} +void AuthPGP::registerToConfigMgr(const std::string& fname,p3ConfigMgr *CfgMgr) +{ + CfgMgr->addConfiguration(fname, instance()); +} +bool AuthPGP::decryptTextFromFile(std::string& text,const std::string& inputfile) +{ + return instance()->mPgpHandler->decryptTextFromFile(instance()->mOwnGpgId,text,inputfile) ; } -bool AuthGPG::removeKeysFromPGPKeyring(const std::set& pgp_ids,std::string& backup_file,uint32_t& error_code) +bool AuthPGP::removeKeysFromPGPKeyring(const std::set& pgp_ids,std::string& backup_file,uint32_t& error_code) { // std::list pids ; // // for(std::list::const_iterator it(pgp_ids.begin());it!=pgp_ids.end();++it) // pids.push_back(RsPgpId(*it)) ; - return PGPHandler::removeKeysFromPGPKeyring(pgp_ids,backup_file,error_code) ; + return instance()->mPgpHandler->removeKeysFromPGPKeyring(pgp_ids,backup_file,error_code) ; } // bool AuthGPG::decryptTextFromString(std::string& encrypted_text,std::string& output) // { -// return PGPHandler::decryptTextFromString(mOwnGpgId,encrypted_text,output) ; +// return instance()->mPgpHandler->decryptTextFromString(mOwnGpgId,encrypted_text,output) ; // } -bool AuthGPG::encryptTextToFile(const std::string& text,const std::string& outfile) +bool AuthPGP::encryptTextToFile(const std::string& text,const std::string& outfile) { - return PGPHandler::encryptTextToFile(mOwnGpgId,text,outfile) ; + return instance()->mPgpHandler->encryptTextToFile(instance()->mOwnGpgId,text,outfile) ; } // bool AuthGPG::encryptTextToString(const std::string& pgp_id,const std::string& text,std::string& outstr) // { -// return PGPHandler::encryptTextToString(RsPgpId(pgp_id),text,outstr) ; +// return instance()->mPgpHandler->encryptTextToString(RsPgpId(pgp_id),text,outstr) ; // } std::string pgp_pwd_callback(void * /*hook*/, const char *uid_title, const char *uid_hint, const char * /*passphrase_info*/, int prev_was_bad,bool *cancelled) @@ -95,7 +107,7 @@ std::string pgp_pwd_callback(void * /*hook*/, const char *uid_title, const char return password ; } -void AuthGPG::init( +void AuthPGP::init( const std::string& path_to_public_keyring, const std::string& path_to_secret_keyring, const std::string& path_to_trustdb, @@ -107,14 +119,14 @@ void AuthGPG::init( std::cerr << "AuthGPG::init() called twice!" << std::endl ; } -// if(cb) PGPHandler::setPassphraseCallback(cb);else - PGPHandler::setPassphraseCallback(pgp_pwd_callback); - _instance = new AuthGPG( path_to_public_keyring, +// if(cb) instance()->mPgpHandler->setPassphraseCallback(cb);else + instance()->mPgpHandler->setPassphraseCallback(pgp_pwd_callback); + _instance = new AuthPGP( path_to_public_keyring, path_to_secret_keyring, path_to_trustdb, pgp_lock_file ); } -void AuthGPG::exit() +void AuthPGP::exit() { if(_instance) { @@ -124,9 +136,8 @@ void AuthGPG::exit() } } -AuthGPG::AuthGPG(const std::string& path_to_public_keyring,const std::string& path_to_secret_keyring,const std::string& path_to_trustdb,const std::string& pgp_lock_file) +AuthPGP::AuthPGP(const std::string& path_to_public_keyring,const std::string& path_to_secret_keyring,const std::string& path_to_trustdb,const std::string& pgp_lock_file) :p3Config(), - PGPHandler(path_to_public_keyring,path_to_secret_keyring,path_to_trustdb,pgp_lock_file), gpgMtxService("AuthGPG-service"), gpgMtxEngine("AuthGPG-engine"), gpgMtxData("AuthGPG-data"), @@ -135,7 +146,9 @@ AuthGPG::AuthGPG(const std::string& path_to_public_keyring,const std::string& pa _force_sync_database(false), mCount(0) { - start("AuthGPG"); + mPgpHandler = new OpenPGPSDKHandler(path_to_public_keyring,path_to_secret_keyring,path_to_trustdb,pgp_lock_file); + + start("AuthGPG"); } /* This function is called when retroshare is first started @@ -149,7 +162,7 @@ AuthGPG::AuthGPG(const std::string& path_to_public_keyring,const std::string& pa //{ // std::list pids ; // -// PGPHandler::availableGPGCertificatesWithPrivateKeys(pids) ; +// mPgpHandler->availableGPGCertificatesWithPrivateKeys(pids) ; // // for(std::list::const_iterator it(pids.begin());it!=pids.end();++it) // ids.push_back( (*it).toStdString() ) ; @@ -165,17 +178,17 @@ AuthGPG::AuthGPG(const std::string& path_to_public_keyring,const std::string& pa * This function must be called successfully (return == 1) * before anything else can be done. (except above fn). */ -int AuthGPG::GPGInit(const RsPgpId &ownId) +int AuthPGP::PgpInit(const RsPgpId &ownId) { #ifdef DEBUG_AUTHGPG std::cerr << "AuthGPG::GPGInit() called with own gpg id : " << ownId.toStdString() << std::endl; #endif - mOwnGpgId = RsPgpId(ownId); + instance()->mOwnGpgId = ownId; //force the validity of the private key. When set to unknown, it caused signature and text encryptions bugs - privateTrustCertificate(ownId, 5); - updateOwnSignatureFlag(mOwnGpgId) ; + instance()->privateTrustCertificate(ownId, 5); + instance()->mPgpHandler->updateOwnSignatureFlag(ownId) ; #ifdef DEBUG_AUTHGPG std::cerr << "AuthGPG::GPGInit finished." << std::endl; @@ -184,11 +197,11 @@ int AuthGPG::GPGInit(const RsPgpId &ownId) return 1; } - AuthGPG::~AuthGPG() + AuthPGP::~AuthPGP() { } -void AuthGPG::threadTick() +void AuthPGP::threadTick() { rstime::rs_usleep(100 * 1000); //100 msec @@ -204,13 +217,13 @@ void AuthGPG::threadTick() /// - checks whether the keyring has changed on disk. /// - merges/updates according to status. /// - PGPHandler::syncDatabase() ; + mPgpHandler->syncDatabase() ; mCount = 0; _force_sync_database = false ; }//if (++count >= 100 || _force_sync_database) } -void AuthGPG::processServices() +void AuthPGP::processServices() { AuthGPGOperation *operation = NULL; AuthGPGService *service = NULL; @@ -251,7 +264,7 @@ void AuthGPG::processServices() /* don't bother loading - if we already have the certificate */ - if (isGPGId(loadOrSave->m_certGpgId)) + if (mPgpHandler->isGPGId(loadOrSave->m_certGpgId)) { #ifdef GPG_DEBUG std::cerr << "AuthGPGimpl::processServices() Skipping load - already have it" << std::endl; @@ -305,66 +318,66 @@ void AuthGPG::processServices() delete operation; } -bool AuthGPG::DoOwnSignature(const void *data, unsigned int datalen, void *buf_sigout, unsigned int *outl, std::string reason /* = "" */) +bool AuthPGP::DoOwnSignature(const void *data, unsigned int datalen, void *buf_sigout, unsigned int *outl, std::string reason /* = "" */) { - return PGPHandler::SignDataBin(mOwnGpgId,data,datalen,(unsigned char *)buf_sigout,outl,false,reason) ; + return instance()->mPgpHandler->SignDataBin(mOwnGpgId,data,datalen,(unsigned char *)buf_sigout,outl,false,reason) ; } /* import to GnuPG and other Certificates */ -bool AuthGPG::VerifySignature(const void *data, int datalen, const void *sig, unsigned int siglen, const PGPFingerprintType& withfingerprint) +bool AuthPGP::VerifySignature(const void *data, int datalen, const void *sig, unsigned int siglen, const PGPFingerprintType& withfingerprint) { - return PGPHandler::VerifySignBin((unsigned char*)data,datalen,(unsigned char*)sig,siglen,withfingerprint) ; + return instance()->mPgpHandler->VerifySignBin((unsigned char*)data,datalen,(unsigned char*)sig,siglen,withfingerprint) ; } -bool AuthGPG::parseSignature(const void *sig, unsigned int siglen, RsPgpId& issuer_id) +bool AuthPGP::parseSignature(const void *sig, unsigned int siglen, RsPgpId& issuer_id) { - return PGPHandler::parseSignature((unsigned char*)sig,siglen,issuer_id) ; + return instance()->mPgpHandler->parseSignature((unsigned char*)sig,siglen,issuer_id) ; } -bool AuthGPG::exportProfile(const std::string& fname,const RsPgpId& exported_id) +bool AuthPGP::exportProfile(const std::string& fname,const RsPgpId& exported_id) { - return PGPHandler::exportGPGKeyPair(fname,exported_id) ; + return instance()->mPgpHandler->exportGPGKeyPair(fname,exported_id) ; } -bool AuthGPG::exportIdentityToString( +bool AuthPGP::exportIdentityToString( std::string& data, const RsPgpId& pgpId, bool includeSignatures, std::string& errorMsg ) { - return PGPHandler::exportGPGKeyPairToString( + return instance()->mPgpHandler->exportGPGKeyPairToString( data, pgpId, includeSignatures, errorMsg); } -bool AuthGPG::importProfile(const std::string& fname,RsPgpId& imported_id,std::string& import_error) +bool AuthPGP::importProfile(const std::string& fname,RsPgpId& imported_id,std::string& import_error) { - return PGPHandler::importGPGKeyPair(fname,imported_id,import_error) ; + return instance()->mPgpHandler->importGPGKeyPair(fname,imported_id,import_error) ; } -bool AuthGPG::importProfileFromString(const std::string &data, RsPgpId &gpg_id, std::string &import_error) +bool AuthPGP::importProfileFromString(const std::string &data, RsPgpId &gpg_id, std::string &import_error) { - return PGPHandler::importGPGKeyPairFromString(data, gpg_id, import_error); + return instance()->mPgpHandler->importGPGKeyPairFromString(data, gpg_id, import_error); } -bool AuthGPG::active() +bool AuthPGP::active() { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxData); /******* LOCKED ******/ - return gpgKeySelected; + return instance()->gpgKeySelected; } -bool AuthGPG::GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId& pgpId, const int keynumbits, std::string& errString) +bool AuthPGP::GeneratePgpCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId& pgpId, const int keynumbits, std::string& errString) { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxEngine); /******* LOCKED ******/ - return PGPHandler::GeneratePGPCertificate(name, email, passwd, pgpId, keynumbits, errString) ; + return instance()->mPgpHandler->GeneratePGPCertificate(name, email, passwd, pgpId, keynumbits, errString) ; } /**** These Two are common */ -std::string AuthGPG::getGPGName(const RsPgpId& id,bool *success) +std::string AuthPGP::getPgpName(const RsPgpId& id,bool *success) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxData); /******* LOCKED ******/ - const PGPCertificateInfo *info = getCertificateInfo(id) ; + const PGPCertificateInfo *info = instance()->mPgpHandler->getCertificateInfo(id) ; if(info != NULL) { @@ -378,11 +391,25 @@ std::string AuthGPG::getGPGName(const RsPgpId& id,bool *success) } } -/**** These Two are common */ -std::string AuthGPG::getGPGEmail(const RsPgpId& id,bool *success) +AuthPGP *AuthPGP::instance() { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - const PGPCertificateInfo *info = getCertificateInfo(id) ; + if(!_instance) + { + RsFatal() << "AuthGPG::instance() called before AuthGPG::init()! This should not happen." << std::endl; + return nullptr; + } + + return _instance; +} +bool AuthPGP::isPGPId(const RsPgpId& id) +{ + return instance()->mPgpHandler->isGPGId(id); +} +/**** These Two are common */ +std::string AuthPGP::getPgpEmail(const RsPgpId& id,bool *success) +{ + RsStackMutex stack(instance()->gpgMtxData); /******* LOCKED ******/ + const PGPCertificateInfo *info = instance()->mPgpHandler->getCertificateInfo(id) ; if(info != NULL) { @@ -398,30 +425,30 @@ std::string AuthGPG::getGPGEmail(const RsPgpId& id,bool *success) /**** GPG versions ***/ -const RsPgpId& AuthGPG::getGPGOwnId() +const RsPgpId& AuthPGP::getPgpOwnId() { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - return mOwnGpgId ; + RsStackMutex stack(instance()->gpgMtxData); /******* LOCKED ******/ + return instance()->mOwnGpgId ; } -std::string AuthGPG::getGPGOwnName() +std::string AuthPGP::getPgpOwnName() { - return getGPGName(mOwnGpgId) ; + return getPgpName(instance()->mOwnGpgId) ; } -bool AuthGPG::getGPGAllList(std::list &ids) +bool AuthPGP::getPgpAllList(std::list &ids) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxData); /******* LOCKED ******/ - PGPHandler::getGPGFilteredList(ids) ; + instance()->mPgpHandler->getGPGFilteredList(ids) ; return true; } -const PGPCertificateInfo *AuthGPG::getCertInfoFromStdString(const std::string& pgp_id) const +const PGPCertificateInfo *AuthPGP::getCertInfoFromStdString(const std::string& pgp_id) const { try { - return PGPHandler::getCertificateInfo(RsPgpId(pgp_id)) ; + return instance()->mPgpHandler->getCertificateInfo(RsPgpId(pgp_id)) ; } catch(std::exception& e) { @@ -429,13 +456,13 @@ const PGPCertificateInfo *AuthGPG::getCertInfoFromStdString(const std::string& p return NULL ; } } -bool AuthGPG::haveSecretKey(const RsPgpId& id) const +bool AuthPGP::haveSecretKey(const RsPgpId& id) { - return PGPHandler::haveSecretKey(id) ; + return instance()->mPgpHandler->haveSecretKey(id) ; } -bool AuthGPG::isKeySupported(const RsPgpId& id) const +bool AuthPGP::isKeySupported(const RsPgpId& id) { - const PGPCertificateInfo *pc = getCertificateInfo(id) ; + const PGPCertificateInfo *pc = instance()->mPgpHandler->getCertificateInfo(id) ; if(pc == NULL) return false ; @@ -443,11 +470,11 @@ bool AuthGPG::isKeySupported(const RsPgpId& id) const return !(pc->_flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_UNSUPPORTED_ALGORITHM) ; } -bool AuthGPG::getGPGDetails(const RsPgpId& pgp_id, RsPeerDetails &d) +bool AuthPGP::getPgpDetails(const RsPgpId& pgp_id, RsPeerDetails &d) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxData); /******* LOCKED ******/ - const PGPCertificateInfo *pc = PGPHandler::getCertificateInfo(pgp_id) ; + const PGPCertificateInfo *pc = instance()->mPgpHandler->getCertificateInfo(pgp_id) ; if(pc == NULL) return false ; @@ -474,28 +501,26 @@ bool AuthGPG::getGPGDetails(const RsPgpId& pgp_id, RsPeerDetails &d) return true; } -bool AuthGPG::getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&)) +bool AuthPGP::getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&)) { - RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - - return PGPHandler::getGPGFilteredList(list,filter) ; + return instance()->mPgpHandler->getGPGFilteredList(list,filter) ; } static bool filter_Validity(const PGPCertificateInfo& /*info*/) { return true ; } //{ return info._validLvl >= PGPCertificateInfo::GPGME_VALIDITY_MARGINAL ; } static bool filter_Accepted(const PGPCertificateInfo& info) { return info._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_ACCEPT_CONNEXION ; } static bool filter_OwnSigned(const PGPCertificateInfo& info) { return info._flags & PGPCertificateInfo::PGP_CERTIFICATE_FLAG_HAS_OWN_SIGNATURE ; } -bool AuthGPG::getGPGValidList(std::list &ids) +bool AuthPGP::getPgpValidList(std::list &ids) { return getGPGFilteredList(ids,&filter_Validity); } -bool AuthGPG::getGPGAcceptedList(std::list &ids) +bool AuthPGP::getPgpAcceptedList(std::list &ids) { return getGPGFilteredList(ids,&filter_Accepted); } -bool AuthGPG::getGPGSignedList(std::list &ids) +bool AuthPGP::getPgpSignedList(std::list &ids) { return getGPGFilteredList(ids,&filter_OwnSigned); } @@ -504,9 +529,9 @@ bool AuthGPG::getGPGSignedList(std::list &ids) // { // RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ // #ifdef LIMIT_CERTIFICATE_SIZE -// certificate = PGPHandler::SaveCertificateToString(RsPgpId(id),false) ; +// certificate = instance()->mPgpHandler->SaveCertificateToString(RsPgpId(id),false) ; // #else -// certificate = PGPHandler::SaveCertificateToString(RsPgpId(id),true) ; +// certificate = instance()->mPgpHandler->SaveCertificateToString(RsPgpId(id),true) ; // #endif // // // #ifdef LIMIT_CERTIFICATE_SIZE @@ -528,20 +553,20 @@ bool AuthGPG::getGPGSignedList(std::list &ids) /* SKTAN : do not know how to use std::string id */ - std::string AuthGPG::SaveCertificateToString(const RsPgpId &id,bool include_signatures) + std::string AuthPGP::SaveCertificateToString(const RsPgpId &id,bool include_signatures) { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxEngine); /******* LOCKED ******/ - return PGPHandler::SaveCertificateToString(id,include_signatures) ; + return instance()->mPgpHandler->SaveCertificateToString(id,include_signatures) ; } /* import to GnuPG and other Certificates */ -bool AuthGPG::LoadPGPKeyFromBinaryData(const unsigned char *data,uint32_t data_len, RsPgpId& gpg_id,std::string& error_string) +bool AuthPGP::LoadPGPKeyFromBinaryData(const unsigned char *data,uint32_t data_len, RsPgpId& gpg_id,std::string& error_string) { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxEngine); /******* LOCKED ******/ - if(PGPHandler::LoadCertificateFromBinaryData(data,data_len,gpg_id,error_string)) + if(instance()->mPgpHandler->LoadCertificateFromBinaryData(data,data_len,gpg_id,error_string)) { - updateOwnSignatureFlag(gpg_id,mOwnGpgId) ; + instance()->mPgpHandler->updateOwnSignatureFlag(gpg_id,instance()->mOwnGpgId) ; return true ; } @@ -549,13 +574,13 @@ bool AuthGPG::LoadPGPKeyFromBinaryData(const unsigned char *data,uint32_t data_l } /* import to GnuPG and other Certificates */ -bool AuthGPG::LoadCertificateFromString(const std::string &str, RsPgpId& gpg_id,std::string& error_string) +bool AuthPGP::LoadCertificateFromString(const std::string &str, RsPgpId& gpg_id,std::string& error_string) { - RsStackMutex stack(gpgMtxEngine); /******* LOCKED ******/ + RsStackMutex stack(instance()->gpgMtxEngine); /******* LOCKED ******/ - if(PGPHandler::LoadCertificateFromString(str,gpg_id,error_string)) + if(instance()->mPgpHandler->LoadCertificateFromString(str,gpg_id,error_string)) { - updateOwnSignatureFlag(gpg_id,mOwnGpgId) ; + instance()->mPgpHandler->updateOwnSignatureFlag(gpg_id,instance()->mOwnGpgId) ; return true ; } @@ -576,7 +601,7 @@ bool AuthGPG::LoadCertificateFromString(const std::string &str, RsPgpId& gpg_id, /*************************************/ /* These take PGP Ids */ -bool AuthGPG::AllowConnection(const RsPgpId& gpg_id, bool accept) +bool AuthPGP::AllowConnection(const RsPgpId& gpg_id, bool accept) { #ifdef GPG_DEBUG std::cerr << "AuthGPG::AllowConnection(" << gpg_id << ")" << std::endl; @@ -584,11 +609,11 @@ bool AuthGPG::AllowConnection(const RsPgpId& gpg_id, bool accept) /* Was a "Reload Certificates" here -> be shouldn't be needed -> and very expensive, try without. */ { - RsStackMutex stack(gpgMtxData); - PGPHandler::setAcceptConnexion(gpg_id,accept) ; + RsStackMutex stack(instance()->gpgMtxData); + instance()->mPgpHandler->setAcceptConnexion(gpg_id,accept) ; } - IndicateConfigChanged(); + instance()->IndicateConfigChanged(); RsServer::notify()->notifyListChange(NOTIFY_LIST_FRIENDS, accept ? NOTIFY_TYPE_ADD : NOTIFY_TYPE_DEL); @@ -596,16 +621,16 @@ bool AuthGPG::AllowConnection(const RsPgpId& gpg_id, bool accept) } /* These take PGP Ids */ -bool AuthGPG::SignCertificateLevel0(const RsPgpId &id) +bool AuthPGP::SignCertificateLevel0(const RsPgpId &id) { #ifdef GPG_DEBUG std::cerr << "AuthGPG::SignCertificat(" << id << ")" << std::endl; #endif - return privateSignCertificate(id) ; + return instance()->privateSignCertificate(id) ; } -bool AuthGPG::RevokeCertificate(const RsPgpId &id) +bool AuthPGP::RevokeCertificate(const RsPgpId &id) { /* remove unused parameter warnings */ (void) id; @@ -617,46 +642,59 @@ bool AuthGPG::RevokeCertificate(const RsPgpId &id) return false; } -bool AuthGPG::TrustCertificate(const RsPgpId& id, int trustlvl) +bool AuthPGP::TrustCertificate(const RsPgpId& id, int trustlvl) { #ifdef GPG_DEBUG std::cerr << "AuthGPG::TrustCertificate(" << id << ", " << trustlvl << ")" << std::endl; #endif - return privateTrustCertificate(id, trustlvl) ; + return instance()->privateTrustCertificate(id, trustlvl) ; } -bool AuthGPG::encryptDataBin(const RsPgpId& pgp_id,const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen) +bool AuthPGP::encryptDataBin(const RsPgpId& pgp_id,const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen) { - return PGPHandler::encryptDataBin(RsPgpId(pgp_id),data,datalen,sign,signlen) ; + return instance()->mPgpHandler->encryptDataBin(RsPgpId(pgp_id),data,datalen,sign,signlen) ; } -bool AuthGPG::decryptDataBin(const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen) +bool AuthPGP::decryptDataBin(const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen) { - return PGPHandler::decryptDataBin(mOwnGpgId,data,datalen,sign,signlen) ; + return instance()->mPgpHandler->decryptDataBin(instance()->mOwnGpgId,data,datalen,sign,signlen) ; } -bool AuthGPG::SignDataBin(const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen, std::string reason /*= ""*/) +bool AuthPGP::SignDataBin(const void *data, unsigned int datalen, unsigned char *sign, unsigned int *signlen, std::string reason /*= ""*/) { - return DoOwnSignature(data, datalen, sign, signlen, reason); + return instance()->DoOwnSignature(data, datalen, sign, signlen, reason); } -bool AuthGPG::VerifySignBin(const void *data, uint32_t datalen, unsigned char *sign, unsigned int signlen, const PGPFingerprintType& withfingerprint) +bool AuthPGP::exportPublicKey( const RsPgpId& id, unsigned char*& mem_block, size_t& mem_size, bool armoured, bool include_signatures ) { - return VerifySignature(data, datalen, sign, signlen, withfingerprint); + return instance()->mPgpHandler->exportPublicKey(id,mem_block,mem_size,armoured,include_signatures); +} + +bool AuthPGP::isPgpPubKeyAvailable(const RsPgpId& pgp_id) +{ + return instance()->mPgpHandler->isPgpPubKeyAvailable(pgp_id); +} +bool AuthPGP::getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) +{ + return instance()->mPgpHandler->getKeyFingerprint(id,fp); +} +bool AuthPGP::VerifySignBin(const void *data, uint32_t datalen, unsigned char *sign, unsigned int signlen, const PGPFingerprintType& withfingerprint) +{ + return instance()->VerifySignature(data, datalen, sign, signlen, withfingerprint); } /* Sign/Trust stuff */ -int AuthGPG::privateSignCertificate(const RsPgpId &id) +int AuthPGP::privateSignCertificate(const RsPgpId &id) { RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ - int ret = PGPHandler::privateSignCertificate(mOwnGpgId,id) ; + int ret = mPgpHandler->privateSignCertificate(mOwnGpgId,id) ; _force_sync_database = true ; return ret ; } /* revoke the signature on Certificate */ -int AuthGPG::privateRevokeCertificate(const RsPgpId &/*id*/) +int AuthPGP::privateRevokeCertificate(const RsPgpId &/*id*/) { //RsStackMutex stack(gpgMtx); /******* LOCKED ******/ std::cerr << __PRETTY_FUNCTION__ << ": not implemented!" << std::endl; @@ -664,7 +702,7 @@ int AuthGPG::privateRevokeCertificate(const RsPgpId &/*id*/) return 0; } -int AuthGPG::privateTrustCertificate(const RsPgpId& id, int trustlvl) +int AuthPGP::privateTrustCertificate(const RsPgpId& id, int trustlvl) { RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ @@ -672,10 +710,10 @@ int AuthGPG::privateTrustCertificate(const RsPgpId& id, int trustlvl) // The trust level is only a user-defined property that has nothing to // do with the fact that we allow connections or not. - if(!isGPGAccepted(id)) + if(!isPGPAccepted(id)) return 0; - int res = PGPHandler::privateTrustCertificate(id,trustlvl) ; + int res = instance()->mPgpHandler->privateTrustCertificate(id,trustlvl) ; _force_sync_database = true ; return res ; } @@ -684,20 +722,24 @@ int AuthGPG::privateTrustCertificate(const RsPgpId& id, int trustlvl) // -------------------------------- Config functions ------------------------------ // // -----------------------------------------------------------------------------------// // -RsSerialiser *AuthGPG::setupSerialiser() +RsSerialiser *AuthPGP::setupSerialiser() { RsSerialiser *rss = new RsSerialiser ; rss->addSerialType(new RsGeneralConfigSerialiser()); return rss ; } +bool AuthPGP::isPGPAccepted(const RsPgpId& id) +{ + return instance()->mPgpHandler->isGPGAccepted(id); +} -bool AuthGPG::saveList(bool& cleanup, std::list& lst) +bool AuthPGP::saveList(bool& cleanup, std::list& lst) { #ifdef GPG_DEBUG std::cerr << "AuthGPG::saveList() called" << std::endl ; #endif std::list ids ; - getGPGAcceptedList(ids) ; // needs to be done before the lock + getPgpAcceptedList(ids) ; // needs to be done before the lock RsStackMutex stack(gpgMtxData); /******* LOCKED ******/ @@ -722,7 +764,7 @@ bool AuthGPG::saveList(bool& cleanup, std::list& lst) return true; } -bool AuthGPG::loadList(std::list& load) +bool AuthPGP::loadList(std::list& load) { #ifdef GPG_DEBUG std::cerr << "AuthGPG::loadList() Item Count: " << load.size() << std::endl; @@ -745,7 +787,7 @@ bool AuthGPG::loadList(std::list& load) std::list::iterator kit; for(kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) if (kit->key != mOwnGpgId.toStdString()) - PGPHandler::setAcceptConnexion(RsPgpId(kit->key), (kit->value == "TRUE")); + instance()->mPgpHandler->setAcceptConnexion(RsPgpId(kit->key), (kit->value == "TRUE")); } delete (*it); } @@ -753,16 +795,16 @@ bool AuthGPG::loadList(std::list& load) return true; } -bool AuthGPG::addService(AuthGPGService *service) +bool AuthPGP::addService(AuthGPGService *service) { - RsStackMutex stack(gpgMtxService); /********* LOCKED *********/ + RsStackMutex stack(instance()->gpgMtxService); /********* LOCKED *********/ - if (std::find(services.begin(), services.end(), service) != services.end()) { + if (std::find(instance()->services.begin(), instance()->services.end(), service) != instance()->services.end()) { /* it exists already! */ return false; } - services.push_back(service); + instance()->services.push_back(service); return true; } diff --git a/libretroshare/src/pqi/authgpg.h b/libretroshare/src/pqi/authgpg.h index 4fe76194c..1db1ad482 100644 --- a/libretroshare/src/pqi/authgpg.h +++ b/libretroshare/src/pqi/authgpg.h @@ -34,7 +34,7 @@ #include "util/rsthreads.h" #include "pqi/p3cfgmgr.h" -#include "pgp/pgphandler.h" +#include "pgp/openpgpsdkhandler.h" #define MAX_GPG_SIGNATURE_SIZE 4096 @@ -89,16 +89,19 @@ public: virtual void setGPGOperation(AuthGPGOperation *operation) = 0; }; -class AuthGPG: public p3Config, public RsTickingThread, public PGPHandler +class AuthPGP: public p3Config, public RsTickingThread { public: - static void init(const std::string& path_to_pubring, - const std::string& path_to_secring, - const std::string& path_to_trustdb, - const std::string& pgp_lock_file); + static void init(const std::string& path_to_pubring, + const std::string& path_to_secring, + const std::string& path_to_trustdb, + const std::string& pgp_lock_file); - static void exit(); - static AuthGPG *getAuthGPG() { return _instance ; } + static void registerToConfigMgr(const std::string& fname,p3ConfigMgr *CfgMgr); + static void exit(); + + static bool isPGPId(const RsPgpId& id) ; + static bool isPGPAccepted(const RsPgpId& id) ; /** * @param ids list of gpg certificate ids (note, not the actual certificates) @@ -118,7 +121,7 @@ public: * (see storage at the end of the class) * ****/ - virtual bool active(); + static bool active(); // /* Initialize */ // virtual bool InitAuth (); @@ -126,10 +129,13 @@ public: /* Init by generating new Own PGP Cert, or selecting existing PGP Cert */ - virtual int GPGInit(const RsPgpId &ownId); - virtual bool GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId &pgpId, const int keynumbits, std::string &errString); + static int PgpInit(const RsPgpId &ownId); + static bool GeneratePgpCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId &pgpId, const int keynumbits, std::string &errString); - /*********************************************************************************/ + static bool getPgpDetailsFromBinaryBlock(const unsigned char *mem,size_t mem_size,RsPgpId& key_id, std::string& name, std::list& signers) ; + static int availablePgpCertificatesWithPrivateKeys(std::list& pgpIds); + + /*********************************************************************************/ /************************* STAGE 3 ***********************************************/ /*********************************************************************************/ /***** @@ -140,29 +146,33 @@ public: * provide access to details in cache list. * ****/ - virtual std::string getGPGName(const RsPgpId &pgp_id,bool *success = NULL); - virtual std::string getGPGEmail(const RsPgpId &pgp_id,bool *success = NULL); + static std::string getPgpName(const RsPgpId &pgp_id,bool *success = NULL); + static std::string getPgpEmail(const RsPgpId &pgp_id,bool *success = NULL); - /* PGP web of trust management */ - virtual const RsPgpId& getGPGOwnId(); - virtual std::string getGPGOwnName(); + static bool exportPublicKey( const RsPgpId& id, unsigned char*& mem_block, size_t& mem_size, bool armoured, bool include_signatures ); - //virtual std::string getGPGOwnEmail(); - virtual bool isKeySupported(const RsPgpId &id) const ; - virtual bool haveSecretKey(const RsPgpId &id) const ; - virtual bool getGPGDetails(const RsPgpId& id, RsPeerDetails &d); - virtual bool getGPGAllList(std::list &ids); - virtual bool getGPGValidList(std::list &ids); - virtual bool getGPGAcceptedList(std::list &ids); - virtual bool getGPGSignedList(std::list &ids); - virtual bool importProfile(const std::string& filename,RsPgpId& gpg_id,std::string& import_error) ; - virtual bool importProfileFromString(const std::string& data,RsPgpId& gpg_id,std::string& import_error) ; - virtual bool exportProfile(const std::string& filename,const RsPgpId& gpg_id) ; - virtual bool exportIdentityToString( + /* PGP web of trust management */ + static const RsPgpId& getPgpOwnId(); + static std::string getPgpOwnName(); + + //virtual std::string getGPGOwnEmail(); + static bool getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) ; + static bool isKeySupported(const RsPgpId &id) ; + static bool isPgpPubKeyAvailable(const RsPgpId& pgp_id); + static bool haveSecretKey(const RsPgpId &id) ; + static bool getPgpDetails(const RsPgpId& id, RsPeerDetails &d); + static bool getPgpAllList(std::list &ids); + static bool getPgpValidList(std::list &ids); + static bool getPgpAcceptedList(std::list &ids); + static bool getPgpSignedList(std::list &ids); + static bool importProfile(const std::string& filename,RsPgpId& gpg_id,std::string& import_error) ; + static bool importProfileFromString(const std::string& data,RsPgpId& gpg_id,std::string& import_error) ; + static bool exportProfile(const std::string& filename,const RsPgpId& gpg_id) ; + static bool exportIdentityToString( std::string& data, const RsPgpId& pgpId, bool includeSignatures, std::string& errorMsg ); - virtual bool removeKeysFromPGPKeyring(const std::set &pgp_ids,std::string& backup_file,uint32_t& error_code) ; + static bool removeKeysFromPGPKeyring(const std::set &pgp_ids,std::string& backup_file,uint32_t& error_code) ; /*********************************************************************************/ /************************* STAGE 4 ***********************************************/ @@ -171,9 +181,9 @@ public: * STAGE 4: Loading and Saving Certificates. (Strings and Files) * ****/ - virtual bool LoadCertificateFromString(const std::string &pem, RsPgpId& gpg_id,std::string& error_string); - virtual bool LoadPGPKeyFromBinaryData(const unsigned char *data,uint32_t data_len, RsPgpId& gpg_id,std::string& error_string); - virtual std::string SaveCertificateToString(const RsPgpId &id,bool include_signatures) ; + static bool LoadCertificateFromString(const std::string &pem, RsPgpId& gpg_id,std::string& error_string); + static bool LoadPGPKeyFromBinaryData(const unsigned char *data,uint32_t data_len, RsPgpId& gpg_id,std::string& error_string); + static std::string SaveCertificateToString(const RsPgpId &id,bool include_signatures) ; // Cached certificates. //bool getCachedGPGCertificate(const RsPgpId &id, std::string &certificate); @@ -188,12 +198,12 @@ public: * done in gpgroot already. * ****/ - virtual bool AllowConnection(const RsPgpId &gpg_id, bool accept); + static bool AllowConnection(const RsPgpId &gpg_id, bool accept); - virtual bool SignCertificateLevel0(const RsPgpId &id); - virtual bool RevokeCertificate(const RsPgpId &id); /* Particularly hard - leave for later */ + static bool SignCertificateLevel0(const RsPgpId &id); + static bool RevokeCertificate(const RsPgpId &id); /* Particularly hard - leave for later */ - virtual bool TrustCertificate(const RsPgpId& id, int trustlvl); //trustlvl is 2 for none, 3 for marginal and 4 for full trust + static bool TrustCertificate(const RsPgpId& id, int trustlvl); //trustlvl is 2 for none, 3 for marginal and 4 for full trust /*********************************************************************************/ /************************* STAGE 7 ***********************************************/ @@ -204,39 +214,39 @@ public: * There should also be Encryption Functions... (do later). * ****/ - virtual bool SignDataBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, std::string reason = ""); - virtual bool VerifySignBin(const void*, uint32_t, unsigned char*, unsigned int, const PGPFingerprintType& withfingerprint); - virtual bool parseSignature(const void *sig, unsigned int siglen, RsPgpId& issuer_id); + static bool SignDataBin(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, std::string reason = ""); + static bool VerifySignBin(const void*, uint32_t, unsigned char*, unsigned int, const PGPFingerprintType& withfingerprint); + static bool parseSignature(const void *sig, unsigned int siglen, RsPgpId& issuer_id); - virtual bool encryptDataBin(const RsPgpId& pgp_id,const void *data, const uint32_t len, unsigned char *encr, unsigned int *encrlen); - virtual bool decryptDataBin(const void *data, const uint32_t len, unsigned char *decr, unsigned int *decrlen); + static bool encryptDataBin(const RsPgpId& pgp_id,const void *data, const uint32_t len, unsigned char *encr, unsigned int *encrlen); + static bool decryptDataBin(const void *data, const uint32_t len, unsigned char *decr, unsigned int *decrlen); - virtual bool decryptTextFromFile( std::string& text,const std::string& filename); - virtual bool encryptTextToFile (const std::string& text,const std::string& filename); + static bool decryptTextFromFile( std::string& text,const std::string& filename); + static bool encryptTextToFile (const std::string& text,const std::string& filename); // virtual bool decryptTextFromString( std::string& encrypted_text,std::string& clear_string); // virtual bool encryptTextToString (const std::string& pgp_id,const std::string& clear_text,std::string& encrypted_string); - bool getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&) = NULL) ; + static bool getGPGFilteredList(std::list& list,bool (*filter)(const PGPCertificateInfo&) = NULL) ; //END of PGP public functions /* GPG service */ - virtual bool addService(AuthGPGService *service) ; + static bool addService(AuthGPGService *service) ; // This is for debug purpose only. Don't use it !! - static void setAuthGPG_debug(AuthGPG *auth_gpg) { _instance = auth_gpg ; } + static void setAuthGPG_debug(AuthPGP *auth_gpg) { _instance = auth_gpg ; } protected: - AuthGPG(const std::string& path_to_pubring, const std::string& path_to_secring,const std::string& path_to_trustdb,const std::string& pgp_lock_file); - virtual ~AuthGPG(); + AuthPGP(const std::string& path_to_pubring, const std::string& path_to_secring,const std::string& path_to_trustdb,const std::string& pgp_lock_file); + virtual ~AuthPGP(); /*****************************************************************/ /*********************** p3config ******************************/ /* Key Functions to be overloaded for Full Configuration */ - virtual RsSerialiser *setupSerialiser(); - virtual bool saveList(bool &cleanup, std::list&); - virtual bool loadList(std::list& load); + virtual RsSerialiser *setupSerialiser() override; + virtual bool saveList(bool &cleanup, std::list&) override; + virtual bool loadList(std::list& load) override; /*****************************************************************/ private: @@ -274,8 +284,7 @@ private: void threadTick() override; /// @see RsTickingThread private: - - static AuthGPG *instance_gpg; // pointeur vers le singleton + static AuthPGP *instance(); RsMutex gpgMtxService; RsMutex gpgMtxEngine; @@ -290,6 +299,8 @@ private: rstime_t mStoreKeyTime; + PGPHandler *mPgpHandler; + RsPgpId mOwnGpgId; bool gpgKeySelected; bool _force_sync_database ; @@ -297,7 +308,7 @@ private: std::list services ; - static AuthGPG *_instance ; + static AuthPGP *_instance ; }; #endif diff --git a/libretroshare/src/pqi/authssl.cc b/libretroshare/src/pqi/authssl.cc index eb89a1ed5..9d70121a3 100644 --- a/libretroshare/src/pqi/authssl.cc +++ b/libretroshare/src/pqi/authssl.cc @@ -759,8 +759,7 @@ X509 *AuthSSLimpl::SignX509ReqWithGPG(X509_REQ *req, long /*days*/) //long version = 0x00; unsigned long chtype = MBSTRING_UTF8; X509_NAME *issuer_name = X509_NAME_new(); - X509_NAME_add_entry_by_txt(issuer_name, "CN", chtype, - (unsigned char *) AuthGPG::getAuthGPG()->getGPGOwnId().toStdString().c_str(), -1, -1, 0); + X509_NAME_add_entry_by_txt(issuer_name, "CN", chtype, (unsigned char *) AuthPGP::getPgpOwnId().toStdString().c_str(), -1, -1, 0); /**** X509_NAME_add_entry_by_NID(issuer_name, 48, 0, (unsigned char *) "email@email.com", -1, -1, 0); @@ -770,7 +769,7 @@ X509 *AuthSSLimpl::SignX509ReqWithGPG(X509_REQ *req, long /*days*/) (unsigned char *) "loc", -1, -1, 0); ****/ - std::cerr << "AuthSSLimpl::SignX509Req() Issuer name: " << AuthGPG::getAuthGPG()->getGPGOwnId().toStdString() << std::endl; + std::cerr << "AuthSSLimpl::SignX509Req() Issuer name: " << AuthPGP::getPgpOwnId().toStdString() << std::endl; #ifdef V07_NON_BACKWARD_COMPATIBLE_CHANGE_002 static const uint64_t CERTIFICATE_SERIAL_NUMBER = RS_CERTIFICATE_VERSION_NUMBER_07_0001 ; @@ -945,7 +944,7 @@ X509 *AuthSSLimpl::SignX509ReqWithGPG(X509_REQ *req, long /*days*/) std::cerr << "Buffers Allocated" << std::endl; /* NOW Sign via GPG Functions */ - if (!AuthGPG::getAuthGPG()->SignDataBin(buf_in, inl, buf_sigout, (unsigned int *) &sigoutl,"AuthSSLimpl::SignX509ReqWithGPG()")) + if (!AuthPGP::SignDataBin(buf_in, inl, buf_sigout, (unsigned int *) &sigoutl,"AuthSSLimpl::SignX509ReqWithGPG()")) { sigoutl = 0; goto err; @@ -1040,7 +1039,7 @@ bool AuthSSLimpl::AuthX509WithGPG(X509 *x509,bool verbose, uint32_t& diagnostic) { RsPgpId issuer = RsX509Cert::getCertIssuer(*x509); RsPeerDetails pd; - if (!AuthGPG::getAuthGPG()->getGPGDetails(issuer, pd)) + if (!AuthPGP::getPgpDetails(issuer, pd)) { RsInfo() << __PRETTY_FUNCTION__ << " X509 NOT authenticated : " << "AuthGPG::getAuthGPG()->getGPGDetails(" << issuer @@ -1185,9 +1184,7 @@ bool AuthSSLimpl::AuthX509WithGPG(X509 *x509,bool verbose, uint32_t& diagnostic) // passed, verify the signature itself - if (!AuthGPG::getAuthGPG()->VerifySignBin( - signed_data, signed_data_length, signature->data, - static_cast(signature->length), pd.fpr )) + if (!AuthPGP::VerifySignBin( signed_data, signed_data_length, signature->data, static_cast(signature->length), pd.fpr )) { diagnostic = RS_SSL_HANDSHAKE_DIAGNOSTIC_WRONG_SIGNATURE; goto err; @@ -1383,7 +1380,7 @@ int AuthSSLimpl::VerifyX509Callback(int /*preverify_ok*/, X509_STORE_CTX* ctx) std::cerr << "******* VerifyX509Callback cert: " << std::hex << ctx->cert <getGPGOwnId() && !AuthGPG::getAuthGPG()->isGPGAccepted(pgpId) ) + if ( !isSslOnlyFriend && pgpId != AuthPGP::getPgpOwnId() && !AuthPGP::isPGPAccepted(pgpId) ) { std::string errMsg = "Connection attempt signed by PGP key id: " + pgpId.toStdString() + " not accepted because it is not" diff --git a/libretroshare/src/pqi/p3peermgr.cc b/libretroshare/src/pqi/p3peermgr.cc index 83c4d9366..d4ff9f95d 100644 --- a/libretroshare/src/pqi/p3peermgr.cc +++ b/libretroshare/src/pqi/p3peermgr.cc @@ -812,11 +812,11 @@ int p3PeerMgrIMPL::getFriendCount(bool ssl, bool online) // count all gpg id's std::list gpgIds; - AuthGPG::getAuthGPG()->getGPGAcceptedList(gpgIds); + AuthPGP::getPgpAcceptedList(gpgIds); // add own gpg id, if we have more than one location std::list ownSslIds; - getAssociatedPeers(AuthGPG::getAuthGPG()->getGPGOwnId(), ownSslIds); + getAssociatedPeers(AuthPGP::getPgpOwnId(), ownSslIds); return gpgIds.size() + ((ownSslIds.size() > 0) ? 1 : 0); } @@ -962,7 +962,7 @@ bool p3PeerMgrIMPL::addFriend(const RsPeerId& input_id, const RsPgpId& input_gpg // check that the PGP key is known - if(!AuthGPG::getAuthGPG()->isGPGId(gpg_id)) + if(!AuthPGP::isPGPId(gpg_id)) { RsErr() << "Trying to add SSL id (" << id << ") to be validated with unknown PGP key (" << gpg_id << ". This is a bug!" << std::endl; return false; @@ -970,7 +970,7 @@ bool p3PeerMgrIMPL::addFriend(const RsPeerId& input_id, const RsPgpId& input_gpg //Authentication is now tested at connection time, we don't store the ssl cert anymore // - if (!AuthGPG::getAuthGPG()->isGPGAccepted(gpg_id) && gpg_id != AuthGPG::getAuthGPG()->getGPGOwnId()) + if (!AuthPGP::isPGPAccepted(gpg_id) && gpg_id != AuthPGP::getPgpOwnId()) { #ifdef PEER_DEBUG std::cerr << "p3PeerMgrIMPL::addFriend() gpg is not accepted" << std::endl; @@ -1024,7 +1024,7 @@ bool p3PeerMgrIMPL::addFriend(const RsPeerId& input_id, const RsPgpId& input_gpg pstate.id = id; pstate.gpg_id = gpg_id; - pstate.name = AuthGPG::getAuthGPG()->getGPGName(gpg_id); + pstate.name = AuthPGP::getPgpName(gpg_id); pstate.vs_disc = vs_disc; pstate.vs_dht = vs_dht; @@ -1126,8 +1126,8 @@ bool p3PeerMgrIMPL::addSslOnlyFriend( const RsPeerId& sslId, const RsPgpId& pgp_ * superficially set to true the PGP signature verification would have been * skipped and the attacker connection would be accepted. * If the PGP key is available add it as full friend. */ - if(AuthGPG::getAuthGPG()->isPgpPubKeyAvailable(pgp_id)) - AuthGPG::getAuthGPG()->AllowConnection(pgp_id, true); + if(AuthPGP::isPgpPubKeyAvailable(pgp_id)) + AuthPGP::AllowConnection(pgp_id, true); else pstate.skip_pgp_signature_validation = true; @@ -1398,11 +1398,11 @@ bool p3PeerMgrIMPL::UpdateOwnAddress( const sockaddr_storage& pLocalAddr, sockaddr_storage_copy(pExtAddr, extAddr); sockaddr_storage_ipv6_to_ipv4(extAddr); -//#ifdef PEER_DEBUG +#ifdef PEER_DEBUG std::cerr << "p3PeerMgrIMPL::UpdateOwnAddress(" << sockaddr_storage_tostring(localAddr) << ", " << sockaddr_storage_tostring(extAddr) << ")" << std::endl; -//#endif +#endif if( rsBanList && !rsBanList->isAddressAccepted(localAddr, @@ -2470,7 +2470,7 @@ bool p3PeerMgrIMPL::loadList(std::list& load) setOwnNetworkMode(pitem->netMode); setOwnVisState(pitem->vs_disc, pitem->vs_dht); - mOwnState.gpg_id = AuthGPG::getAuthGPG()->getGPGOwnId(); + mOwnState.gpg_id = AuthPGP::getPgpOwnId(); mOwnState.location = AuthSSL::getAuthSSL()->getOwnLocation(); } else @@ -2642,7 +2642,7 @@ bool p3PeerMgrIMPL::loadList(std::list& load) #endif for(uint32_t i=0;ipgp_ids.size();++i) - if(AuthGPG::getAuthGPG()->isGPGAccepted(sitem->pgp_ids[i]) || sitem->pgp_ids[i] == AuthGPG::getAuthGPG()->getGPGOwnId()) + if(AuthPGP::isPGPAccepted(sitem->pgp_ids[i]) || sitem->pgp_ids[i] == AuthPGP::getPgpOwnId()) { mFriendsPermissionFlags[sitem->pgp_ids[i]] = sitem->service_flags[i] ; #ifdef PEER_DEBUG @@ -2684,7 +2684,7 @@ bool p3PeerMgrIMPL::loadList(std::list& load) for(auto group_pair:groupList) { for(auto profileIdIt(group_pair.second.peerIds.begin());profileIdIt!=group_pair.second.peerIds.end();) - if(AuthGPG::getAuthGPG()->isGPGAccepted(*profileIdIt) || *profileIdIt == AuthGPG::getAuthGPG()->getGPGOwnId()) + if(AuthPGP::isPGPAccepted(*profileIdIt) || *profileIdIt == AuthPGP::getPgpOwnId()) ++profileIdIt; else { diff --git a/libretroshare/src/pqi/pqifdbin.cc b/libretroshare/src/pqi/pqifdbin.cc index 68798bf3d..5ba6af155 100644 --- a/libretroshare/src/pqi/pqifdbin.cc +++ b/libretroshare/src/pqi/pqifdbin.cc @@ -21,24 +21,46 @@ ******************************************************************************/ #include "util/rsprint.h" +#include "util/rsfile.h" #include "pqi/pqifdbin.h" -RsFdBinInterface::RsFdBinInterface(int file_descriptor) - : mCLintConnt(file_descriptor),mIsActive(file_descriptor!=0) +RsFdBinInterface::RsFdBinInterface(int file_descriptor, bool is_socket) + : mCLintConnt(file_descriptor),mIsSocket(is_socket),mIsActive(false) { mTotalReadBytes=0; mTotalInBufferBytes=0; mTotalWrittenBytes=0; mTotalOutBufferBytes=0; + + if(file_descriptor!=0) + setSocket(file_descriptor); } void RsFdBinInterface::setSocket(int s) { - if(mIsActive != 0) - { - RsErr() << "Changing socket to active FsBioInterface! Canceling all pending R/W data." ; - close(); - } + if(mIsActive != 0) + { + RsErr() << "Changing socket to active FsBioInterface! Canceling all pending R/W data." ; + close(); + } +#ifndef WINDOWS_SYS + int flags = fcntl(s,F_GETFL); + + if(!(flags & O_NONBLOCK)) + { + RsWarn() << "Trying to use a blocking file descriptor in RsFdBinInterface. This is not going to work! Setting the socket to be non blocking."; + unix_fcntl_nonblock(s); + } + +#else + // On windows, there is no way to determine whether a socket is blocking or not, so we set it to non blocking whatsoever. + if (mIsSocket) { + unix_fcntl_nonblock(s); + } else { + RsFileUtil::set_fd_nonblock(s); + } +#endif + mCLintConnt = s; mIsActive = (s!=0); } @@ -64,7 +86,15 @@ int RsFdBinInterface::read_pending() char inBuffer[1025]; memset(inBuffer,0,1025); - ssize_t readbytes = recv(mCLintConnt, inBuffer, sizeof(inBuffer),MSG_DONTWAIT); + ssize_t readbytes; +#if WINDOWS_SYS + if (mIsSocket) + // Windows needs recv for sockets + readbytes = recv(mCLintConnt, inBuffer, sizeof(inBuffer), 0); + else +#endif + readbytes = read(mCLintConnt, inBuffer, sizeof(inBuffer)); // Needs read instead of recv which is only for sockets. + // Sockets should be set to non blocking by the client process. if(readbytes == 0) { @@ -76,20 +106,28 @@ int RsFdBinInterface::read_pending() } if(readbytes < 0) { - if(errno != EWOULDBLOCK && errno != EAGAIN) + if(errno != 0 && errno != EWOULDBLOCK && errno != EAGAIN) +#ifdef WINDOWS_SYS + // A non blocking read to file descriptor gets ERROR_NO_DATA for empty data + if (mIsSocket == true || GetLastError() != ERROR_NO_DATA) +#endif RsErr() << "read() failed. Errno=" << errno ; return mTotalInBufferBytes; } +#ifdef DEBUG_FS_BIN RsDbg() << "clintConnt: " << mCLintConnt << ", readbytes: " << readbytes ; +#endif // display some debug info if(readbytes > 0) { +#ifdef DEBUG_FS_BIN RsDbg() << "Received the following bytes: " << RsUtil::BinToHex( reinterpret_cast(inBuffer),readbytes,50) << std::endl; - //RsDbg() << "Received the following bytes: " << std::string(inBuffer,readbytes) << std::endl; + RsDbg() << "Received the following bytes: " << std::string(inBuffer,readbytes) << std::endl; +#endif void *ptr = malloc(readbytes); @@ -102,7 +140,9 @@ int RsFdBinInterface::read_pending() mTotalInBufferBytes += readbytes; mTotalReadBytes += readbytes; +#ifdef DEBUG_FS_BIN RsDbg() << "Socket: " << mCLintConnt << ". Total read: " << mTotalReadBytes << ". Buffer size: " << mTotalInBufferBytes ; +#endif } return mTotalInBufferBytes; } @@ -113,7 +153,14 @@ int RsFdBinInterface::write_pending() return mTotalOutBufferBytes; auto& p = out_buffer.front(); - int written = write(mCLintConnt, p.first, p.second); + int written; +#if WINDOWS_SYS + if (mIsSocket) + // Windows needs send for sockets + written = send(mCLintConnt, (char*) p.first, p.second, 0); + else +#endif + written = write(mCLintConnt, p.first, p.second); if(written < 0) { @@ -129,11 +176,15 @@ int RsFdBinInterface::write_pending() return mTotalOutBufferBytes; } +#ifdef DEBUG_FS_BIN RsDbg() << "clintConnt: " << mCLintConnt << ", written: " << written ; +#endif // display some debug info +#ifdef DEBUG_FS_BIN RsDbg() << "Sent the following bytes: " << RsUtil::BinToHex( reinterpret_cast(p.first),written,50) << std::endl; +#endif if(written < p.second) { @@ -173,6 +224,19 @@ void RsFdBinInterface::clean() in_buffer.clear(); out_buffer.clear(); } + +int RsFdBinInterface::readline(void *data, int len) +{ + int n=0; + + for(auto p:in_buffer) + for(int i=0;i(p.first)[i] == '\n') + return readdata(data,n+1); + + return 0; +} + int RsFdBinInterface::readdata(void *data, int len) { // read incoming bytes in the buffer @@ -254,6 +318,12 @@ bool RsFdBinInterface::moretoread(uint32_t /* usec */) { return mTotalInBufferBytes > 0; } + +bool RsFdBinInterface::moretowrite(uint32_t /* usec */) +{ + return mTotalOutBufferBytes > 0 ; +} + bool RsFdBinInterface::cansend(uint32_t) { return isactive(); diff --git a/libretroshare/src/pqi/pqifdbin.h b/libretroshare/src/pqi/pqifdbin.h index 4c532b436..a3709c22e 100644 --- a/libretroshare/src/pqi/pqifdbin.h +++ b/libretroshare/src/pqi/pqifdbin.h @@ -25,7 +25,7 @@ class RsFdBinInterface: public BinInterface { public: - RsFdBinInterface(int file_descriptor); + RsFdBinInterface(int file_descriptor, bool is_socket); ~RsFdBinInterface(); // Implements BinInterface methods @@ -41,9 +41,14 @@ public: // int readdata(void *data, int len) override; + // Read at most len bytes only if \n is encountered within that range. Otherwise, nothing is changed. + // + int readline(void *data, int len) ; + int netstatus() override; int isactive() override; bool moretoread(uint32_t usec) override; + bool moretowrite(uint32_t usec) ; bool cansend(uint32_t usec) override; int close() override; @@ -65,6 +70,7 @@ private: int write_pending(); int mCLintConnt; + bool mIsSocket; bool mIsActive; uint32_t mTotalReadBytes; uint32_t mTotalInBufferBytes; diff --git a/libretroshare/src/pqi/pqinetwork.cc b/libretroshare/src/pqi/pqinetwork.cc index a5ca76035..2b786f488 100644 --- a/libretroshare/src/pqi/pqinetwork.cc +++ b/libretroshare/src/pqi/pqinetwork.cc @@ -21,15 +21,6 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifdef WINDOWS_SYS -# include "util/rswin.h" -# include "util/rsmemory.h" -# include -#endif // WINDOWS_SYS - -#ifdef __ANDROID__ -# include -#endif // def __ANDROID__ #include #include @@ -43,6 +34,28 @@ #include "util/rsnet.h" #include "util/stacktrace.h" +#ifdef WINDOWS_SYS +# include "util/rswin.h" +# include "util/rsmemory.h" +# include +#endif // WINDOWS_SYS + +/// @See: android_ifaddrs/README.adoc +#ifdef __ANDROID__ +# include +#endif // def __ANDROID__ + +#ifdef WINDOWS_SYS /* Windows - define errno */ +int errno; +#else /* Windows - define errno */ +#include +#endif + +#ifdef __HAIKU__ +# include +# define IFF_RUNNING 0x0001 +#endif + static struct RsLog::logInfo pqinetzoneInfo = {RsLog::Default, "pqinet"}; #define pqinetzone &pqinetzoneInfo @@ -50,21 +63,6 @@ static struct RsLog::logInfo pqinetzoneInfo = {RsLog::Default, "pqinet"}; * #define NET_DEBUG 1 ****/ -#ifdef WINDOWS_SYS /* Windows - define errno */ - -int errno; - -#else /* Windows - define errno */ - -#include - -#endif - -#ifdef __HAIKU__ - #include - #define IFF_RUNNING 0x0001 -#endif - /********************************** WINDOWS/UNIX SPECIFIC PART ******************/ #ifndef WINDOWS_SYS @@ -270,18 +268,16 @@ int inet_aton(const char *name, struct in_addr *addr) #endif /********************************** WINDOWS/UNIX SPECIFIC PART ******************/ - +#include "util/cxx17retrocompat.h" #include #ifdef WINDOWS_SYS # include # include # pragma comment(lib, "IPHLPAPI.lib") #elif defined(__ANDROID__) && __ANDROID_API__ < 24 -# include -# include -# include -# include -#else // not __ANDROID__ nor WINDOWS => Linux and other unixes +/// @See: android_ifaddrs/README.adoc +# include "rs_android/ifaddrs-android.h" +#else // not WINDOWS => Linux and other unixes # include # include #endif // WINDOWS_SYS @@ -324,20 +320,12 @@ bool getLocalAddresses(std::vector& addrs) } } free(adapter_addresses); -#elif defined(__ANDROID__) && __ANDROID_API__ < 24 - for(auto& qAddr: QNetworkInterface::allAddresses()) - { - sockaddr_storage tmpAddr; - sockaddr_storage_clear(tmpAddr); - if(sockaddr_storage_ipv4_aton(tmpAddr, qAddr.toString().toStdString().c_str())) - addrs.push_back(tmpAddr); - } -#else // not WINDOWS_SYS not ANDROID => Linux and other unixes +#else // not WINDOWS_SYS => Linux and other unixes struct ifaddrs *ifsaddrs, *ifa; if(getifaddrs(&ifsaddrs) != 0) { - std::cerr << __PRETTY_FUNCTION__ << " FATAL ERROR: " << errno << " " - << strerror(errno) << std::endl; + RS_ERR( "getifaddrs failed with: ", errno, " ", + rs_errno_to_condition(errno) ); print_stacktrace(); return false; } @@ -346,18 +334,19 @@ bool getLocalAddresses(std::vector& addrs) { sockaddr_storage tmp; sockaddr_storage_clear(tmp); - if (sockaddr_storage_copyip(tmp, *reinterpret_cast(ifa->ifa_addr))) + if(sockaddr_storage_copyip( + tmp, + *reinterpret_cast(ifa->ifa_addr) )) addrs.push_back(tmp); } freeifaddrs(ifsaddrs); #endif // WINDOWS_SYS #ifdef NET_DEBUG - std::list::iterator it; - std::cout << "getLocalAddresses(...) returning: <" ; - for(it = addrs.begin(); it != addrs.end(); ++it) - std::cout << sockaddr_storage_iptostring(*it) << ", "; - std::cout << ">" << std::endl; + auto&& dbg = RS_DBG("returning: ["); + for(auto& addr: std::as_const(addrs)) + dbg << sockaddr_storage_iptostring(addr) << ", "; + dbg << "]" << std::endl; #endif return !addrs.empty(); diff --git a/libretroshare/src/pqi/pqinetwork.h b/libretroshare/src/pqi/pqinetwork.h index e4238ee5e..9519bf197 100644 --- a/libretroshare/src/pqi/pqinetwork.h +++ b/libretroshare/src/pqi/pqinetwork.h @@ -4,7 +4,8 @@ * libretroshare: retroshare core library * * * * Copyright (C) 2004-2006 Robert Fernie * - * Copyright (C) 2015-2018 Gioacchino Mazzurco * + * Copyright (C) 2015-2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,8 +21,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef MRK_PQI_NETWORKING_HEADER -#define MRK_PQI_NETWORKING_HEADER +#pragma once #include @@ -86,9 +86,13 @@ extern int errno; /* Define extern errno, to duplicate unix behaviour */ #include #include +#include "util/rsdeprecate.h" + // Same def - different functions... +RS_DEPRECATED_FOR("use std::error_condition instead") void showSocketError(std::string &out); +RS_DEPRECATED_FOR("use std::error_condition instead") std::string socket_errorType(int err); bool getLocalAddresses(std::vector & addrs); @@ -103,10 +107,6 @@ int unix_getsockopt_error(int sockfd, int *err); #ifdef WINDOWS_SYS // WINDOWS /******************* WINDOWS SPECIFIC PART ******************/ +RS_DEPRECATED_FOR("use std::error_condition instead") int WinToUnixError(int error); #endif - - - -#endif - diff --git a/libretroshare/src/pqi/pqissl.cc b/libretroshare/src/pqi/pqissl.cc index 1cfff8fbd..6a455dfeb 100644 --- a/libretroshare/src/pqi/pqissl.cc +++ b/libretroshare/src/pqi/pqissl.cc @@ -1213,8 +1213,7 @@ int pqissl::Authorise_SSL_Connection() } RsPgpId pgpId = RsX509Cert::getCertIssuer(*peercert); - if( !isSslOnlyFriend && pgpId != AuthGPG::getAuthGPG()->getGPGOwnId() && - !AuthGPG::getAuthGPG()->isGPGAccepted(pgpId) ) + if( !isSslOnlyFriend && pgpId != AuthPGP::getPgpOwnId() && !AuthPGP::isPGPAccepted(pgpId) ) { RsFatal() << __PRETTY_FUNCTION__ << " pgpId: " << pgpId << " is not friend. It is very unlikely to happen at this " diff --git a/libretroshare/src/pqi/pqissllistener.cc b/libretroshare/src/pqi/pqissllistener.cc index fa25bfe0b..ad2129928 100644 --- a/libretroshare/src/pqi/pqissllistener.cc +++ b/libretroshare/src/pqi/pqissllistener.cc @@ -797,8 +797,7 @@ int pqissllistener::completeConnection(int fd, IncomingSSLInfo& info) exit(failure); } - if( !isSslOnlyFriend && pgpId != AuthGPG::getAuthGPG()->getGPGOwnId() && - !AuthGPG::getAuthGPG()->isGPGAccepted(pgpId) ) + if( !isSslOnlyFriend && pgpId != AuthPGP::getPgpOwnId() && !AuthPGP::isPGPAccepted(pgpId) ) { RsFatal() << __PRETTY_FUNCTION__ << " pgpId: " << pgpId << " is not friend. It is very unlikely to happen at this " diff --git a/libretroshare/src/pqi/rstcpsocket.cc b/libretroshare/src/pqi/rstcpsocket.cc index 25f830914..f9bf03483 100644 --- a/libretroshare/src/pqi/rstcpsocket.cc +++ b/libretroshare/src/pqi/rstcpsocket.cc @@ -1,16 +1,43 @@ +/******************************* BEGIN WINDOWS/UNIX SPECIFIC PART ******************/ +#ifndef WINDOWS_SYS #include #include +#include #include +#else +#include +// Missing defines in MinGW +#ifndef MSG_WAITALL +#define MSG_WAITALL 8 +#endif +#endif +/********************************* END WINDOWS/UNIX SPECIFIC PART ******************/ #include #include #include "rstcpsocket.h" RsTcpSocket::RsTcpSocket(const std::string& tcp_address,uint16_t tcp_port) - :RsFdBinInterface(0),mState(DISCONNECTED),mConnectAddress(tcp_address),mConnectPort(tcp_port),mSocket(0) + :RsFdBinInterface(0, true),mState(DISCONNECTED),mConnectAddress(tcp_address),mConnectPort(tcp_port),mSocket(0) { } -int RsTcpSocket::connect() + +RsTcpSocket::RsTcpSocket() + :RsFdBinInterface(0, true),mState(DISCONNECTED),mConnectAddress("0.0.0.0"),mConnectPort(0),mSocket(0) +{ +} + +bool RsTcpSocket::connect(const std::string& tcp_address,uint16_t tcp_port) +{ + if(mState == CONNECTED) + close(); + + mConnectPort = tcp_port; + mConnectAddress = tcp_address; + + return connect(); +} +bool RsTcpSocket::connect() { int CreateSocket = 0; char dataReceived[1024]; @@ -23,18 +50,19 @@ int RsTcpSocket::connect() printf("Socket not created \n"); return false; } - ipOfServer.sin_family = AF_INET; ipOfServer.sin_port = htons(mConnectPort); ipOfServer.sin_addr.s_addr = inet_addr(mConnectAddress.c_str()); - if(::connect(mSocket, (struct sockaddr *)&ipOfServer, sizeof(ipOfServer))<0) + if(::connect(CreateSocket, (struct sockaddr *)&ipOfServer, sizeof(ipOfServer))<0) { - printf("Connection failed due to port and ip problems, or server is not available\n"); + printf("Connection failed due to port and ip problems, or server is not available. Socket=%d,ConnectPort=%d,ConnectAddress=%s Errno=%d\n",mSocket,mConnectPort,mConnectAddress.c_str(),errno); return false; } mState = CONNECTED; - setSocket(mSocket); + + unix_fcntl_nonblock(CreateSocket); + setSocket(CreateSocket); return true; } @@ -42,6 +70,7 @@ int RsTcpSocket::connect() int RsTcpSocket::close() { RsFdBinInterface::close(); + mState = DISCONNECTED; return !::close(mSocket); } @@ -50,16 +79,12 @@ RsThreadedTcpSocket::RsThreadedTcpSocket(const std::string& tcp_address,uint16_t : RsTcpSocket(tcp_address,tcp_port) { } - +RsThreadedTcpSocket::RsThreadedTcpSocket() : RsTcpSocket() +{ +} void RsThreadedTcpSocket::run() { - if(!connect()) - { - RsErr() << "Cannot connect socket to " << connectAddress() << ":" << connectPort() ; - return ; - } - - while(connectionState() == CONNECTED) + while(!shouldStop() && connectionState() == CONNECTED) { tick(); std::this_thread::sleep_for(std::chrono::milliseconds(200)); diff --git a/libretroshare/src/pqi/rstcpsocket.h b/libretroshare/src/pqi/rstcpsocket.h index bdc127f91..a51c400ef 100644 --- a/libretroshare/src/pqi/rstcpsocket.h +++ b/libretroshare/src/pqi/rstcpsocket.h @@ -6,6 +6,8 @@ class RsTcpSocket: public RsFdBinInterface { public: RsTcpSocket(const std::string& tcp_address,uint16_t tcp_port); + RsTcpSocket(); + virtual ~RsTcpSocket()=default; enum State: uint8_t { UNKNOWN = 0x00, @@ -13,8 +15,10 @@ public: CONNECTED = 0x02 }; - // Return 1 when OK, 0 otherwise. - int connect(); + // Return true when OK, false otherwise. + bool connect(); + + bool connect(const std::string& tcp_address,uint16_t tcp_port); // Returns 1 when OK, 0 otherwise. int close(); @@ -34,6 +38,7 @@ class RsThreadedTcpSocket: public RsTcpSocket, public RsThread { public: RsThreadedTcpSocket(const std::string& tcp_address,uint16_t tcp_port); + RsThreadedTcpSocket(); virtual ~RsThreadedTcpSocket(); virtual void run() override; diff --git a/libretroshare/src/retroshare/rsevents.h b/libretroshare/src/retroshare/rsevents.h index 9b9fbf106..845c43205 100644 --- a/libretroshare/src/retroshare/rsevents.h +++ b/libretroshare/src/retroshare/rsevents.h @@ -103,10 +103,16 @@ enum class RsEventType : uint32_t /// @see rspeers.h NETWORK = 16, + /// @see RsMailTagEvent + MAIL_TAG = 17, + /** Emitted to update library clients about file hashing being completed */ FILE_HASHING_COMPLETED = 20, - __MAX /// Used internally, keep last + /// @see rspeers.h + TOR_MANAGER = 21, + + __MAX /// Used internally, keep last }; enum class RsEventsErrorNum : int32_t diff --git a/libretroshare/src/retroshare/rsgxsforums.h b/libretroshare/src/retroshare/rsgxsforums.h index 4967d46c5..ba13bd16e 100644 --- a/libretroshare/src/retroshare/rsgxsforums.h +++ b/libretroshare/src/retroshare/rsgxsforums.h @@ -450,6 +450,17 @@ public: const std::string& matchString, std::vector& searchResults ) = 0; + /** + * @brief Request Synchronization with available peers + * Usually syncronization already happen automatically so be carefull + * to call this method only if necessary. + * It has been thinked for use cases like mobile phone where internet + * connection is intermittent and calling this may be useful when a system + * event about connection being available or about to go offline is received + * @jsonapi{development} + * @return Success or error details + */ + virtual std::error_condition requestSynchronization() = 0; //////////////////////////////////////////////////////////////////////////// /* Following functions are deprecated and should not be considered a stable diff --git a/libretroshare/src/retroshare/rsinit.h b/libretroshare/src/retroshare/rsinit.h index d26e7ac05..ba2c33765 100644 --- a/libretroshare/src/retroshare/rsinit.h +++ b/libretroshare/src/retroshare/rsinit.h @@ -159,6 +159,7 @@ public: ERR_ALREADY_RUNNING, /// Another istance is running already ERR_CANT_ACQUIRE_LOCK, /// Another istance is already running? ERR_NO_AVAILABLE_ACCOUNT, /// Used in retroshare-service -U list when no account is available + ERR_CANNOT_CONFIGURE_TOR, /// cannot start/configure Tor for an auto-tor node ERR_UNKNOWN /// Unkown error, maybe password is wrong? }; @@ -184,6 +185,7 @@ public: static bool isPortable(); static bool isWindowsXP(); static bool collectEntropy(uint32_t bytes) ; + static bool startAutoTor(); /*! * \brief lockFilePath @@ -218,6 +220,7 @@ public: static void setAutoLogin(bool autoLogin); static bool RsClearAutoLogin() ; + static std::string executablePath() ; private: /** @brief Lock profile directory * param[in] accountDir account directory to lock diff --git a/libretroshare/src/retroshare/rsmsgs.h b/libretroshare/src/retroshare/rsmsgs.h index 6264af47a..5e317d4ae 100644 --- a/libretroshare/src/retroshare/rsmsgs.h +++ b/libretroshare/src/retroshare/rsmsgs.h @@ -308,6 +308,9 @@ enum class RsMailStatusEventCode: uint8_t /// An error occurred attempting to sign the message SIGNATURE_FAILED = 0x04, + + MESSAGE_CHANGED = 0x05, + TAG_CHANGED = 0x06, }; struct RsMailStatusEvent : RsEvent @@ -329,6 +332,32 @@ struct RsMailStatusEvent : RsEvent ~RsMailStatusEvent() override = default; }; +enum class RsMailTagEventCode: uint8_t +{ + TAG_ADDED = 0x00, + TAG_CHANGED = 0x01, + TAG_REMOVED = 0x02, +}; + +struct RsMailTagEvent : RsEvent +{ + RsMailTagEvent() : RsEvent(RsEventType::MAIL_TAG) {} + + RsMailTagEventCode mMailTagEventCode; + std::set mChangedMsgTagIds; + + /// @see RsEvent + void serial_process( RsGenericSerializer::SerializeJob j, + RsGenericSerializer::SerializeContext& ctx) override + { + RsEvent::serial_process(j, ctx); + RS_SERIAL_PROCESS(mChangedMsgTagIds); + RS_SERIAL_PROCESS(mMailTagEventCode); + } + + ~RsMailTagEvent() override = default; +}; + #define RS_CHAT_PUBLIC 0x0001 #define RS_CHAT_PRIVATE 0x0002 #define RS_CHAT_AVATAR_AVAILABLE 0x0004 diff --git a/libretroshare/src/retroshare/rsnotify.h b/libretroshare/src/retroshare/rsnotify.h index e8c9de614..aeb701066 100644 --- a/libretroshare/src/retroshare/rsnotify.h +++ b/libretroshare/src/retroshare/rsnotify.h @@ -128,14 +128,12 @@ const uint32_t RS_MESSAGE_CONNECT_ATTEMPT = 0x0001; const int NOTIFY_LIST_NEIGHBOURS = 1; const int NOTIFY_LIST_FRIENDS = 2; const int NOTIFY_LIST_SEARCHLIST = 4; -const int NOTIFY_LIST_MESSAGELIST = 5; const int NOTIFY_LIST_CHANNELLIST = 6; const int NOTIFY_LIST_TRANSFERLIST = 7; const int NOTIFY_LIST_CONFIG = 8; const int NOTIFY_LIST_DIRLIST_LOCAL = 9; const int NOTIFY_LIST_DIRLIST_FRIENDS = 10; const int NOTIFY_LIST_FORUMLIST_LOCKED = 11; // use connect with Qt::QueuedConnection -const int NOTIFY_LIST_MESSAGE_TAGS = 12; const int NOTIFY_LIST_PUBLIC_CHAT = 13; const int NOTIFY_LIST_PRIVATE_INCOMING_CHAT = 14; const int NOTIFY_LIST_PRIVATE_OUTGOING_CHAT = 15; diff --git a/libretroshare/src/retroshare/rspeers.h b/libretroshare/src/retroshare/rspeers.h index f49ef7fc2..e00576908 100644 --- a/libretroshare/src/retroshare/rspeers.h +++ b/libretroshare/src/retroshare/rspeers.h @@ -887,7 +887,7 @@ public: // Certificate utils virtual bool cleanCertificate( const std::string& certstr, std::string& cleanCert, - bool& is_short_format, uint32_t& error_code ) = 0; + bool& is_short_format, uint32_t& error_code, RsPeerDetails& details) = 0; virtual std::string saveCertificateToString(const RsPeerId &id) = 0; virtual bool signGPGCertificate(const RsPgpId &gpg_id,const std::string& gpg_passphrase) = 0; diff --git a/libretroshare/src/retroshare/rstor.h b/libretroshare/src/retroshare/rstor.h new file mode 100644 index 000000000..654b79938 --- /dev/null +++ b/libretroshare/src/retroshare/rstor.h @@ -0,0 +1,182 @@ +#pragma once + +// This code stands as an interface for the automatic configuration +// of Tor hidden services to be used by retroshare. +// +// The correct way to use it is to: +// +// 1 - properly set data and hidden service directories. This allowd the TorManager +// to save its keys for the hidden service, or to load one that has previously been created +// +// 2 - call setupHiddenService(). This creates/loads the hidden service. +// +// 3 - call RsTor::start() +// +// 4 - loop/wait until RsTor::getHiddenServiceStatus(service_id) +// returns RsTorHiddenServiceStatus::ONLINE +// +// 5 - call RsTor::getHiddenserviceInfo to properly setup RS internal ports and addresses: +// +// RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port); +// RsTor::getProxyServerInfo(proxy_server_address,proxy_server_port) ; +// +// rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address, service_target_port); +// rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address, service_port); +// rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address,proxy_server_port) ; + +#include "retroshare/rsevents.h" + +namespace Tor { + class TorManager; +} + +enum class RsTorManagerEventCode: uint8_t +{ + UNKNOWN = 0x00, + TOR_STATUS_CHANGED = 0x01, + BOOTSTRAP_STATUS_CHANGED = 0x02, + TOR_CONNECTIVITY_CHANGED = 0x03, + TOR_MANAGER_ERROR = 0x04, + CONFIGURATION_NEEDED = 0x05, + TOR_MANAGER_STOPPED = 0x06, +}; + +// Status of the Tor hidden service setup/loaded by RS + +enum class RsTorHiddenServiceStatus: uint8_t { + ERROR = 0x00, + NOT_CREATED = 0x01, + OFFLINE = 0x02, + ONLINE = 0x03 +}; + +// Status of the connection/authentication between RS and the Tor service + +enum class RsTorConnectivityStatus: uint8_t { + ERROR = 0x00, + NOT_CONNECTED = 0x01, + CONNECTING = 0x02, + SOCKET_CONNECTED = 0x03, + AUTHENTICATING = 0x04, + AUTHENTICATED = 0x05, + HIDDEN_SERVICE_READY = 0x06, + UNKNOWN = 0x07 +}; + +// Status of the Tor service with which RS is talking. + +enum class RsTorStatus: uint8_t { + UNKNOWN = 0x00, + OFFLINE = 0x01, + READY = 0x02 +}; + +struct RsTorManagerEvent: public RsEvent +{ + RsTorManagerEvent(): RsEvent(RsEventType::TOR_MANAGER), + mTorManagerEventType(RsTorManagerEventCode::UNKNOWN) + {} + + RsTorManagerEventCode mTorManagerEventType; + + RsTorConnectivityStatus mTorConnectivityStatus; + RsTorStatus mTorStatus; + std::string mErrorMessage; + + ///* @see RsEvent @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeContext& ctx ) override + { + RsEvent::serial_process(j, ctx); + RS_SERIAL_PROCESS(mTorManagerEventType); + RS_SERIAL_PROCESS(mTorConnectivityStatus); + RS_SERIAL_PROCESS(mTorStatus); + RS_SERIAL_PROCESS(mErrorMessage); + } + + ~RsTorManagerEvent() = default; +}; + +class RsTor +{ +public: + /*! + * \brief isTorAvailable + * \return true if a Tor executble has been found. False otherwise. + */ + static bool isTorAvailable() ; + + /*! + * \brief torStatus + * \return Status of the Tor service used by RS + */ + static RsTorStatus torStatus() ; + + /*! + * \brief torConnectivityStatus + * \return Status of the connectivity/authentication between RS and Tor + */ + static RsTorConnectivityStatus torConnectivityStatus() ; + + static void setTorDataDirectory(const std::string& dir); + static void setHiddenServiceDirectory(const std::string& dir); + + static bool setupHiddenService(); + + /*! + * \brief getProxyServerInfo + * \param server_address Address of the proxy used by RS to send data in the Tor network. Usually 127.0.0.1 + * \param server_port Port of the proxy used by RS to send data in the Tor network. Usually 9050. + */ + static void getProxyServerInfo(std::string& server_address, uint16_t& server_port); + + /*! + * \brief getHiddenServiceStatus + * \param service_id onion address of the hidden service (if the service is OFFLINE or ONLINE) + * \return Status of the created/loaded hidden service. + */ + static RsTorHiddenServiceStatus getHiddenServiceStatus(std::string& service_id); + + /*! + * \brief start + * Launches the Tor management threads. + */ + static bool start(); + + /*! + * \brief stop + * Stop the Tor management threads. + */ + static void stop(); + + /*! + * \brief getHiddenServiceInfo + * Gets information about the hidden service setup by RS to run. + * \param service_id + * \param service_onion_address + * \param service_port + * \param service_target_address + * \param target_port + * \return + */ + static bool getHiddenServiceInfo(std::string& service_id, + std::string& service_onion_address, + uint16_t& service_port, + std::string& service_target_address, + uint16_t& target_port); + + /*! + * \brief bootstrapStatus + * \return Log messages of the Tor bootstrapping status. + */ + static std::map bootstrapStatus(); + static std::list logMessages(); + + static std::string socksAddress(); + static uint16_t socksPort(); + + static bool hasError(); + static std::string errorMessage(); + +private: + static Tor::TorManager *instance(); +}; diff --git a/libretroshare/src/rs_android/LocalArray.h b/libretroshare/src/rs_android/LocalArray.h new file mode 100644 index 000000000..2ab708aff --- /dev/null +++ b/libretroshare/src/rs_android/LocalArray.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOCAL_ARRAY_H_included +#define LOCAL_ARRAY_H_included + +#include +#include + +/** + * A fixed-size array with a size hint. That number of bytes will be allocated + * on the stack, and used if possible, but if more bytes are requested at + * construction time, a buffer will be allocated on the heap (and deallocated + * by the destructor). + * + * The API is intended to be a compatible subset of C++0x's std::array. + */ +template +class LocalArray { +public: + /** + * Allocates a new fixed-size array of the given size. If this size is + * less than or equal to the template parameter STACK_BYTE_COUNT, an + * internal on-stack buffer will be used. Otherwise a heap buffer will + * be allocated. + */ + LocalArray(size_t desiredByteCount) : mSize(desiredByteCount) { + if (desiredByteCount > STACK_BYTE_COUNT) { + mPtr = new char[mSize]; + } else { + mPtr = &mOnStackBuffer[0]; + } + } + + /** + * Frees the heap-allocated buffer, if there was one. + */ + ~LocalArray() { + if (mPtr != &mOnStackBuffer[0]) { + delete[] mPtr; + } + } + + // Capacity. + size_t size() { return mSize; } + bool empty() { return mSize == 0; } + + // Element access. + char& operator[](size_t n) { return mPtr[n]; } + const char& operator[](size_t n) const { return mPtr[n]; } + +private: + char mOnStackBuffer[STACK_BYTE_COUNT]; + char* mPtr; + size_t mSize; + + // Disallow copy and assignment. + LocalArray(const LocalArray&); + void operator=(const LocalArray&); +}; + +#endif // LOCAL_ARRAY_H_included diff --git a/libretroshare/src/rs_android/README-ifaddrs-android.adoc b/libretroshare/src/rs_android/README-ifaddrs-android.adoc new file mode 100644 index 000000000..0c0065f92 --- /dev/null +++ b/libretroshare/src/rs_android/README-ifaddrs-android.adoc @@ -0,0 +1,49 @@ += README Android ifaddrs + +Android API level < 24 doesn't provide `getifaddrs` and related functions, we +have tested multiple ways to overcome this issue. + + +== Non Weorking alternative implementations + +https://github.com/kmackay/android-ifaddrs +https://github.com/morristech/android-ifaddrs +https://www.openhub.net/p/android-ifaddrs/ + +Compiles but then segfault at runtime. + + +== Qt implementation + +Using `QNetworkInterface::allAddresses()` provided by Qt works but at time of +writing (Q4 2021) on newer Android the log is flooded by warnings as we reported +here +https://bugreports.qt.io/browse/QTBUG-78659 +plus depending on Qt networking module just for this is frustrating. + +Update: the warning flood seems have been fixed in later Qt versions +https://bugreports.qt.io/browse/QTBUG-86394 + +This solution was the first working we implemented in our code it has been +removed to avoid dependency on Qt, as lighter alternatives are possible. + + +== Code copied from Android Gingerbread release + +As explained here +https://stackoverflow.com/a/57112520 + +even older Android have `getifaddrs` implementations but doesn't make them +accessible in the API, in particular the one included in Android Gingerbread + +https://android.googlesource.com/platform/libcore/+/refs/heads/gingerbread-release/luni/src/main/native/ifaddrs-android.h +https://android.googlesource.com/platform/libcore/+/refs/heads/gingerbread-release/ + +is particularly easy to include in our code base and compile. + +This solution seems the best fitting and doesn't introduce dependency on Qt. +Newer Android releases (expecially 11) have introduced multiple restrictions +on network information access so we suggest you to prepare different APK for +different API level in order to use the `getifaddrs` provided by Android NDK +which deal gracefully with those restrictions as soon as available. + diff --git a/libretroshare/src/rs_android/ScopedFd.h b/libretroshare/src/rs_android/ScopedFd.h new file mode 100644 index 000000000..d2b7935fa --- /dev/null +++ b/libretroshare/src/rs_android/ScopedFd.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCOPED_FD_H_included +#define SCOPED_FD_H_included + +#include + +// A smart pointer that closes the given fd on going out of scope. +// Use this when the fd is incidental to the purpose of your function, +// but needs to be cleaned up on exit. +class ScopedFd { +public: + explicit ScopedFd(int fd) : fd(fd) { + } + + ~ScopedFd() { + close(fd); + } + + int get() const { + return fd; + } + +private: + int fd; + + // Disallow copy and assignment. + ScopedFd(const ScopedFd&); + void operator=(const ScopedFd&); +}; + +#endif // SCOPED_FD_H_included diff --git a/libretroshare/src/util/androiddebug.h b/libretroshare/src/rs_android/androidcoutcerrcatcher.hpp similarity index 83% rename from libretroshare/src/util/androiddebug.h rename to libretroshare/src/rs_android/androidcoutcerrcatcher.hpp index c358be553..43cf16dd7 100644 --- a/libretroshare/src/util/androiddebug.h +++ b/libretroshare/src/rs_android/androidcoutcerrcatcher.hpp @@ -1,9 +1,9 @@ /******************************************************************************* - * libretroshare/src/util: androiddebug.h * * * * libretroshare: retroshare core library * * * - * Copyright (C) 2016 Gioacchino Mazzurco * + * Copyright (C) 2016-2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -36,10 +36,10 @@ * class at the beginning of the main of your program to get them (stdout and * stderr) on logcat output. */ -class AndroidStdIOCatcher +class AndroidCoutCerrCatcher { public: - AndroidStdIOCatcher(const std::string& dTag = "RetroShare", + AndroidCoutCerrCatcher(const std::string& dTag = "RetroShare", android_LogPriority stdout_pri = ANDROID_LOG_INFO, android_LogPriority stderr_pri = ANDROID_LOG_ERROR) : tag(dTag), cout_pri(stdout_pri), cerr_pri(stderr_pri), should_stop(false) @@ -63,10 +63,10 @@ public: pthread_detach(thr); } - ~AndroidStdIOCatcher() + ~AndroidCoutCerrCatcher() { should_stop = true; - pthread_join(thr, NULL); + pthread_join(thr, nullptr); } private: @@ -79,11 +79,13 @@ private: pthread_t thr; std::atomic should_stop; - static void *thread_func(void* instance) + static void* thread_func(void* instance) { - __android_log_write(ANDROID_LOG_INFO, "RetroShare", "Android debugging start"); + __android_log_write( + ANDROID_LOG_INFO, "RetroShare", + "Android standard I/O catcher start" ); - AndroidStdIOCatcher &i = *static_cast(instance); + AndroidCoutCerrCatcher &i = *static_cast(instance); std::string out_buf; std::string err_buf; @@ -113,9 +115,11 @@ private: usleep(10000); } - __android_log_write(ANDROID_LOG_INFO, "RetroShare", "Android debugging stop"); + __android_log_write( + ANDROID_LOG_INFO, "RetroShare", + "Android standard I/O catcher stop" ); - return NULL; + return nullptr; } }; diff --git a/libretroshare/src/rs_android/errorconditionwrap.cpp b/libretroshare/src/rs_android/errorconditionwrap.cpp new file mode 100644 index 000000000..e27495878 --- /dev/null +++ b/libretroshare/src/rs_android/errorconditionwrap.cpp @@ -0,0 +1,42 @@ +/******************************************************************************* + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "rs_android/rsjni.hpp" + +namespace jni +{ +Local> MakeAnything( + ThingToMake, JNIEnv& env, + const std::error_condition& ec ) +{ + auto& clazz = jni::Class::Singleton(env); + + static auto method = + clazz.GetConstructor(env); + + jni::jint value = ec.value(); + auto message = jni::Make(env, ec.message()); + auto category = jni::Make(env, ec.category().name()); + + return clazz.New(env, method, value, message, category); +} +} diff --git a/libretroshare/src/rs_android/ifaddrs-android.h b/libretroshare/src/rs_android/ifaddrs-android.h new file mode 100644 index 000000000..446d8d2be --- /dev/null +++ b/libretroshare/src/rs_android/ifaddrs-android.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IFADDRS_ANDROID_H_included +#define IFADDRS_ANDROID_H_included + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LocalArray.h" +#include "ScopedFd.h" + +// Android (bionic) doesn't have getifaddrs(3)/freeifaddrs(3). +// We fake it here, so java_net_NetworkInterface.cpp can use that API +// with all the non-portable code being in this file. + +// Source-compatible subset of the BSD struct. +struct ifaddrs { + // Pointer to next struct in list, or NULL at end. + ifaddrs* ifa_next; + + // Interface name. + char* ifa_name; + + // Interface flags. + unsigned int ifa_flags; + + // Interface network address. + sockaddr* ifa_addr; + + // Interface netmask. + sockaddr* ifa_netmask; + + ifaddrs(ifaddrs* next) + : ifa_next(next), ifa_name(NULL), ifa_flags(0), ifa_addr(NULL), ifa_netmask(NULL) + { + } + + ~ifaddrs() { + delete ifa_next; + delete[] ifa_name; + delete ifa_addr; + delete ifa_netmask; + } + + // Sadly, we can't keep the interface index for portability with BSD. + // We'll have to keep the name instead, and re-query the index when + // we need it later. + bool setNameAndFlagsByIndex(int interfaceIndex) { + // Get the name. + char buf[IFNAMSIZ]; + char* name = if_indextoname(interfaceIndex, buf); + if (name == NULL) { + return false; + } + ifa_name = new char[strlen(name) + 1]; + strcpy(ifa_name, name); + + // Get the flags. + ScopedFd fd(socket(AF_INET, SOCK_DGRAM, 0)); + if (fd.get() == -1) { + return false; + } + ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, name); + int rc = ioctl(fd.get(), SIOCGIFFLAGS, &ifr); + if (rc == -1) { + return false; + } + ifa_flags = ifr.ifr_flags; + return true; + } + + // Netlink gives us the address family in the header, and the + // sockaddr_in or sockaddr_in6 bytes as the payload. We need to + // stitch the two bits together into the sockaddr that's part of + // our portable interface. + void setAddress(int family, void* data, size_t byteCount) { + // Set the address proper... + sockaddr_storage* ss = new sockaddr_storage; + memset(ss, 0, sizeof(*ss)); + ifa_addr = reinterpret_cast(ss); + ss->ss_family = family; + uint8_t* dst = sockaddrBytes(family, ss); + memcpy(dst, data, byteCount); + } + + // Netlink gives us the prefix length as a bit count. We need to turn + // that into a BSD-compatible netmask represented by a sockaddr*. + void setNetmask(int family, size_t prefixLength) { + // ...and work out the netmask from the prefix length. + sockaddr_storage* ss = new sockaddr_storage; + memset(ss, 0, sizeof(*ss)); + ifa_netmask = reinterpret_cast(ss); + ss->ss_family = family; + uint8_t* dst = sockaddrBytes(family, ss); + memset(dst, 0xff, prefixLength / 8); + if ((prefixLength % 8) != 0) { + dst[prefixLength/8] = (0xff << (8 - (prefixLength % 8))); + } + } + + // Returns a pointer to the first byte in the address data (which is + // stored in network byte order). + uint8_t* sockaddrBytes(int family, sockaddr_storage* ss) { + if (family == AF_INET) { + sockaddr_in* ss4 = reinterpret_cast(ss); + return reinterpret_cast(&ss4->sin_addr); + } else if (family == AF_INET6) { + sockaddr_in6* ss6 = reinterpret_cast(ss); + return reinterpret_cast(&ss6->sin6_addr); + } + return NULL; + } + +private: + // Disallow copy and assignment. + ifaddrs(const ifaddrs&); + void operator=(const ifaddrs&); +}; + +// FIXME: use iovec instead. +struct addrReq_struct { + nlmsghdr netlinkHeader; + ifaddrmsg msg; +}; + +inline bool sendNetlinkMessage(int fd, const void* data, size_t byteCount) { + ssize_t sentByteCount = TEMP_FAILURE_RETRY(send(fd, data, byteCount, 0)); + return (sentByteCount == static_cast(byteCount)); +} + +inline ssize_t recvNetlinkMessage(int fd, char* buf, size_t byteCount) { + return TEMP_FAILURE_RETRY(recv(fd, buf, byteCount, 0)); +} + +// Source-compatible with the BSD function. +inline int getifaddrs(ifaddrs** result) { + // Simplify cleanup for callers. + *result = NULL; + + // Create a netlink socket. + ScopedFd fd(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)); + if (fd.get() < 0) { + return -1; + } + + // Ask for the address information. + addrReq_struct addrRequest; + memset(&addrRequest, 0, sizeof(addrRequest)); + addrRequest.netlinkHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH; + addrRequest.netlinkHeader.nlmsg_type = RTM_GETADDR; + addrRequest.netlinkHeader.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(addrRequest))); + addrRequest.msg.ifa_family = AF_UNSPEC; // All families. + addrRequest.msg.ifa_index = 0; // All interfaces. + if (!sendNetlinkMessage(fd.get(), &addrRequest, addrRequest.netlinkHeader.nlmsg_len)) { + return -1; + } + + // Read the responses. + LocalArray<0> buf(65536); // We don't necessarily have std::vector. + ssize_t bytesRead; + while ((bytesRead = recvNetlinkMessage(fd.get(), &buf[0], buf.size())) > 0) { + nlmsghdr* hdr = reinterpret_cast(&buf[0]); + for (; NLMSG_OK(hdr, (size_t)bytesRead); hdr = NLMSG_NEXT(hdr, bytesRead)) { + switch (hdr->nlmsg_type) { + case NLMSG_DONE: + return 0; + case NLMSG_ERROR: + return -1; + case RTM_NEWADDR: + { + ifaddrmsg* address = reinterpret_cast(NLMSG_DATA(hdr)); + rtattr* rta = IFA_RTA(address); + size_t ifaPayloadLength = IFA_PAYLOAD(hdr); + while (RTA_OK(rta, ifaPayloadLength)) { + if (rta->rta_type == IFA_LOCAL) { + int family = address->ifa_family; + if (family == AF_INET || family == AF_INET6) { + *result = new ifaddrs(*result); + if (!(*result)->setNameAndFlagsByIndex(address->ifa_index)) { + return -1; + } + (*result)->setAddress(family, RTA_DATA(rta), RTA_PAYLOAD(rta)); + (*result)->setNetmask(family, address->ifa_prefixlen); + } + } + rta = RTA_NEXT(rta, ifaPayloadLength); + } + } + break; + } + } + } + // We only get here if recv fails before we see a NLMSG_DONE. + return -1; +} + +// Source-compatible with the BSD function. +inline void freeifaddrs(ifaddrs* addresses) { + delete addresses; +} + +#endif // IFADDRS_ANDROID_H_included diff --git a/libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java b/libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java new file mode 100644 index 000000000..c32aa5b5d --- /dev/null +++ b/libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java @@ -0,0 +1,98 @@ +/* + * RetroShare + * Copyright (C) 2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package org.retroshare.service; + +import android.util.Log; +import android.content.Context; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; + + +public class AssetHelper +{ + public static boolean copyAsset( + Context ctx, String assetPath, String destinationFilePath ) + { + Log.d(TAG, "copyAsset " + assetPath + " -> " + destinationFilePath); + + InputStream in; + OutputStream out; + + try { in = ctx.getAssets().open(assetPath); } + catch(Exception e) + { + Log.e( + TAG, + "Failure opening asset: " + assetPath + " " + e.getMessage() ); + return false; + } + + try { out = new FileOutputStream(destinationFilePath); } + catch(Exception e) + { + Log.e( + TAG, + "Failure opening destination: " + destinationFilePath + " " + + e.getMessage() ); + return false; + } + + try + { + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) out.write(buf, 0, len); + } + catch(IOException e) + { + Log.e( + TAG, + "Failure coping: " + assetPath + " -> " + destinationFilePath + + " " + e.getMessage() ); + return false; + } + + try { in.close(); } + catch(IOException e) + { + Log.e(TAG, "Failure closing: " + assetPath + " " + e.getMessage() ); + return false; + } + + try { out.close(); } + catch(IOException e) + { + Log.e( + TAG, + "Failure closing: " + destinationFilePath + " " + + e.getMessage() ); + return false; + } + + return true; + } + + private static final String TAG = "RetroShare AssetHelper.java"; +} diff --git a/libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java b/libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java new file mode 100644 index 000000000..c0830f5d2 --- /dev/null +++ b/libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java @@ -0,0 +1,48 @@ +/* + * RetroShare + * Copyright (C) 2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package org.retroshare.service; + +public class ErrorConditionWrap +{ + public ErrorConditionWrap( + int value, String message, String categoryName ) + { + mValue = value; + mMessage = message; + mCategoryName = categoryName; + } + + public int value() { return mValue; } + public String message() { return mMessage; } + public String categoryName() { return mCategoryName; } + + public boolean toBool() { return mValue != 0; } + + @Override + public String toString() + { return String.format("%d", mValue)+" "+mMessage+" [" + mCategoryName+ "]"; } + + private int mValue = 0; + private String mMessage; + private String mCategoryName; +} diff --git a/libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java b/libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java new file mode 100644 index 000000000..818f06348 --- /dev/null +++ b/libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java @@ -0,0 +1,141 @@ +/* + * RetroShare + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package org.retroshare.service; + +import android.app.Service; +import android.os.IBinder; +import android.os.Bundle; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.app.ActivityManager; + + +public class RetroShareServiceAndroid extends Service +{ + public static final int DEFAULT_JSON_API_PORT = 9092; + public static final String DEFAULT_JSON_API_BINDING_ADDRESS = "127.0.0.1"; + + static { System.loadLibrary("retroshare-service"); } + + public static void start( + Context ctx, int jsonApiPort, String jsonApiBindAddress ) + { + Log.d(TAG, "start"); + Intent intent = new Intent(ctx, RetroShareServiceAndroid.class); + intent.putExtra(JSON_API_PORT_KEY, jsonApiPort); + intent.putExtra(JSON_API_BIND_ADDRESS_KEY, jsonApiBindAddress); + ctx.startService(intent); + } + + public static void stop(Context ctx) + { + Log.d(TAG, "stop"); + ctx.stopService(new Intent(ctx, RetroShareServiceAndroid.class)); + } + + public static boolean isRunning(Context ctx) + { + Log.d(TAG, "isRunning"); + ActivityManager manager = + (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); + for( ActivityManager.RunningServiceInfo service : + manager.getRunningServices(Integer.MAX_VALUE) ) + if( RetroShareServiceAndroid.class.getName() + .equals(service.service.getClassName()) ) + return true; + return false; + } + + public static Context getServiceContext() + { + if(sServiceContext == null) + { + Log.e(TAG, "getServiceContext() called before onCreate"); + throw new NullPointerException(); + } + return sServiceContext; + } + + @Override + public int onStartCommand( + Intent intent, int flags, int startId ) + { + if(intent == null) + { + Log.i(TAG, "onStartCommand called without intent"); + return Service.START_REDELIVER_INTENT; + } + + int jsonApiPort = DEFAULT_JSON_API_PORT; + String jsonApiBindAddress = DEFAULT_JSON_API_BINDING_ADDRESS; + + Bundle args = intent.getExtras(); + if(args.containsKey(JSON_API_PORT_KEY)) + jsonApiPort = args.getInt(JSON_API_PORT_KEY); + if(args.containsKey(JSON_API_BIND_ADDRESS_KEY)) + jsonApiBindAddress = + args.getString(JSON_API_BIND_ADDRESS_KEY); + + ErrorConditionWrap ec = nativeStart(jsonApiPort, jsonApiBindAddress); + if(ec.toBool()) Log.e(TAG, "onStartCommand(...) " + ec.toString()); + + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onCreate () + { + super.onCreate(); + sServiceContext = this; + } + + @Override + public void onDestroy() + { + ErrorConditionWrap ec = nativeStop(); + if(ec.toBool()) Log.e(TAG, "onDestroy() " + ec.toString()); + sServiceContext = null; + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent arg0) { return null; } + + private static final String JSON_API_PORT_KEY = + RetroShareServiceAndroid.class.getCanonicalName() + + "/JSON_API_PORT_KEY"; + + private static final String JSON_API_BIND_ADDRESS_KEY = + RetroShareServiceAndroid.class.getCanonicalName() + + "/JSON_API_BIND_ADDRESS_KEY" ; + + private static final String TAG = "RetroShareServiceAndroid.java"; + + private static Context sServiceContext; + + protected static native ErrorConditionWrap nativeStart( + int jsonApiPort, String jsonApiBindAddress ); + + protected static native ErrorConditionWrap nativeStop(); +} diff --git a/libretroshare/src/rs_android/retroshareserviceandroid.cpp b/libretroshare/src/rs_android/retroshareserviceandroid.cpp new file mode 100644 index 000000000..6e156edca --- /dev/null +++ b/libretroshare/src/rs_android/retroshareserviceandroid.cpp @@ -0,0 +1,103 @@ +/* + * RetroShare Service Android + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include +#include + +#include "util/stacktrace.h" +#include "retroshare/rsinit.h" +#include "retroshare/rsiface.h" +#include "util/rsdebug.h" + +#include "rs_android/retroshareserviceandroid.hpp" +#include "rs_android/rsjni.hpp" + + +/*static*/ std::unique_ptr +RetroShareServiceAndroid::sAndroidCoutCerrCatcher = nullptr; + +using ErrorConditionWrap = RsJni::ErrorConditionWrap; + +/*static*/ jni::Local> +RetroShareServiceAndroid::start( + JNIEnv& env, jni::Class&, + jni::jint jsonApiPort, const jni::String& jsonApiBindAddress ) +{ + if(jsonApiPort < 0 || jsonApiPort > std::numeric_limits::max()) + { + RS_ERR("Got invalid JSON API port: ", jsonApiPort); + return jni::Make(env, std::errc::invalid_argument); + } + + RsInfo() << "\n" << + "+================================================================+\n" + "| o---o o |\n" + "| \\ / - Retroshare Service Android - / \\ |\n" + "| o o---o |\n" + "+================================================================+" + << std::endl << std::endl; + + sAndroidCoutCerrCatcher = std::make_unique(); + + RsInit::InitRsConfig(); + RsControl::earlyInitNotificationSystem(); + + RsConfigOptions conf; + conf.jsonApiPort = static_cast(jsonApiPort); + conf.jsonApiBindAddress = jni::Make(env, jsonApiBindAddress); + + // Dirty workaround plugins not supported on Android ATM + conf.main_executable_path = " "; + + int initResult = RsInit::InitRetroShare(conf); + if(initResult != RS_INIT_OK) + { + RS_ERR("Retroshare core initalization failed with: ", initResult); + return jni::Make(env, std::errc::no_child_process); + } + + return jni::Make(env, std::error_condition()); +} + +jni::Local> RetroShareServiceAndroid::stop( + JNIEnv& env, jni::Class& ) +{ + if(RsControl::instance()->isReady()) + { + RsControl::instance()->rsGlobalShutDown(); + return jni::Make(env, std::error_condition()); + } + + sAndroidCoutCerrCatcher.reset(); + + return jni::Make(env, std::errc::no_such_process); +} + +jni::Local > +RetroShareServiceAndroid::getAndroidContext(JNIEnv& env) +{ + auto& clazz = jni::Class::Singleton(env); + static auto method = + clazz.GetStaticMethod()>( + env, "getServiceContext" ); + return clazz.Call(env, method); +} diff --git a/libretroshare/src/rs_android/retroshareserviceandroid.hpp b/libretroshare/src/rs_android/retroshareserviceandroid.hpp new file mode 100644 index 000000000..bd8f88f31 --- /dev/null +++ b/libretroshare/src/rs_android/retroshareserviceandroid.hpp @@ -0,0 +1,84 @@ +/* + * RetroShare Service Android + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +#pragma once + +#include +#include + +#include + +#include "rs_android/rsjni.hpp" +#include "rs_android/androidcoutcerrcatcher.hpp" + +#include "util/stacktrace.h" + +/** Provide native methods that are registered into corresponding Java class + * to start/stop RetroShare with reasonable comfort on Android platform */ +struct RetroShareServiceAndroid +{ + static constexpr auto Name() + { return "org/retroshare/service/RetroShareServiceAndroid"; } + + using ErrorConditionWrap = RsJni::ErrorConditionWrap; + + /** + * Called from RetroShareServiceAndroid Java to init libretroshare + * @param[in] env the usual JNI parafernalia + * @param[in] jclass the usual JNI parafernalia + * @param[in] jsonApiPort port on which JSON API server will listen + * @param[in] jsonApiBindAddress binding address of the JSON API server + * @note Yeah you read it well we use a full 32 bit signed integer for JSON + * API port. This is because Java lack even the minimum decency to implement + * unsigned integral types so we need to wrap the port (16 bit unsigned + * integer everywhere reasonable) into a full integer and then check at + * runtime the value. + */ + static jni::Local> start( + JNIEnv& env, jni::Class& jclass, + jni::jint jsonApiPort, const jni::String& jsonApiBindAddress ); + + /** + * Called from RetroShareServiceAndroid Java to shutdown libretroshare + * @param[in] env the usual JNI parafernalia + * @param[in] jclass the usual JNI parafernalia + */ + static jni::Local> stop( + JNIEnv& env, jni::Class& ); + + struct Context + { + /// JNI parafernalia + static constexpr auto Name() { return "android/content/Context"; } + }; + + /// Return RetroShare Service Android Context + static jni::Local> getAndroidContext(JNIEnv& env); + +private: + /** Doesn't involve complex liftime handling stuff better let the runtime + * handle costruction (ASAP)/destruction for us */ + static CrashStackTrace CrashStackTrace; + + /** Involve threads, file descriptors etc. better handle lifetime + * explicitely */ + static std::unique_ptr sAndroidCoutCerrCatcher; +}; diff --git a/libretroshare/src/rs_android/rsjni.cpp b/libretroshare/src/rs_android/rsjni.cpp new file mode 100644 index 000000000..c9fc2c8ed --- /dev/null +++ b/libretroshare/src/rs_android/rsjni.cpp @@ -0,0 +1,70 @@ +/******************************************************************************* + * RetroShare JNI utilities * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "rs_android/rsjni.hpp" +#include "rs_android/retroshareserviceandroid.hpp" + +rs_view_ptr RsJni::mJvm = nullptr; + + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad_retroshare(JavaVM* vm, void*) +{ + RS_DBG(vm); + + RsJni::mJvm = vm; + + jni::JNIEnv& env { jni::GetEnv(*vm) }; + + /** Ensure singleton refereces to our own Java classes are inizialized here + * because default Java class loader which is the one accessible by native + * threads which is not main even if attached, is not capable to find them. + * https://stackoverflow.com/questions/20752352/classnotfoundexception-when-finding-a-class-in-jni-background-thread + * https://groups.google.com/g/android-ndk/c/2gkr1mXKn_E */ + jni::Class::Singleton(env); + jni::Class::Singleton(env); + + jni::RegisterNatives( + env, *jni::Class::Singleton(env), + jni::MakeNativeMethod< + decltype(&RetroShareServiceAndroid::start), + &RetroShareServiceAndroid::start >("nativeStart"), + jni::MakeNativeMethod< + decltype(&RetroShareServiceAndroid::stop), + &RetroShareServiceAndroid::stop >("nativeStop") + ); + + return jni::Unwrap(jni::jni_version_1_2); +} + +#ifdef RS_LIBRETROSHARE_EXPORT_JNI_ONLOAD +/** If libretroshare is linked statically to other components which already + * export JNI_OnLoad then a symbol clash may happen + * if RS_LIBRETROSHARE_EXPORT_JNI_ONLOAD is defined. + * @see JNI_OnLoad_retroshare should instead be called from the exported + * JNI_OnLoad */ +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* _reserved) +{ + RS_DBG(vm); + return JNI_OnLoad_retroshare(vm, _reserved); +} +#endif // def RS_LIBRETROSHARE_EXPORT_JNI_ONLOAD diff --git a/libretroshare/src/rs_android/rsjni.hpp b/libretroshare/src/rs_android/rsjni.hpp new file mode 100644 index 000000000..339dfd5c7 --- /dev/null +++ b/libretroshare/src/rs_android/rsjni.hpp @@ -0,0 +1,90 @@ +/******************************************************************************* + * RetroShare JNI utilities * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#include +#include + +#include + +#include "util/rsmemory.h" +#include "util/cxx23retrocompat.h" + + +/** Store JVM pointer safely and register native methods */ +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad_retroshare(JavaVM* vm, void*); + + +/** Provide library wide JVM access with some safe measures + * The JVM pointer is set properly by @see JNI_OnLoad_retroshare + */ +class RsJni +{ +public: + static inline JavaVM& getVM() + { + if(!mJvm) // [[unlikely]] + { + RS_FATAL( "Attempt to access JVM before JNI_OnLoad_retroshare ", + std::errc::bad_address ); + print_stacktrace(); + std::exit(std::to_underlying(std::errc::bad_address)); + } + + return *mJvm; + } + + friend jint JNI_OnLoad_retroshare(JavaVM* vm, void*); + + /** Provide a comfortable way to access Android package assets like + * bdboot.txt from C++ */ + struct AssetHelper + { + static constexpr auto Name() + { return "org/retroshare/service/AssetHelper"; } + }; + + /** Provide a comfortable way to propagate C++ error_conditions to Java + * callers */ + struct ErrorConditionWrap + { + static constexpr auto Name() + { return "org/retroshare/service/ErrorConditionWrap"; } + }; + +private: + static rs_view_ptr mJvm; +}; + + +namespace jni +{ +/** Provides idiomatic way of creating instances via +@code{.cpp} + jni::Make(env, std::error_condition()); +@endcode */ +jni::Local> +MakeAnything( + jni::ThingToMake, JNIEnv& env, + const std::error_condition& ec ); +} diff --git a/libretroshare/src/rsitems/itempriorities.h b/libretroshare/src/rsitems/itempriorities.h index a376a9d64..7c2b92cb6 100644 --- a/libretroshare/src/rsitems/itempriorities.h +++ b/libretroshare/src/rsitems/itempriorities.h @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2011-2011 by Cyril Soler * + * Copyright (C) 2011-2018 Cyril Soler * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -21,7 +23,9 @@ *******************************************************************************/ #pragma once -#include +#include + +using RsItemPriority = uint8_t; // This file centralises QoS priorities for all transfer RsItems // diff --git a/libretroshare/src/rsitems/rsitem.h b/libretroshare/src/rsitems/rsitem.h index 3ef44f859..70844f840 100644 --- a/libretroshare/src/rsitems/rsitem.h +++ b/libretroshare/src/rsitems/rsitem.h @@ -3,7 +3,8 @@ * * * libretroshare: retroshare core library * * * - * Copyright (C) 2018 Gioacchino Mazzurco * + * Copyright (C) 2018-2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -28,6 +29,9 @@ #include "serialiser/rsserializer.h" #include "serialiser/rsserializable.h" #include "util/stacktrace.h" +#include "rsitems/itempriorities.h" +#include "rsitems/rsserviceids.h" + #include @@ -42,8 +46,13 @@ struct RsItem : RsMemoryManagement::SmallObject, RsSerializable virtual ~RsItem(); - /// TODO: Do this make sense with the new serialization system? - virtual void clear() = 0; + /** TODO: Does the existence of this method make sense with the new + * serialization system? **/ + virtual void clear() + { + RS_ERR("Called without being overridden, report to developers"); + print_stacktrace(); + } /// @deprecated use << ostream operator instead RS_DEPRECATED_FOR("<< ostream operator") @@ -70,14 +79,21 @@ struct RsItem : RsMemoryManagement::SmallObject, RsSerializable uint8_t PacketType(); uint8_t PacketSubType() const; + /** For Service Packets, @deprecated use the costructor with priority + * paramether instead */ + RS_DEPRECATED RsItem(uint8_t ver, uint16_t service, uint8_t subtype); + /// For Service Packets - RsItem(uint8_t ver, uint16_t service, uint8_t subtype); + RsItem( uint8_t ver, RsServiceType service, uint8_t subtype, + RsItemPriority prio ); + uint16_t PacketService() const; /* combined Packet class/type (mid 16bits) */ void setPacketService(uint16_t service); inline uint8_t priority_level() const { return _priority_level ;} inline void setPriorityLevel(uint8_t l) { _priority_level = l ;} +#ifdef RS_DEAD_CODE /* * TODO: This default implementation should be removed and childs structs * implement ::serial_process(...) as soon as all the codebase is ported to @@ -90,11 +106,12 @@ struct RsItem : RsMemoryManagement::SmallObject, RsSerializable "overriding Class is: ", typeid(*this).name() ); print_stacktrace(); } +#endif //def RS_DEAD_CODE protected: uint32_t type; RsPeerId peerId; - uint8_t _priority_level; + RsItemPriority _priority_level; }; /// TODO: Do this make sense with the new serialization system? @@ -108,9 +125,17 @@ public: uint32_t getRawLength() { return len; } void * getRawData() { return data; } - virtual void clear() {} +// virtual void clear() override {} virtual std::ostream &print(std::ostream &out, uint16_t indent = 0); + virtual void serial_process(RsGenericSerializer::SerializeJob, + RsGenericSerializer::SerializeContext&) override + { + RS_ERR( "called by an item using new serialization system ", + typeid(*this).name() ); + print_stacktrace(); + } + private: void *data; uint32_t len; diff --git a/libretroshare/src/rsitems/rsnxsitems.cc b/libretroshare/src/rsitems/rsnxsitems.cc index e8178e045..94409bc19 100644 --- a/libretroshare/src/rsitems/rsnxsitems.cc +++ b/libretroshare/src/rsitems/rsnxsitems.cc @@ -64,6 +64,12 @@ RsItem *RsNxsSerialiser::create_item(uint16_t service_id,uint8_t item_subtype) c if(service_id != SERVICE_TYPE) return NULL ; + switch(static_cast(item_subtype)) + { + case RsNxsSubtype::PULL_REQUEST: + return new RsNxsPullRequestItem(static_cast(service_id)); + } + switch(item_subtype) { case RS_PKT_SUBTYPE_NXS_SYNC_GRP_REQ_ITEM: return new RsNxsSyncGrpReqItem(SERVICE_TYPE) ; diff --git a/libretroshare/src/rsitems/rsnxsitems.h b/libretroshare/src/rsitems/rsnxsitems.h index 2b6731bfc..c7b6c58e3 100644 --- a/libretroshare/src/rsitems/rsnxsitems.h +++ b/libretroshare/src/rsitems/rsnxsitems.h @@ -3,7 +3,10 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2012 Christopher Evi-Parker,Robert Fernie* + * Copyright (C) 2012 Christopher Evi-Parker * + * Copyright (C) 2012 Robert Fernie * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -19,8 +22,7 @@ * along with this program. If not, see . * * * *******************************************************************************/ -#ifndef RSNXSITEMS_H -#define RSNXSITEMS_H +#pragma once #include #include @@ -33,8 +35,13 @@ #include "serialiser/rstlvkeys.h" #include "gxs/rsgxsdata.h" -// These items have "flag type" numbers, but this is not used. +enum class RsNxsSubtype : uint8_t +{ + PULL_REQUEST = 0x90 /// @see RsNxsPullRequestItem +}; +// These items have "flag type" numbers, but this is not used. +// TODO: refactor as C++11 enum class const uint8_t RS_PKT_SUBTYPE_NXS_SYNC_GRP_REQ_ITEM = 0x01; const uint8_t RS_PKT_SUBTYPE_NXS_SYNC_GRP_ITEM = 0x02; const uint8_t RS_PKT_SUBTYPE_NXS_SYNC_GRP_STATS_ITEM = 0x03; @@ -47,14 +54,15 @@ const uint8_t RS_PKT_SUBTYPE_NXS_MSG_ITEM = 0x20; const uint8_t RS_PKT_SUBTYPE_NXS_TRANSAC_ITEM = 0x40; const uint8_t RS_PKT_SUBTYPE_NXS_GRP_PUBLISH_KEY_ITEM = 0x80; -// possibility create second service to deal with this functionality +#ifdef RS_DEAD_CODE +// possibility create second service to deal with this functionality const uint8_t RS_PKT_SUBTYPE_EXT_SEARCH_GRP = 0x0001; const uint8_t RS_PKT_SUBTYPE_EXT_SEARCH_MSG = 0x0002; const uint8_t RS_PKT_SUBTYPE_EXT_DELETE_GRP = 0x0004; const uint8_t RS_PKT_SUBTYPE_EXT_DELETE_MSG = 0x0008; const uint8_t RS_PKT_SUBTYPE_EXT_SEARCH_REQ = 0x0010; - +#endif // def RS_DEAD_CODE /*! * Base class for Network exchange service @@ -65,17 +73,14 @@ const uint8_t RS_PKT_SUBTYPE_EXT_SEARCH_REQ = 0x0010; */ class RsNxsItem : public RsItem { - public: - RsNxsItem(uint16_t servtype, uint8_t subtype) : RsItem(RS_PKT_VERSION_SERVICE, servtype, subtype), transactionNumber(0) - { - setPriorityLevel(QOS_PRIORITY_RS_GXS_NET); - return; - } - virtual ~RsNxsItem(){} - virtual void clear() = 0; + RsNxsItem(uint16_t servtype, uint8_t subtype): + RsItem(RS_PKT_VERSION_SERVICE, servtype, subtype), transactionNumber(0) + { setPriorityLevel(QOS_PRIORITY_RS_GXS_NET); } - uint32_t transactionNumber; // set to zero if this is not a transaction item + virtual ~RsNxsItem() = default; + + uint32_t transactionNumber; // set to zero if this is not a transaction item }; @@ -362,6 +367,22 @@ public: }; +/*! + * Used to request to a peer pull updates from us ASAP without waiting GXS sync + * timer */ +struct RsNxsPullRequestItem: RsItem +{ + explicit RsNxsPullRequestItem(RsServiceType servtype): + RsItem( RS_PKT_VERSION_SERVICE, + servtype, + static_cast(RsNxsSubtype::PULL_REQUEST), + QOS_PRIORITY_RS_GXS_NET ) {} + + /// @see RsSerializable + void serial_process( RsGenericSerializer::SerializeJob, + RsGenericSerializer::SerializeContext& ) override {} +}; + /*! * Used to respond to a RsGrpMsgsReq @@ -401,6 +422,7 @@ struct RsNxsMsg : RsNxsItem RsGxsMsgMetaData* metaData; }; +#ifdef RS_DEAD_CODE /*! * Used to request a search of user data */ @@ -422,7 +444,7 @@ public: RsTlvBinaryData serviceSearchItem; // service aware of item class uint32_t expiration; // expiration date }; - +#endif //def RS_DEAD_CODE #ifdef UNUSED_CODE @@ -503,14 +525,12 @@ class RsNxsSerialiser : public RsServiceSerializer { public: - explicit RsNxsSerialiser(uint16_t servtype) : RsServiceSerializer(servtype), SERVICE_TYPE(servtype) {} - virtual ~RsNxsSerialiser() {} + explicit RsNxsSerialiser(uint16_t servtype): + RsServiceSerializer(servtype), SERVICE_TYPE(servtype) {} + virtual ~RsNxsSerialiser() = default; virtual RsItem *create_item(uint16_t service_id,uint8_t item_subtype) const ; protected: const uint16_t SERVICE_TYPE; }; - - -#endif // RSNXSITEMS_H diff --git a/libretroshare/src/rsserver/p3face-config.cc b/libretroshare/src/rsserver/p3face-config.cc index 3251f33eb..62c60815b 100644 --- a/libretroshare/src/rsserver/p3face-config.cc +++ b/libretroshare/src/rsserver/p3face-config.cc @@ -118,7 +118,7 @@ void RsServer::rsGlobalShutDown() // if(mWire) mWire->join(); // #endif - AuthGPG::exit(); + AuthPGP::exit(); mShutdownCallback(0); } diff --git a/libretroshare/src/rsserver/p3peers.cc b/libretroshare/src/rsserver/p3peers.cc index 9e6e961d6..53cd3b5d7 100644 --- a/libretroshare/src/rsserver/p3peers.cc +++ b/libretroshare/src/rsserver/p3peers.cc @@ -255,7 +255,7 @@ bool p3Peers::setPeerMaximumRates(const RsPgpId& pid,uint32_t maxUploadRate,uint bool p3Peers::haveSecretKey(const RsPgpId& id) { - return AuthGPG::getAuthGPG()->haveSecretKey(id); + return AuthPGP::haveSecretKey(id); } /* There are too many dependancies of this function @@ -274,7 +274,7 @@ bool p3Peers::getPeerDetails(const RsPeerId& id, RsPeerDetails &d) if (id == sOwnId) { mPeerMgr->getOwnNetStatus(ps); - ps.gpg_id = AuthGPG::getAuthGPG()->getGPGOwnId(); + ps.gpg_id = AuthPGP::getPgpOwnId(); } else if (!mPeerMgr->getFriendNetStatus(id, ps)) { @@ -560,17 +560,17 @@ bool p3Peers::isProxyAddress(const uint32_t type, const sockaddr_storage& addr) bool p3Peers::isKeySupported(const RsPgpId& id) { - return AuthGPG::getAuthGPG()->isKeySupported(id); + return AuthPGP::isKeySupported(id); } std::string p3Peers::getGPGName(const RsPgpId &gpg_id) { /* get from mAuthMgr as it should have more peers? */ - return AuthGPG::getAuthGPG()->getGPGName(gpg_id); + return AuthPGP::getPgpName(gpg_id); } bool p3Peers::isPgpFriend(const RsPgpId& pgpId) -{ return AuthGPG::getAuthGPG()->isGPGAccepted(pgpId); } +{ return AuthPGP::isPGPAccepted(pgpId); } bool p3Peers::isSslOnlyFriend(const RsPeerId& sslId) { @@ -598,7 +598,7 @@ std::string p3Peers::getPeerName(const RsPeerId& ssl) #endif std::string name; if (ssl == AuthSSL::getAuthSSL()->OwnId()) - return AuthGPG::getAuthGPG()->getGPGOwnName(); + return AuthPGP::getPgpOwnName(); if (mPeerMgr->getPeerName(ssl, name)) { @@ -618,7 +618,7 @@ bool p3Peers::getGPGAllList(std::list &ids) #endif /* get from mAuthMgr */ - AuthGPG::getAuthGPG()->getGPGAllList(ids); + AuthPGP::getPgpAllList(ids); return true; } @@ -629,7 +629,7 @@ bool p3Peers::getGPGValidList(std::list &ids) #endif /* get from mAuthMgr */ - AuthGPG::getAuthGPG()->getGPGValidList(ids); + AuthPGP::getPgpValidList(ids); return true; } @@ -640,14 +640,14 @@ bool p3Peers::getGPGSignedList(std::list &ids) #endif /* get from mAuthMgr */ - AuthGPG::getAuthGPG()->getGPGSignedList(ids); + AuthPGP::getPgpSignedList(ids); return true; } bool p3Peers::getPgpFriendList(std::vector& pgpIds) { std::list ids; - if(AuthGPG::getAuthGPG()->getGPGAcceptedList(ids)) + if(AuthPGP::getPgpAcceptedList(ids)) { pgpIds.clear(); std::copy(ids.begin(), ids.end(), std::back_inserter(pgpIds)); @@ -661,7 +661,7 @@ bool p3Peers::getGPGAcceptedList(std::list &ids) #ifdef P3PEERS_DEBUG std::cerr << "p3Peers::getGPGAcceptedList()" << std::endl; #endif - AuthGPG::getAuthGPG()->getGPGAcceptedList(ids); + AuthPGP::getPgpAcceptedList(ids); return true; } @@ -677,7 +677,7 @@ bool p3Peers::getAssociatedSSLIds(const RsPgpId &gpg_id, std::list &id bool p3Peers::gpgSignData(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen, std::string reason /* = "" */) { - return AuthGPG::getAuthGPG()->SignDataBin(data,len,sign,signlen, reason); + return AuthPGP::SignDataBin(data,len,sign,signlen, reason); } RsPgpId p3Peers::pgpIdFromFingerprint(const RsPgpFingerprint& fpr) @@ -692,7 +692,7 @@ bool p3Peers::getGPGDetails(const RsPgpId &pgp_id, RsPeerDetails &d) #endif /* get from mAuthMgr */ - bool res = AuthGPG::getAuthGPG()->getGPGDetails(pgp_id, d); + bool res = AuthPGP::getPgpDetails(pgp_id, d); d.isOnlyGPGdetail = true ; d.service_perm_flags = mPeerMgr->servicePermissionFlags(pgp_id) ; @@ -707,7 +707,7 @@ const RsPgpId& p3Peers::getGPGOwnId() #endif /* get from mAuthMgr */ - return AuthGPG::getAuthGPG()->getGPGOwnId(); + return AuthPGP::getPgpOwnId(); } RsPgpId p3Peers::getGPGId(const RsPeerId& sslid) @@ -719,7 +719,7 @@ RsPgpId p3Peers::getGPGId(const RsPeerId& sslid) /* get from mAuthMgr */ if (sslid == AuthSSL::getAuthSSL()->OwnId()) { - return AuthGPG::getAuthGPG()->getGPGOwnId(); + return AuthPGP::getPgpOwnId(); } peerState pcs; if (mPeerMgr->getFriendNetStatus(sslid, pcs)) @@ -740,12 +740,12 @@ bool p3Peers::addFriend(const RsPeerId &ssl_id, const RsPgpId &gpg_id,ServicePe #ifdef P3PEERS_DEBUG std::cerr << "p3Peers::addFriend() with : id : " << id << "; gpg_id : " << gpg_id << std::endl; #endif - if(AuthGPG::getAuthGPG()->isGPGId(gpg_id)) + if(AuthPGP::isPGPId(gpg_id)) { #ifdef P3PEERS_DEBUG std::cerr << "p3Peers::addFriend() Authorising GPG Id: " << gpg_id << std::endl; #endif - if (AuthGPG::getAuthGPG()->AllowConnection(gpg_id, true)) + if (AuthPGP::AllowConnection(gpg_id, true)) { #ifdef P3PEERS_DEBUG std::cerr << "p3Peers::addFriend() Authorization OK." << std::endl; @@ -798,7 +798,7 @@ bool p3Peers::addSslOnlyFriend( const RsPeerId& sslId, const RsPgpId& pgp_id,con bool p3Peers::removeKeysFromPGPKeyring(const std::set& pgp_ids,std::string& backup_file,uint32_t& error_code) { - return AuthGPG::getAuthGPG()->removeKeysFromPGPKeyring(pgp_ids,backup_file,error_code) ; + return AuthPGP::removeKeysFromPGPKeyring(pgp_ids,backup_file,error_code) ; } bool p3Peers::removeFriendLocation(const RsPeerId &sslId) @@ -818,7 +818,7 @@ bool p3Peers::removeFriend(const RsPgpId& gpgId) #ifdef P3PEERS_DEBUG std::cerr << "p3Peers::removeFriend() " << gpgId << std::endl; #endif - if (gpgId == AuthGPG::getAuthGPG()->getGPGOwnId()) { + if (gpgId == AuthPGP::getPgpOwnId()) { std::cerr << "p3Peers::removeFriend() ERROR we're not going to remove our own GPG id." << std::endl; return false; } @@ -826,7 +826,7 @@ bool p3Peers::removeFriend(const RsPgpId& gpgId) #ifdef P3PEERS_DEBUG std::cerr << "p3Peers::removeFriend() Removing GPG Id: " << gpgId << std::endl; #endif - if (AuthGPG::getAuthGPG()->AllowConnection(gpgId, false)) + if (AuthPGP::AllowConnection(gpgId, false)) { #ifdef P3PEERS_DEBUG std::cerr << "p3Peers::removeFriend() OK." << std::endl; @@ -1114,9 +1114,7 @@ std::string p3Peers::getPGPKey(const RsPgpId& pgp_id,bool include_signatures) rs_owner_ptr mem_block = nullptr; size_t mem_block_size = 0; - if( !AuthGPG::getAuthGPG()->exportPublicKey( - RsPgpId(pgp_id), mem_block, mem_block_size, - false, include_signatures ) ) + if( !AuthPGP::exportPublicKey( RsPgpId(pgp_id), mem_block, mem_block_size, false, include_signatures ) ) { RsErr() << __PRETTY_FUNCTION__ << " Failure retriving certificate for id " << pgp_id @@ -1147,8 +1145,7 @@ bool p3Peers::GetPGPBase64StringAndCheckSum( rs_owner_ptr mem_block = nullptr; size_t mem_block_size = 0; - if(!AuthGPG::getAuthGPG()->exportPublicKey( - gpg_id,mem_block,mem_block_size,false,false )) + if(!AuthPGP::exportPublicKey( gpg_id,mem_block,mem_block_size,false,false )) return false; RsBase64::encode(mem_block, mem_block_size, gpg_base64_string, true, false); @@ -1506,7 +1503,7 @@ std::string p3Peers::GetRetroshareInvite( const RsPeerId& sslId, RetroshareInvit unsigned char *mem_block = nullptr; size_t mem_block_size = 0; - if(!AuthGPG::getAuthGPG()->exportPublicKey( RsPgpId(detail.gpg_id), mem_block, mem_block_size, false, !!(invite_flags & RetroshareInviteFlags::PGP_SIGNATURES) )) + if(!AuthPGP::exportPublicKey( RsPgpId(detail.gpg_id), mem_block, mem_block_size, false, !!(invite_flags & RetroshareInviteFlags::PGP_SIGNATURES) )) { std::cerr << "Cannot output certificate for id \"" << detail.gpg_id << "\". Sorry." << std::endl; @@ -1542,7 +1539,7 @@ bool p3Peers::loadCertificateFromString( } RsPgpId gpgid; - bool res = AuthGPG::getAuthGPG()->LoadCertificateFromString( crt->armouredPGPKey(), gpgid, error_string ); + bool res = AuthPGP::LoadCertificateFromString( crt->armouredPGPKey(), gpgid, error_string ); gpg_id = gpgid; ssl_id = crt->sslid(); @@ -1559,7 +1556,7 @@ bool p3Peers::loadCertificateFromString( } bool p3Peers::loadPgpKeyFromBinaryData( const unsigned char *bin_key_data,uint32_t bin_key_len, RsPgpId& gpg_id, std::string& error_string ) { - bool res = AuthGPG::getAuthGPG()->LoadPGPKeyFromBinaryData( bin_key_data,bin_key_len, gpg_id, error_string ); + bool res = AuthPGP::LoadPGPKeyFromBinaryData( bin_key_data,bin_key_len, gpg_id, error_string ); if(res) mPeerMgr->notifyPgpKeyReceived(gpg_id); @@ -1578,9 +1575,7 @@ bool p3Peers::loadDetailsFromStringCert( const std::string &certstr, RsCertificate& cert = *certPtr; - if(!AuthGPG::getAuthGPG()->getGPGDetailsFromBinaryBlock( - cert.pgp_key(), cert.pgp_key_size(), - pd.gpg_id, pd.name, pd.gpgSigners )) + if(!AuthPGP::getPgpDetailsFromBinaryBlock( cert.pgp_key(), cert.pgp_key_size(), pd.gpg_id, pd.name, pd.gpgSigners )) return false; Dbg4() << __PRETTY_FUNCTION__ << " Parsing cert for sslid, location, ext " @@ -1620,11 +1615,11 @@ bool p3Peers::loadDetailsFromStringCert( const std::string &certstr, return true; } -bool p3Peers::cleanCertificate(const std::string &certstr, std::string &cleanCert,bool& is_short_format,uint32_t& error_code) +bool p3Peers::cleanCertificate(const std::string &certstr, std::string &cleanCert,bool& is_short_format,uint32_t& error_code,RsPeerDetails& details) { RsCertificate::Format format ; - bool res = RsCertificate::cleanCertificate(certstr,cleanCert,format,error_code,true) ; + bool res = RsCertificate::cleanCertificate(certstr,cleanCert,format,error_code,true,details) ; if(format == RsCertificate::RS_CERTIFICATE_RADIX) is_short_format = false; @@ -1658,7 +1653,7 @@ bool p3Peers::signGPGCertificate(const RsPgpId &id, const std::string &gpg_pass rsNotify->cachePgpPassphrase(gpg_passphrase); rsNotify->setDisableAskPassword(true); - bool res = AuthGPG::getAuthGPG()->SignCertificateLevel0(id); + bool res = AuthPGP::SignCertificateLevel0(id); rsNotify->clearPgpPassphrase(); rsNotify->setDisableAskPassword(false); @@ -1672,7 +1667,7 @@ bool p3Peers::trustGPGCertificate(const RsPgpId &id, uint32_t trustlvl) std::cerr << "p3Peers::TrustCertificate() " << id; std::cerr << std::endl; #endif - return AuthGPG::getAuthGPG()->TrustCertificate(id, trustlvl); + return AuthPGP::TrustCertificate(id, trustlvl); } /* Group Stuff */ diff --git a/libretroshare/src/rsserver/p3peers.h b/libretroshare/src/rsserver/p3peers.h index c2ff96d10..91630e12d 100644 --- a/libretroshare/src/rsserver/p3peers.h +++ b/libretroshare/src/rsserver/p3peers.h @@ -163,7 +163,7 @@ public: virtual bool loadPgpKeyFromBinaryData( const unsigned char *bin_key_data,uint32_t bin_key_len, RsPgpId& gpg_id, std::string& error_string ) override; virtual bool loadDetailsFromStringCert(const std::string &cert, RsPeerDetails &pd, uint32_t& error_code) override; - virtual bool cleanCertificate(const std::string &certstr, std::string &cleanCert, bool &is_short_format, uint32_t& error_code) override; + virtual bool cleanCertificate(const std::string &certstr, std::string &cleanCert, bool &is_short_format, uint32_t& error_code, RsPeerDetails& details) override; virtual std::string saveCertificateToString(const RsPeerId &id) override; virtual bool signGPGCertificate(const RsPgpId &id,const std::string& gpg_passphrase) override; diff --git a/libretroshare/src/rsserver/p3serverconfig.cc b/libretroshare/src/rsserver/p3serverconfig.cc index 6d3343dc7..c50eb2c89 100644 --- a/libretroshare/src/rsserver/p3serverconfig.cc +++ b/libretroshare/src/rsserver/p3serverconfig.cc @@ -140,7 +140,7 @@ bool p3ServerConfig::setConfigurationOption(uint32_t key, const std::string &opt int p3ServerConfig::getConfigNetStatus(RsConfigNetStatus &status) { status.ownId = AuthSSL::getAuthSSL()->OwnId(); - status.ownName = AuthGPG::getAuthGPG()->getGPGOwnName(); + status.ownName = AuthPGP::getPgpOwnName(); // Details from PeerMgr. peerState pstate; diff --git a/libretroshare/src/rsserver/rsaccounts.cc b/libretroshare/src/rsserver/rsaccounts.cc index 8be56073c..5d848571a 100644 --- a/libretroshare/src/rsserver/rsaccounts.cc +++ b/libretroshare/src/rsserver/rsaccounts.cc @@ -32,12 +32,12 @@ #include #include - +#include #include #include "retroshare/rsinit.h" #include "rsaccounts.h" - +#include "util/rsdebug.h" #include "util/rsdir.h" #include "util/rsstring.h" #include "util/folderiterator.h" @@ -48,6 +48,11 @@ #include +#ifdef __ANDROID__ +# include "rs_android/rsjni.hpp" +# include "rs_android/retroshareserviceandroid.hpp" +#endif + // Global singleton declaration of data. RsAccountsDetail* RsAccounts::rsAccountsDetails = nullptr; @@ -328,22 +333,7 @@ bool RsAccountsDetail::defaultBaseDirectory() { std::string basedir; -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ -#ifndef WINDOWS_SYS - - // unix: homedir + /.retroshare - char *h = getenv("HOME"); - if (h == NULL) - { - std::cerr << "defaultBaseDirectory() Error: cannot determine $HOME dir" - << std::endl; - return false ; - } - - basedir = h; - basedir += "/.retroshare"; - -#else +#ifdef WINDOWS_SYS if (RsInit::isPortable()) { // use directory "Data" in portable version @@ -375,13 +365,53 @@ bool RsAccountsDetail::defaultBaseDirectory() } basedir += "\\RetroShare"; } -#endif -/******************************** WINDOWS/UNIX SPECIFIC PART ******************/ +#elif defined (__ANDROID__) // def WINDOWS_SYS + + struct ApplicationInfo + { + static constexpr auto Name() + { return "android/content/pm/ApplicationInfo"; } + }; + + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto androidContext = RetroShareServiceAndroid::getAndroidContext(env); + auto& contextClass = + jni::Class::Singleton(env); + + auto& applicationInfoClass = jni::Class::Singleton(env); + + auto getApplicationInfo = + contextClass.GetMethod ()>( + env, "getApplicationInfo" ); + + auto applicationInfo = androidContext.Call(env, getApplicationInfo); + + auto dataDirField = jni::Field( + env, applicationInfoClass, "dataDir" ); + + jni::Local dataDir = applicationInfo.Get( + env, dataDirField ); + + basedir = jni::Make(env, dataDir) + "/.retroshare/"; + +#else // def WINDOWS_SYS, if defined (__ANDROID__) + // unix: homedir + /.retroshare + char* h = getenv("HOME"); + if(h == nullptr) + { + RS_ERR("cannot determine $HOME dir"); + return false ; + } + + basedir = h; + basedir += "/.retroshare"; +#endif // def WINDOWS_SYS /* store to class variable */ mBaseDirectory = basedir; - std::cerr << "defaultBaseDirectory() = " << mBaseDirectory; - std::cerr << std::endl; + + RS_INFO(mBaseDirectory); return true; } @@ -701,10 +731,10 @@ static bool checkAccount(const std::string &accountdir, AccountDetails &account, if(! RsAccounts::GetPGPLoginDetails(account.mPgpId, account.mPgpName, account.mPgpEmail)) return false ; - if(!AuthGPG::getAuthGPG()->haveSecretKey(account.mPgpId)) + if(!AuthPGP::haveSecretKey(account.mPgpId)) return false ; - if(!AuthGPG::getAuthGPG()->isKeySupported(account.mPgpId)) + if(!AuthPGP::isKeySupported(account.mPgpId)) { std::string keystring = account.mPgpId.toStdString() + " " + account.mPgpName + "<" + account.mPgpEmail ; unsupported_keys[keystring].push_back("Location: " + account.mLocation + "  (" + account.mSslId.toStdString() + ")") ; @@ -811,14 +841,15 @@ static bool checkAccount(const std::string &accountdir, AccountDetails &account, /* Use RetroShare's exe dir */ dataDirectory = "."; -#elif defined(ANDROID) +#elif defined(__ANDROID__) + // TODO: This is probably not really used on Android dataDirectory = PathBaseDirectory()+"/usr/share/retroshare"; -#elif defined(DATA_DIR) +#elif defined(RS_DATA_DIR) // cppcheck-suppress ConfigurationNotChecked - dataDirectory = DATA_DIR; + dataDirectory = RS_DATA_DIR; // For all other OS the data directory must be set in libretroshare.pro #else -# error "For your target OS automatic data dir discovery is not supported, cannot compile if DATA_DIR variable not set." +# error "For your target OS automatic data dir discovery is not supported, cannot compile if RS_DATA_DIR variable not set." #endif if (!check) @@ -851,9 +882,10 @@ static bool checkAccount(const std::string &accountdir, AccountDetails &account, /* Generating GPGme Account */ -int RsAccountsDetail::GetPGPLogins(std::list &pgpIds) { - AuthGPG::getAuthGPG()->availableGPGCertificatesWithPrivateKeys(pgpIds); - return 1; +int RsAccountsDetail::GetPGPLogins(std::list& pgpIds) +{ + AuthPGP::availablePgpCertificatesWithPrivateKeys(pgpIds); + return 1; } int RsAccountsDetail::GetPGPLoginDetails(const RsPgpId& id, std::string &name, std::string &email) @@ -863,10 +895,10 @@ int RsAccountsDetail::GetPGPLoginDetails(const RsPgpId& id, std::string &na #endif bool ok = true ; - name = AuthGPG::getAuthGPG()->getGPGName(id,&ok); + name = AuthPGP::getPgpName(id,&ok); if(!ok) return 0 ; - email = AuthGPG::getAuthGPG()->getGPGEmail(id,&ok); + email = AuthPGP::getPgpEmail(id,&ok); if(!ok) return 0 ; @@ -886,7 +918,7 @@ bool RsAccountsDetail::SelectPGPAccount(const RsPgpId& pgpId) { bool retVal = false; - if (0 < AuthGPG::getAuthGPG() -> GPGInit(pgpId)) + if (0 < AuthPGP::PgpInit(pgpId)) { retVal = true; #ifdef DEBUG_ACCOUNTS @@ -906,7 +938,7 @@ bool RsAccountsDetail::SelectPGPAccount(const RsPgpId& pgpId) bool RsAccountsDetail::GeneratePGPCertificate(const std::string& name, const std::string& email, const std::string& passwd, RsPgpId &pgpId, const int keynumbits, std::string &errString) { - return AuthGPG::getAuthGPG()->GeneratePGPCertificate(name, email, passwd, pgpId, keynumbits, errString); + return AuthPGP::GeneratePgpCertificate(name, email, passwd, pgpId, keynumbits, errString); } // PGP Support Functions. @@ -918,29 +950,34 @@ void RsAccountsDetail::getUnsupportedKeys(std::mapexportProfile(fname,id); + return AuthPGP::exportProfile(fname,id); } bool RsAccountsDetail::importIdentity(const std::string& fname,RsPgpId& id,std::string& import_error) { - return AuthGPG::getAuthGPG()->importProfile(fname,id,import_error); + return AuthPGP::importProfile(fname,id,import_error); } bool RsAccountsDetail::importIdentityFromString(const std::string &data, RsPgpId &imported_pgp_id, std::string &import_error) { - return AuthGPG::getAuthGPG()->importProfileFromString(data, imported_pgp_id, import_error); + return AuthPGP::importProfileFromString(data, imported_pgp_id, import_error); } bool RsAccountsDetail::exportIdentityToString( std::string& data, const RsPgpId& pgpId, bool includeSignatures, std::string& errorMsg ) { - return AuthGPG::getAuthGPG()->exportIdentityToString( + return AuthPGP::exportIdentityToString( data, pgpId, includeSignatures, errorMsg ); } bool RsAccountsDetail::copyGnuPGKeyrings() { +#ifdef __ANDROID__ + RS_ERR(std::errc::not_supported); + print_stacktrace(); + return false; +#else std::string pgp_dir = PathPGPDirectory() ; if(!RsDirUtil::checkCreateDirectory(pgp_dir)) @@ -992,6 +1029,7 @@ bool RsAccountsDetail::copyGnuPGKeyrings() } return true ; +#endif // def __ANDROID__ } @@ -1020,7 +1058,7 @@ bool RsAccountsDetail::GenerateSSLCertificate(const RsPgpId& pgp_id, const s int nbits = 4096; - //std::string pgp_name = AuthGPG::getAuthGPG()->getGPGName(pgp_id); + //std::string pgp_name = AuthGPG::getGPGName(pgp_id); // Create the filename ..... // Temporary Directory for creating files.... @@ -1295,7 +1333,7 @@ bool RsAccounts::init(const std::string& opt_base_dir,int& error_code) if(!RsDirUtil::checkCreateDirectory(pgp_dir)) throw std::runtime_error("Cannot create pgp directory " + pgp_dir) ; - AuthGPG::init( pgp_dir + "/retroshare_public_keyring.gpg", + AuthPGP::init( pgp_dir + "/retroshare_public_keyring.gpg", pgp_dir + "/retroshare_secret_keyring.gpg", pgp_dir + "/retroshare_trustdb.gpg", pgp_dir + "/lock"); diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index fc67c0798..6e448fd73 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -32,8 +32,9 @@ #endif #ifdef __ANDROID__ -# include // To install bdboot.txt -# include // for QString::fromStdString(...) +# include +# include "rs_android/rsjni.hpp" +# include "rs_android/retroshareserviceandroid.hpp" #endif #include "util/argstream.h" @@ -43,6 +44,7 @@ #include "util/folderiterator.h" #include "util/rsstring.h" #include "retroshare/rsinit.h" +#include "retroshare/rstor.h" #include "retroshare/rsnotify.h" #include "retroshare/rsiface.h" #include "plugins/pluginmanager.h" @@ -144,6 +146,7 @@ struct RsInitConfig {} RsFileHash main_executable_hash; + std::string mainExecutablePath; #ifdef WINDOWS_SYS bool portable; @@ -195,7 +198,7 @@ static const int SSLPWD_LEN = 64; void RsInit::InitRsConfig() { - RsInfo() << " libretroshare version: " << RS_HUMAN_READABLE_VERSION + RsInfo() << "libretroshare version: " << RS_HUMAN_READABLE_VERSION << std::endl; rsInitConfig = new RsInitConfig; @@ -305,6 +308,7 @@ int RsInit::InitRetroShare(const RsConfigOptions& conf) rsInitConfig->optBaseDir = conf.optBaseDir; rsInitConfig->jsonApiPort = conf.jsonApiPort; rsInitConfig->jsonApiBindAddress = conf.jsonApiBindAddress; + rsInitConfig->mainExecutablePath = conf.main_executable_path; #ifdef PTW32_STATIC_LIB // for static PThreads under windows... we need to init the library... @@ -512,7 +516,7 @@ RsInit::LoadCertificateStatus RsInit::LockAndLoadCertificates( if(!RsAccounts::GetAccountDetails(accountId, pgpId, pgpName, pgpEmail, location)) throw RsInit::ERR_UNKNOWN; // invalid PreferredAccount; - if(0 == AuthGPG::getAuthGPG() -> GPGInit(pgpId)) + if(0 == AuthPGP::PgpInit(pgpId)) throw RsInit::ERR_UNKNOWN; // PGP Error. LoadCertificateStatus retVal = @@ -912,8 +916,8 @@ int RsServer::StartupRetroShare() /* History Manager */ mHistoryMgr = new p3HistoryMgr(); mPeerMgr = new p3PeerMgrIMPL( AuthSSL::getAuthSSL()->OwnId(), - AuthGPG::getAuthGPG()->getGPGOwnId(), - AuthGPG::getAuthGPG()->getGPGOwnName(), + AuthPGP::getPgpOwnId(), + AuthPGP::getPgpOwnName(), AuthSSL::getAuthSSL()->getOwnLocation()); mNetMgr = new p3NetMgrIMPL(); mLinkMgr = new p3LinkMgrIMPL(mPeerMgr, mNetMgr); @@ -1012,32 +1016,32 @@ int RsServer::StartupRetroShare() uint64_t tmp_size ; if (!RsDirUtil::checkFile(bootstrapfile,tmp_size,true)) { - std::cerr << "DHT bootstrap file not in ConfigDir: " << bootstrapfile - << std::endl; -#ifdef __ANDROID__ - QFile bdbootRF("assets:/values/bdboot.txt"); - if(!bdbootRF.open(QIODevice::ReadOnly | QIODevice::Text)) - std::cerr << __PRETTY_FUNCTION__ - << " bdbootRF(assets:/values/bdboot.txt).open(...) fail: " - << bdbootRF.errorString().toStdString() << std::endl; - else - { - QFile bdbootCF(QString::fromStdString(bootstrapfile)); - if(!bdbootCF.open(QIODevice::WriteOnly | QIODevice::Text)) - std::cerr << __PRETTY_FUNCTION__ << " bdbootCF(" - << bootstrapfile << ").open(...) fail: " - << bdbootRF.errorString().toStdString() << std::endl; - else - { - bdbootCF.write(bdbootRF.readAll()); - bdbootCF.close(); - std::cerr << "Installed DHT bootstrap file not in ConfigDir: " - << bootstrapfile << std::endl; - } + RS_INFO("DHT bootstrap file not in ConfigDir: ", bootstrapfile); - bdbootRF.close(); - } -#else +#ifdef __ANDROID__ + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + + using AContext = RetroShareServiceAndroid::Context; + + auto& assetHelperClass = jni::Class::Singleton(env); + + static auto copyAsset = + assetHelperClass.GetStaticMethod< + jni::jboolean(jni::Object, jni::String, jni::String)>( + env, "copyAsset" ); + + auto androidContext = RetroShareServiceAndroid::getAndroidContext(env); + + jni::jboolean result = assetHelperClass.Call( + env, copyAsset, + androidContext, + jni::Make(env, "values/bdboot.txt"), + jni::Make(env, bootstrapfile) ); + + if(!result) RS_ERR("Failure installing ", bootstrapfile); + +#else // def __ANDROID__ std::cerr << "Checking for Installation DHT bootstrap file " << installfile << std::endl; if ((installfile != "") && (RsDirUtil::checkFile(installfile,tmp_size))) { @@ -1616,7 +1620,8 @@ int RsServer::StartupRetroShare() //mConfigMgr->addConfiguration("ftserver.cfg", ftserver); // - mConfigMgr->addConfiguration("gpg_prefs.cfg" , AuthGPG::getAuthGPG()); + AuthPGP::registerToConfigMgr(std::string("gpg_prefs.cfg"),mConfigMgr); + mConfigMgr->addConfiguration("gxsnettunnel.cfg", mGxsNetTunnel); mConfigMgr->addConfiguration("peers.cfg" , mPeerMgr); mConfigMgr->addConfiguration("general.cfg" , mGeneralConfig); @@ -1802,7 +1807,7 @@ int RsServer::StartupRetroShare() /* Add AuthGPG services */ /**************************************************************************/ - //AuthGPG::getAuthGPG()->addService(mDisc); + //AuthGPG::addService(mDisc); /**************************************************************************/ /* Force Any Last Configuration Options */ @@ -1933,6 +1938,52 @@ int RsServer::StartupRetroShare() return 1; } +std::string RsInit::executablePath() +{ + return rsInitConfig->mainExecutablePath; +} +bool RsInit::startAutoTor() +{ + std::cerr << "(II) node is an automated Tor node => launching Tor auto-configuration." << std::endl; + // Now that we know the Tor service running, and we know the SSL id, we can make sure it provides a viable hidden service + + std::string tor_hidden_service_dir = RsAccounts::AccountDirectory() + "/hidden_service/" ; + + RsTor::setTorDataDirectory(RsAccounts::ConfigDirectory() + "/tor/"); + RsTor::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)) ; + + if(! RsTor::start() || RsTor::hasError()) + { + std::cerr << "(EE) Tor cannot be started on your system: "+RsTor::errorMessage() << std::endl ; + return false ; + } + std::cerr << "(II) Tor has been started." << std::endl; + + // now start/create the hidden service as needed. + + std::string service_id; + RsTor::setupHiddenService(); + + while(RsTor::torStatus() != RsTorStatus::READY && RsTor::getHiddenServiceStatus(service_id) != RsTorHiddenServiceStatus::ONLINE) // runs until some status is reached: either tor works, or it fails. + { + rstime::rs_usleep(0.5*1000*1000) ; + + std::cerr << "(II) Hidden service ID: " << service_id << ", status: " << (int)RsTor::getHiddenServiceStatus(service_id) << std::endl; + if(RsTor::hasError()) + { + std::string error_msg = RsTor::errorMessage(); + + std::cerr << "(EE) Tor hidden service cannot be started: " << error_msg << std::endl; + return false; + } + // process Qt event loop to deal with messages of online/offline info + // QCoreApplication::processEvents(); + } + return true; +} + RsInit::LoadCertificateStatus RsLoginHelper::attemptLogin(const RsPeerId& account, const std::string& password) { if(isLoggedIn()) return RsInit::ERR_ALREADY_RUNNING; @@ -1952,6 +2003,16 @@ RsInit::LoadCertificateStatus RsLoginHelper::attemptLogin(const RsPeerId& accoun rsNotify->setDisableAskPassword(false) ; rsNotify->clearPgpPassphrase() ; + bool is_hidden_node = false; + bool is_auto_tor = false ; + bool is_first_time = false ; + + RsAccounts::getCurrentAccountOptions(is_hidden_node,is_auto_tor,is_first_time); + + if(is_auto_tor) + if(!RsInit::startAutoTor()) + return RsInit::ERR_CANNOT_CONFIGURE_TOR; + if(ret == RsInit::OK && RsControl::instance()->StartupRetroShare() == 1) return RsInit::OK; diff --git a/libretroshare/src/rsserver/rsloginhandler.cc b/libretroshare/src/rsserver/rsloginhandler.cc index dbe023235..a0834b0af 100644 --- a/libretroshare/src/rsserver/rsloginhandler.cc +++ b/libretroshare/src/rsserver/rsloginhandler.cc @@ -60,8 +60,7 @@ bool RsLoginHandler::checkAndStoreSSLPasswdIntoGPGFile( return true ; } - bool ok = AuthGPG::getAuthGPG()->encryptTextToFile( - ssl_passwd, getSSLPasswdFileName(ssl_id)); + bool ok = AuthPGP::encryptTextToFile( ssl_passwd, getSSLPasswdFileName(ssl_id)); if (!ok) std::cerr << "Encrypting went wrong !" << std::endl; @@ -90,7 +89,7 @@ bool RsLoginHandler::getSSLPasswdFromGPGFile(const RsPeerId& ssl_id,std::string& #endif std::string plain; - if ( AuthGPG::getAuthGPG()->decryptTextFromFile( plain, getSSLPasswdFileName(ssl_id)) ) + if ( AuthPGP::decryptTextFromFile( plain, getSSLPasswdFileName(ssl_id)) ) { sslPassword = plain; #ifdef DEBUG_RSLOGINHANDLER diff --git a/libretroshare/src/serialiser/rsserial.cc b/libretroshare/src/serialiser/rsserial.cc index 52123abf1..4d3cda811 100644 --- a/libretroshare/src/serialiser/rsserial.cc +++ b/libretroshare/src/serialiser/rsserial.cc @@ -3,7 +3,9 @@ * * * libretroshare: retroshare core library * * * - * Copyright 2007-2008 by Robert Fernie * + * Copyright (C) 2007-2008 Robert Fernie * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -20,21 +22,20 @@ * * *******************************************************************************/ -#include "serialiser/rsbaseserial.h" - -#include "util/rsthreads.h" -#include "util/rsstring.h" -#include "util/rsprint.h" - -#include "rsitems/rsitem.h" -#include "rsitems/itempriorities.h" - -#include +#include #include #include #include #include +#include "serialiser/rsbaseserial.h" +#include "util/cxx23retrocompat.h" +#include "util/rsthreads.h" +#include "util/rsstring.h" +#include "util/rsprint.h" +#include "rsitems/rsitem.h" +#include "rsitems/itempriorities.h" + /*** * #define RSSERIAL_DEBUG 1 @@ -166,11 +167,17 @@ uint8_t RsItem::PacketSubType() const /* For Service Packets */ RsItem::RsItem(uint8_t ver, uint16_t service, uint8_t subtype) { - _priority_level = QOS_PRIORITY_UNKNOWN ; // This value triggers PQIInterface to complain about undefined priorities + // This value triggers PQIInterface to complain about undefined priorities + _priority_level = QOS_PRIORITY_UNKNOWN; type = (ver << 24) + (service << 8) + subtype; - return; } +RsItem::RsItem( uint8_t ver, RsServiceType service, uint8_t subtype, + RsItemPriority prio ): + type(static_cast( + (ver << 24) + (std::to_underlying(service) << 8) + subtype )), + _priority_level(prio) {} + uint16_t RsItem::PacketService() const { return (type >> 8) & 0xFFFF; diff --git a/libretroshare/src/services/broadcastdiscoveryservice.cc b/libretroshare/src/services/broadcastdiscoveryservice.cc index 076244b44..380cf8e5d 100644 --- a/libretroshare/src/services/broadcastdiscoveryservice.cc +++ b/libretroshare/src/services/broadcastdiscoveryservice.cc @@ -1,7 +1,8 @@ /******************************************************************************* * RetroShare Broadcast Domain Discovery * * * - * Copyright (C) 2019 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -25,16 +26,17 @@ #include #include -#ifdef __ANDROID__ -# include -#endif // def __ANDROID__ - #include "services/broadcastdiscoveryservice.h" #include "retroshare/rspeers.h" #include "serialiser/rsserializable.h" #include "serialiser/rsserializer.h" #include "retroshare/rsevents.h" +#ifdef __ANDROID__ +# include "rs_android/retroshareserviceandroid.hpp" +#endif // def __ANDROID__ + + /*extern*/ RsBroadcastDiscovery* rsBroadcastDiscovery = nullptr; struct BroadcastDiscoveryPack : RsSerializable @@ -99,7 +101,7 @@ BroadcastDiscoveryService::BroadcastDiscoveryService( if(mRsPeers.isHiddenNode(mRsPeers.getOwnId())) return; #ifdef __ANDROID__ - createMulticastLock(); + createAndroidMulticastLock(); #endif // def __ANDROID__ enableMulticastListening(); @@ -228,19 +230,47 @@ RsBroadcastDiscoveryResult BroadcastDiscoveryService::createResult( bool BroadcastDiscoveryService::isMulticastListeningEnabled() { #ifdef __ANDROID__ - return assertMulticastLockIsvalid() && - mWifiMulticastLock.callMethod("isHeld"); -#endif // def __ANDROID__ + if(!mAndroidWifiMulticastLock) + { + RS_ERR("Android multicast lock not initialized!"); + return false; + } + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto& multicastLockClass = jni::Class::Singleton(env); + + auto isHeld = + multicastLockClass.GetMethod( + env, "isHeld" ); + + return mAndroidWifiMulticastLock.Call(env, isHeld); +#else // def __ANDROID__ return true; +#endif // def __ANDROID__ } bool BroadcastDiscoveryService::enableMulticastListening() { #ifdef __ANDROID__ - if(assertMulticastLockIsvalid() && !isMulticastListeningEnabled()) + if(!mAndroidWifiMulticastLock) { - mWifiMulticastLock.callMethod("acquire"); + RS_ERR("Android multicast lock not initialized!"); + return false; + } + + if(!isMulticastListeningEnabled()) + { + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto& multicastLockClass = jni::Class::Singleton(env); + + auto acquire = + multicastLockClass.GetMethod( + env, "acquire" ); + + mAndroidWifiMulticastLock.Call(env, acquire); + return true; } #endif // def __ANDROID__ @@ -251,9 +281,24 @@ bool BroadcastDiscoveryService::enableMulticastListening() bool BroadcastDiscoveryService::disableMulticastListening() { #ifdef __ANDROID__ - if(assertMulticastLockIsvalid() && isMulticastListeningEnabled()) + if(!mAndroidWifiMulticastLock) { - mWifiMulticastLock.callMethod("release"); + RS_ERR("Android multicast lock not initialized!"); + return false; + } + + if(isMulticastListeningEnabled()) + { + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + auto& multicastLockClass = jni::Class::Singleton(env); + + auto release = + multicastLockClass.GetMethod( + env, "release" ); + + mAndroidWifiMulticastLock.Call(env, release); + return true; } #endif // def __ANDROID__ @@ -262,56 +307,57 @@ bool BroadcastDiscoveryService::disableMulticastListening() } #ifdef __ANDROID__ -bool BroadcastDiscoveryService::createMulticastLock() + +bool BroadcastDiscoveryService::createAndroidMulticastLock() { - Dbg2() << __PRETTY_FUNCTION__ << std::endl; - - constexpr auto fname = __PRETTY_FUNCTION__; - const auto failure = [&](const std::string& err) + if(mAndroidWifiMulticastLock) { - RsErr() << fname << " " << err << std::endl; - return false; - }; - - if(mWifiMulticastLock.isValid()) - return failure("mWifiMulticastLock is already initialized"); - - QAndroidJniObject context = QtAndroid::androidContext(); - if(!context.isValid()) - return failure("Cannot retrieve Android context"); - - QAndroidJniObject WIFI_SERVICE = QAndroidJniObject::getStaticObjectField( - "android.content.Context", "WIFI_SERVICE", "Ljava/lang/String;"); - if(!WIFI_SERVICE.isValid()) - return failure("Cannot retrieve Context.WIFI_SERVICE value"); - - QAndroidJniObject wifiManager = context.callObjectMethod( - "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", - WIFI_SERVICE.object() ); - if(!wifiManager.isValid()) - return failure("Cannot retrieve Android Wifi Manager"); - - mWifiMulticastLock = wifiManager.callObjectMethod( - "createMulticastLock", - "(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;", - QAndroidJniObject::fromString(fname).object() ); - if(!mWifiMulticastLock.isValid()) - return failure("Cannot create WifiManager.MulticastLock"); - - return true; -} - -bool BroadcastDiscoveryService::assertMulticastLockIsvalid() -{ - if(!mWifiMulticastLock.isValid()) - { - RsErr() << __PRETTY_FUNCTION__ << " mWifiMulticastLock is invalid!" - << std::endl; + RS_ERR("Android multicast lock is already initialized"); print_stacktrace(); return false; } + + auto uenv = jni::GetAttachedEnv(RsJni::getVM()); + JNIEnv& env = *uenv; + + using AContextTag = RetroShareServiceAndroid::Context; + using AContext = jni::Class; + static auto& contextClass = AContext::Singleton(env); + + auto wifiServiceField = jni::StaticField( + env, contextClass, "WIFI_SERVICE"); + + jni::Local WIFI_SERVICE = contextClass.Get( + env, wifiServiceField ); + + auto androidContext = RetroShareServiceAndroid::getAndroidContext(env); + + auto getSystemService = + contextClass.GetMethod (jni::String)>( + env, "getSystemService" ); + + struct WifiManager + { static constexpr auto Name() { return "android/net/wifi/WifiManager"; } }; + + auto& wifiManagerClass = jni::Class::Singleton(env); + + auto wifiManager = jni::Cast( + env, wifiManagerClass, + androidContext.Call(env, getSystemService, WIFI_SERVICE) ); + + auto createMulticastLock = + wifiManagerClass.GetMethod(jni::String)>( + env, "createMulticastLock" ); + + mAndroidWifiMulticastLock = jni::NewGlobal( + env, wifiManager.Call( + env, createMulticastLock, + jni::Make( + env, "RetroShare BroadcastDiscoveryService" ) ) ); + return true; } + #endif // def __ANDROID__ RsBroadcastDiscovery::~RsBroadcastDiscovery() = default; diff --git a/libretroshare/src/services/broadcastdiscoveryservice.h b/libretroshare/src/services/broadcastdiscoveryservice.h index ebbec04c1..f4204c324 100644 --- a/libretroshare/src/services/broadcastdiscoveryservice.h +++ b/libretroshare/src/services/broadcastdiscoveryservice.h @@ -1,7 +1,8 @@ /******************************************************************************* * RetroShare Broadcast Domain Discovery * * * - * Copyright (C) 2019 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Gioacchino Mazzurco * + * Copyright (C) 2019-2021 Asociación Civil Altermundi * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * @@ -27,14 +28,16 @@ #include -#ifdef __ANDROID__ -# include -#endif // def __ANDROID__ - #include "retroshare/rsbroadcastdiscovery.h" #include "util/rsthreads.h" #include "util/rsdebug.h" +#ifdef __ANDROID__ +# include +# include "rs_android/rsjni.hpp" +#endif // def __ANDROID__ + + namespace UDC = udpdiscovery; class RsPeers; @@ -42,7 +45,7 @@ class BroadcastDiscoveryService : public RsBroadcastDiscovery, public RsTickingThread { public: - BroadcastDiscoveryService(RsPeers& pRsPeers); + explicit BroadcastDiscoveryService(RsPeers& pRsPeers); ~BroadcastDiscoveryService() override; /// @see RsBroadcastDiscovery @@ -71,26 +74,27 @@ protected: std::map mDiscoveredData; RsMutex mDiscoveredDataMutex; - RsPeers& mRsPeers; // TODO: std::shared_ptr mRsPeers; + RsPeers& mRsPeers; RsBroadcastDiscoveryResult createResult( const UDC::IpPort& ipp, const std::string& uData ); #ifdef __ANDROID__ - /** Android WifiManager.MulticastLock */ - QAndroidJniObject mWifiMulticastLock; + struct AndroidMulticastLock + { + static constexpr auto Name() + { return "android/net/wifi/WifiManager$MulticastLock"; } + }; + + jni::Global> mAndroidWifiMulticastLock; /** Initialize the wifi multicast lock without acquiring it * Needed to enable multicast listening in Android, for RetroShare broadcast * discovery inspired by: * https://github.com/flutter/flutter/issues/16335#issuecomment-420547860 */ - bool createMulticastLock(); - - /** Return false if mWifiMulticastLock is invalid and print error messages */ - bool assertMulticastLockIsvalid(); - -#endif // def __ANDROID__ + bool createAndroidMulticastLock(); +#endif RS_SET_CONTEXT_DEBUG_LEVEL(3) }; diff --git a/libretroshare/src/services/p3gxsforums.cc b/libretroshare/src/services/p3gxsforums.cc index 6cb3ae5c6..97d880880 100644 --- a/libretroshare/src/services/p3gxsforums.cc +++ b/libretroshare/src/services/p3gxsforums.cc @@ -900,10 +900,19 @@ bool p3GxsForums::markRead(const RsGxsGrpMsgIdPair& msgId, bool read) bool p3GxsForums::subscribeToForum(const RsGxsGroupId& groupId, bool subscribe ) { uint32_t token; - if( !RsGenExchange::subscribeToGroup(token, groupId, subscribe) || waitToken(token) != RsTokenService::COMPLETE ) return false; + if( !RsGenExchange::subscribeToGroup(token, groupId, subscribe) || + waitToken(token) != RsTokenService::COMPLETE ) return false; - RsGxsGroupId grp; - acknowledgeGrp(token,grp); + RsGxsGroupId grp; + acknowledgeGrp(token, grp); + + /* Since subscribe has been requested, the caller is most probably + * interested in getting the group messages ASAP so check updates from peers + * without waiting GXS sync timer. + * Do it here as this is meaningful or not depending on the service. + * Do it only after the token has been completed otherwise the pull have no + * effect. */ + if(subscribe) RsGenExchange::netService()->checkUpdatesFromPeers(); return true; } @@ -1150,6 +1159,13 @@ std::error_condition p3GxsForums::setPostKeepForever( } } +std::error_condition p3GxsForums::requestSynchronization() +{ + auto errc = RsGenExchange::netService()->checkUpdatesFromPeers(); + if(errc) return errc; + return RsGenExchange::netService()->requestPull(); +} + /* so we need the same tick idea as wiki for generating dummy forums */ diff --git a/libretroshare/src/services/p3gxsforums.h b/libretroshare/src/services/p3gxsforums.h index b499e7b0d..9400de652 100644 --- a/libretroshare/src/services/p3gxsforums.h +++ b/libretroshare/src/services/p3gxsforums.h @@ -175,6 +175,8 @@ public: rs_owner_ptr& resultData, uint32_t& resultSize ) override; #endif + std::error_condition requestSynchronization() override; + /// implementation of rsGxsGorums /// bool getGroupData(const uint32_t &token, std::vector &groups) override; diff --git a/libretroshare/src/services/p3idservice.cc b/libretroshare/src/services/p3idservice.cc index 7182b6a25..bf034bcb2 100644 --- a/libretroshare/src/services/p3idservice.cc +++ b/libretroshare/src/services/p3idservice.cc @@ -1067,7 +1067,7 @@ bool p3IdService::createIdentity(uint32_t& token, RsIdentityParameters ¶ms) if(params.isPgpLinked) { - ssdata.pgp.pgpId = AuthGPG::getAuthGPG()->getGPGOwnId(); + ssdata.pgp.pgpId = AuthPGP::getPgpOwnId(); ssdata.pgp.lastCheckTs = time(nullptr); } @@ -3619,7 +3619,7 @@ RsGenExchange::ServiceCreate_Return p3IdService::service_CreateGroup( unsigned int sign_size = MAX_SIGN_SIZE; memset(signarray,0,MAX_SIGN_SIZE) ; // just in case. - int result = AuthGPG::getAuthGPG()->SignDataBin( + int result = AuthPGP::SignDataBin( static_cast(hash.toByteArray()), hash.SIZE_IN_BYTES, signarray, &sign_size, __PRETTY_FUNCTION__ ) @@ -4096,7 +4096,7 @@ void p3IdService::getPgpIdList() #endif // DEBUG_IDS std::list list; - mPgpUtils->getGPGAllList(list); + mPgpUtils->getPgpAllList(list); RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/ @@ -4593,7 +4593,7 @@ void p3IdService::generateDummy_FriendPGP() // Now Generate for friends. std::list gpgids; std::list::const_iterator it; - mPgpUtils->getGPGAllList(gpgids); + mPgpUtils->getPgpAllList(gpgids); RsGxsIdGroup id; diff --git a/libretroshare/src/services/p3msgservice.cc b/libretroshare/src/services/p3msgservice.cc index 5974d7bf3..44ad7e024 100644 --- a/libretroshare/src/services/p3msgservice.cc +++ b/libretroshare/src/services/p3msgservice.cc @@ -220,8 +220,6 @@ void p3MsgService::processIncomingMsg(RsMsgItem *mi) for(std::list::const_iterator it(mi->attachment.items.begin());it!=mi->attachment.items.end();++it) rsFiles->FileRequest((*it).name,(*it).hash,(*it).filesize,std::string(),RS_FILE_REQ_ANONYMOUS_ROUTING,srcIds) ; } - - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_ADD); } bool p3MsgService::checkAndRebuildPartialMessage(RsMsgItem *ci) @@ -283,16 +281,11 @@ int p3MsgService::incomingMsgs() void p3MsgService::handleIncomingItem(RsMsgItem *mi) { - bool changed = false ; - // only returns true when a msg is complete. if(checkAndRebuildPartialMessage(mi)) { processIncomingMsg(mi); - changed = true ; } - if(changed) - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } void p3MsgService::statusChange(const std::list &plist) @@ -350,7 +343,6 @@ void p3MsgService::checkSizeAndSendMessage(RsMsgItem *msg) int p3MsgService::checkOutgoingMessages() { - bool changed = false; std::list output_queue; auto pEvent = std::make_shared(); @@ -400,7 +392,6 @@ int p3MsgService::checkOutgoingMessages() { (mit->second)->msgFlags &= ~RS_MSG_FLAGS_PENDING; toErase.push_back(mit->first); - changed = true; } else { @@ -442,9 +433,6 @@ int p3MsgService::checkOutgoingMessages() else checkSizeAndSendMessage(*it); - if(changed) - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); - if(rsEvents && !pEvent->mChangedMsgIds.empty()) rsEvents->postEvent(pEvent); @@ -954,8 +942,6 @@ bool p3MsgService::removeMsgId(const std::string &mid) setMessageTag(mid, 0, false); setMsgParentId(msgId, 0); - - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); } if(rsEvents && !pEvent->mChangedMsgIds.empty()) @@ -968,7 +954,6 @@ bool p3MsgService::markMsgIdRead(const std::string &mid, bool unreadByUser) { std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); - bool changed = false; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ @@ -992,18 +977,18 @@ bool p3MsgService::markMsgIdRead(const std::string &mid, bool unreadByUser) if (mi->msgFlags != msgFlags) { - changed = true; IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + auto pEvent = std::make_shared(); + pEvent->mMailStatusEventCode = RsMailStatusEventCode::MESSAGE_CHANGED; + pEvent->mChangedMsgIds.insert(mid); + rsEvents->postEvent(pEvent); } } else { return false; } } /* UNLOCKED */ - if (changed) { - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); - } - return true; } @@ -1012,8 +997,6 @@ bool p3MsgService::setMsgFlag(const std::string &mid, uint32_t flag, uint32_t std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); - bool changed = false; - { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ @@ -1033,15 +1016,15 @@ bool p3MsgService::setMsgFlag(const std::string &mid, uint32_t flag, uint32_t mit->second->msgFlags |= flag; if (mit->second->msgFlags != oldFlag) { - changed = true; IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ + + auto pEvent = std::make_shared(); + pEvent->mMailStatusEventCode = RsMailStatusEventCode::MESSAGE_CHANGED; + pEvent->mChangedMsgIds.insert(mid); + rsEvents->postEvent(pEvent); } } /* UNLOCKED */ - if (changed) { - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); - } - return true; } @@ -1137,7 +1120,10 @@ uint32_t p3MsgService::sendMessage(RsMsgItem* item) IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST, NOTIFY_TYPE_ADD); // deprecated + auto pEvent = std::make_shared(); + pEvent->mMailStatusEventCode = RsMailStatusEventCode::MESSAGE_SENT; + pEvent->mChangedMsgIds.insert(std::to_string(item->msgId)); + rsEvents->postEvent(pEvent); return item->msgId; } @@ -1175,8 +1161,11 @@ uint32_t p3MsgService::sendDistantMessage(RsMsgItem *item, const RsGxsId& from) IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ - RsServer::notify()->notifyListChange( NOTIFY_LIST_MESSAGELIST, - NOTIFY_TYPE_ADD ); + auto pEvent = std::make_shared(); + pEvent->mMailStatusEventCode = RsMailStatusEventCode::MESSAGE_SENT; + pEvent->mChangedMsgIds.insert(std::to_string(item->msgId)); + rsEvents->postEvent(pEvent); + return item->msgId; } @@ -1210,8 +1199,6 @@ bool p3MsgService::MessageSend(MessageInfo &info) // Update info for caller info.msgId = std::to_string(msg->msgId); info .msgflags = msg->msgFlags; - - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_ADD);// deprecated. Should be removed. Oct. 28, 2020 } auto pEvent = std::make_shared(); @@ -1418,10 +1405,9 @@ bool p3MsgService::MessageToDraft(MessageInfo &info, const std::string &msgParen IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ - // RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); - auto pEvent = std::make_shared(); pEvent->mMailStatusEventCode = RsMailStatusEventCode::MESSAGE_SENT; + pEvent->mChangedMsgIds.insert(std::to_string(msg->msgId)); rsEvents->postEvent(pEvent); return true; @@ -1452,7 +1438,7 @@ bool p3MsgService::getMessageTagTypes(MsgTagType& tags) bool p3MsgService::setMessageTagType(uint32_t tagId, std::string& text, uint32_t rgb_color) { - int nNotifyType = 0; + auto ev = std::make_shared(); { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ @@ -1475,7 +1461,8 @@ bool p3MsgService::setMessageTagType(uint32_t tagId, std::string& text, uint32 mTags.insert(std::pair(tagId, tagType)); - nNotifyType = NOTIFY_TYPE_ADD; + ev->mMailTagEventCode = RsMailTagEventCode::TAG_ADDED; + ev->mChangedMsgTagIds.insert(std::to_string(tagId)); } else { if (mit->second->text != text || mit->second->rgb_color != rgb_color) { /* modify existing tag */ @@ -1489,17 +1476,18 @@ bool p3MsgService::setMessageTagType(uint32_t tagId, std::string& text, uint32 } mit->second->rgb_color = rgb_color; - nNotifyType = NOTIFY_TYPE_MOD; + ev->mMailTagEventCode = RsMailTagEventCode::TAG_CHANGED; + ev->mChangedMsgTagIds.insert(std::to_string(tagId)); } } } /* UNLOCKED */ - if (nNotifyType) { + if (!ev->mChangedMsgTagIds.empty()) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGE_TAGS, nNotifyType); - + rsEvents->postEvent(ev); + return true; } @@ -1513,6 +1501,9 @@ bool p3MsgService::removeMessageTagType(uint32_t tagId) return false; } + auto msgEvent = std::make_shared(); + msgEvent->mMailStatusEventCode = RsMailStatusEventCode::TAG_CHANGED; + { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ @@ -1540,7 +1531,10 @@ bool p3MsgService::removeMessageTagType(uint32_t tagId) delete(tag); mMsgTags.erase(mit1++); - continue; + } + + if (msgEvent->mChangedMsgIds.find(std::to_string(mit1->first)) == msgEvent->mChangedMsgIds.end()) { + msgEvent->mChangedMsgIds.insert(std::to_string(mit1->first)); } } ++mit1; @@ -1554,7 +1548,14 @@ bool p3MsgService::removeMessageTagType(uint32_t tagId) IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGE_TAGS, NOTIFY_TYPE_DEL); + auto ev = std::make_shared(); + ev->mMailTagEventCode = RsMailTagEventCode::TAG_REMOVED; + ev->mChangedMsgTagIds.insert(std::to_string(tagId)); + rsEvents->postEvent(ev); + + if (!msgEvent->mChangedMsgIds.empty()) { + rsEvents->postEvent(msgEvent); + } return true; } @@ -1595,7 +1596,8 @@ bool p3MsgService::setMessageTag(const std::string &msgId, uint32_t tagId, bool } } - int nNotifyType = 0; + auto ev = std::make_shared(); + ev->mMailStatusEventCode = RsMailStatusEventCode::TAG_CHANGED; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ @@ -1614,7 +1616,7 @@ bool p3MsgService::setMessageTag(const std::string &msgId, uint32_t tagId, bool mMsgTags.insert(std::pair(tag->msgId, tag)); - nNotifyType = NOTIFY_TYPE_ADD; + ev->mChangedMsgIds.insert(msgId); } } else { RsMsgTags* tag = mit->second; @@ -1632,18 +1634,18 @@ bool p3MsgService::setMessageTag(const std::string &msgId, uint32_t tagId, bool tag->tagIds.push_back(tagId); /* keep the list sorted */ tag->tagIds.sort(); - nNotifyType = NOTIFY_TYPE_ADD; + ev->mChangedMsgIds.insert(msgId); } } else { if (tagId == 0) { /* remove all */ delete(tag); mMsgTags.erase(mit); - nNotifyType = NOTIFY_TYPE_DEL; + ev->mChangedMsgIds.insert(msgId); } else { if (lit != tag->tagIds.end()) { tag->tagIds.erase(lit); - nNotifyType = NOTIFY_TYPE_DEL; + ev->mChangedMsgIds.insert(msgId); if (tag->tagIds.empty()) { /* remove empty tag */ @@ -1657,10 +1659,10 @@ bool p3MsgService::setMessageTag(const std::string &msgId, uint32_t tagId, bool } /* UNLOCKED */ - if (nNotifyType) { + if (!ev->mChangedMsgIds.empty()) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGE_TAGS, nNotifyType); + rsEvents->postEvent(ev); return true; } @@ -1687,8 +1689,9 @@ bool p3MsgService::MessageToTrash(const std::string &mid, bool bTrash) std::map::iterator mit; uint32_t msgId = atoi(mid.c_str()); - bool bChanged = false; bool bFound = false; + auto pEvent = std::make_shared(); + pEvent->mMailStatusEventCode = RsMailStatusEventCode::MESSAGE_CHANGED; { RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/ @@ -1711,23 +1714,25 @@ bool p3MsgService::MessageToTrash(const std::string &mid, bool bTrash) if (bTrash) { if ((mi->msgFlags & RS_MSG_FLAGS_TRASH) == 0) { mi->msgFlags |= RS_MSG_FLAGS_TRASH; - bChanged = true; + pEvent->mChangedMsgIds.insert(std::to_string(mi->msgId)); } } else { if (mi->msgFlags & RS_MSG_FLAGS_TRASH) { mi->msgFlags &= ~RS_MSG_FLAGS_TRASH; - bChanged = true; + pEvent->mChangedMsgIds.insert(std::to_string(mi->msgId)); } } } } - if (bChanged) { + if (!pEvent->mChangedMsgIds.empty()) { IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/ checkOutgoingMessages(); - RsServer::notify()->notifyListChange(NOTIFY_LIST_MESSAGELIST,NOTIFY_TYPE_MOD); + if(rsEvents) { + rsEvents->postEvent(pEvent); + } } return bFound; @@ -2111,14 +2116,12 @@ void p3MsgService::notifyDataStatus( const GRouterMsgPropagationId& id, msgOutgoing.erase(it2); #endif - RsServer::notify()->notifyListChange( NOTIFY_LIST_MESSAGELIST, - NOTIFY_TYPE_ADD ); IndicateConfigChanged(); if(rsEvents) { auto pEvent = std::make_shared(); - pEvent->mMailStatusEventCode = RsMailStatusEventCode::NEW_MESSAGE; + pEvent->mMailStatusEventCode = RsMailStatusEventCode::MESSAGE_CHANGED; pEvent->mChangedMsgIds.insert(std::to_string(msg_id)); rsEvents->postEvent(pEvent); } @@ -2267,8 +2270,6 @@ bool p3MsgService::notifyGxsTransSendStatus( RsGxsTransId mailId, } } - RsServer::notify()->notifyListChange( NOTIFY_LIST_MESSAGELIST, - NOTIFY_TYPE_ADD ); IndicateConfigChanged(); } else if( status >= GxsTransSendStatus::FAILED_RECEIPT_SIGNATURE ) diff --git a/retroshare-gui/src/TorControl/AddOnionCommand.cpp b/libretroshare/src/tor/AddOnionCommand.cpp similarity index 74% rename from retroshare-gui/src/TorControl/AddOnionCommand.cpp rename to libretroshare/src/tor/AddOnionCommand.cpp index fb2047dc7..e06bb3ede 100644 --- a/retroshare-gui/src/TorControl/AddOnionCommand.cpp +++ b/libretroshare/src/tor/AddOnionCommand.cpp @@ -38,19 +38,19 @@ using namespace Tor; AddOnionCommand::AddOnionCommand(HiddenService *service) - : m_service(service) + : m_service(service), mSucceeded([](){}), mFailed([](int){}) { - Q_ASSERT(m_service); + assert(m_service); } bool AddOnionCommand::isSuccessful() const { - return statusCode() == 250 && m_errorMessage.isEmpty(); + return statusCode() == 250 && m_errorMessage.empty(); } -QByteArray AddOnionCommand::build() +ByteArray AddOnionCommand::build() { - QByteArray out("ADD_ONION"); + ByteArray out("ADD_ONION"); if (m_service->privateKey().isLoaded()) { out += " "; @@ -61,42 +61,44 @@ QByteArray AddOnionCommand::build() out += " NEW:BEST"; // this is v3, but without control of key type. Generates a RSA1024 key on older Tor versions. } - foreach (const HiddenService::Target &target, m_service->targets()) { + for(const HiddenService::Target& target: m_service->targets()) + { out += " Port="; - out += QByteArray::number(target.servicePort); + out += RsUtil::NumberToString(target.servicePort); out += ","; - out += target.targetAddress.toString().toLatin1(); + out += target.targetAddress; out += ":"; - out += QByteArray::number(target.targetPort); + out += RsUtil::NumberToString(target.targetPort); } out.append("\r\n"); return out; } -void AddOnionCommand::onReply(int statusCode, const QByteArray &data) +void AddOnionCommand::onReply(int statusCode, const ByteArray &data) { TorControlCommand::onReply(statusCode, data); if (statusCode != 250) { - m_errorMessage = QString::fromLatin1(data); + m_errorMessage = data.toString(); return; } - const QByteArray keyPrefix("PrivateKey="); - const QByteArray sidPrefix("ServiceID="); + const ByteArray keyPrefix("PrivateKey="); + const ByteArray sidPrefix("ServiceID="); - if(data.startsWith("ServiceID=")){ - QByteArray service_id = data.mid(sidPrefix.size()); + if(data.startsWith(sidPrefix)) + { + ByteArray service_id = data.mid(sidPrefix.size()); m_service->setServiceId(service_id); } - if (data.startsWith(keyPrefix)) { - - QByteArray keyData(data.mid(keyPrefix.size())); + if (data.startsWith(keyPrefix)) + { + ByteArray keyData(data.mid(keyPrefix.size())); CryptoKey key; if (!key.loadFromTorMessage(keyData)) { - m_errorMessage = QStringLiteral("Key structure check failed"); + m_errorMessage = "Key structure check failed"; return; } @@ -108,9 +110,9 @@ void AddOnionCommand::onFinished(int statusCode) { TorControlCommand::onFinished(statusCode); if (isSuccessful()) - emit succeeded(); + mSucceeded(); else - emit failed(statusCode); + mFailed(statusCode); } diff --git a/retroshare-gui/src/TorControl/AddOnionCommand.h b/libretroshare/src/tor/AddOnionCommand.h similarity index 78% rename from retroshare-gui/src/TorControl/AddOnionCommand.h rename to libretroshare/src/tor/AddOnionCommand.h index 7c0afaf5e..4b20de986 100644 --- a/retroshare-gui/src/TorControl/AddOnionCommand.h +++ b/libretroshare/src/tor/AddOnionCommand.h @@ -30,13 +30,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ADDONIONCOMMAND_H -#define ADDONIONCOMMAND_H +#pragma once #include "TorControlCommand.h" -#include -#include -#include namespace Tor { @@ -45,33 +41,26 @@ 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(); + ByteArray build(); - QString errorMessage() const { return m_errorMessage; } + std::string errorMessage() const { return m_errorMessage; } bool isSuccessful() const; -signals: - void succeeded(); - void failed(int code); + void set_succeeded_callback(const std::function& f) { mSucceeded=f;} + void set_failed_callback(const std::function& f) { mFailed=f;} protected: HiddenService *m_service; - QString m_errorMessage; + std::string m_errorMessage; - virtual void onReply(int statusCode, const QByteArray &data); + std::function mSucceeded; + std::function mFailed; + + virtual void onReply(int statusCode, const ByteArray &data); virtual void onFinished(int statusCode); }; } - -#endif // ADDONIONCOMMAND_H - diff --git a/retroshare-gui/src/TorControl/AuthenticateCommand.cpp b/libretroshare/src/tor/AuthenticateCommand.cpp similarity index 78% rename from retroshare-gui/src/TorControl/AuthenticateCommand.cpp rename to libretroshare/src/tor/AuthenticateCommand.cpp index 497c28f89..ab510c24b 100644 --- a/retroshare-gui/src/TorControl/AuthenticateCommand.cpp +++ b/libretroshare/src/tor/AuthenticateCommand.cpp @@ -38,27 +38,29 @@ AuthenticateCommand::AuthenticateCommand() { } -QByteArray AuthenticateCommand::build(const QByteArray &data) +ByteArray AuthenticateCommand::build(const ByteArray& data) { if (data.isNull()) - return QByteArray("AUTHENTICATE\r\n"); + return ByteArray("AUTHENTICATE\r\n"); - return QByteArray("AUTHENTICATE ") + data.toHex() + "\r\n"; + return ByteArray("AUTHENTICATE ") + data.toHex() + "\r\n"; } -void AuthenticateCommand::onReply(int statusCode, const QByteArray &data) +void AuthenticateCommand::onReply(int statusCode, const ByteArray &data) { TorControlCommand::onReply(statusCode, data); - m_statusMessage = QString::fromLatin1(data); + m_statusMessage = data.toString(); } 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); + m_statusMessage = "Authentication failed - incorrect password"; + } + else if (statusCode != 250) + { + if (m_statusMessage.empty()) + m_statusMessage = "Authentication failed (error " + RsUtil::NumberToString(statusCode) + ")"; } TorControlCommand::onFinished(statusCode); } diff --git a/retroshare-gui/src/TorControl/AuthenticateCommand.h b/libretroshare/src/tor/AuthenticateCommand.h similarity index 85% rename from retroshare-gui/src/TorControl/AuthenticateCommand.h rename to libretroshare/src/tor/AuthenticateCommand.h index 79c901d98..1d520ea22 100644 --- a/retroshare-gui/src/TorControl/AuthenticateCommand.h +++ b/libretroshare/src/tor/AuthenticateCommand.h @@ -30,9 +30,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AUTHENTICATECOMMAND_H -#define AUTHENTICATECOMMAND_H +#pragma once +#include "bytearray.h" #include "TorControlCommand.h" namespace Tor @@ -40,24 +40,20 @@ namespace Tor class AuthenticateCommand : public TorControlCommand { - Q_OBJECT - public: AuthenticateCommand(); - QByteArray build(const QByteArray &data = QByteArray()); + ByteArray build(const ByteArray& data = ByteArray()); bool isSuccessful() const { return statusCode() == 250; } - QString errorMessage() const { return m_statusMessage; } + std::string errorMessage() const { return m_statusMessage; } protected: - virtual void onReply(int statusCode, const QByteArray &data); + virtual void onReply(int statusCode, const ByteArray &data); virtual void onFinished(int statusCode); private: - QString m_statusMessage; + std::string m_statusMessage; }; } - -#endif // AUTHENTICATECOMMAND_H diff --git a/libretroshare/src/tor/CryptoKey.cpp b/libretroshare/src/tor/CryptoKey.cpp new file mode 100644 index 000000000..802caaf0c --- /dev/null +++ b/libretroshare/src/tor/CryptoKey.cpp @@ -0,0 +1,153 @@ +/* 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 "CryptoKey.h" + +#include +#include +#include + +#include "stdio.h" +#include "util/rsdebug.h" +#include "util/rsrandom.h" +#include "util/rsdir.h" +#include "retroshare/rsids.h" +#include "bytearray.h" + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) +{ + *p = r->p; + *q = r->q; +} +#define RSA_bits(o) (BN_num_bits((o)->n)) +#endif + +CryptoKey::CryptoKey() +{ +} + +CryptoKey::~CryptoKey() +{ + clear(); +} + +void CryptoKey::clear() +{ + key_data.clear(); +} + +bool CryptoKey::loadFromFile(const std::string& path) +{ + FILE *file = fopen(path.c_str(),"r"); + + if (!file) + { + RsWarn() << "Failed to open Tor key file " << path << ": errno = " << errno ; + return false; + } + + ByteArray data ; + int c; + while(EOF != (c=fgetc(file))) + data.push_back((unsigned char)c); + + fclose(file); + + if(data.startsWith("-----BEGIN RSA PRIVATE KEY-----")) + { + std::cerr << "Note: Reading/converting Tor v2 key format." << std::endl; + + // This to be compliant with old format. New format is oblivious to the type of key so we dont need a header + data = data.replace(ByteArray("-----BEGIN RSA PRIVATE KEY-----"),ByteArray()); + data = data.replace(ByteArray("-----END RSA PRIVATE KEY-----"),ByteArray()); + data = data.replace(ByteArray("\n"),ByteArray()); + data = data.replace(ByteArray("\t"),ByteArray()); + + data = ByteArray("RSA1024:")+data; + } + + std::cerr << "Have read the following key: " << std::endl; + std::cerr << data.toString() << std::endl; + + key_data = data; + + return true; +} + +bool CryptoKey::loadFromTorMessage(const ByteArray& b) +{ + // note: We should probably check the structure a bit more, for security. + + std::cerr << "Loading new key:" << std::endl; + + if(b.startsWith("RSA1024")) + std::cerr << " type: RSA-1024 (Tor v2)" << std::endl; + else if(b.startsWith("ED25519-V3")) + std::cerr << " type: ED25519-V3 (Tor v3)" << std::endl; + else if(b.indexOf(':')) + { + std::cerr << " unknown type, or bad syntax in key: \"" << b.left(b.indexOf(':')).toString() << "\". Not accepted." << std::endl; + return false; + } + + key_data = b; + return true; +} + +/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */ +ByteArray torControlHashedPassword(const ByteArray &password) +{ + ByteArray salt(8); + RsRandom::random_bytes(&salt[0],8); + + uint32_t count = ((uint32_t)16 + (96 & 15)) << ((96 >> 4) + 6); + + SHA_CTX hash; + SHA1_Init(&hash); + + ByteArray tmp = salt + password; + while (count) + { + int c = std::min((size_t)count, tmp.size()); + SHA1_Update(&hash, reinterpret_cast(tmp.data()), 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 ByteArray("16:") + salt.toHex().toUpper() + ByteArray("60") + ByteArray(md, 20).toHex().toUpper(); +} diff --git a/retroshare-gui/src/TorControl/SecureRNG.h b/libretroshare/src/tor/CryptoKey.h similarity index 75% rename from retroshare-gui/src/TorControl/SecureRNG.h rename to libretroshare/src/tor/CryptoKey.h index f3b2a4b64..ab6fb320f 100644 --- a/retroshare-gui/src/TorControl/SecureRNG.h +++ b/libretroshare/src/tor/CryptoKey.h @@ -30,22 +30,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SECURERNG_H -#define SECURERNG_H +#pragma once -#include +#include "bytearray.h" -class SecureRNG +class CryptoKey { public: - static bool seed(); + enum KeyType { + PrivateKey, + PublicKey + }; - static void random(char *buf, int size); - static QByteArray random(int size); + enum KeyFormat { + PEM, + DER + }; - static QByteArray randomPrintable(int length); - static unsigned randomInt(unsigned max); - static quint64 randomInt64(quint64 max); + CryptoKey(); + ~CryptoKey(); + + bool loadFromFile(const std::string &path); + void clear(); + + const ByteArray bytes() const { return key_data; } + bool loadFromTorMessage(const ByteArray& b); + bool isLoaded() const { return !key_data.isNull(); } + +private: + ByteArray key_data; }; -#endif // SECURERNG_H +ByteArray torControlHashedPassword(const ByteArray &password); diff --git a/retroshare-gui/src/TorControl/GetConfCommand.cpp b/libretroshare/src/tor/GetConfCommand.cpp similarity index 60% rename from retroshare-gui/src/TorControl/GetConfCommand.cpp rename to libretroshare/src/tor/GetConfCommand.cpp index 933def562..02f903b4a 100644 --- a/retroshare-gui/src/TorControl/GetConfCommand.cpp +++ b/libretroshare/src/tor/GetConfCommand.cpp @@ -32,7 +32,6 @@ #include "GetConfCommand.h" #include "StrUtil.h" -#include using namespace Tor; @@ -41,25 +40,25 @@ GetConfCommand::GetConfCommand(Type t) { } -QByteArray GetConfCommand::build(const QByteArray &key) +ByteArray GetConfCommand::build(const std::string &key) { - return build(QList() << key); + return build(std::list { key } ); } -QByteArray GetConfCommand::build(const QList &keys) +ByteArray GetConfCommand::build(const std::list &keys) { - QByteArray out; + ByteArray out; if (type == GetConf) { out = "GETCONF"; } else if (type == GetInfo) { out = "GETINFO"; } else { - Q_ASSERT(false); + assert(false); return out; } - foreach (const QByteArray &key, keys) { - out.append(' '); + for(const ByteArray &key: keys) { + out.push_back(' '); out.append(key); } @@ -67,49 +66,29 @@ QByteArray GetConfCommand::build(const QList &keys) return out; } -void GetConfCommand::onReply(int statusCode, const QByteArray &data) +void GetConfCommand::onReply(int statusCode, const ByteArray &data) { TorControlCommand::onReply(statusCode, data); if (statusCode != 250) return; int kep = data.indexOf('='); - QString key = QString::fromLatin1(data.mid(0, kep)); - QVariant value; + std::string key = data.mid(0, kep).toString(); + std::string value; if (kep >= 0) - value = QString::fromLatin1(unquotedString(data.mid(kep + 1))); + value = unquotedString(data.mid(kep + 1)).toString(); 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); - } + m_results[key].push_back(value); } -void GetConfCommand::onDataLine(const QByteArray &data) +void GetConfCommand::onDataLine(const ByteArray &data) { - if (m_lastKey.isEmpty()) { - qWarning() << "torctrl: Unexpected data line in GetConf command"; + if (m_lastKey.empty()) { + RsWarn() << "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); - } + m_results[m_lastKey].push_back(data.toString()); } void GetConfCommand::onDataFinished() @@ -117,8 +96,13 @@ void GetConfCommand::onDataFinished() m_lastKey.clear(); } -QVariant GetConfCommand::get(const QByteArray &key) const +std::list GetConfCommand::get(const std::string& key) const { - return m_results.value(QString::fromLatin1(key)); + auto it = m_results.find(key); + + if(it != m_results.end()) + return it->second; + else + return std::list(); } diff --git a/retroshare-gui/src/TorControl/GetConfCommand.h b/libretroshare/src/tor/GetConfCommand.h similarity index 76% rename from retroshare-gui/src/TorControl/GetConfCommand.h rename to libretroshare/src/tor/GetConfCommand.h index 0de97d1b7..d3ca3bfc3 100644 --- a/retroshare-gui/src/TorControl/GetConfCommand.h +++ b/libretroshare/src/tor/GetConfCommand.h @@ -30,23 +30,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GETCONFCOMMAND_H -#define GETCONFCOMMAND_H +#pragma once +#include #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, @@ -56,22 +49,20 @@ public: GetConfCommand(Type type); - QByteArray build(const QByteArray &key); - QByteArray build(const QList &keys); + ByteArray build(const std::string &key); + ByteArray build(const std::list &keys); - const QVariantMap &results() const { return m_results; } - QVariant get(const QByteArray &key) const; + const std::map > &results() const { return m_results; } + std::list get(const std::string &key) const; protected: - virtual void onReply(int statusCode, const QByteArray &data); - virtual void onDataLine(const QByteArray &data); + virtual void onReply(int statusCode, const ByteArray &data); + virtual void onDataLine(const ByteArray &data); virtual void onDataFinished(); private: - QVariantMap m_results; - QString m_lastKey; + std::map > m_results; + std::string m_lastKey; }; } - -#endif // GETCONFCOMMAND_H diff --git a/retroshare-gui/src/TorControl/HiddenService.cpp b/libretroshare/src/tor/HiddenService.cpp similarity index 53% rename from retroshare-gui/src/TorControl/HiddenService.cpp rename to libretroshare/src/tor/HiddenService.cpp index b2b58626a..a6b083eae 100644 --- a/retroshare-gui/src/TorControl/HiddenService.cpp +++ b/libretroshare/src/tor/HiddenService.cpp @@ -32,34 +32,32 @@ #include "HiddenService.h" #include "TorControl.h" -#include "TorSocket.h" #include "CryptoKey.h" -#include "Useful.h" -#include -#include -#include -#include +#include "util/rsdir.h" + +#include using namespace Tor; -HiddenService::HiddenService(QObject *parent) - : QObject(parent), m_status(NotCreated) +HiddenService::HiddenService(HiddenServiceClient *client) + : m_status(NotCreated), m_client(client) { } -HiddenService::HiddenService(const QString &path, QObject *parent) - : QObject(parent), m_dataPath(path), m_status(NotCreated) +HiddenService::HiddenService(HiddenServiceClient *client,const std::string& path) + : m_dataPath(path), m_status(NotCreated), m_client(client) { /* Set the initial status and, if possible, load the hostname */ - if (QDir(m_dataPath).exists(QLatin1String("private_key"))) { + if(RsDirUtil::fileExists(m_dataPath + "/private_key")) + { loadPrivateKey(); - if (!m_hostname.isEmpty()) + if (!m_hostname.empty()) m_status = Offline; } } -HiddenService::HiddenService(const CryptoKey &privateKey, const QString &path, QObject *parent) - : QObject(parent), m_dataPath(path), m_status(NotCreated) +HiddenService::HiddenService(HiddenServiceClient *client,const CryptoKey &privateKey, const std::string &path) + : m_dataPath(path), m_status(NotCreated), m_client(client) { setPrivateKey(privateKey); m_status = Offline; @@ -73,69 +71,89 @@ void HiddenService::setStatus(Status newStatus) Status old = m_status; m_status = newStatus; - emit statusChanged(m_status, old); + if(m_client) + m_client->hiddenServiceStatusChanged(m_status,old); //emit statusChanged(m_status, old); if (m_status == Online) - emit serviceOnline(); + if(m_client) + m_client->hiddenServiceOnline(); //emit serviceOnline(); } void HiddenService::addTarget(const Target &target) { - m_targets.append(target); + m_targets.push_back(target); } -void HiddenService::addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort) +void HiddenService::addTarget(uint16_t servicePort, std::string targetAddress, uint16_t targetPort) { Target t = { targetAddress, servicePort, targetPort }; - m_targets.append(t); + m_targets.push_back(t); } -void HiddenService::setServiceId(const QByteArray& sid) +void HiddenService::setServiceId(const ByteArray& sid) { m_service_id = sid; - m_hostname = sid + ".onion"; + m_hostname = sid.toString() + ".onion"; - emit hostnameChanged(); + if(m_client) + m_client->hiddenServiceHostnameChanged(); // emit hostnameChanged(); } void HiddenService::setPrivateKey(const CryptoKey &key) { if (m_privateKey.isLoaded()) { - BUG() << "Cannot change the private key on an existing HiddenService"; + RsErr() << "Cannot change the private key on an existing HiddenService"; return; } -#ifdef TO_REMOVE - if (!key.isPrivate()) { - BUG() << "Cannot create a hidden service with a public key"; - return; - } -#endif - m_privateKey = key; - emit privateKeyChanged(); + if(m_client) + m_client->hiddenServicePrivateKeyChanged(); //emit privateKeyChanged(); } -void HiddenService::loadPrivateKey() +bool HiddenService::loadPrivateKey() { - if (m_privateKey.isLoaded() || m_dataPath.isEmpty()) - return; + if (m_privateKey.isLoaded() || m_dataPath.empty()) + return false; - bool ok = m_privateKey.loadFromFile(m_dataPath + QLatin1String("/private_key")); + bool ok = m_privateKey.loadFromFile(m_dataPath + "/private_key"); if (!ok) { - qWarning() << "Failed to load hidden service key"; - return; + RsWarn() << "Failed to load hidden service key"; + return false; } - emit privateKeyChanged(); + // Also load the onion address stored in "hostname" file. This is not needed, except for early display + // of the onion address, since the onion address will be re-computed by Tor (to the same value) when the + // service is published. + + std::ifstream i((m_dataPath + "/hostname").c_str()); + + if(i) + { + std::string s; + i >> s; + if(ByteArray(s).endsWith(ByteArray(".onion"))) + { + m_hostname = s; + m_service_id = s.substr(0,s.length() - std::string(".onion").length()); + + RsDbg() << "Read existing hostname: " << m_hostname; + } + i.close(); + } + + if(m_client) + m_client->hiddenServicePrivateKeyChanged(); // emit privateKeyChanged(); + + return true; } void HiddenService::servicePublished() { loadPrivateKey(); - if (m_hostname.isEmpty()) { + if (m_hostname.empty()) { std::cerr << "Failed to read hidden service hostname" << std::endl; return; } diff --git a/retroshare-gui/src/TorControl/HiddenService.h b/libretroshare/src/tor/HiddenService.h similarity index 59% rename from retroshare-gui/src/TorControl/HiddenService.h rename to libretroshare/src/tor/HiddenService.h index 20fa1d851..604d8083f 100644 --- a/retroshare-gui/src/TorControl/HiddenService.h +++ b/libretroshare/src/tor/HiddenService.h @@ -30,31 +30,35 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef HIDDENSERVICE_H -#define HIDDENSERVICE_H +#pragma once -#include -#include -#include #include "CryptoKey.h" +#include "bytearray.h" namespace Tor { -class TorSocket; +// This class is used to receive synchroneous notifications from the hidden service. +// Each client should implement its own notification handling. -class HiddenService : public QObject +class HiddenServiceClient { - Q_OBJECT - Q_DISABLE_COPY(HiddenService) +public: + virtual void hiddenServiceStatusChanged(int /* newStatus */, int /* oldStatus */) =0; + virtual void hiddenServiceOnline() =0; + virtual void hiddenServicePrivateKeyChanged() =0; + virtual void hiddenServiceHostnameChanged() =0; +}; - friend class TorControlPrivate; +class HiddenService +{ + friend class TorControl; public: struct Target { - QHostAddress targetAddress; - quint16 servicePort, targetPort; + std::string targetAddress; + uint16_t servicePort, targetPort; }; enum Status @@ -64,45 +68,42 @@ public: Online /* Published */ }; - HiddenService(QObject *parent = 0); - HiddenService(const QString &dataPath, QObject *parent = 0); - HiddenService(const CryptoKey &privateKey, const QString &dataPath = QString(), QObject *parent = 0); + HiddenService(HiddenServiceClient *client); + HiddenService(HiddenServiceClient *client, const std::string &dataPath); + HiddenService(HiddenServiceClient *client, const CryptoKey &privateKey, const std::string &dataPath = std::string()); Status status() const { return m_status; } - const QString& hostname() const { return m_hostname; } - const QString serviceId() const { return QString(m_service_id); } - const QString& dataPath() const { return m_dataPath; } + const std::string& hostname() const { return m_hostname; } + const std::string serviceId() const { return m_service_id.toString(); } + const std::string& dataPath() const { return m_dataPath; } CryptoKey privateKey() { return m_privateKey; } void setPrivateKey(const CryptoKey &privateKey); - void setServiceId(const QByteArray& sid); + void setServiceId(const ByteArray &sid); - const QList &targets() const { return m_targets; } + const std::list &targets() const { return m_targets; } void addTarget(const Target &target); - void addTarget(quint16 servicePort, QHostAddress targetAddress, quint16 targetPort); + void addTarget(uint16_t servicePort, std::string targetAddress, uint16_t targetPort); -signals: - void statusChanged(int newStatus, int oldStatus); - void serviceOnline(); - void privateKeyChanged(); - void hostnameChanged(); - -private slots: void servicePublished(); private: - QString m_dataPath; - QList m_targets; - QString m_hostname; + std::string m_dataPath; + std::list m_targets; + std::string m_hostname; Status m_status; CryptoKey m_privateKey; - QByteArray m_service_id; + ByteArray m_service_id; - void loadPrivateKey(); + bool loadPrivateKey(); void setStatus(Status newStatus); + + HiddenServiceClient *m_client; + + // make the object non copyable + HiddenService(const HiddenService&) {} + HiddenService& operator=(const HiddenService&) { return *this ; } }; } - -#endif // HIDDENSERVICE_H diff --git a/retroshare-gui/src/TorControl/PendingOperation.cpp b/libretroshare/src/tor/PendingOperation.cpp similarity index 77% rename from retroshare-gui/src/TorControl/PendingOperation.cpp rename to libretroshare/src/tor/PendingOperation.cpp index 4a11f3e75..ec6a9b6fd 100644 --- a/retroshare-gui/src/TorControl/PendingOperation.cpp +++ b/libretroshare/src/tor/PendingOperation.cpp @@ -30,10 +30,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include "PendingOperation.h" -PendingOperation::PendingOperation(QObject *parent) - : QObject(parent), m_finished(false) +PendingOperation::PendingOperation() + : m_finished(false),mFinishedCallback([](){}), mSuccessCallback([](){}),mErrorCallback([](const std::string&){}) { } @@ -44,41 +45,44 @@ bool PendingOperation::isFinished() const bool PendingOperation::isSuccess() const { - return m_finished && m_errorMessage.isNull(); + return m_finished && m_errorMessage.empty(); } bool PendingOperation::isError() const { - return m_finished && !m_errorMessage.isNull(); + return m_finished && !m_errorMessage.empty(); } -QString PendingOperation::errorMessage() const +std::string PendingOperation::errorMessage() const { return m_errorMessage; } -void PendingOperation::finishWithError(const QString &message) +void PendingOperation::finishWithError(const std::string &message) { - if (message.isEmpty()) - m_errorMessage = QStringLiteral("Unknown Error"); + if (message.empty()) + m_errorMessage = "Unknown Error"; m_errorMessage = message; if (!m_finished) { m_finished = true; - emit finished(); - emit error(m_errorMessage); + + mErrorCallback(m_errorMessage); + mFinishedCallback(); } } void PendingOperation::finishWithSuccess() { - Q_ASSERT(m_errorMessage.isNull()); + assert(m_errorMessage.empty()); if (!m_finished) { m_finished = true; - emit finished(); + if (isSuccess()) - emit success(); + mSuccessCallback(); + + mFinishedCallback(); } } diff --git a/retroshare-gui/src/TorControl/PendingOperation.h b/libretroshare/src/tor/PendingOperation.h similarity index 73% rename from retroshare-gui/src/TorControl/PendingOperation.h rename to libretroshare/src/tor/PendingOperation.h index 8f776a8d7..cec170f9e 100644 --- a/retroshare-gui/src/TorControl/PendingOperation.h +++ b/libretroshare/src/tor/PendingOperation.h @@ -30,10 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PENDINGOPERATION_H -#define PENDINGOPERATION_H +#pragma once -#include +#include +#include /* Represents an asynchronous operation for reporting status * @@ -48,40 +48,27 @@ * PendingOperation will emit finished() and one of success() or * error() when completed. */ -class PendingOperation : public QObject +class PendingOperation { - 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); + PendingOperation(); bool isFinished() const; bool isSuccess() const; bool isError() const; - QString errorMessage() const; + std::string 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 finishWithError(const std::string &errorMessage); void finishWithSuccess(); + void set_finished_callback(const std::function& f) { mFinishedCallback = f; } private: bool m_finished; - QString m_errorMessage; + std::string m_errorMessage; + + std::function mFinishedCallback; + std::function mSuccessCallback; + std::function mErrorCallback; + }; -Q_DECLARE_METATYPE(PendingOperation*) - -#endif diff --git a/retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp b/libretroshare/src/tor/ProtocolInfoCommand.cpp similarity index 78% rename from retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp rename to libretroshare/src/tor/ProtocolInfoCommand.cpp index a365a5c4d..cdb7d648b 100644 --- a/retroshare-gui/src/TorControl/ProtocolInfoCommand.cpp +++ b/libretroshare/src/tor/ProtocolInfoCommand.cpp @@ -33,21 +33,20 @@ #include "ProtocolInfoCommand.h" #include "TorControl.h" #include "StrUtil.h" -#include using namespace Tor; ProtocolInfoCommand::ProtocolInfoCommand(TorControl *m) - : manager(m) + : manager(m),m_authMethods(0) { } -QByteArray ProtocolInfoCommand::build() +ByteArray ProtocolInfoCommand::build() { - return QByteArray("PROTOCOLINFO 1\r\n"); + return ByteArray("PROTOCOLINFO 1\r\n"); } -void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data) +void ProtocolInfoCommand::onReply(int statusCode, const ByteArray &data) { TorControlCommand::onReply(statusCode, data); if (statusCode != 250) @@ -55,14 +54,15 @@ void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data) if (data.startsWith("AUTH ")) { - QList tokens = splitQuotedStrings(data.mid(5), ' '); + std::list tokens = splitQuotedStrings(data.mid(5), ' '); - foreach (QByteArray token, tokens) + for(ByteArray token: tokens) { if (token.startsWith("METHODS=")) { - QList textMethods = unquotedString(token.mid(8)).split(','); - for (QList::Iterator it = textMethods.begin(); it != textMethods.end(); ++it) + std::list textMethods = unquotedString(token.mid(8)).split(','); + + for (std::list::iterator it = textMethods.begin(); it != textMethods.end(); ++it) { if (*it == "NULL") m_authMethods |= AuthNull; @@ -74,12 +74,12 @@ void ProtocolInfoCommand::onReply(int statusCode, const QByteArray &data) } else if (token.startsWith("COOKIEFILE=")) { - m_cookieFile = QString::fromLatin1(unquotedString(token.mid(11))); + m_cookieFile = unquotedString(token.mid(11)).toString(); } } } else if (data.startsWith("VERSION Tor=")) { - m_torVersion = QString::fromLatin1(unquotedString(data.mid(12, data.indexOf(' ', 12)))); + m_torVersion = unquotedString(data.mid(12, data.indexOf(' ', 12))).toString(); } } diff --git a/retroshare-gui/src/TorControl/ProtocolInfoCommand.h b/libretroshare/src/tor/ProtocolInfoCommand.h similarity index 74% rename from retroshare-gui/src/TorControl/ProtocolInfoCommand.h rename to libretroshare/src/tor/ProtocolInfoCommand.h index 7789cfefd..b90e2fe7c 100644 --- a/retroshare-gui/src/TorControl/ProtocolInfoCommand.h +++ b/libretroshare/src/tor/ProtocolInfoCommand.h @@ -30,11 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PROTOCOLINFOCOMMAND_H -#define PROTOCOLINFOCOMMAND_H +#pragma once #include "TorControlCommand.h" -#include +#include "retroshare/rsflags.h" namespace Tor { @@ -43,36 +42,31 @@ class TorControl; class ProtocolInfoCommand : public TorControlCommand { - Q_OBJECT - Q_DISABLE_COPY(ProtocolInfoCommand) - public: - enum AuthMethod + enum { - AuthUnknown = 0, - AuthNull = 0x1, + AuthUnknown = 0x0, + AuthNull = 0x1, AuthHashedPassword = 0x2, - AuthCookie = 0x4 + AuthCookie = 0x4 }; - Q_DECLARE_FLAGS(AuthMethods, AuthMethod) + typedef uint8_t AuthMethod; ProtocolInfoCommand(TorControl *manager); - QByteArray build(); + ByteArray build(); - AuthMethods authMethods() const { return m_authMethods; } - QString torVersion() const { return m_torVersion; } - QString cookieFile() const { return m_cookieFile; } + AuthMethod authMethods() const { return m_authMethods; } + std::string torVersion() const { return m_torVersion; } + std::string cookieFile() const { return m_cookieFile; } protected: - virtual void onReply(int statusCode, const QByteArray &data); + virtual void onReply(int statusCode, const ByteArray &data); private: TorControl *manager; - AuthMethods m_authMethods; - QString m_torVersion; - QString m_cookieFile; + AuthMethod m_authMethods; + std::string m_torVersion; + std::string m_cookieFile; }; } - -#endif // PROTOCOLINFOCOMMAND_H diff --git a/retroshare-gui/src/TorControl/SetConfCommand.cpp b/libretroshare/src/tor/SetConfCommand.cpp similarity index 62% rename from retroshare-gui/src/TorControl/SetConfCommand.cpp rename to libretroshare/src/tor/SetConfCommand.cpp index f13a9fdef..463305731 100644 --- a/retroshare-gui/src/TorControl/SetConfCommand.cpp +++ b/libretroshare/src/tor/SetConfCommand.cpp @@ -36,7 +36,7 @@ using namespace Tor; SetConfCommand::SetConfCommand() - : m_resetMode(false) + : m_resetMode(false), mConfSucceeded([](){}), mConfFailed([](int){}) { } @@ -50,57 +50,44 @@ bool SetConfCommand::isSuccessful() const return statusCode() == 250; } -QByteArray SetConfCommand::build(const QByteArray &key, const QByteArray &value) +ByteArray SetConfCommand::build(const std::string &key, const std::string &value) { - return build(QList >() << qMakePair(key, value)); + return build(std::list > { std::make_pair(key, value) } ); } -QByteArray SetConfCommand::build(const QVariantMap &data) +ByteArray SetConfCommand::build(const std::list >& data) { - QList > out; + ByteArray out(m_resetMode ? "RESETCONF" : "SETCONF"); - for (QVariantMap::ConstIterator it = data.begin(); it != data.end(); it++) { - QByteArray key = it.key().toLatin1(); + for (auto& p:data) + { + out += " " ; + out += p.first; - 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())); + if (!p.second.empty()) + { + out += "=" ; + out += quotedString(p.second); } } - 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) +void SetConfCommand::onReply(int statusCode, const ByteArray &data) { TorControlCommand::onReply(statusCode, data); if (statusCode != 250) - m_errorMessage = QString::fromLatin1(data); + m_errorMessage = data.toString(); } void SetConfCommand::onFinished(int statusCode) { TorControlCommand::onFinished(statusCode); if (isSuccessful()) - emit setConfSucceeded(); + mConfSucceeded(); else - emit setConfFailed(statusCode); + mConfFailed(statusCode); } diff --git a/retroshare-gui/src/TorControl/SetConfCommand.h b/libretroshare/src/tor/SetConfCommand.h similarity index 72% rename from retroshare-gui/src/TorControl/SetConfCommand.h rename to libretroshare/src/tor/SetConfCommand.h index 5bdcb9329..bf7713038 100644 --- a/retroshare-gui/src/TorControl/SetConfCommand.h +++ b/libretroshare/src/tor/SetConfCommand.h @@ -30,49 +30,39 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SETCONFCOMMAND_H -#define SETCONFCOMMAND_H +#pragma once +#include #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); + ByteArray build(const std::string &key, const std::string &value); + ByteArray build(const std::list > &data); - QString errorMessage() const { return m_errorMessage; } + std::string errorMessage() const { return m_errorMessage; } bool isSuccessful() const; -signals: - void setConfSucceeded(); - void setConfFailed(int code); + void set_ConfSucceeded_callback(const std::function& f) { mConfSucceeded=f; } + void set_ConfFailed_callback (const std::function& f){ mConfFailed=f; } protected: - QString m_errorMessage; + std::string m_errorMessage; bool m_resetMode; - virtual void onReply(int statusCode, const QByteArray &data); + std::function mConfSucceeded; + std::function mConfFailed; + + virtual void onReply(int statusCode, const ByteArray &data); virtual void onFinished(int statusCode); }; } - -#endif // SETCONFCOMMAND_H diff --git a/retroshare-gui/src/TorControl/StrUtil.cpp b/libretroshare/src/tor/StrUtil.cpp similarity index 78% rename from retroshare-gui/src/TorControl/StrUtil.cpp rename to libretroshare/src/tor/StrUtil.cpp index 81de151b0..558013fef 100644 --- a/retroshare-gui/src/TorControl/StrUtil.cpp +++ b/libretroshare/src/tor/StrUtil.cpp @@ -32,14 +32,14 @@ #include "StrUtil.h" -QByteArray quotedString(const QByteArray &string) +ByteArray quotedString(const ByteArray &string) { - QByteArray out; + ByteArray out; out.reserve(string.size() * 2); - out.append('"'); + out.push_back('"'); - for (int i = 0; i < string.size(); ++i) + for (ByteArray::size_type i = 0; i < string.size(); ++i) { switch (string[i]) { @@ -50,48 +50,48 @@ QByteArray quotedString(const QByteArray &string) out.append("\\\\"); break; default: - out.append(string[i]); + out.push_back(string[i]); break; } } - out.append('"'); + out.push_back('"'); return out; } -QByteArray unquotedString(const QByteArray &string) +ByteArray unquotedString(const ByteArray& string) { if (string.size() < 2 || string[0] != '"') return string; - QByteArray out; + ByteArray out; out.reserve(string.size() - 2); - for (int i = 1; i < string.size(); ++i) + for (ByteArray::size_type i = 1; i < string.size(); ++i) { switch (string[i]) { case '\\': if (++i < string.size()) - out.append(string[i]); + out.push_back(string[i]); break; case '"': return out; default: - out.append(string[i]); + out.push_back(string[i]); } } return out; } -QList splitQuotedStrings(const QByteArray &input, char separator) +std::list splitQuotedStrings(const ByteArray &input, char separator) { - QList out; + std::list out; bool inquote = false; - int start = 0; + ByteArray::size_type start = 0; - for (int i = 0; i < input.size(); ++i) + for (ByteArray::size_type i = 0; i < input.size(); ++i) { switch (input[i]) { @@ -106,13 +106,13 @@ QList splitQuotedStrings(const QByteArray &input, char separator) if (!inquote && input[i] == separator) { - out.append(input.mid(start, i - start)); + out.push_back(input.mid(start, i - start)); start = i+1; } } if (start < input.size()) - out.append(input.mid(start)); + out.push_back(input.mid(start)); return out; } diff --git a/retroshare-gui/src/TorControl/StrUtil.h b/libretroshare/src/tor/StrUtil.h similarity index 89% rename from retroshare-gui/src/TorControl/StrUtil.h rename to libretroshare/src/tor/StrUtil.h index c86d2c6ec..8f986a9a8 100644 --- a/retroshare-gui/src/TorControl/StrUtil.h +++ b/libretroshare/src/tor/StrUtil.h @@ -33,14 +33,15 @@ #ifndef STRINGUTIL_H #define STRINGUTIL_H -#include -#include +#include -QByteArray quotedString(const QByteArray &string); +#include "bytearray.h" + +ByteArray quotedString(const ByteArray &string); /* Return the unquoted contents of a string, either until an end quote or an unescaped separator character. */ -QByteArray unquotedString(const QByteArray &string); +ByteArray unquotedString(const ByteArray &string); -QList splitQuotedStrings(const QByteArray &input, char separator); +std::list splitQuotedStrings(const ByteArray& input, char separator); #endif // STRINGUTIL_H diff --git a/libretroshare/src/tor/TorControl.cpp b/libretroshare/src/tor/TorControl.cpp new file mode 100644 index 000000000..9b456036b --- /dev/null +++ b/libretroshare/src/tor/TorControl.cpp @@ -0,0 +1,793 @@ +/* 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 +#include "util/rsdir.h" + +#include "retroshare/rstor.h" +#include "TorControl.h" +#include "TorControlSocket.h" +#include "HiddenService.h" +#include "ProtocolInfoCommand.h" +#include "AuthenticateCommand.h" +#include "SetConfCommand.h" +#include "GetConfCommand.h" +#include "AddOnionCommand.h" +#include "StrUtil.h" +#include "PendingOperation.h" + +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; + +TorControl::TorControl() + : mControlPort(0),mSocksPort(0),mStatus(NotConnected), mTorStatus(TorUnknown),mHasOwnership(false) +{ + mSocket = new TorControlSocket(this); +} + +TorControl::~TorControl() +{ + delete(mSocket); +} + +static RsTorConnectivityStatus torConnectivityStatus(Tor::TorControl::Status t) +{ + switch(t) + { + default: + case TorControl::Error: return RsTorConnectivityStatus::ERROR; + case TorControl::NotConnected: return RsTorConnectivityStatus::NOT_CONNECTED; + case TorControl::Connecting: return RsTorConnectivityStatus::CONNECTING; + case TorControl::SocketConnected: return RsTorConnectivityStatus::SOCKET_CONNECTED; + case TorControl::Authenticating: return RsTorConnectivityStatus::AUTHENTICATING; + case TorControl::Authenticated: return RsTorConnectivityStatus::AUTHENTICATED; + case TorControl::HiddenServiceReady: return RsTorConnectivityStatus::HIDDEN_SERVICE_READY; + case TorControl::Unknown: return RsTorConnectivityStatus::UNKNOWN; + } +} +static RsTorStatus torStatus(Tor::TorControl::TorStatus t) +{ + switch(t) + { + default: + case TorControl::TorUnknown: return RsTorStatus::UNKNOWN; + case TorControl::TorOffline: return RsTorStatus::OFFLINE; + case TorControl::TorReady: return RsTorStatus::READY; + } +} + +void TorControl::setStatus(TorControl::Status n) +{ + if (n == mStatus) + return; + + TorControl::Status old = mStatus; + mStatus = n; + + if (old == TorControl::Error) + mErrorMessage.clear(); + + std::cerr << "Setting status to s=" << mStatus << " val=" << (int)torConnectivityStatus(mStatus) << std::endl; + + if(rsEvents) + { + auto ev = std::make_shared(); + + ev->mTorManagerEventType = RsTorManagerEventCode::TOR_STATUS_CHANGED; + ev->mTorStatus = ::torStatus(mTorStatus); + ev->mTorConnectivityStatus = torConnectivityStatus(mStatus); + + rsEvents->sendEvent(ev); + } + mStatusChanged_callback(mStatus, old); +} + +void TorControl::setTorStatus(TorControl::TorStatus n) +{ + if (n == mTorStatus) + return; + + RsDbg() << "Setting TorStatus=" << n ; + mTorStatus = n; + + if(rsEvents) + { + auto ev = std::make_shared(); + + ev->mTorManagerEventType = RsTorManagerEventCode::TOR_STATUS_CHANGED; + ev->mTorStatus = ::torStatus(mTorStatus); + ev->mTorConnectivityStatus = torConnectivityStatus(mStatus); + + rsEvents->sendEvent(ev); + } +} + +void TorControl::setError(const std::string &message) +{ + mErrorMessage = message; + setStatus(TorControl::Error); + + RsWarn() << "torctrl: Error:" << mErrorMessage; +} + +TorControl::Status TorControl::status() const +{ + return mStatus; +} + +TorControl::TorStatus TorControl::torStatus() const +{ + return mTorStatus; +} + +std::string TorControl::torVersion() const +{ + return mTorVersion; +} + +std::string TorControl::errorMessage() const +{ + return mErrorMessage; +} + +bool TorControl::hasConnectivity() const +{ + return torStatus() == TorReady && !mSocksAddress.empty(); +} + +std::string TorControl::socksAddress() const +{ + return mSocksAddress; +} + +uint16_t TorControl::socksPort() const +{ + return mSocksPort; +} + +std::list TorControl::hiddenServices() const +{ + return mServices; +} + +std::map TorControl::bootstrapStatus() const +{ + return mBootstrapStatus; +} + +void TorControl::setAuthPassword(const ByteArray &password) +{ + mAuthPassword = password; +} + +void TorControl::connect(const std::string &address, uint16_t port) +{ + if (status() > Connecting) + { + torCtrlDebug() << "Ignoring TorControl::connect due to existing connection" << std::endl; + return; + } + + mTorAddress = address; + mControlPort = port; + setTorStatus(TorUnknown); + + if(mSocket->isRunning()) + mSocket->fullstop(); + + setStatus(Connecting); + + if(mSocket->connectToHost(address, port)) + { + setStatus(SocketConnected); + setTorStatus(TorOffline); // connected and running, but not yet ready + } +} + +void TorControl::reconnect() +{ + assert(!mTorAddress.empty() && mControlPort); + + if (mTorAddress.empty() || !mControlPort || status() >= Connecting) + return; + + setStatus(Connecting); + mSocket->connectToHost(mTorAddress, mControlPort); +} + +void TorControl::authenticateReply(TorControlCommand *sender) +{ + AuthenticateCommand *command = dynamic_cast(sender); + assert(command); + assert(mStatus == TorControl::Authenticating); + if (!command) + return; + + if (!command->isSuccessful()) { + setError(command->errorMessage()); + return; + } + + torCtrlDebug() << "torctrl: Authentication successful" << std::endl; + setStatus(TorControl::Authenticated); + + TorControlCommand *clientEvents = new TorControlCommand; + clientEvents->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data);}); + + mSocket->registerEvent(ByteArray("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 (mHasOwnership) + saveConfiguration(); +} + + +void TorControl::authenticate() +{ + assert(mStatus == TorControl::SocketConnected); + + setStatus(TorControl::Authenticating); + torCtrlDebug() << "torctrl: Connected socket; querying information for authentication" << std::endl; + + ProtocolInfoCommand *command = new ProtocolInfoCommand(this); + + command->set_finished_callback( [this](TorControlCommand *sender) { protocolInfoReply(sender); }); + command->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data); }); + + mSocket->sendCommand(command, command->build()); +} + +void TorControl::socketDisconnected() +{ + /* Clear some internal state */ + mTorVersion.clear(); + mSocksAddress.clear(); + mSocksPort = 0; + setTorStatus(TorControl::TorUnknown); + + /* This emits the disconnected() signal as well */ + setStatus(TorControl::NotConnected); +} + +void TorControl::socketError(const std::string& s) +{ + setError("Connection failed: " + s); +} + +void TorControl::protocolInfoReply(TorControlCommand *sender) +{ + ProtocolInfoCommand *info = dynamic_cast(sender); + if (!info) + return; + + mTorVersion = info->torVersion(); + + if (mStatus == TorControl::Authenticating) + { + AuthenticateCommand *auth = new AuthenticateCommand; + + auth->set_finished_callback( [this](TorControlCommand *sender) { authenticateReply(sender); }); + + ByteArray data; + ProtocolInfoCommand::AuthMethod methods = info->authMethods(); + + if(methods & ProtocolInfoCommand::AuthNull) + { + torCtrlDebug() << "torctrl: Using null authentication" << std::endl; + data = auth->build(); + } + else if ((methods & ProtocolInfoCommand::AuthCookie) && !info->cookieFile().empty()) + { + std::string cookieFile = info->cookieFile(); + std::string cookieError; + torCtrlDebug() << "torctrl: Using cookie authentication with file" << cookieFile << std::endl; + + FILE *f = fopen(cookieFile.c_str(),"r"); + + if(f) + { + std::string cookie; + char c; + while((c=getc(f))!=EOF) + cookie += c; + fclose(f); + + /* 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 = "Unexpected file size"; + } + else + cookieError = "Cannot open file " + cookieFile + ". errno=" + RsUtil::NumberToString(errno); + + if (!cookieError.empty() || 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 & ProtocolInfoCommand::AuthHashedPassword) && !mAuthPassword.empty()) + { + torCtrlDebug() << "torctrl: Unable to read authentication cookie file:" << cookieError << std::endl; + goto usePasswordAuth; + } + + setError("Unable to read authentication cookie file: " + cookieError); + delete auth; + return; + } + } + else if ((methods & ProtocolInfoCommand::AuthHashedPassword) && !mAuthPassword.empty()) + { + usePasswordAuth: + torCtrlDebug() << "torctrl: Using hashed password authentication with AuthPasswd=\"" << mAuthPassword.toString() << "\"" << std::endl; + data = auth->build(mAuthPassword); + } + else + { + if (methods & ProtocolInfoCommand::AuthHashedPassword) + setError("Tor requires a control password to connect, but no password is configured."); + else + setError("Tor is not configured to accept any supported authentication methods."); + delete auth; + return; + } + + mSocket->sendCommand(auth, data); + } +} + +void TorControl::getTorInfo() +{ + assert(isConnected()); + + GetConfCommand *command = new GetConfCommand(GetConfCommand::GetInfo); + //connect(command, &TorControlCommand::finished, this, &TorControl::getTorInfoReply); + command->set_finished_callback( [this](TorControlCommand *sender) { getTorInfoReply(sender); }); + command->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data); }); + + std::list keys{ "status/circuit-established","status/bootstrap-phase" }; + + keys.push_back("net/listeners/socks"); + + mSocket->sendCommand(command, command->build(keys)); +} + +void TorControl::getTorInfoReply(TorControlCommand *sender) +{ + GetConfCommand *command = dynamic_cast(sender); + if (!command) + return; + + std::list listenAddresses = splitQuotedStrings(command->get("net/listeners/socks").front(), ' '); + + for (const auto& add:listenAddresses) { + ByteArray value = unquotedString(add); + int sepp = value.indexOf(':'); + std::string address(value.mid(0, sepp).toString()); + uint16_t port = (uint16_t)value.mid(sepp+1).toInt(); + + /* 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 (mSocksAddress.empty() || address == mSocket->peerAddress()) { + mSocksAddress = address; + mSocksPort = port; + if (address == mSocket->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 (!mSocksAddress.empty()) { + torCtrlDebug() << "torctrl: SOCKS address is " << mSocksAddress << ":" << mSocksPort << std::endl; + + if(rsEvents) + { + auto ev = std::make_shared(); + + ev->mTorManagerEventType = RsTorManagerEventCode::TOR_CONNECTIVITY_CHANGED; + ev->mTorConnectivityStatus = torConnectivityStatus(mStatus); + ev->mTorStatus = ::torStatus(mTorStatus); + rsEvents->sendEvent(ev); + } + } + + if (ByteArray(command->get("status/circuit-established").front()).toInt() == 1) + { + torCtrlDebug() << "torctrl: Tor indicates that circuits have been established; state is TorReady" << std::endl; + setTorStatus(TorControl::TorReady); + } +// else +// setTorStatus(TorControl::TorOffline); + + auto bootstrap = command->get("status/bootstrap-phase"); + if (!bootstrap.empty()) + updateBootstrap(splitQuotedStrings(bootstrap.front(), ' ')); +} + +void TorControl::addHiddenService(HiddenService *service) +{ + if (std::find(mServices.begin(),mServices.end(),service) != mServices.end()) + return; + + mServices.push_back(service); +} + +void TorControl::publishServices() +{ + torCtrlDebug() << "Publish Services... " ; + + assert(isConnected()); + if (mServices.empty()) + { + std::cerr << "No service regstered!" << std::endl; + return; + } + std::cerr << std::endl; + + if (torVersionAsNewAs("0.2.7")) { + for(HiddenService *service: mServices) + { + if (service->hostname().empty()) + torCtrlDebug() << "torctrl: Creating a new hidden service" << std::endl; + else + torCtrlDebug() << "torctrl: Publishing hidden service: " << service->hostname() << std::endl; + AddOnionCommand *onionCommand = new AddOnionCommand(service); + //protocolInfoReplyQObject::connect(onionCommand, &AddOnionCommand::succeeded, service, &HiddenService::servicePublished); + onionCommand->set_succeeded_callback( [this,service]() { checkHiddenService(service) ; }); + mSocket->sendCommand(onionCommand, onionCommand->build()); + } + } else { + torCtrlDebug() << "torctrl: Using legacy SETCONF hidden service configuration for tor" << mTorVersion << std::endl; + SetConfCommand *command = new SetConfCommand; + std::list > torConfig; + + for(HiddenService *service: mServices) + { + if (service->dataPath().empty()) + continue; + + if (service->privateKey().isLoaded() && !RsDirUtil::fileExists(service->dataPath() + "/private_key")) { + // This case can happen if tor is downgraded after the profile is created + RsWarn() << "Cannot publish ephemeral hidden services with this version of tor; skipping"; + continue; + } + + torCtrlDebug() << "torctrl: Configuring hidden service at" << service->dataPath() << std::endl; + + torConfig.push_back(std::make_pair("HiddenServiceDir", service->dataPath())); + + const std::list &targets = service->targets(); + for (auto tit:targets) + { + std::string target = RsUtil::NumberToString(tit.servicePort) + " " + +tit.targetAddress + ":" + +RsUtil::NumberToString(tit.targetPort); + torConfig.push_back(std::make_pair("HiddenServicePort", target)); + } + + command->set_ConfSucceeded_callback( [this,service]() { checkHiddenService(service); }); + //QObject::connect(command, &SetConfCommand::setConfSucceeded, service, &HiddenService::servicePublished); + } + + if (!torConfig.empty()) + mSocket->sendCommand(command, command->build(torConfig)); + } +} + +void TorControl::checkHiddenService(HiddenService *service) +{ + service->servicePublished(); + + if(service->status() == HiddenService::Online) + { + RsDbg() << "Hidden service published and ready!" ; + + setStatus(TorControl::HiddenServiceReady); + } +} + +void TorControl::shutdown() +{ + if (!hasOwnership()) { + RsWarn() << "torctrl: Ignoring shutdown command for a tor instance I don't own"; + return; + } + + mSocket->sendCommand(ByteArray("SIGNAL SHUTDOWN\r\n")); +} + +void TorControl::shutdownSync() +{ + if (!hasOwnership()) { + RsWarn() << "torctrl: Ignoring shutdown command for a tor instance I don't own"; + return; + } + + shutdown(); + while (mSocket->moretowrite(0)) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + mSocket->close(); +} + +void TorControl::statusEvent(int /* code */, const ByteArray &data) +{ + std::list tokens = splitQuotedStrings(data.trimmed(), ' '); + if (tokens.size() < 3) + return; + + const ByteArray& tok2 = *(++(++tokens.begin())); + torCtrlDebug() << "torctrl: status event:" << data.trimmed().toString() << " tok2=\"" << tok2.toString() << "\"" << std::endl; + + if (tok2 == "CIRCUIT_ESTABLISHED") + setTorStatus(TorControl::TorReady); + else if (tok2 == "CIRCUIT_NOT_ESTABLISHED") + setTorStatus(TorControl::TorOffline); + else if (tok2 == "BOOTSTRAP") + { + tokens.pop_front(); + updateBootstrap(tokens); + } +} + +void TorControl::updateBootstrap(const std::list &data) +{ + std::cerr << "********** Updating bootstrap status ************" << std::endl; + mBootstrapStatus.clear(); + // WARN or NOTICE + mBootstrapStatus["severity"] = (*data.begin()).toString(); + + auto dat = data.begin(); + ++dat; + + for(;dat!=data.end();++dat) { // for(int i = 1; i < data.size(); i++) { + int equals = (*dat).indexOf('='); + ByteArray key = (*dat).mid(0, equals); + ByteArray value; + + if (equals >= 0) + value = unquotedString((*dat).mid(equals + 1)); + + mBootstrapStatus[key.toLower().toString()] = value.toString(); + } + + if(rsEvents) + { + auto ev = std::make_shared(); + + ev->mTorManagerEventType = RsTorManagerEventCode::BOOTSTRAP_STATUS_CHANGED; + ev->mTorConnectivityStatus = torConnectivityStatus(mStatus); + ev->mTorStatus = ::torStatus(mTorStatus); + rsEvents->sendEvent(ev); + } +} + +TorControlCommand *TorControl::getConfiguration(const std::string& options) +{ + GetConfCommand *command = new GetConfCommand(GetConfCommand::GetConf); + command->set_replyLine_callback([this](int code, const ByteArray &data) { statusEvent(code,data); }); + mSocket->sendCommand(command, command->build(options)); + + return command; +} + +TorControlCommand *TorControl::setConfiguration(const std::list >& options) +{ + SetConfCommand *command = new SetConfCommand; + command->setResetMode(true); + mSocket->sendCommand(command, command->build(options)); + + return command; +} + +namespace Tor { + +class SaveConfigOperation : public PendingOperation +{ +public: + SaveConfigOperation() + : PendingOperation(), command(0) + { + } + + void start(TorControlSocket *socket) + { + assert(!command); + command = new GetConfCommand(GetConfCommand::GetInfo); + command->set_finished_callback([this](TorControlCommand *sender){ configTextReply(sender); }); + + socket->sendCommand(command, command->build(std::list { "config-text" , "config-file" } )); + } + + void configTextReply(TorControlCommand * /*sender*/) + { + assert(command); + if (!command) + return; + + auto lpath = command->get("config-file"); + std::string path = (lpath.empty()?std::string():lpath.front()); + + if (path.empty()) { + finishWithError("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 + + auto filename = RsDirUtil::getFileName(path); + + if(filename != "torrc" || !RsDirUtil::fileExists(path)) + { + finishWithError("Refusing to write torrc to unacceptable path " + path); + return; + } + + std::ofstream file(path); + + if (!file.is_open()) { + finishWithError("Failed opening torrc file for writing: permissions error?"); + 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 + }; + + auto configText = command->get("config-text") ; + + for(const auto& value: configText) + { + ByteArray line(value); + + bool skip = false; + for (const char **key = bannedKeys; *key; key++) { + if (line.startsWith(*key)) { + skip = true; + break; + } + } + if (skip) + continue; + + file << line.toString() << std::endl; + } + + file.close(); + + torCtrlDebug() << "torctrl: Wrote torrc file" << std::endl; + finishWithSuccess(); + } + +private: + GetConfCommand *command; +}; + +} + +PendingOperation *TorControl::saveConfiguration() +{ + if (!hasOwnership()) { + RsWarn() << "torctrl: Ignoring save configuration command for a tor instance I don't own"; + return 0; + } + + SaveConfigOperation *operation = new SaveConfigOperation(); + + operation->set_finished_callback( [operation]() { delete operation; }); + operation->start(mSocket); + + return operation; +} + +bool TorControl::hasOwnership() const +{ + return mHasOwnership; +} + +void TorControl::takeOwnership() +{ + mHasOwnership = true; + mSocket->sendCommand(ByteArray("TAKEOWNERSHIP\r\n")); + + // Reset PID-based polling + std::list > options; + options.push_back(std::make_pair("__OwningControllerProcess",std::string())); + setConfiguration(options); +} + +bool TorControl::torVersionAsNewAs(const std::string& match) const +{ + auto split = ByteArray(torVersion()).split(ByteArray(".-")); + auto matchSplit = ByteArray(match).split(ByteArray(".-")); + + int split_size = split.size(); + auto b_split(split.begin()); + auto b_matchsplit(matchSplit.begin()); + + for(int i=0;;) + { + int currentVal,matchVal; + bool ok1 = RsUtil::StringToInt((*b_split).toString(),currentVal); + bool ok2 = RsUtil::StringToInt((*b_matchsplit).toString(),matchVal); + + if (!ok1 || !ok2) + return false; + if (currentVal > matchVal) + return true; + if (currentVal < matchVal) + return false; + + ++i; + + if(i >= split_size) + return false; + + ++b_split; + ++b_matchsplit; + } + + // Versions are equal, up to the length of match + return true; +} + + diff --git a/libretroshare/src/tor/TorControl.h b/libretroshare/src/tor/TorControl.h new file mode 100644 index 000000000..be73aa3f4 --- /dev/null +++ b/libretroshare/src/tor/TorControl.h @@ -0,0 +1,152 @@ +/* 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. + */ + +#pragma once + +#include + +#include "PendingOperation.h" +#include "bytearray.h" +#include "TorControlSocket.h" + + +namespace Tor +{ + +class HiddenService; +class TorControlSocket; +class TorControlCommand; + +class TorControl : public TorControlSocketClient +{ +public: + enum Status + { + Error = 0x00, + NotConnected = 0x01, + Connecting = 0x02, + SocketConnected = 0x03, + Authenticating = 0x04, + Authenticated = 0x05, + HiddenServiceReady = 0x06, + Unknown = 0x07 + }; + + enum TorStatus + { + TorUnknown = 0x00, + TorOffline = 0x01, + TorReady = 0x02 + }; + + explicit TorControl(); + virtual ~TorControl(); + + /* Information */ + Status status() const; + TorStatus torStatus() const; + std::string torVersion() const; + bool torVersionAsNewAs(const std::string &version) const; + std::string errorMessage() const; + + bool hasConnectivity() const; + std::string socksAddress() const; + uint16_t socksPort() const; + + /* Authentication */ + void setAuthPassword(const ByteArray& password); + + /* Connection */ + bool isConnected() const { return status() >= Authenticated; } + void connect(const std::string &address, uint16_t port); + void authenticate(); + + /* 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 */ + std::list hiddenServices() const; + void addHiddenService(HiddenService *service); + + std::map bootstrapStatus() const; + TorControlCommand *getConfiguration(const std::string &options); + TorControlCommand *setConfiguration(const std::list > &options); + PendingOperation *saveConfiguration(); + + void set_statusChanged_callback(const std::function& f) { mStatusChanged_callback = f ;} + void set_connected_callback(const std::function& f) { mConnected_callback = f ;} + void set_disconnected_callback(const std::function& f) { mDisconnected_callback = f ;} + + virtual void socketError(const std::string &s) override; + + /* Instruct Tor to shutdown */ + void shutdown(); + /* Call shutdown(), and wait synchronously for the command to be written */ + void shutdownSync(); + + void reconnect(); + + void getTorInfo(); +private: + TorControlSocket *mSocket; + std::string mTorAddress; + std::string mErrorMessage; + std::string mTorVersion; + ByteArray mAuthPassword; + std::string mSocksAddress; + std::list mServices; + uint16_t mControlPort, mSocksPort; + TorControl::Status mStatus; + TorControl::TorStatus mTorStatus; + std::map mBootstrapStatus; + bool mHasOwnership; + + void checkHiddenService(HiddenService *service); + void getTorInfoReply(TorControlCommand *sender); + void setStatus(TorControl::Status n); + void statusEvent(int code, const ByteArray &data); + void setTorStatus(TorControl::TorStatus n); + void updateBootstrap(const std::list& data); + void setError(const std::string& message); + void publishServices(); + void protocolInfoReply(TorControlCommand *sender); + void socketDisconnected(); + void authenticateReply(TorControlCommand *sender); + + std::function mStatusChanged_callback; + std::function mConnected_callback; + std::function mDisconnected_callback; +}; + +} diff --git a/retroshare-gui/src/TorControl/TorControlCommand.cpp b/libretroshare/src/tor/TorControlCommand.cpp similarity index 80% rename from retroshare-gui/src/TorControl/TorControlCommand.cpp rename to libretroshare/src/tor/TorControlCommand.cpp index 48d4aab8b..127b6a04a 100644 --- a/retroshare-gui/src/TorControl/TorControlCommand.cpp +++ b/libretroshare/src/tor/TorControlCommand.cpp @@ -31,33 +31,33 @@ */ #include "TorControlCommand.h" -#include using namespace Tor; TorControlCommand::TorControlCommand() - : m_finalStatus(0) + : m_finalStatus(0), + mReplyLine ( std::function([](int, const ByteArray &){})), + mFinished ( std::function([](TorControlCommand*){})) { } -void TorControlCommand::onReply(int statusCode, const QByteArray &data) +void TorControlCommand::onReply(int statusCode, const ByteArray &data) { - emit replyLine(statusCode, data); + mReplyLine(statusCode, data); } void TorControlCommand::onFinished(int statusCode) { m_finalStatus = statusCode; - emit finished(); + mFinished(this); } -void TorControlCommand::onDataLine(const QByteArray &data) +void TorControlCommand::onDataLine(const ByteArray &data) { - Q_UNUSED(data); } void TorControlCommand::onDataFinished() { - qWarning() << "torctrl: Unexpected data response for command"; + RsWarn() << "torctrl: Unexpected data response for command"; } diff --git a/retroshare-gui/src/TorControl/TorControlCommand.h b/libretroshare/src/tor/TorControlCommand.h similarity index 70% rename from retroshare-gui/src/TorControl/TorControlCommand.h rename to libretroshare/src/tor/TorControlCommand.h index 894381054..8dee6aadb 100644 --- a/retroshare-gui/src/TorControl/TorControlCommand.h +++ b/libretroshare/src/tor/TorControlCommand.h @@ -30,41 +30,44 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TORCONTROLCOMMAND_H -#define TORCONTROLCOMMAND_H +#pragma once -#include -#include +#include +#include "bytearray.h" namespace Tor { -class TorControlCommand : public QObject -{ - Q_OBJECT - Q_DISABLE_COPY(TorControlCommand) +class ProtocolInfoCommand; +class TorControlCommand +{ friend class TorControlSocket; public: TorControlCommand(); + virtual ~TorControlCommand()=default; int statusCode() const { return m_finalStatus; } -signals: - void replyLine(int statusCode, const QByteArray &data); - void finished(); + void set_replyLine_callback( const std::function& f) { mReplyLine=f ; } + void set_finished_callback( const std::function& f) { mFinished=f; }; -protected: - virtual void onReply(int statusCode, const QByteArray &data); +public: + virtual void onReply(int statusCode, const ByteArray &data); virtual void onFinished(int statusCode); - virtual void onDataLine(const QByteArray &data); + virtual void onDataLine(const ByteArray &data); virtual void onDataFinished(); private: int m_finalStatus; + + // Disable copy + TorControlCommand(const TorControlCommand&){} + TorControlCommand& operator=(const TorControlCommand&){ return *this; } + + std::function mReplyLine; + std::function mFinished; }; } - -#endif // TORCONTROLCOMMAND_H diff --git a/retroshare-gui/src/TorControl/TorControlSocket.cpp b/libretroshare/src/tor/TorControlSocket.cpp similarity index 57% rename from retroshare-gui/src/TorControl/TorControlSocket.cpp rename to libretroshare/src/tor/TorControlSocket.cpp index c6b1eb707..19b373f67 100644 --- a/retroshare-gui/src/TorControl/TorControlSocket.cpp +++ b/libretroshare/src/tor/TorControlSocket.cpp @@ -34,15 +34,12 @@ #include "TorControlSocket.h" #include "TorControlCommand.h" -#include using namespace Tor; -TorControlSocket::TorControlSocket(QObject *parent) - : QTcpSocket(parent), currentCommand(0), inDataReply(false) +TorControlSocket::TorControlSocket(TorControlSocketClient *client) + : RsThreadedTcpSocket(),currentCommand(0), inDataReply(false),mClient(client) { - connect(this, SIGNAL(readyRead()), this, SLOT(process())); - connect(this, SIGNAL(disconnected()), this, SLOT(clear())); } TorControlSocket::~TorControlSocket() @@ -50,24 +47,43 @@ TorControlSocket::~TorControlSocket() clear(); } -void TorControlSocket::sendCommand(TorControlCommand *command, const QByteArray &data) +bool TorControlSocket::connectToHost(const std::string& tcp_address,uint16_t tcp_port) { - Q_ASSERT(data.endsWith("\r\n")); + if(RsTcpSocket::connect(tcp_address,tcp_port)) + { + start("TorControlSocket"); + return true; + } + else + return false; - commandQueue.append(command); - write(data); +} +std::string TorControlSocket::peerAddress() const +{ + if(connectionState() == State::CONNECTED) + return connectAddress(); + else + return std::string(); +} +void TorControlSocket::sendCommand(TorControlCommand *command, const ByteArray& data) +{ + assert(data.endsWith(ByteArray("\r\n"))); - std::cerr << "[TOR CTRL] Sent: \"" << QString(data.trimmed()).toStdString() << "\"" << std::endl; + commandQueue.push_back(command); + senddata((void*)data.data(),data.size()); + + std::cerr << "[TOR CTRL] Sent: \"" << data.trimmed().toString() << "\"" << std::endl; } -void TorControlSocket::registerEvent(const QByteArray &event, TorControlCommand *command) +void TorControlSocket::registerEvent(const ByteArray &event, TorControlCommand *command) { - eventCommands.insert(event, command); + eventCommands.insert(std::make_pair(event, command)); - QByteArray data("SETEVENTS"); - foreach (const QByteArray &key, eventCommands.keys()) { + ByteArray data("SETEVENTS"); + for(auto it:eventCommands) + { data += ' '; - data += key; + data += it.first; } data += "\r\n"; @@ -76,30 +92,53 @@ void TorControlSocket::registerEvent(const QByteArray &event, TorControlCommand void TorControlSocket::clear() { - qDeleteAll(commandQueue); + for(auto cmd:commandQueue) delete cmd; commandQueue.clear(); - qDeleteAll(eventCommands); + + for(auto cmd:eventCommands) delete cmd.second; eventCommands.clear(); + inDataReply = false; currentCommand = 0; } -void TorControlSocket::setError(const QString &message) +void TorControlSocket::setError(const std::string &message) { m_errorMessage = message; - emit error(message); + mClient->socketError(message); abort(); } +ByteArray TorControlSocket::readline(int s) +{ + ByteArray b(s); + int real_size; + + if(! (real_size = RsTcpSocket::readline(b.data(),s))) + return ByteArray(); + else + { + b.resize(real_size); + return b; + } +} + void TorControlSocket::process() { for (;;) { - if (!canReadLine()) + if (!moretoread(0)) return; - QByteArray line = readLine(5120); - if (!line.endsWith("\r\n")) { - setError(QStringLiteral("Invalid control message syntax")); + ByteArray line = readline(5120); + + if(line.empty()) // This happens when the incoming buffer isn't empty yet doesn't have a full line already. + { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + continue; + } + + if (!line.endsWith(ByteArray("\r\n"))) { + setError("Invalid control message syntax"); return; } line.chop(2); @@ -118,7 +157,7 @@ void TorControlSocket::process() } if (line.size() < 4) { - setError(QStringLiteral("Invalid control message syntax")); + setError("Invalid control message syntax"); return; } @@ -131,7 +170,7 @@ void TorControlSocket::process() line = line.mid(4); if (!isFinalReply && !inDataReply && type != '-') { - setError(QStringLiteral("Invalid control message syntax")); + setError("Invalid control message syntax"); return; } @@ -140,10 +179,15 @@ void TorControlSocket::process() if (!currentCommand) { int space = line.indexOf(' '); if (space > 0) - currentCommand = eventCommands.value(line.mid(0, space)); + { + auto it = eventCommands.find(line.mid(0, space).toString()); + + if(it != eventCommands.end()) + currentCommand = it->second; + } if (!currentCommand) { - qWarning() << "torctrl: Ignoring unknown event"; + RsWarn() << "torctrl: Ignoring unknown event"; continue; } } @@ -156,23 +200,36 @@ void TorControlSocket::process() continue; } - if (commandQueue.isEmpty()) { - qWarning() << "torctrl: Received unexpected data"; + if (commandQueue.empty()) { + RsWarn() << "torctrl: Received unexpected data"; continue; } - TorControlCommand *command = commandQueue.first(); + TorControlCommand *command = commandQueue.front(); if (command) command->onReply(statusCode, line); if (inDataReply) { currentCommand = command; } else if (isFinalReply) { - commandQueue.takeFirst(); + commandQueue.pop_front(); if (command) { command->onFinished(statusCode); - command->deleteLater(); + delete command; // should we "delete later" ? } } } } + +int TorControlSocket::tick() +{ + bool rw = RsTcpSocket::tick(); + + if(moretoread(0)) + process(); + + if(!rw) + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // temporisation when nothing happens + + return 0; // not sure about what we should return here. +} diff --git a/retroshare-gui/src/TorControl/TorControlSocket.h b/libretroshare/src/tor/TorControlSocket.h similarity index 63% rename from retroshare-gui/src/TorControl/TorControlSocket.h rename to libretroshare/src/tor/TorControlSocket.h index 2db911503..40437ed76 100644 --- a/retroshare-gui/src/TorControl/TorControlSocket.h +++ b/libretroshare/src/tor/TorControlSocket.h @@ -30,48 +30,58 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TORCONTROLSOCKET_H -#define TORCONTROLSOCKET_H +#pragma once -#include -#include +#include "pqi/rstcpsocket.h" +#include "bytearray.h" namespace Tor { class TorControlCommand; -class TorControlSocket : public QTcpSocket +class TorControlSocketClient { -Q_OBJECT public: - explicit TorControlSocket(QObject *parent = 0); + virtual void socketError(const std::string& s) = 0; +}; + +class TorControlSocket : public RsThreadedTcpSocket +{ +public: + explicit TorControlSocket(TorControlSocketClient *client); virtual ~TorControlSocket(); - QString errorMessage() const { return m_errorMessage; } + std::string errorMessage() const { return m_errorMessage; } - void registerEvent(const QByteArray &event, TorControlCommand *handler); + bool connectToHost(const std::string& tcp_address,uint16_t tcp_port); + void registerEvent(const ByteArray &event, TorControlCommand *handler); - void sendCommand(const QByteArray &data) { sendCommand(0, data); } - void sendCommand(TorControlCommand *command, const QByteArray &data); + void sendCommand(const ByteArray& data) { sendCommand(0, data); } + void sendCommand(TorControlCommand *command, const ByteArray &data); -signals: - void error(const QString &message); + ByteArray readline(int s); + + // threaded TcpSocket + + virtual int tick() override; + + std::string peerAddress() const; + + const std::string& errorString() const { return m_errorMessage ;} -private slots: void process(); void clear(); private: - QQueue commandQueue; - QHash eventCommands; - QString m_errorMessage; + std::list commandQueue; + std::map eventCommands; + std::string m_errorMessage; TorControlCommand *currentCommand; bool inDataReply; + TorControlSocketClient *mClient; - void setError(const QString &message); + void setError(const std::string& message); }; } - -#endif // TORCONTROLSOCKET_H diff --git a/libretroshare/src/tor/TorManager.cpp b/libretroshare/src/tor/TorManager.cpp new file mode 100644 index 000000000..0b0f59cef --- /dev/null +++ b/libretroshare/src/tor/TorManager.cpp @@ -0,0 +1,877 @@ +/* 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 +#include + +// This works on linux only. I have no clue how to do that on windows. Anyway, this +// is only needed for an assert that should normaly never be triggered. + +#if !defined(_WIN32) && !defined(__MINGW32__) +#include +#endif + +#include "util/rsdir.h" +#include "retroshare/rsinit.h" + +#include "TorManager.h" +#include "TorProcess.h" +#include "TorControl.h" +#include "CryptoKey.h" +#include "HiddenService.h" +#include "GetConfCommand.h" + +using namespace Tor; + +static TorManager *rsTor = nullptr; + +namespace Tor +{ + +class TorManagerPrivate : public TorProcessClient +{ +public: + TorManager *q; + TorProcess *process; + TorControl *control; + std::string dataDir; + std::string hiddenServiceDir; + std::list logMessages; + std::string errorMessage; + bool configNeeded; + + HiddenService *hiddenService ; + + explicit TorManagerPrivate(TorManager *parent = 0); + virtual ~TorManagerPrivate(); + + std::string torExecutablePath() const; + bool createDataDir(const std::string &path); + bool createDefaultTorrc(const std::string &path); + + void setError(const std::string &errorMessage); + + virtual void processStateChanged(int state) override; + virtual void processErrorChanged(const std::string &errorMessage) override; + virtual void processLogMessage(const std::string &message) override; + +//public slots: + void controlStatusChanged(int status); + void getConfFinished(TorControlCommand *sender); +}; + +} + +TorManager::TorManager() + : d(new TorManagerPrivate(this)) +{ +} + +TorManager::~TorManager() +{ + delete(d); +} + +TorManagerPrivate::TorManagerPrivate(TorManager *parent) + : q(parent) + , process(0) + , control(new TorControl()) + , configNeeded(false) + , hiddenService(NULL) +{ + control->set_statusChanged_callback([this](int new_status,int /*old_status*/) { controlStatusChanged(new_status); }); +} + +TorManagerPrivate::~TorManagerPrivate() +{ + delete(control); +} + +TorManager *TorManager::instance() +{ + static TorManager *p = 0; + if (!p) + p = new TorManager(); + return p; +} + +TorControl *TorManager::control() +{ + return d->control; +} + +TorProcess *TorManager::process() +{ + return d->process; +} + +std::string TorManager::torDataDirectory() const +{ + return d->dataDir; +} + +void TorManager::setTorDataDirectory(const std::string &path) +{ + d->dataDir = path; + + if (!d->dataDir.empty() && !ByteArray(d->dataDir).endsWith('/')) + d->dataDir += '/'; +} + +std::string TorManager::hiddenServiceDirectory() const +{ + return d->hiddenServiceDir; +} +void TorManager::setHiddenServiceDirectory(const std::string &path) +{ + d->hiddenServiceDir = path; + + if (!d->hiddenServiceDir.empty() && !(d->hiddenServiceDir.back() == '/')) + d->hiddenServiceDir += '/'; +} + +static bool test_listening_port(const std::string& /*address*/,uint16_t port) +{ + int sockfd; + struct sockaddr_in serv_addr ; + + /* First call to socket() function */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + + if (sockfd < 0) + return false; + + /* Initialize socket structure */ + memset((char *) &serv_addr, 0,sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + /* Now bind the host address using bind() call.*/ + if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR on binding"); + close(sockfd); + return false; + } + unix_fcntl_nonblock(sockfd); + int res = listen(sockfd,5); + + close(sockfd); + + if(!res) + return true; + + return false; +} +bool TorManager::setupHiddenService() +{ + if(d->hiddenService != NULL) + { + RsErr() << "TorManager: setupHiddenService() called twice! Not doing anything this time." ; + return true ; + } + + std::string keyData ;//= m_settings->read("serviceKey").toString(); + std::string legacyDir = d->hiddenServiceDir; + + RsDbg() << "TorManager: setting up hidden service." << std::endl; + + if(legacyDir.empty()) + { + RsErr() << "legacy dir not set! Cannot proceed." ; + return false ; + } + + RsDbg() << "Using legacy dir: " << legacyDir ; + auto key_path = RsDirUtil::makePath(legacyDir,"/private_key"); + + if (!legacyDir.empty() && RsDirUtil::fileExists(key_path)) + { + std::cerr << "Attempting to load key from legacy filesystem format from file \"" << key_path << "\"" << std::endl; + + d->hiddenService = new Tor::HiddenService(this,legacyDir); + + if(!d->hiddenService->privateKey().bytes().empty()) + { + RsDbg() << "Got key from legacy dir: " ; + RsDbg() << d->hiddenService->privateKey().bytes().toHex().toString() ; + } + else + RsWarn() << "Failed to load existing hidden service. Creating a new one." ; + } + else + { + d->hiddenService = new Tor::HiddenService(this,legacyDir); + + RsDbg() << "Creating new hidden service." << std::endl; + } + + assert(d->hiddenService); + + // Generally, these are not used, and we bind to localhost and port 0 + // for an automatic (and portable) selection. + + std::string address = "127.0.0.1"; // we only listen from localhost + unsigned short hidden_service_port = 7934;//(quint16)m_settings->read("localListenPort").toInt(); + + do + { + hidden_service_port = 1025 + (RsRandom::random_u32() >> 17); + + std::cerr << "Testing listening address:port " << address << ":" << hidden_service_port ; + std::cerr.flush(); + } + while(!test_listening_port(address,hidden_service_port)); + + std::cerr << ": OK - Adding hidden service to TorControl." << std::endl; + + // Note: 9878 is quite arbitrary, but since each RS node generates its own hidden service, all of them + // can use the same port without any conflict. + + d->hiddenService->addTarget(9878, "127.0.0.1",hidden_service_port); + control()->addHiddenService(d->hiddenService); + + return true ; +} + +void TorManager::hiddenServiceStatusChanged(int new_status,int old_status) +{ + std::cerr << "Hidden service status changed from " << old_status << " to " << new_status << std::endl; + + if(rsEvents) + { + auto ev = std::make_shared(); + ev->mTorManagerEventType = RsTorManagerEventCode::TOR_STATUS_CHANGED; + ev->mTorConnectivityStatus = RsTorConnectivityStatus::HIDDEN_SERVICE_READY; + ev->mTorStatus = RsTorStatus::READY; + + rsEvents->sendEvent(ev); + } +} + +void TorManager::hiddenServicePrivateKeyChanged() +{ + if(!d->hiddenService) + return ; + + std::string key = d->hiddenService->privateKey().bytes().toString(); + + std::ofstream s(d->hiddenServiceDir + "/private_key"); + +#ifdef TO_REMOVE + s << "-----BEGIN RSA PRIVATE KEY-----" << endl; + + for(int i=0;ihiddenService) + return ; + + std::string outfile2_name = RsDirUtil::makePath(d->hiddenServiceDir,"/hostname") ; + std::ofstream of(outfile2_name); + + std::string hostname(d->hiddenService->hostname()); + + of << hostname << std::endl; + of.close(); + + std::cerr << "Hidden service hostname changed: " << hostname << std::endl; +} + +bool TorManager::configurationNeeded() const +{ + return d->configNeeded; +} + +const std::list& TorManager::logMessages() const +{ + return d->logMessages; +} + +bool TorManager::hasError() const +{ + return !d->errorMessage.empty(); +} + +std::string TorManager::errorMessage() const +{ + return d->errorMessage; +} + +bool TorManager::startTorManager() +{ + if (!d->errorMessage.empty()) { + d->errorMessage.clear(); + + //emit errorChanged(); // not needed because there's no error to handle + } + +#ifdef TODO + SettingsObject settings("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(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("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 +#endif + { + // Launch a bundled Tor instance + std::string executable = d->torExecutablePath(); + + std::cerr << "Executable path: " << executable << std::endl; + + if (executable.empty()) { + d->setError("Cannot find tor executable"); + return false; + } + + if (!d->process) { + d->process = new TorProcess(d); + + // QObject::connect(d->process, SIGNAL(stateChanged(int)), d, SLOT(processStateChanged(int))); + // QObject::connect(d->process, SIGNAL(errorMessageChanged(std::string)), d, SLOT(processErrorChanged(std::string))); + // QObject::connect(d->process, SIGNAL(logMessage(std::string)), d, SLOT(processLogMessage(std::string))); + } + + if (!RsDirUtil::checkCreateDirectory(d->dataDir)) + { + d->setError(std::string("Cannot write data location: ") + d->dataDir); + return false; + } + + std::string defaultTorrc = RsDirUtil::makePath(d->dataDir,"default_torrc"); + + if (!RsDirUtil::fileExists(defaultTorrc) && !d->createDefaultTorrc(defaultTorrc)) + { + d->setError("Cannot write data files: "+defaultTorrc); + return false; + } + + std::string torrc = RsDirUtil::makePath(d->dataDir,"torrc"); + uint64_t file_size; + + bool torrc_exists = RsDirUtil::checkFile(torrc,file_size); + + if(!torrc_exists || torrc.size() == 0) + { + d->configNeeded = true; + + if(rsEvents) + { + auto ev = std::make_shared(); + ev->mTorManagerEventType = RsTorManagerEventCode::CONFIGURATION_NEEDED; + ev->mTorConnectivityStatus = RsTorConnectivityStatus::UNKNOWN; + ev->mTorStatus = RsTorStatus::UNKNOWN; + rsEvents->sendEvent(ev); + } + //emit configurationNeededChanged(); + } + + std::cerr << "Starting Tor process:" << std::endl; + std::cerr << " Tor executable path: " << executable << std::endl; + std::cerr << " Tor data directory : " << d->dataDir << std::endl; + std::cerr << " Tor default torrc : " << defaultTorrc << std::endl; + + d->process->setExecutable(executable); + d->process->setDataDir(d->dataDir); + d->process->setDefaultTorrc(defaultTorrc); + } + + std::cerr << "Starting Tor manager thread:" << std::endl; + RsThread::start("TorManager"); + return true ; +} + +void TorManager::run() +{ + d->process->start(); + + while(!shouldStop()) + { + threadTick(); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + d->control->shutdownSync(); + d->process->stop(); + + if(rsEvents) + { + auto ev = std::make_shared(); + ev->mTorManagerEventType = RsTorManagerEventCode::TOR_MANAGER_STOPPED; + rsEvents->sendEvent(ev); + } +} + +void TorManager::threadTick() +{ + d->process->tick(); + + if(d->process->state() != TorProcess::Ready) + return; + + switch(d->control->status()) + { + case TorControl::Unknown: + case TorControl::Connecting: + break; + + case TorControl::NotConnected: + RsDbg() << "Connecting to tor process at " << d->process->controlHost() << ":" << d->process->controlPort() << "..." ; + d->control->connect(d->process->controlHost(),d->process->controlPort()); + break; + + case TorControl::SocketConnected: + RsDbg() << "Connection established." ; + + if(d->hiddenService == nullptr) + { + RsDbg() << "Setting up hidden service" ; + setupHiddenService(); + } + + d->control->setAuthPassword(d->process->controlPassword()); + d->control->authenticate(); + break; + + case TorControl::Authenticating: + RsDbg() << "Authenticating..." ; + break; + + case TorControl::Authenticated: + + RsDbg() << "Authenticated. Looking for hidden services."; + break; + + case TorControl::HiddenServiceReady: + if(d->control->torStatus() < TorControl::TorReady) + { + d->control->getTorInfo(); // forces TorControl to check its state. + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + break; + + case TorControl::Error: + d->control->shutdown(); + d->control->reconnect(); + break; + } +} + +bool TorManager::getProxyServerInfo(std::string& 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(std::string& service_id,std::string& service_onion_address,uint16_t& service_port, std::string& service_target_address,uint16_t& target_port) +{ + auto hidden_services = control()->hiddenServices(); + + if(hidden_services.empty()) + return false ; + + // Only return the first one. + + for(auto it(hidden_services.begin());it!=hidden_services.end();++it) + { + service_onion_address = (*it)->hostname(); + service_id = (*it)->serviceId(); + + for(auto it2((*it)->targets().begin());it2!=(*it)->targets().end();++it2) + { + service_port = (*it2).servicePort ; + service_target_address = (*it2).targetAddress ; + target_port = (*it2).targetPort; + break ; + } + break ; + } + return true ; +} + +void TorManagerPrivate::processStateChanged(int state) +{ + RsInfo() << "state: " << state << " passwd=\"" << process->controlPassword().toString() << "\" " << process->controlHost() + << ":" << process->controlPort() << std::endl; + + if (state == TorProcess::Ready) { + control->setAuthPassword(process->controlPassword()); + control->connect(process->controlHost(), process->controlPort()); + } +} + +void TorManagerPrivate::processErrorChanged(const std::string &errorMessage) +{ + std::cerr << "tor error:" << errorMessage << std::endl; + setError(errorMessage); +} + +void TorManagerPrivate::processLogMessage(const std::string &message) +{ + std::cerr << "tor:" << message << std::endl; + if (logMessages.size() >= 50) + logMessages.pop_front(); + + auto p = message.find_first_of('\n'); + + logMessages.push_back((p==std::string::npos)?message:message.substr(0,p)); +} + +void TorManagerPrivate::controlStatusChanged(int status) +{ + if (status == TorControl::Authenticated) { + if (!configNeeded) { + // If DisableNetwork is 1, trigger configurationNeeded + auto cmd = control->getConfiguration("DisableNetwork"); + cmd->set_finished_callback( [this](TorControlCommand *sender) { getConfFinished(sender) ; }); + } + + if (process) { + // Take ownership via this control socket + control->takeOwnership(); + } + } +} + +void TorManagerPrivate::getConfFinished(TorControlCommand *sender) +{ + GetConfCommand *command = dynamic_cast(sender); + if (!command) + return; + + int n; + + for(auto str:command->get("DisableNetwork")) + if(RsUtil::StringToInt(str,n) && n==1 && !configNeeded) + { + configNeeded = true; + + if(rsEvents) + { + auto ev = std::make_shared(); + ev->mTorManagerEventType = RsTorManagerEventCode::CONFIGURATION_NEEDED; + ev->mTorConnectivityStatus = RsTorConnectivityStatus::UNKNOWN; + ev->mTorStatus = RsTorStatus::UNKNOWN; + rsEvents->sendEvent(ev); + } + } +} + +std::string TorManagerPrivate::torExecutablePath() const +{ + std::string path; +#ifdef TODO + SettingsObject settings("tor"); + path = settings.read("executablePath").toString(); + + if (!path.isEmpty() && QFile::exists(path)) + return path; +#endif + +#ifdef WINDOWS_SYS + std::string filename("/tor/tor.exe"); +#else + std::string filename("/tor"); +#endif + + path = RsDirUtil::getDirectory(RsInit::executablePath()); + std::string tor_exe_path = RsDirUtil::makePath(path,filename); + + if (RsDirUtil::fileExists(tor_exe_path)) + return tor_exe_path; + +#ifdef BUNDLED_TOR_PATH + path = BUNDLED_TOR_PATH; + tor_exe_path = RsDirUtil::makePath(path,filename); + + if (RsDirUtil::fileExists(tor_exe_path)) + return tor_exe_path; +#endif + +#ifdef __APPLE__ + // on MacOS, try traditional brew installation path + + path = "/usr/local/opt/tor/bin" ; + tor_exe_path = RsDirUtil::makePath(path,filename); + + if (RsDirUtil::fileExists(tor_exe_path)) + return tor_exe_path; +#endif + +#ifdef __linux__ + // On linux try system-installed tor /usr/bin/tor + + if(RsDirUtil::fileExists("/usr/bin/tor")) + return std::string("/usr/bin/tor"); +#endif + + RsErr() << "Could not find Tor executable anywhere!" ; + // Try $PATH + return filename.substr(1); +} + +bool TorManagerPrivate::createDataDir(const std::string &path) +{ + return RsDirUtil::checkCreateDirectory(path); +} + +bool TorManagerPrivate::createDefaultTorrc(const std::string &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"; + + FILE *f = fopen(path.c_str(),"w"); + + if (!f) + return false; + + fprintf(f,"%s",defaultTorrcContent); + + fclose(f); + return true; +} + +void TorManagerPrivate::setError(const std::string &message) +{ + errorMessage = message; + + if(rsEvents) + { + auto ev = std::make_shared(); + + ev->mTorManagerEventType = RsTorManagerEventCode::TOR_MANAGER_ERROR; + ev->mErrorMessage = message; + rsEvents->sendEvent(ev); + } + //emit q->errorChanged(); +} + +bool RsTor::isTorAvailable() +{ + return !instance()->d->torExecutablePath().empty(); +} + +bool RsTor::getHiddenServiceInfo(std::string& service_id, + std::string& service_onion_address, + uint16_t& service_port, + std::string& service_target_address, + uint16_t& target_port) +{ + std::string sid; + std::string soa; + std::string sta; + + if(!instance()->getHiddenServiceInfo(sid,soa,service_port,sta,target_port)) + return false; + + service_id = sid; + service_onion_address = soa; + service_target_address = sta; + + return true; +} + +std::list RsTor::logMessages() +{ + return instance()->logMessages(); +} + +std::string RsTor::socksAddress() +{ + return instance()->control()->socksAddress(); +} +uint16_t RsTor::socksPort() +{ + return instance()->control()->socksPort(); +} + +RsTorStatus RsTor::torStatus() +{ + TorControl::TorStatus ts = instance()->control()->torStatus(); + + switch(ts) + { + case TorControl::TorOffline: return RsTorStatus::OFFLINE; + case TorControl::TorReady: return RsTorStatus::READY; + + default: + case TorControl::TorUnknown: return RsTorStatus::UNKNOWN; + } +} + +RsTorConnectivityStatus RsTor::torConnectivityStatus() +{ + TorControl::Status ts = instance()->control()->status(); + + switch(ts) + { + default: + case Tor::TorControl::Error : return RsTorConnectivityStatus::ERROR; + case Tor::TorControl::NotConnected : return RsTorConnectivityStatus::NOT_CONNECTED; + case Tor::TorControl::Authenticating: return RsTorConnectivityStatus::AUTHENTICATING; + case Tor::TorControl::Connecting: return RsTorConnectivityStatus::CONNECTING; + case Tor::TorControl::Authenticated : return RsTorConnectivityStatus::AUTHENTICATED; + case Tor::TorControl::HiddenServiceReady : return RsTorConnectivityStatus::HIDDEN_SERVICE_READY; + } +} + +bool RsTor::setupHiddenService() +{ + return instance()->setupHiddenService(); +} + +RsTorHiddenServiceStatus RsTor::getHiddenServiceStatus(std::string& service_id) +{ + service_id.clear(); + auto list = instance()->control()->hiddenServices(); + + if(list.empty()) + return RsTorHiddenServiceStatus::NOT_CREATED; + + service_id = (*list.begin())->serviceId(); + + switch((*list.begin())->status()) + { + default: + case Tor::HiddenService::NotCreated: return RsTorHiddenServiceStatus::NOT_CREATED; + case Tor::HiddenService::Offline : return RsTorHiddenServiceStatus::OFFLINE; + case Tor::HiddenService::Online : return RsTorHiddenServiceStatus::ONLINE; + } +} + +std::map RsTor::bootstrapStatus() +{ + return instance()->control()->bootstrapStatus(); +} + +bool RsTor::hasError() +{ + return instance()->hasError(); +} +std::string RsTor::errorMessage() +{ + return instance()->errorMessage(); +} + +void RsTor::getProxyServerInfo(std::string& server_address, uint16_t& server_port) +{ + std::string qserver_address; + instance()->getProxyServerInfo(qserver_address,server_port); + + server_address = qserver_address; +} + +bool RsTor::start() +{ + return instance()->startTorManager(); +} + +void RsTor::stop() +{ + if (rsTor) { + if (rsTor->isRunning()) { + rsTor->fullstop(); + } + delete(rsTor); + rsTor= nullptr; + } +} + +void RsTor::setTorDataDirectory(const std::string& dir) +{ + instance()->setTorDataDirectory(dir); +} +void RsTor::setHiddenServiceDirectory(const std::string& dir) +{ + instance()->setHiddenServiceDirectory(dir); +} + +TorManager *RsTor::instance() +{ +#if !defined(_WIN32) && !defined(__MINGW32__) + assert(getpid() == syscall(SYS_gettid));// make sure we're not in a thread +#endif + + if(rsTor == nullptr) + rsTor = new TorManager; + + return rsTor; +} diff --git a/retroshare-gui/src/TorControl/TorManager.h b/libretroshare/src/tor/TorManager.h similarity index 60% rename from retroshare-gui/src/TorControl/TorManager.h rename to libretroshare/src/tor/TorManager.h index e9acc1208..a96370bc7 100644 --- a/retroshare-gui/src/TorControl/TorManager.h +++ b/libretroshare/src/tor/TorManager.h @@ -32,12 +32,10 @@ // This code has been further modified to fit Retroshare context. -#ifndef TORMANAGER_H -#define TORMANAGER_H +#pragma once -#include -#include -#include +#include "retroshare/rstor.h" +#include "HiddenService.h" namespace Tor { @@ -48,31 +46,21 @@ class TorManagerPrivate; /* Run/connect to an instance of Tor according to configuration, and manage * UI interaction, first time configuration, etc. */ -class TorManager : public QObject + +class TorManager : public HiddenServiceClient, public RsThread, public RsTor { - Q_OBJECT - - Q_PROPERTY(bool configurationNeeded READ configurationNeeded NOTIFY configurationNeededChanged) - Q_PROPERTY(QStringList logMessages READ logMessages CONSTANT) - Q_PROPERTY(Tor::TorProcess* process READ process CONSTANT) - Q_PROPERTY(Tor::TorControl* control READ control CONSTANT) - Q_PROPERTY(bool hasError READ hasError NOTIFY errorChanged) - Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorChanged) - Q_PROPERTY(QString torDataDirectory READ torDataDirectory WRITE setTorDataDirectory) - public: - static bool isTorAvailable() ; static TorManager *instance(); + virtual ~TorManager(); TorProcess *process(); TorControl *control(); + std::string torDataDirectory() const; + void setTorDataDirectory(const std::string &path); - QString torDataDirectory() const; - void setTorDataDirectory(const QString &path); - - QString hiddenServiceDirectory() const; - void setHiddenServiceDirectory(const QString &path); + std::string hiddenServiceDirectory() const; + void setHiddenServiceDirectory(const std::string &path); // Starts a hidden service, loading it from the config directory that has been set earlier. bool setupHiddenService() ; @@ -80,32 +68,30 @@ public: // True on first run or when the Tor configuration wizard needs to be shown bool configurationNeeded() const; - QStringList logMessages() const; + const std::list& logMessages() const; bool hasError() const; - QString errorMessage() const; + std::string 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); + bool getHiddenServiceInfo(std::string& service_id,std::string& service_onion_address,uint16_t& service_port, std::string& service_target_address,uint16_t& target_port); + bool getProxyServerInfo(std::string &proxy_server_adress, uint16_t& proxy_server_port); -public slots: - bool start(); + bool startTorManager(); -private slots: - void hiddenServicePrivateKeyChanged(); - void hiddenServiceHostnameChanged(); - void hiddenServiceStatusChanged(int old_status,int new_status); + virtual void hiddenServiceOnline() override {} // do nothing here. + virtual void hiddenServicePrivateKeyChanged() override; + virtual void hiddenServiceHostnameChanged() override; + virtual void hiddenServiceStatusChanged(int new_status, int old_status) override; -signals: - void configurationNeededChanged(); - void errorChanged(); + // Thread stuff + + virtual void run() override; + void threadTick() ; private: - explicit TorManager(QObject *parent = 0); + explicit TorManager(); TorManagerPrivate *d; + friend class RsTor; }; } - -#endif -# diff --git a/libretroshare/src/tor/TorProcess.cpp b/libretroshare/src/tor/TorProcess.cpp new file mode 100644 index 000000000..b7b66da90 --- /dev/null +++ b/libretroshare/src/tor/TorProcess.cpp @@ -0,0 +1,510 @@ +/* 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 +#include + +#include "util/rsdir.h" +#include "util/rsfile.h" +#include "pqi/pqifdbin.h" + +#include "TorProcess.h" +#include "CryptoKey.h" + +#ifdef WINDOWS_SYS +#include "util/rsstring.h" + +#include +#define pipe(fds) _pipe(fds, 1024, _O_BINARY) +#endif + +using namespace Tor; + +static const int INTERVAL_BETWEEN_CONTROL_PORT_READ_TRIES = 5; // try every 5 secs. + +TorProcess::TorProcess(TorProcessClient *client) + : m_client(client), mState(TorProcess::NotStarted), mControlPort(0), mLastTryReadControlPort(0) +{ + mControlPortReadNbTries=0; + +} + +TorProcess::~TorProcess() +{ + if (state() > NotStarted) + stop(); +} + +std::string TorProcess::executable() const +{ + return mExecutable; +} + +void TorProcess::setExecutable(const std::string &path) +{ + mExecutable = path; +} + +std::string TorProcess::dataDir() const +{ + return mDataDir; +} + +void TorProcess::setDataDir(const std::string &path) +{ + mDataDir = path; +} + +std::string TorProcess::defaultTorrc() const +{ + return mDefaultTorrc; +} + +void TorProcess::setDefaultTorrc(const std::string &path) +{ + mDefaultTorrc = path; +} + +std::list TorProcess::extraSettings() const +{ + return mExtraSettings; +} + +void TorProcess::setExtraSettings(const std::list &settings) +{ + mExtraSettings = settings; +} + +TorProcess::State TorProcess::state() const +{ + return mState; +} + +std::string TorProcess::errorMessage() const +{ + return mErrorMessage; +} + +// Does a popen, but dup all file descriptors (STDIN STDOUT and STDERR) to the +// FDs supplied by the parent process + +int popen3(int fd[3],const std::vector& args,TorProcessHandle& pid) +{ + RsErr() << "Launching Tor in background..." ; + + int i, e; + int p[3][2]; + // set all the FDs to invalid + for(i=0; i<3; i++) + p[i][0] = p[i][1] = -1; + // create the pipes + for(int i=0; i<3; i++) + if(pipe(p[i])) + goto error; + +#ifdef WINDOWS_SYS + // Set up members of the PROCESS_INFORMATION structure. + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + STARTUPINFO si; + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.hStdInput = (HANDLE) _get_osfhandle(p[STDIN_FILENO][0]); + si.hStdOutput = (HANDLE) _get_osfhandle(p[STDOUT_FILENO][1]); + si.hStdError = (HANDLE) _get_osfhandle(p[STDERR_FILENO][1]); + si.wShowWindow = SW_HIDE; + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + + if (si.hStdInput != INVALID_HANDLE_VALUE && + si.hStdOutput != INVALID_HANDLE_VALUE && + si.hStdError != INVALID_HANDLE_VALUE) { + // build commandline + std::string cmd; + for (std::vector::const_iterator it = args.begin(); it != args.end(); ++it) { + if (it != args.begin()) { + cmd += " "; + } + cmd += *it; + } + + std::wstring wcmd; + if (!librs::util::ConvertUtf8ToUtf16(cmd, wcmd)) { + goto error; + } + + WINBOOL success = CreateProcess(nullptr, + (LPWSTR) wcmd.c_str(), // command line + nullptr, // process security attributes + nullptr, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + nullptr, // use parent's environment + nullptr, // use parent's current directory + &si, // STARTUPINFO pointer + &pi); // receives PROCESS_INFORMATION + + if (success) { + pid = pi.hProcess; + + CloseHandle(pi.hThread); + + fd[STDIN_FILENO] = p[STDIN_FILENO][1]; + close(p[STDIN_FILENO][0]); + fd[STDOUT_FILENO] = p[STDOUT_FILENO][0]; + close(p[STDOUT_FILENO][1]); + fd[STDERR_FILENO] = p[STDERR_FILENO][0]; + close(p[STDERR_FILENO][1]); + + // success + return 0; + } + } + + // fall through error + +#else + { + const char *arguments[args.size()+1]; + int n=0; + + // We first pushed everything into a vector of strings to save the pointers obtained from string returning methods + // by the time the process is launched. + + for(uint32_t i=0;i(arguments)); + + // if we are there, then we failed to launch our program + perror("Could not launch"); + fprintf(stderr," \"%s\"\n",*arguments); + } + } +#endif + +error: + e = errno; + // preserve original error + RsErr() << "An error occurred while trying to launch tor in background." ; + for(i=0; i<3; i++) { + close(p[i][0]); + close(p[i][1]); + } + errno = e; + return -1; +} + +void TorProcess::start() +{ + if (state() > NotStarted) + return; + + mErrorMessage.clear(); + + if (mExecutable.empty() || mDataDir.empty()) { + mErrorMessage = "Tor executable and data directory not specified"; + mState = Failed; + + if(m_client) m_client->processStateChanged(mState); // emit stateChanged(d->state); + if(m_client) m_client->processErrorChanged(mErrorMessage); // emit errorMessageChanged(d->errorMessage); + return; + } + + if (!ensureFilesExist()) { + mState = Failed; + if(m_client) m_client->processErrorChanged(mErrorMessage);// emit errorMessageChanged(d->errorMessage); + if(m_client) m_client->processStateChanged(mState);// emit stateChanged(d->state); + return; + } + + ByteArray password = controlPassword(); + ByteArray hashedPassword = torControlHashedPassword(password); + + if (password.empty() || hashedPassword.empty()) { + mErrorMessage = "Random password generation failed"; + mState = Failed; + if(m_client) m_client->processErrorChanged(mErrorMessage);// emit errorMessageChanged(d->errorMessage); + if(m_client) m_client->processStateChanged(mState); // emit stateChanged(d->state); + } + else + RsDbg() << "Using ControlPasswd=\"" << password.toString() << "\", hashed version=\"" << hashedPassword.toString() << "\"" ; + + mState = Starting; + + if(m_client) m_client->processStateChanged(mState);// emit stateChanged(d->state); + + if (RsDirUtil::fileExists(controlPortFilePath())) + RsDirUtil::removeFile(controlPortFilePath()); + + mControlPort = 0; + mControlHost.clear(); + + // Launch the process + + std::vector args; + + args.push_back(mExecutable); + + if (!mDefaultTorrc.empty()) + { + args.push_back("--defaults-torrc"); + args.push_back(mDefaultTorrc); + } + + args.push_back("-f"); + args.push_back(torrcPath()); + + args.push_back("DataDirectory") ; + args.push_back(mDataDir); + + args.push_back("HashedControlPassword") ; + args.push_back(hashedPassword.toString()); + + args.push_back("ControlPort") ; + args.push_back("auto"); + + args.push_back("ControlPortWriteToFile"); + args.push_back(controlPortFilePath()); + + args.push_back("__OwningControllerProcess") ; + args.push_back(RsUtil::NumberToString(getpid())); + + for(auto s:mExtraSettings) + args.push_back(s); + + int fd[3]; // File descriptors array + + if(popen3(fd,args,mTorProcessId)) + { + RsErr() << "Could not start Tor process. errno=" << errno ; + mState = Failed; + return; // stop the control thread + } + + RsFileUtil::set_fd_nonblock(fd[STDOUT_FILENO]); + RsFileUtil::set_fd_nonblock(fd[STDERR_FILENO]); + + mStdOutFD = new RsFdBinInterface(fd[STDOUT_FILENO], false); + mStdErrFD = new RsFdBinInterface(fd[STDERR_FILENO], false); +} + +void TorProcess::tick() +{ + mStdOutFD->tick(); + mStdErrFD->tick(); + + unsigned char buff[1024]; + int s; + + if((s=mStdOutFD->readline(buff,1024))) logMessage(std::string((char*)buff,s)); + if((s=mStdErrFD->readline(buff,1024))) logMessage(std::string((char*)buff,s)); + + if(!mStdOutFD->isactive() && !mStdErrFD->isactive()) + { + RsErr() << "Tor process died. Exiting TorControl process." ; + stop(); + return; + } + time_t now = time(nullptr); + + if(mControlPortReadNbTries <= 10 && (mControlPort==0 || mControlHost.empty()) && mLastTryReadControlPort + INTERVAL_BETWEEN_CONTROL_PORT_READ_TRIES < now) + { + mLastTryReadControlPort = now; + + if(tryReadControlPort()) + { + mState = Ready; + m_client->processStateChanged(mState);// stateChanged(mState); + } + else if(mControlPortReadNbTries > 10) + { + mState = Failed; + m_client->processStateChanged(mState);// stateChanged(mState); + } + } +} + +void TorProcess::stop() +{ + if (state() < Starting) + return; + + while(mState == Starting) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + +#ifdef WINDOWS_SYS + TerminateProcess (mTorProcessId, 0); +#else + kill(mTorProcessId,SIGTERM); +#endif + + RsInfo() << "Tor process has been normally terminated. Exiting."; + + mState = NotStarted; + + if(m_client) m_client->processStateChanged(mState);// emit stateChanged(d->state); +} + +void TorProcess::stateChanged(int newState) +{ + if(m_client) + m_client->processStateChanged(newState); +} +void TorProcess::errorMessageChanged(const std::string& errorMessage) +{ + if(m_client) + m_client->processErrorChanged(errorMessage); +} +void TorProcess::logMessage(const std::string& message) +{ + if(m_client) + m_client->processLogMessage(message); +} + +ByteArray TorProcess::controlPassword() +{ + if (mControlPassword.empty()) + mControlPassword = RsRandom::printable(16); + + return mControlPassword; +} + +std::string TorProcess::controlHost() +{ + return mControlHost; +} + +unsigned short TorProcess::controlPort() +{ + return mControlPort; +} + +bool TorProcess::ensureFilesExist() +{ + if(!RsDirUtil::checkCreateDirectory(mDataDir)) + { + mErrorMessage = "Cannot create Tor data directory: " + mDataDir; + return false; + } + + if (!RsDirUtil::fileExists(torrcPath())) + { + FILE *f = RsDirUtil::rs_fopen(torrcPath().c_str(),"w"); + + if(!f) + { + mErrorMessage = "Cannot create Tor configuration file: " + torrcPath(); + return false; + } + else + fclose(f); + } + + return true; +} + +std::string TorProcess::torrcPath() const +{ + return RsDirUtil::makePath(mDataDir,"torrc"); +} + +std::string TorProcess::controlPortFilePath() const +{ + return RsDirUtil::makePath(mDataDir,"control-port"); +} + +bool TorProcess::tryReadControlPort() +{ + FILE *file = RsDirUtil::rs_fopen(controlPortFilePath().c_str(),"r"); + RsDbg() << "Trying to read control port" ; + + if(file) + { + char *line = nullptr; + size_t tmp_buffsize = 0; + + size_t size = RsFileUtil::rs_getline(&line,&tmp_buffsize,file); + ByteArray data = ByteArray((unsigned char*)line,size).trimmed(); + free(line); + + fclose(file); + + int p; + if (data.startsWith("PORT=") && (p = data.lastIndexOf(':')) > 0) { + mControlHost = data.mid(5, p - 5).toString(); + mControlPort = data.mid(p+1).toInt(); + + if (!mControlHost.empty() && mControlPort > 0) + { + RsDbg() << "Got control host/port = " << mControlHost << ":" << mControlPort ; + return true; + } + } + } + return false; +} diff --git a/retroshare-gui/src/TorControl/TorProcess.h b/libretroshare/src/tor/TorProcess.h similarity index 52% rename from retroshare-gui/src/TorControl/TorProcess.h rename to libretroshare/src/tor/TorProcess.h index ad489dc43..9069e13cb 100644 --- a/retroshare-gui/src/TorControl/TorProcess.h +++ b/libretroshare/src/tor/TorProcess.h @@ -33,24 +33,36 @@ #ifndef TORPROCESS_H #define TORPROCESS_H -#include -#include +#include "bytearray.h" +#include "util/rsthreads.h" + +class RsFdBinInterface ; + +#ifdef WINDOWS_SYS + #define TorProcessHandle HANDLE +#else + #define TorProcessHandle pid_t +#endif namespace Tor { class TorProcessPrivate; +// This class is used to inherit calls from the TorProcess + +class TorProcessClient +{ +public: + virtual void processStateChanged(int) = 0; + virtual void processErrorChanged(const std::string&) = 0; + virtual void processLogMessage(const std::string&) = 0; +}; + /* Launches and controls a Tor instance with behavior suitable for bundling * an instance with the application. */ -class TorProcess : public QObject +class TorProcess { - 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, @@ -60,38 +72,66 @@ public: Ready }; - explicit TorProcess(QObject *parent = 0); + explicit TorProcess(TorProcessClient *client); virtual ~TorProcess(); - QString executable() const; - void setExecutable(const QString &path); + std::string executable() const; + void setExecutable(const std::string &path); - QString dataDir() const; - void setDataDir(const QString &path); + std::string dataDir() const; + void setDataDir(const std::string &path); - QString defaultTorrc() const; - void setDefaultTorrc(const QString &path); + std::string defaultTorrc() const; + void setDefaultTorrc(const std::string &path); - QStringList extraSettings() const; - void setExtraSettings(const QStringList &settings); + std::list extraSettings() const; + void setExtraSettings(const std::list &settings); State state() const; - QString errorMessage() const; - QHostAddress controlHost(); - quint16 controlPort(); - QByteArray controlPassword(); + std::string errorMessage() const; + std::string controlHost(); + unsigned short controlPort(); + ByteArray controlPassword(); + + void stateChanged(int newState); + void errorMessageChanged(const std::string &errorMessage); + void logMessage(const std::string &message); -public slots: void start(); void stop(); - -signals: - void stateChanged(int newState); - void errorMessageChanged(const QString &errorMessage); - void logMessage(const QString &message); + void tick(); private: - TorProcessPrivate *d; + TorProcessClient *m_client; + + std::string mExecutable; + std::string mDataDir; + std::string mDefaultTorrc; + std::list mExtraSettings; + TorProcess::State mState; + std::string mErrorMessage; + std::string mControlHost; + unsigned short mControlPort; + ByteArray mControlPassword; + + int controlPortAttempts; + + std::string torrcPath() const; + std::string controlPortFilePath() const; + bool ensureFilesExist(); + + TorProcessHandle mTorProcessId; + time_t mLastTryReadControlPort ; + int mControlPortReadNbTries ; + + void processStarted(); + void processFinished(); + void processError(std::string error); + void processReadable(); + bool tryReadControlPort(); + + RsFdBinInterface *mStdOutFD; + RsFdBinInterface *mStdErrFD; }; } diff --git a/libretroshare/src/tor/TorTypes.h b/libretroshare/src/tor/TorTypes.h new file mode 100644 index 000000000..393e6a849 --- /dev/null +++ b/libretroshare/src/tor/TorTypes.h @@ -0,0 +1,142 @@ +#pragma once + +#include +#include +#include +#include + +namespace Tor +{ + +class NonCopiable { +public: + NonCopiable(){} + virtual ~NonCopiable()=default; +private: + NonCopiable(const NonCopiable& nc) {} + virtual NonCopiable& operator=(const NonCopiable& nc) { return *this ; } +}; + +class TorByteArray: public std::vector +{ +public: + TorByteArray(const unsigned char *data,uint32_t len) + { + clear(); + for(uint32_t i=0;i size()) + return false; + + for(uint32_t i=0;i size()) + return false; + + for(uint32_t i=0;i size()) + throw std::runtime_error("Length out of range in TorByteArray::mid()"); + + TorByteArray b; + for(uint32_t i=0;i<(uint32_t)length;++i) + b.push_back(data()[i+start]); + + return b; + } + + static TorByteArray number(uint64_t n) + { + std::ostringstream o; + o << n ; + return TorByteArray(o.str()); + } +}; + +typedef std::string TorHostAddress; +} diff --git a/retroshare-gui/src/TorControl/Useful.h b/libretroshare/src/tor/Useful.h similarity index 92% rename from retroshare-gui/src/TorControl/Useful.h rename to libretroshare/src/tor/Useful.h index 6bb6e44c1..cdead13e7 100644 --- a/retroshare-gui/src/TorControl/Useful.h +++ b/libretroshare/src/tor/Useful.h @@ -30,11 +30,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UTILS_USEFUL_H -#define UTILS_USEFUL_H +#pragma once -#include -#include +#include "util/rsdebug.h" /* Print a warning for bug conditions, and assert on a debug build. * @@ -50,7 +48,7 @@ * triggered unless the code or logic is wrong. */ #if !defined(QT_NO_DEBUG) || defined(QT_FORCE_ASSERTS) -# define BUG() Explode(__FILE__,__LINE__), qWarning() << "BUG:" +# define BUG() Explode(__FILE__,__LINE__), RsWarn() << "BUG:" namespace { class Explode { @@ -59,13 +57,10 @@ public: int line; Explode(const char *file, int line) : file(file), line(line) { } ~Explode() { - qt_assert("something broke!", file, line); + RsErr() << "something broke! in file " << file << line; } }; } #else # define BUG() qWarning() << "BUG:" #endif - -#endif - diff --git a/libretroshare/src/tor/bytearray.h b/libretroshare/src/tor/bytearray.h new file mode 100644 index 000000000..e81db0a17 --- /dev/null +++ b/libretroshare/src/tor/bytearray.h @@ -0,0 +1,195 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include "util/rsprint.h" +#include "util/rsdebug.h" + +// This class re-implements QByteArray from Qt library. + +class ByteArray: public std::vector +{ +public: + ByteArray() =default; + explicit ByteArray(int n) : std::vector(n) {} + explicit ByteArray(const unsigned char *d,int n) : std::vector(n) { memcpy(data(),d,n); } + virtual ~ByteArray() =default; + + ByteArray(const std::string& c) { resize(c.size()); memcpy(data(),c.c_str(),c.size()); } + const ByteArray& operator=(const std::string& c) { resize(c.size()); memcpy(data(),c.c_str(),c.size()); return *this; } + + bool isNull() const { return empty(); } + ByteArray toHex() const { return ByteArray(RsUtil::BinToHex(data(),size(),0)); } + std::string toString() const { std::string res; for(auto c:*this) res += c; return res; } + + ByteArray operator+(const ByteArray& b) const { auto res(*this); for(unsigned char c:b) res.push_back(c); return res; } + ByteArray operator+(const std::string& b) const { return operator+(ByteArray(b)); } + + void append(const ByteArray& b) { for(auto c:b) push_back(c); } + void append(const char *b) { for(uint32_t n=0;b[n]!=0;++n) push_back(b[n]); } + + template void append(const T) = delete;// Prevents any implicit when calling the preceding functions which actually causes real bugs. + + ByteArray& operator+=(char b) { push_back(b); return *this; } + ByteArray& operator+=(const ByteArray& b) { for(auto c:b) push_back(c); return *this; } + ByteArray& operator+=(const char *b) { for(uint32_t n=0;b[n]!=0;++n) push_back(b[n]); return *this;} + + ByteArray left(uint32_t l) const { auto res = *this; res.resize(std::min((uint32_t)size(),l)); return res; } + ByteArray toUpper() const { auto res = *this; for(uint32_t i=0;i='a') res[i] += int('A')-int('a'); return res; } + ByteArray toLower() const { auto res = *this; for(uint32_t i=0;i='A') res[i] += int('a')-int('A'); return res; } + + int toInt() const + { + std::istringstream is(toString().c_str()); + + int res = -1; + is >> res ; + + return res; + } + bool endsWith(const ByteArray& b) const { return size() >= b.size() && !memcmp(&data()[size()-b.size()],b.data(),b.size()); } + bool endsWith(char b) const { return size() > 0 && back()==b; } + bool startsWith(const ByteArray& b) const { return b.size() <= size() && !strncmp((char*)b.data(),(char*)data(),std::min(size(),b.size())); } + bool startsWith(const char *b) const + { + for(uint32_t n=0;b[n]!=0;++n) + if(n >= size() || b[n]!=(*this)[n]) + return false; + + return true; + } + + bool operator==(const char *b) const + { + uint32_t n; + for(n=0;b[n]!=0;++n) + if(n >= size() || b[n]!=(*this)[n]) + return false; + + return n==size(); + } + + ByteArray mid(uint32_t n,int s=-1) const + { + ByteArray res((s>=0)?s:(size()-n)); + memcpy(res.data(),&data()[n],res.size()); + return res; + } + + int indexOf(unsigned char c,int from=0) const + { + for(uint32_t i=from;i split(unsigned char sep) + { + std::list res; + ByteArray current_block; + + for(uint32_t i=0;i split(const ByteArray& sep) + { + std::list res; + ByteArray current_block; + + for(uint32_t i=0;i=0;--i) + if(operator[](i) == s) + return i; + + return -1; + } +}; diff --git a/libretroshare/src/use_libretroshare.pri b/libretroshare/src/use_libretroshare.pri index 0cca29150..7ceda74ba 100644 --- a/libretroshare/src/use_libretroshare.pri +++ b/libretroshare/src/use_libretroshare.pri @@ -111,11 +111,7 @@ PRE_TARGETDEPS += $$pretargetStaticLibs(sLibs) LIBS += $$linkDynamicLibs(dLibs) android-* { -## ifaddrs is missing on Android to add them don't use the one from -## https://github.com/morristech/android-ifaddrs -## because it crash, use QNetworkInterface from Qt instead - CONFIG *= qt - QT *= network + INCLUDEPATH *= $$clean_path($${RS_SRC_PATH}/supportlibs/jni.hpp/include/) } ################################### Pkg-Config Stuff ############################# diff --git a/libretroshare/src/util/cxx23retrocompat.h b/libretroshare/src/util/cxx23retrocompat.h new file mode 100644 index 000000000..cd96b7cbf --- /dev/null +++ b/libretroshare/src/util/cxx23retrocompat.h @@ -0,0 +1,34 @@ +/******************************************************************************* + * RetroShare C++23 backwards compatibility utilities * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Gioacchino Mazzurco * + * Copyright (C) 2021 Asociación Civil Altermundi * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ +#pragma once + +#include + +#if ! defined(__cpp_lib_to_underlying) +namespace std +{ +template +constexpr underlying_type_t to_underlying(Enum e) noexcept +{ return static_cast>(e); } +} +#endif // ! defined(__cpp_lib_to_underlying) diff --git a/libretroshare/src/util/rsdir.cc b/libretroshare/src/util/rsdir.cc index aa7e59299..24bfbd3e3 100644 --- a/libretroshare/src/util/rsdir.cc +++ b/libretroshare/src/util/rsdir.cc @@ -60,6 +60,12 @@ #define canonicalize_file_name(p) realpath(p, NULL) #endif +#ifdef WINDOWS_SYS +#define FIND_OF_DIRECTORY_SEPARATOR "/\\\0" +#else +#define FIND_OF_DIRECTORY_SEPARATOR '/' +#endif + /**** * #define RSDIR_DEBUG 1 ****/ @@ -68,7 +74,7 @@ bool std::filesystem::create_directories(const std::string& path) { for( std::string::size_type lastIndex = 0; lastIndex < std::string::npos; - lastIndex = path.find('/', lastIndex) ) + lastIndex = path.find_first_of(FIND_OF_DIRECTORY_SEPARATOR, lastIndex) ) { std::string&& curDir = path.substr(0, ++lastIndex); if(!RsDirUtil::checkCreateDirectory(curDir)) @@ -84,6 +90,25 @@ bool std::filesystem::create_directories(const std::string& path) # include #endif // __cplusplus < 201703L +std::string RsDirUtil::getFileName(const std::string& full_file_path) +{ + std::string::size_type n = full_file_path.find_last_of(FIND_OF_DIRECTORY_SEPARATOR); + + if(n == std::string::npos) + return full_file_path; + else + return full_file_path.substr(n+1); +} + +std::string RsDirUtil::getDirectory(const std::string& full_file_path) +{ + std::string::size_type n = full_file_path.find_last_of(FIND_OF_DIRECTORY_SEPARATOR); + + if(n == std::string::npos) + return std::string(); + else + return full_file_path.substr(0,n); +} std::string RsDirUtil::getTopDir(const std::string& dir) { std::string top; @@ -92,10 +117,10 @@ std::string RsDirUtil::getTopDir(const std::string& dir) */ int i,j; int len = dir.length(); - for(j = len - 1; (j > 0) && (dir[j] == '/'); j--) ; - for(i = j; (i > 0) && (dir[i] != '/'); i--) ; + for(j = len - 1; (j > 0) && RsDirUtil::isDirectorySeparator(dir[j]); j--) ; + for(i = j; (i > 0) && !RsDirUtil::isDirectorySeparator(dir[i]); i--) ; - if (dir[i] == '/') + if (RsDirUtil::isDirectorySeparator(dir[i])) i++; for(; i <= j; i++) @@ -125,9 +150,9 @@ const char *RsDirUtil::scanf_string_for_uint(int bytes) bool RsDirUtil::splitDirFromFile(const std::string& full_path,std::string& dir, std::string& file) { - int i = full_path.rfind('/', full_path.size()-1); + std::string::size_type i = full_path.find_last_of(FIND_OF_DIRECTORY_SEPARATOR, full_path.size()-1); - if(i == full_path.size()-1) // '/' not found! + if(i == std::string::npos) // '/' not found! { file = full_path ; dir = "." ; @@ -146,13 +171,13 @@ void RsDirUtil::removeTopDir(const std::string& dir, std::string& path) /* remove the subdir: [/][dir1.../][/] */ - int j = dir.find_last_not_of('/'); - int i = dir.rfind('/', j); + int j = dir.find_last_not_of(FIND_OF_DIRECTORY_SEPARATOR); + int i = dir.find_last_of(FIND_OF_DIRECTORY_SEPARATOR, j); /* remove any more slashes */ if (i > 0) { - i = dir.find_last_not_of('/', i); + i = dir.find_last_not_of(FIND_OF_DIRECTORY_SEPARATOR, i); } if (i > 0) @@ -169,8 +194,8 @@ std::string RsDirUtil::getRootDir(const std::string& dir) */ int i,j; int len = dir.length(); - for(i = 0; (i < len) && (dir[i] == '/'); i++) ; - for(j = i; (j < len) && (dir[j] != '/'); j++) ; + for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(dir[i]); i++) ; + for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(dir[j]); j++) ; if (i == j) return root; /* empty */ for(; i < j; i++) @@ -187,12 +212,12 @@ std::string RsDirUtil::removeRootDir(const std::string& path) std::string output; /* chew leading '/'s */ - for(i = 0; (i < len) && (path[i] == '/'); i++) ; + for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(path[i]); i++) ; if (i == len) return output; /* empty string */ - for(j = i; (j < len) && (path[j] != '/'); j++) ; /* run to next '/' */ - for(; (j < len) && (path[j] == '/'); j++) ; /* chew leading '/'s */ + for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(path[j]); j++) ; /* run to next '/' */ + for(; (j < len) && RsDirUtil::isDirectorySeparator(path[j]); j++) ; /* chew leading '/'s */ for(; j < len; j++) { @@ -213,7 +238,7 @@ std::string RsDirUtil::removeRootDirs(const std::string& path, const std::string if ((root.length() < 1) || (path.length() < 1)) return notroot; - if ((path[0] == '/') && (root[0] != '/')) + if (RsDirUtil::isDirectorySeparator(path[0]) && !RsDirUtil::isDirectorySeparator(root[0])) { i++; } @@ -232,7 +257,7 @@ std::string RsDirUtil::removeRootDirs(const std::string& path, const std::string return notroot; } - if (path[i] == '/') + if (RsDirUtil::isDirectorySeparator(path[i])) { i++; } @@ -256,7 +281,7 @@ int RsDirUtil::breakupDirList(const std::string& path, unsigned int i; for(i = 0; i < path.length(); i++) { - if (path[i] == '/') + if (RsDirUtil::isDirectorySeparator(path[i])) { if (i - start > 0) { @@ -871,11 +896,26 @@ std::string RsDirUtil::convertPathToUnix(std::string path) return path; } +bool RsDirUtil::isDirectorySeparator(const char &c) +{ + if (c == '/') { + return true; + } + +#ifdef WINDOWS_SYS + if (c == '\\') { + return true; + } +#endif + + return false; +} + std::string RsDirUtil::makePath(const std::string &path1, const std::string &path2) { std::string path = path1; - if (path.empty() == false && *path.rbegin() != '/') { + if (path.empty() == false && !RsDirUtil::isDirectorySeparator(*path.rbegin())) { path += "/"; } path += path2; @@ -1012,10 +1052,10 @@ std::wstring RsDirUtil::getWideTopDir(std::wstring dir) */ int i,j; int len = dir.length(); - for(j = len - 1; (j > 0) && (dir[j] == '/'); j--); - for(i = j; (i > 0) && (dir[i] != '/'); i--); + for(j = len - 1; (j > 0) && RsDirUtil::isDirectorySeparator(dir[j]); j--); + for(i = j; (i > 0) && !RsDirUtil::isDirectorySeparator(dir[i]); i--); - if (dir[i] == '/') + if (RsDirUtil::isDirectorySeparator(dir[i])) i++; for(; i <= j; i++) @@ -1034,11 +1074,11 @@ std::wstring RsDirUtil::removeWideTopDir(std::wstring dir) */ int i,j; int len = dir.length(); - for(j = len - 1; (j > 0) && (dir[j] == '/'); j--); - for(i = j; (i >= 0) && (dir[i] != '/'); i--); + for(j = len - 1; (j > 0) && RsDirUtil::isDirectorySeparator(dir[j]); j--); + for(i = j; (i >= 0) && !RsDirUtil::isDirectorySeparator(dir[i]); i--); /* remove any more slashes */ - for(; (i >= 0) && (dir[i] == '/'); i--); + for(; (i >= 0) && RsDirUtil::isDirectorySeparator(dir[i]); i--); for(j = 0; j <= i; j++) { @@ -1056,8 +1096,8 @@ std::wstring RsDirUtil::getWideRootDir(std::wstring dir) */ int i,j; int len = dir.length(); - for(i = 0; (i < len) && (dir[i] == '/'); i++); - for(j = i; (j < len) && (dir[j] != '/'); j++); + for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(dir[i]); i++); + for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(dir[j]); j++); if (i == j) return root; /* empty */ for(; i < j; i++) @@ -1074,12 +1114,12 @@ std::wstring RsDirUtil::removeWideRootDir(std::wstring path) std::wstring output; /* chew leading '/'s */ - for(i = 0; (i < len) && (path[i] == '/'); i++); + for(i = 0; (i < len) && RsDirUtil::isDirectorySeparator(path[i]); i++); if (i == len) return output; /* empty string */ - for(j = i; (j < len) && (path[j] != '/'); j++); /* run to next '/' */ - for(; (j < len) && (path[j] == '/'); j++); /* chew leading '/'s */ + for(j = i; (j < len) && !RsDirUtil::isDirectorySeparator(path[j]); j++); /* run to next '/' */ + for(; (j < len) && RsDirUtil::isDirectorySeparator(path[j]); j++); /* chew leading '/'s */ for(; j < len; j++) { @@ -1100,7 +1140,7 @@ std::wstring RsDirUtil::removeWideRootDirs(std::wstring path, std::wstring root) if ((root.length() < 1) || (path.length() < 1)) return notroot; - if ((path[0] == '/') && (root[0] != '/')) + if (RsDirUtil::isDirectorySeparator(path[0]) && !RsDirUtil::isDirectorySeparator(root[0])) { i++; } @@ -1119,7 +1159,7 @@ std::wstring RsDirUtil::removeWideRootDirs(std::wstring path, std::wstring root) return notroot; } - if (path[i] == '/') + if (RsDirUtil::isDirectorySeparator(path[i])) { i++; } @@ -1143,7 +1183,7 @@ int RsDirUtil::breakupWideDirList(std::wstring path, unsigned int i; for(i = 0; i < path.length(); i++) { - if (path[i] == '/') + if (RsDirUtil::isDirectorySeparator(path[i])) { if (i - start > 0) { diff --git a/libretroshare/src/util/rsdir.h b/libretroshare/src/util/rsdir.h index 9f4b2e6b5..c50caaf87 100644 --- a/libretroshare/src/util/rsdir.h +++ b/libretroshare/src/util/rsdir.h @@ -58,12 +58,20 @@ class RsStackFileLock namespace RsDirUtil { +// Returns the name of the directory on top of the given path (as opposed to the full path to that directory) std::string getTopDir(const std::string&); std::string getRootDir(const std::string&); std::string removeRootDir(const std::string& path); void removeTopDir(const std::string& dir, std::string &path); std::string removeRootDirs(const std::string& path, const std::string& root); +// Returns the filename at the end of the path. An empty string is returned if the path is a directory path. +std::string getFileName(const std::string& full_file_path); + +// Returns the directory (full path) that contains the given path (filename or directory). +// If a directory is supplied, the same path is returned. +std::string getDirectory(const std::string& full_file_path); + // Renames file from to file to. Files should be on the same file system. // returns true if succeed, false otherwise. bool renameFile(const std::string& from,const std::string& to) ; @@ -107,6 +115,12 @@ rstime_t lastWriteTime( std::error_condition& errc = RS_DEFAULT_STORAGE_PARAM(std::error_condition) ); bool checkDirectory(const std::string& dir); + +/*! + * \brief checkCreateDirectory + * \param dir + * \return false when the directory does not exist and could not be created. + */ bool checkCreateDirectory(const std::string& dir); // Removes all symbolic links along the path and computes the actual location of the file/dir passed as argument. @@ -159,6 +173,7 @@ bool getWideFileHash(std::wstring filepath, RsFileHash &hash, u FILE *rs_fopen(const char* filename, const char* mode); std::string convertPathToUnix(std::string path); +bool isDirectorySeparator(const char &c); /** Concatenate two path pieces putting '/' separator between them only if * needed */ diff --git a/libretroshare/src/util/rsfile.cc b/libretroshare/src/util/rsfile.cc new file mode 100644 index 000000000..6d3f7b1ab --- /dev/null +++ b/libretroshare/src/util/rsfile.cc @@ -0,0 +1,107 @@ +/******************************************************************************* + * libretroshare/src/util: rsfile.cc * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "util/rsfile.h" + +#ifdef WINDOWS_SYS +#include +#include +#include +#include +#else +#include +#endif + +int RsFileUtil::set_fd_nonblock(int fd) +{ + int ret = 0; + +/******************* OS SPECIFIC PART ******************/ +#ifdef WINDOWS_SYS + DWORD mode = PIPE_NOWAIT; + WINBOOL result = SetNamedPipeHandleState((HANDLE) _get_osfhandle(fd), &mode, nullptr, nullptr); + + if (!result) { + ret = -1; + } +#else // ie UNIX + int flags = fcntl(fd, F_GETFL); + ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK); +#endif + + return ret; +} + +ssize_t RsFileUtil::rs_getline(char **lineptr, size_t *n, FILE *stream) +{ +/******************* OS SPECIFIC PART ******************/ +#ifdef WINDOWS_SYS + if (lineptr == nullptr || n == nullptr || stream == nullptr) { + errno = EINVAL; + return -1; + } + + if (*lineptr == nullptr || *n < 1) { + *n = BUFSIZ; + *lineptr = (char*) malloc(*n); + if (*lineptr == nullptr) { + *n = 0; + return -1; + } + } + + char *ptr = *lineptr; + while (true) { + int c = fgetc(stream); + if (c == -1) { + if (feof(stream)) { + *ptr = '\0'; + return (ssize_t) (ptr - *lineptr); + } + return -1; + } + + *ptr = c; + ++ptr; + + if (c == '\n') { + *ptr = '\0'; + return ptr - *lineptr; + } + if (ptr + 2 >= *lineptr + *n) { + size_t new_size = *n * 2; + ssize_t diff = ptr - *lineptr; + + char *new_lineptr = (char*) realloc(*lineptr, new_size); + if (new_lineptr == nullptr) { + return -1; + } + + *lineptr = new_lineptr; + *n = new_size; + ptr = new_lineptr + diff; + } + } +#else // ie UNIX + return getline(lineptr, n, stream); +#endif +} diff --git a/libretroshare/src/util/rsfile.h b/libretroshare/src/util/rsfile.h new file mode 100644 index 000000000..9a23b6a4c --- /dev/null +++ b/libretroshare/src/util/rsfile.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * libretroshare/src/util: rsfile.h * + * * + * libretroshare: retroshare core library * + * * + * Copyright (C) 2021 Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#pragma once + +#include + +namespace RsFileUtil { + +int set_fd_nonblock(int fd); +ssize_t rs_getline(char **lineptr, size_t *n, FILE *stream); + +} diff --git a/libretroshare/src/util/rsprint.cc b/libretroshare/src/util/rsprint.cc index 4e547736a..5410ab0d0 100644 --- a/libretroshare/src/util/rsprint.cc +++ b/libretroshare/src/util/rsprint.cc @@ -22,6 +22,7 @@ #include "util/rsprint.h" #include "util/rsstring.h" +#include #include #include #include @@ -45,6 +46,13 @@ std::string RsUtil::NumberToString(uint64_t n,bool hex) return os.str(); } +bool RsUtil::StringToInt(const std::string& s,int& n) +{ + if(sscanf(s.c_str(),"%d",&n) == 1) + return true; + else + return false; +} std::string RsUtil::BinToHex(const std::string &bin) { return BinToHex(bin.c_str(), bin.length()); diff --git a/libretroshare/src/util/rsprint.h b/libretroshare/src/util/rsprint.h index dcaab6311..bdfcb942b 100644 --- a/libretroshare/src/util/rsprint.h +++ b/libretroshare/src/util/rsprint.h @@ -36,6 +36,10 @@ std::string BinToHex(const char *arr, const uint32_t len); std::string BinToHex(const unsigned char *arr, const uint32_t len, uint32_t max_len=0); bool HexToBin(const std::string& input,unsigned char *data, const uint32_t len); std::string NumberToString(uint64_t n, bool hex=false); + +// Returns in n the int that can be read in the string. Returns false when no int is fond. +bool StringToInt(const std::string& s,int& n); + std::string HashId(const std::string &id, bool reverse = false); std::vector BinToSha256(const std::vector &in); diff --git a/libretroshare/src/util/rsrandom.cc b/libretroshare/src/util/rsrandom.cc index dc7d9ac67..96a24b140 100644 --- a/libretroshare/src/util/rsrandom.cc +++ b/libretroshare/src/util/rsrandom.cc @@ -139,8 +139,12 @@ double RsRandom::random_f64() /*static*/ std::string RsRandom::printable(uint32_t length) { - std::string ret(length, 0); - random_bytes(reinterpret_cast(&ret[0]), length); - for(uint32_t i=0; i +# Copyright (C) 2021 Asociación Civil Altermundi +# +# SPDX-License-Identifier: CC0-1.0 + +cmake_minimum_required (VERSION 3.12.0) +project(openpgpsdk) + +find_package(ZLIB REQUIRED) +find_package(BZip2 REQUIRED) +find_package(OpenSSL REQUIRED) + +file(GLOB SOURCES src/openpgpsdk/*.c) +add_library(${PROJECT_NAME} ${SOURCES}) + +target_include_directories( + ${PROJECT_NAME} + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src + PRIVATE ${OPENSSL_INCLUDE_DIR} + PRIVATE ${BZIP2_INCLUDE_DIRS} + PRIVATE ${ZLIB_INCLUDE_DIRS} ) + +target_link_libraries( + ${PROJECT_NAME} + OpenSSL::SSL OpenSSL::Crypto BZip2::BZip2 ZLIB::ZLIB ) diff --git a/plugins/VOIP/gui/AudioWizard.cpp b/plugins/VOIP/gui/AudioWizard.cpp index cbf24d9b4..8c0841d1f 100644 --- a/plugins/VOIP/gui/AudioWizard.cpp +++ b/plugins/VOIP/gui/AudioWizard.cpp @@ -104,8 +104,8 @@ AudioWizard::AudioWizard(QWidget *p) : QWizard(p) { iMaxPeak = 0; iTicks = 0; - qpTalkingOn = QPixmap::fromImage(QImage(QLatin1String("skin:talking_on.svg")).scaled(64,64)); - qpTalkingOff = QPixmap::fromImage(QImage(QLatin1String("skin:talking_off.svg")).scaled(64,64)); + qpTalkingOn = QPixmap::fromImage(QImage(QLatin1String(":/images/talking_on.svg")).scaled(64,64)); + qpTalkingOff = QPixmap::fromImage(QImage(QLatin1String(":/images/talking_off.svg")).scaled(64,64)); bInit = false; diff --git a/plugins/VOIP/gui/VOIPConfigPanel.cpp b/plugins/VOIP/gui/VOIPConfigPanel.cpp index a46169dcb..a9912af8b 100644 --- a/plugins/VOIP/gui/VOIPConfigPanel.cpp +++ b/plugins/VOIP/gui/VOIPConfigPanel.cpp @@ -92,6 +92,9 @@ VOIPConfigPanel::VOIPConfigPanel(QWidget * parent, Qt::WindowFlags flags) inputAudioProcessor = NULL; inputAudioDevice = NULL; + graph_source = nullptr; + videoInput = nullptr; + videoProcessor = nullptr; qtTick = NULL; ui.qcbTransmit->addItem(tr("Continuous"), RsVOIP::AudioTransmitContinous); @@ -209,11 +212,16 @@ VOIPConfigPanel::~VOIPConfigPanel() void VOIPConfigPanel::clearPipeline() { - delete qtTick; + if (qtTick) { + delete qtTick; + qtTick = nullptr; + } - graph_source->stop() ; - graph_source->setVideoInput(NULL) ; - graph_source=nullptr; // is deleted by setSource below. This is a bad design. + if (graph_source) { + graph_source->stop() ; + graph_source->setVideoInput(NULL) ; + graph_source=nullptr; // is deleted by setSource below. This is a bad design. + } ui.voipBwGraph->setSource(nullptr); @@ -225,8 +233,10 @@ void VOIPConfigPanel::clearPipeline() videoInput = nullptr; } - delete videoProcessor; - videoProcessor = nullptr; + if (videoProcessor) { + delete videoProcessor; + videoProcessor = nullptr; + } if (inputAudioDevice) { inputAudioDevice->stop(); diff --git a/retroshare-gui/src/TorControl/CryptoKey.cpp b/retroshare-gui/src/TorControl/CryptoKey.cpp deleted file mode 100644 index 9be9a6699..000000000 --- a/retroshare-gui/src/TorControl/CryptoKey.cpp +++ /dev/null @@ -1,527 +0,0 @@ -/* 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 "CryptoKey.h" -#include "SecureRNG.h" -#include "Useful.h" -#include -#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 - -#ifdef TO_REMOVE -void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen); -bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen); -#endif - -CryptoKey::CryptoKey() -{ -} - -CryptoKey::~CryptoKey() -{ - clear(); -} - -#ifdef TO_REMOVE -CryptoKey::Data::~Data() -{ - if (key) - { - RSA_free(key); - key = 0; - } -} -#endif - -void CryptoKey::clear() -{ - key_data.clear(); -} - -#ifdef TO_REMOVE -bool CryptoKey::loadFromData(const QByteArray &data, KeyType type, KeyFormat format) -{ - RSA *key = NULL; - clear(); - - if (data.isEmpty()) - return false; - - if (format == PEM) { - BIO *b = BIO_new_mem_buf((void*)data.constData(), -1); - - if (type == PrivateKey) - key = PEM_read_bio_RSAPrivateKey(b, NULL, NULL, NULL); - else - key = PEM_read_bio_RSAPublicKey(b, NULL, NULL, NULL); - - BIO_free(b); - } else if (format == DER) { - const uchar *dp = reinterpret_cast(data.constData()); - - if (type == PrivateKey) - key = d2i_RSAPrivateKey(NULL, &dp, data.size()); - else - key = d2i_RSAPublicKey(NULL, &dp, data.size()); - } else { - Q_UNREACHABLE(); - } - - if (!key) { - qWarning() << "Failed to parse" << (type == PrivateKey ? "private" : "public") << "key from data"; - return false; - } - - d = new Data(key); - return true; -} -#endif - -bool CryptoKey::loadFromFile(const QString& path) -{ - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) - { - qWarning() << "Failed to open Tor key file " << path << ": " << file.errorString(); - return false; - } - - QByteArray data = file.readAll(); - file.close(); - - if(data.contains("-----BEGIN RSA PRIVATE KEY-----")) - { - std::cerr << "Note: Reading/converting Tor v2 key format." << std::endl; - - // This to be compliant with old format. New format is oblivious to the type of key so we dont need a header - data = data.replace("-----BEGIN RSA PRIVATE KEY-----",nullptr); - data = data.replace("-----END RSA PRIVATE KEY-----",nullptr); - data = data.replace("\n",nullptr); - data = data.replace("\t",nullptr); - - data = "RSA1024:"+data; - } - - std::cerr << "Have read the following key: " << std::endl; - std::cerr << QString(data).toStdString() << std::endl; - - key_data = data; - - return true; -} - -bool CryptoKey::loadFromTorMessage(const QByteArray& b) -{ - // note: We should probably check the structure a bit more, for security. - - std::cerr << "Loading new key:" << std::endl; - - if(b.startsWith("RSA1024")) - std::cerr << " type: RSA-1024 (Tor v2)" << std::endl; - else if(b.startsWith("ED25519-V3")) - std::cerr << " type: ED25519-V3 (Tor v3)" << std::endl; - else if(b.indexOf(':')) - { - std::cerr << " unknown type, or bad syntax in key: \"" << b.left(b.indexOf(':')).toStdString() << "\". Not accepted." << std::endl; - return false; - } - - key_data = b; - return true; -} - -/* Cryptographic hash of a password as expected by Tor's HashedControlPassword */ -QByteArray torControlHashedPassword(const QByteArray &password) -{ - QByteArray salt = SecureRNG::random(8); - if (salt.isNull()) - return QByteArray(); - - int count = ((quint32)16 + (96 & 15)) << ((96 >> 4) + 6); - - SHA_CTX hash; - SHA1_Init(&hash); - - QByteArray tmp = salt + password; - while (count) - { - int c = qMin(count, tmp.size()); - SHA1_Update(&hash, reinterpret_cast(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(); -} - - -#ifdef TO_REMOVE -bool CryptoKey::isPrivate() const -{ - if (!isLoaded()) { - return false; - } else { - const BIGNUM *p, *q; - RSA_get0_factors(d->key, &p, &q); - return (p != 0); - } -} - -int CryptoKey::bits() const -{ - return isLoaded() ? RSA_bits(d->key) : 0; -} - -QByteArray CryptoKey::publicKeyDigest() const -{ - if (!isLoaded()) - return QByteArray(); - - QByteArray buf = encodedPublicKey(DER); - - QByteArray re(20, 0); - bool ok = SHA1(reinterpret_cast(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; -} - -/* Copyright (c) 2001-2004, Roger Dingledine - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson - * Copyright (c) 2007-2010, The Tor Project, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * Neither the names of the copyright owners nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" - -/* Implements base32 encoding as in rfc3548. Requires that srclen*8 is a multiple of 5. */ -void base32_encode(char *dest, unsigned destlen, const char *src, unsigned srclen) -{ - unsigned i, bit, v, u; - unsigned nbits = srclen * 8; - - /* We need an even multiple of 5 bits, and enough space */ - if ((nbits%5) != 0 || destlen > (nbits/5)+1) { - Q_ASSERT(false); - memset(dest, 0, destlen); - return; - } - - for (i = 0, bit = 0; bit < nbits; ++i, bit += 5) - { - /* set v to the 16-bit value starting at src[bits/8], 0-padded. */ - v = ((quint8) src[bit / 8]) << 8; - if (bit + 5 < nbits) - v += (quint8) src[(bit/8)+1]; - - /* set u to the 5-bit value at the bit'th bit of src. */ - u = (v >> (11 - (bit % 8))) & 0x1F; - dest[i] = BASE32_CHARS[u]; - } - - dest[i] = '\0'; -} - -/* Implements base32 decoding as in rfc3548. Requires that srclen*5 is a multiple of 8. */ -bool base32_decode(char *dest, unsigned destlen, const char *src, unsigned srclen) -{ - unsigned int i, j, bit; - unsigned nbits = srclen * 5; - - /* We need an even multiple of 8 bits, and enough space */ - if ((nbits%8) != 0 || (nbits/8)+1 > destlen) { - Q_ASSERT(false); - return false; - } - - char *tmp = new char[srclen]; - - /* Convert base32 encoded chars to the 5-bit values that they represent. */ - for (j = 0; j < srclen; ++j) - { - if (src[j] > 0x60 && src[j] < 0x7B) - tmp[j] = src[j] - 0x61; - else if (src[j] > 0x31 && src[j] < 0x38) - tmp[j] = src[j] - 0x18; - else if (src[j] > 0x40 && src[j] < 0x5B) - tmp[j] = src[j] - 0x41; - else - { - delete[] tmp; - return false; - } - } - - /* Assemble result byte-wise by applying five possible cases. */ - for (i = 0, bit = 0; bit < nbits; ++i, bit += 8) - { - switch (bit % 40) - { - case 0: - dest[i] = (((quint8)tmp[(bit/5)]) << 3) + (((quint8)tmp[(bit/5)+1]) >> 2); - break; - case 8: - dest[i] = (((quint8)tmp[(bit/5)]) << 6) + (((quint8)tmp[(bit/5)+1]) << 1) - + (((quint8)tmp[(bit/5)+2]) >> 4); - break; - case 16: - dest[i] = (((quint8)tmp[(bit/5)]) << 4) + (((quint8)tmp[(bit/5)+1]) >> 1); - break; - case 24: - dest[i] = (((quint8)tmp[(bit/5)]) << 7) + (((quint8)tmp[(bit/5)+1]) << 2) - + (((quint8)tmp[(bit/5)+2]) >> 3); - break; - case 32: - dest[i] = (((quint8)tmp[(bit/5)]) << 5) + ((quint8)tmp[(bit/5)+1]); - break; - } - } - - delete[] tmp; - return true; -} - -#endif diff --git a/retroshare-gui/src/TorControl/CryptoKey.h b/retroshare-gui/src/TorControl/CryptoKey.h deleted file mode 100644 index c99703444..000000000 --- a/retroshare-gui/src/TorControl/CryptoKey.h +++ /dev/null @@ -1,106 +0,0 @@ -/* 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(); - -#ifdef TO_REMOVE - bool loadFromData(const QByteArray &data, KeyType type, KeyFormat format = PEM); - bool loadFromFile(const QString &path, KeyType type, KeyFormat format = PEM); -#endif - bool loadFromFile(const QString &path); - void clear(); - - const QByteArray bytes() const { return key_data; } - bool loadFromTorMessage(const QByteArray& b); - bool isLoaded() const { return !key_data.isNull(); } -#ifdef TO_REMOVE - bool isPrivate() const; - - QByteArray publicKeyDigest() const; - QByteArray encodedPublicKey(KeyFormat format) const; - QByteArray encodedPrivateKey(KeyFormat format) const; - QString torServiceID() const; - int bits() const; - - // Calculate and sign SHA-256 digest of data using this key and PKCS #1 v2.0 padding - QByteArray signData(const QByteArray &data) const; - // Verify a signature as per signData - bool verifyData(const QByteArray &data, QByteArray signature) const; - - // Sign the input SHA-256 digest using this key and PKCS #1 v2.0 padding - QByteArray signSHA256(const QByteArray &digest) const; - // Verify a signature as per signSHA256 - bool verifySHA256(const QByteArray &digest, QByteArray signature) const; -#endif - -private: -#ifdef TO_REMOVE - struct Data : public QSharedData - { - typedef struct rsa_st RSA; - RSA *key; - - Data(RSA *k = 0) : key(k) { } - ~Data(); - }; -#endif - - QByteArray key_data; -#ifdef TO_REMOVE - QExplicitlySharedDataPointer d; -#endif -}; - -QByteArray torControlHashedPassword(const QByteArray &password); - -#endif // CRYPTOKEY_H diff --git a/retroshare-gui/src/TorControl/SecureRNG.cpp b/retroshare-gui/src/TorControl/SecureRNG.cpp deleted file mode 100644 index 60cf3c048..000000000 --- a/retroshare-gui/src/TorControl/SecureRNG.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/* 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 -#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/Settings.cpp b/retroshare-gui/src/TorControl/Settings.cpp deleted file mode 100644 index b20d330b7..000000000 --- a/retroshare-gui/src/TorControl/Settings.cpp +++ /dev/null @@ -1,553 +0,0 @@ -/* 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 deleted file mode 100644 index 79ad032d1..000000000 --- a/retroshare-gui/src/TorControl/Settings.h +++ /dev/null @@ -1,257 +0,0 @@ -/* 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/TorControl.cpp b/retroshare-gui/src/TorControl/TorControl.cpp deleted file mode 100644 index 29979e2b3..000000000 --- a/retroshare-gui/src/TorControl/TorControl.cpp +++ /dev/null @@ -1,788 +0,0 @@ -/* 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 deleted file mode 100644 index 5d208656e..000000000 --- a/retroshare-gui/src/TorControl/TorControl.h +++ /dev/null @@ -1,144 +0,0 @@ -/* 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 = 0x00, - Connecting = 0x01, - Authenticating = 0x02, - Connected = 0x03 - }; - - enum TorStatus - { - TorUnknown = 0x00, - TorOffline = 0x01, - TorReady = 0x02 - }; - - - explicit TorControl(QObject *parent = 0); - - /* Information */ - Status status() const; - TorStatus torStatus() const; - QString torVersion() const; - bool torVersionAsNewAs(const QString &version) const; - QString errorMessage() const; - - bool hasConnectivity() const; - QHostAddress socksAddress() const; - quint16 socksPort() const; - QNetworkProxy connectionProxy(); - - /* Authentication */ - void setAuthPassword(const QByteArray &password); - - /* Connection */ - bool isConnected() const { return status() == Connected; } - void connect(const QHostAddress &address, quint16 port); - - /* Ownership means that tor is managed by this socket, and we - * can shut it down, own its configuration, etc. */ - bool hasOwnership() const; - void takeOwnership(); - - /* Hidden Services */ - QList 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/TorControlWindow.cpp b/retroshare-gui/src/TorControl/TorControlWindow.cpp index 6da6b8d7a..ddab3f281 100644 --- a/retroshare-gui/src/TorControl/TorControlWindow.cpp +++ b/retroshare-gui/src/TorControl/TorControlWindow.cpp @@ -9,42 +9,59 @@ #include #include "util/rstime.h" +#include "retroshare/rstor.h" +#include "retroshare/rsevents.h" + #include "TorControlWindow.h" -#include "TorManager.h" -#include "TorControl.h" -#include "HiddenService.h" +#include "util/qtthreadsutils.h" -TorControlDialog::TorControlDialog(Tor::TorManager *tm,QWidget *parent) - : mTorManager(tm) +TorControlDialog::TorControlDialog(QWidget *) { - setupUi(this) ; + setupUi(this) ; - QObject::connect(tm->control(),SIGNAL(statusChanged(int,int)),this,SLOT(statusChanged())) ; - QObject::connect(tm->control(),SIGNAL(connected()),this,SLOT(statusChanged())); - QObject::connect(tm->control(),SIGNAL(disconnected()),this,SLOT(statusChanged())); - QObject::connect(tm->control(),SIGNAL(bootstrapStatusChanged()),this,SLOT(statusChanged())); - QObject::connect(tm->control(),SIGNAL(connectivityChanged()),this,SLOT(statusChanged())); - QObject::connect(tm ,SIGNAL(errorChanged()),this,SLOT(statusChanged())); + mEventHandlerId = 0; // very important! - //QTimer::singleShot(2000,this,SLOT(checkForHiddenService())) ; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) + { + RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); + }, mEventHandlerId, RsEventType::TOR_MANAGER ); - mIncomingServer = new QTcpServer(this) ; - mHiddenService = NULL ; - mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_UNKNOWN; - //mBootstrapPhaseFinished = false ; + mIncomingServer = new QTcpServer(this) ; - connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection())); + connect(mIncomingServer, SIGNAL(QTcpServer::newConnection()), this, SLOT(onIncomingConnection())); - QTimer *timer = new QTimer ; + QTimer *timer = new QTimer ; - QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ; - timer->start(500) ; + QObject::connect(timer,SIGNAL(timeout()),this,SLOT(showLog())) ; + timer->start(300) ; - // Hide some debug output for the released version + // Hide some debug output for the released version - setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint ); + setWindowFlags( Qt::Dialog | Qt::FramelessWindowHint ); - adjustSize(); + adjustSize(); +} + +TorControlDialog::~TorControlDialog() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); +} + +void TorControlDialog::handleEvent_main_thread(std::shared_ptr event) +{ + if(event->mType != RsEventType::TOR_MANAGER) return; + + const RsTorManagerEvent *fe = dynamic_cast(event.get()); + if(!fe) + return; + + switch(fe->mTorManagerEventType) + { + case RsTorManagerEventCode::TOR_STATUS_CHANGED: + case RsTorManagerEventCode::TOR_CONNECTIVITY_CHANGED: statusChanged(fe->mTorStatus,fe->mTorConnectivityStatus); + break; + default: ; + } } void TorControlDialog::onIncomingConnection() @@ -52,67 +69,66 @@ void TorControlDialog::onIncomingConnection() std::cerr << "Incoming connection !!" << std::endl; } -void TorControlDialog::statusChanged() +void TorControlDialog::statusChanged(RsTorStatus torstatus, RsTorConnectivityStatus tor_control_status) { - int tor_control_status = mTorManager->control()->status(); - int torstatus = mTorManager->control()->torStatus(); - QString tor_control_status_str,torstatus_str ; - if(mTorManager->hasError()) - mErrorMsg = mTorManager->errorMessage() ; + if(RsTor::hasError()) + mErrorMsg = QString::fromStdString(RsTor::errorMessage()) ; switch(tor_control_status) { default: - case Tor::TorControl::Error : tor_control_status_str = "Error" ; break ; - case Tor::TorControl::NotConnected: tor_control_status_str = "Not connected" ; break ; - case Tor::TorControl::Connecting: tor_control_status_str = "Connecting" ; break ; - case Tor::TorControl::Authenticating: tor_control_status_str = "Authenticating" ; break ; - case Tor::TorControl::Connected: tor_control_status_str = "Connected" ; break ; - } + case RsTorConnectivityStatus::ERROR: tor_control_status_str = tr("Error") ; break ; + case RsTorConnectivityStatus::NOT_CONNECTED: tor_control_status_str = tr("Not connected") ; break ; + case RsTorConnectivityStatus::CONNECTING: tor_control_status_str = tr("Connecting") ; break ; + case RsTorConnectivityStatus::SOCKET_CONNECTED: tor_control_status_str = tr("Socket connected") ; break ; + case RsTorConnectivityStatus::AUTHENTICATING: tor_control_status_str = tr("Authenticating") ; break ; + case RsTorConnectivityStatus::AUTHENTICATED: tor_control_status_str = tr("Authenticated") ; break ; + case RsTorConnectivityStatus::HIDDEN_SERVICE_READY: tor_control_status_str = tr("Hidden service ready") ; break ; + case RsTorConnectivityStatus::UNKNOWN: tor_control_status_str = tr("Unknown") ; break ; + } switch(torstatus) { default: - case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ; - case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ; - case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ; + case RsTorStatus::UNKNOWN: torstatus_str = tr("Unknown") ; break ; + case RsTorStatus::OFFLINE: torstatus_str = tr("Tor offline") ; break ; + case RsTorStatus::READY: torstatus_str = tr("Tor ready") ; break ; } torStatus_LB->setText(torstatus_str) ; - if(torstatus == Tor::TorControl::TorUnknown) + if(torstatus == RsTorStatus::UNKNOWN) torStatus_LB->setToolTip(tr("Check that Tor is accessible in your executable path")) ; else torStatus_LB->setToolTip("") ; - QVariantMap qvm = mTorManager->control()->bootstrapStatus(); + std::map qvm = RsTor::bootstrapStatus(); QString bootstrapstatus_str ; std::cerr << "Tor control status: " << tor_control_status_str.toStdString() << std::endl; std::cerr << "Tor status: " << torstatus_str.toStdString() << std::endl; - std::cerr << "Bootstrap status map: " << std::endl; for(auto it(qvm.begin());it!=qvm.end();++it) - std::cerr << " " << it.key().toStdString() << " : " << it.value().toString().toStdString() << std::endl; + std::cerr << " " << it->first << " : " << it->second << std::endl; - if(!qvm["progress"].toString().isNull()) - torBootstrapStatus_LB->setText(qvm["progress"].toString() + " % (" + qvm["summary"].toString() + ")") ; + if(!qvm["progress"].empty()) + torBootstrapStatus_LB->setText(QString::fromStdString(qvm["progress"]) + " % (" + QString::fromStdString(qvm["summary"]) + ")") ; else torBootstrapStatus_LB->setText(tr("[Waiting for Tor...]")) ; - QString service_id ; - QString onion_address ; - QHostAddress service_target_address ; + std::string service_id ; + std::string onion_address ; + std::string service_target_address ; uint16_t service_port ; uint16_t target_port ; - if(mTorManager->getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port)) + if(RsTor::getHiddenServiceInfo(service_id,onion_address,service_port, service_target_address,target_port)) { - hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + service_target_address.toString() + ":" + QString::number(target_port)); - onionAddress_LB->setText(onion_address); + hiddenServiceAddress_LB->setText(QString::number(service_port) + ":" + QString::fromStdString(service_target_address) + ":" + QString::number(target_port)); + onionAddress_LB->setText(QString::fromStdString(onion_address)); } else { @@ -123,34 +139,39 @@ void TorControlDialog::statusChanged() showLog(); adjustSize(); - QCoreApplication::processEvents(); // forces update + QCoreApplication::processEvents(); // forces update } void TorControlDialog::showLog() { - static std::set already_seen ; + static std::set already_seen ; - QString s ; - QStringList logmsgs = mTorManager->logMessages() ; - bool can_print = false ; + std::string s ; + std::list logmsgs = RsTor::logMessages() ; + bool can_print = false ; - for(QStringList::const_iterator it(logmsgs.begin());it!=logmsgs.end();++it) - { + for(auto it(logmsgs.begin());it!=logmsgs.end();++it) + { s += *it + "\n" ; - if(already_seen.find(*it) == already_seen.end()) - { - can_print = true ; - already_seen.insert(*it); - } + if(already_seen.find(*it) == already_seen.end()) + { + can_print = true ; + already_seen.insert(*it); + } - if(can_print) - std::cerr << "[TOR DEBUG LOG] " << (*it).toStdString() << std::endl; - } + if(can_print) + { + std::cerr << "[TOR DEBUG LOG] " << *it << std::endl; -// torLog_TB->setText(s) ;: + QString s = QString::fromStdString(*it); + int n = s.indexOf(QString("Bootstrapped")); - std::cerr << "Connexion Proxy: " << mTorManager->control()->socksAddress().toString().toStdString() << ":" << mTorManager->control()->socksPort() << std::endl; + if(n >= 0) + torBootstrapStatus_LB->setText(s.mid(n+QString("Bootstrapped").length())); + } + } + //std::cerr << "Connexion Proxy: " << RsTor::socksAddress() << ":" << QString::number(RsTor::socksPort()).toStdString() << std::endl; } TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg) @@ -161,9 +182,9 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg) return TorControlDialog::TOR_STATUS_FAIL ; } - switch(mTorManager->control()->torStatus()) + switch(RsTor::torStatus()) { - case Tor::TorControl::TorReady: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ; + case RsTorStatus::READY: rstime::rs_usleep(1*1000*1000);return TOR_STATUS_OK ; default: return TOR_STATUS_UNKNOWN ; } @@ -171,60 +192,35 @@ TorControlDialog::TorStatus TorControlDialog::checkForTor(QString& error_msg) TorControlDialog::HiddenServiceStatus TorControlDialog::checkForHiddenService() { - std::cerr << "Checking for hidden services:" ; + std::cerr << "Checking for hidden services:" ; - switch(mHiddenServiceStatus) - { - default: - case HIDDEN_SERVICE_STATUS_UNKNOWN: { + std::string service_id; - std::cerr << " trying to setup. " ; + RsTorHiddenServiceStatus service_status = RsTor::getHiddenServiceStatus(service_id); - if(!mTorManager->setupHiddenService()) - { - mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_FAIL ; - std::cerr << "Failed." << std::endl; - return mHiddenServiceStatus ; - } - std::cerr << "Done." << std::endl; - mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_REQUESTED ; - return mHiddenServiceStatus ; - } + if(service_id.empty()) + { + std::cerr << "Not ready yet." << std::endl; + return HIDDEN_SERVICE_STATUS_REQUESTED ; + } + else + { + if(mHiddenService.empty()) + mHiddenService = service_id ; - case HIDDEN_SERVICE_STATUS_REQUESTED: { - QList hidden_services = mTorManager->control()->hiddenServices(); + std::cerr << "New service acquired. Status is " << (int)service_status ; - if(hidden_services.empty()) - { - std::cerr << "Not ready yet." << std::endl; - return mHiddenServiceStatus ; - } - else - { - if(mHiddenService == NULL) - mHiddenService = *(hidden_services.begin()) ; + if(service_status == RsTorHiddenServiceStatus::ONLINE) + { + std::cerr << ": published and running!" << std::endl; - Tor::HiddenService::Status hss = mHiddenService->status(); - - std::cerr << "New service acquired. Status is " << hss ; - - if(hss == Tor::HiddenService::Online) - { - mHiddenServiceStatus = HIDDEN_SERVICE_STATUS_OK ; - std::cerr << ": published and running!" << std::endl; - - return mHiddenServiceStatus ; - } - else - { - std::cerr << ": not ready yet." << std::endl; - return mHiddenServiceStatus ; - } - } - } - case HIDDEN_SERVICE_STATUS_OK : - std::cerr << "New service acquired." << std::endl; - return mHiddenServiceStatus ; - } + return HIDDEN_SERVICE_STATUS_OK ; + } + else + { + std::cerr << ": not ready yet." << std::endl; + return HIDDEN_SERVICE_STATUS_REQUESTED ; + } + } } diff --git a/retroshare-gui/src/TorControl/TorControlWindow.h b/retroshare-gui/src/TorControl/TorControlWindow.h index c4e8ce522..412c08ed5 100644 --- a/retroshare-gui/src/TorControl/TorControlWindow.h +++ b/retroshare-gui/src/TorControl/TorControlWindow.h @@ -1,3 +1,5 @@ +#include "retroshare/rsevents.h" +#include "retroshare/rstor.h" #include "ui_TorControlWindow.h" class QTcpServer ; @@ -12,7 +14,8 @@ class TorControlDialog: public QWidget, public Ui::TorControlDialog Q_OBJECT public: - TorControlDialog(Tor::TorManager *tm,QWidget *parent =NULL); + TorControlDialog(QWidget *parent =NULL); + virtual ~TorControlDialog(); enum TorStatus { TOR_STATUS_UNKNOWN = 0x00, @@ -34,15 +37,14 @@ public: protected slots: void showLog(); - void statusChanged(); - void onIncomingConnection(); + void statusChanged(RsTorStatus torstatus,RsTorConnectivityStatus tor_control_status); + void onIncomingConnection(); + void handleEvent_main_thread(std::shared_ptr event); private: QString mErrorMsg ; - HiddenServiceStatus mHiddenServiceStatus ; - - Tor::TorManager *mTorManager ; - Tor::HiddenService *mHiddenService ; + std::string mHiddenService; QTcpServer *mIncomingServer ; + RsEventsHandlerId_t mEventHandlerId; }; diff --git a/retroshare-gui/src/TorControl/TorControlWindow.ui b/retroshare-gui/src/TorControl/TorControlWindow.ui index 602146641..6077485e5 100644 --- a/retroshare-gui/src/TorControl/TorControlWindow.ui +++ b/retroshare-gui/src/TorControl/TorControlWindow.ui @@ -40,6 +40,12 @@ + + + 0 + 0 + + @@ -55,6 +61,12 @@ + + + 0 + 0 + + Tor status: @@ -79,8 +91,14 @@ + + + 0 + 0 + + - Hidden service address: + Hidden address: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -89,8 +107,14 @@ + + + 0 + 0 + + - Tor bootstrap status: + Tor status: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -106,6 +130,12 @@ + + + 0 + 0 + + Onion address: diff --git a/retroshare-gui/src/TorControl/TorManager.cpp b/retroshare-gui/src/TorControl/TorManager.cpp deleted file mode 100644 index 9f31777e6..000000000 --- a/retroshare-gui/src/TorControl/TorManager.cpp +++ /dev/null @@ -1,526 +0,0 @@ -/* 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; -} - -bool TorManager::isTorAvailable() -{ - return !instance()->d->torExecutablePath().isNull(); -} - -QString TorManager::torDataDirectory() const -{ - return d->dataDir; -} - -void TorManager::setTorDataDirectory(const QString &path) -{ - d->dataDir = QDir::fromNativeSeparators(path); - - if (!d->dataDir.isEmpty() && !d->dataDir.endsWith(QLatin1Char('/'))) - d->dataDir.append(QLatin1Char('/')); -} - -QString TorManager::hiddenServiceDirectory() const -{ - return d->hiddenServiceDir; -} -void TorManager::setHiddenServiceDirectory(const QString &path) -{ - d->hiddenServiceDir = QDir::fromNativeSeparators(path); - - if (!d->hiddenServiceDir.isEmpty() && !d->hiddenServiceDir.endsWith(QLatin1Char('/'))) - d->hiddenServiceDir.append(QLatin1Char('/')); -} - -bool TorManager::setupHiddenService() -{ - if(d->hiddenService != NULL) - { - std::cerr << "TorManager: setupHiddenService() called twice! Not doing anything this time." << std::endl; - return true ; - } - - QString keyData ;//= m_settings->read("serviceKey").toString(); - QString legacyDir = d->hiddenServiceDir; - - std::cerr << "TorManager: setting up hidden service." << std::endl; - - if(legacyDir.isNull()) - { - std::cerr << "legacy dir not set! Cannot proceed." << std::endl; - return false ; - } - - std::cerr << "Using legacy dir: " << legacyDir.toStdString() << std::endl; - - if (!legacyDir.isEmpty() && QFile::exists(legacyDir + QLatin1String("/private_key"))) - { - std::cerr << "Attempting to load key from legacy filesystem format in " << legacyDir.toStdString() << std::endl; - - CryptoKey key; - if (!key.loadFromFile(legacyDir + QLatin1String("/private_key"))) - { - qWarning() << "Cannot load legacy format key from" << legacyDir << "for conversion"; - return false; - } - - d->hiddenService = new Tor::HiddenService(key, legacyDir, this); - - std::cerr << "Got key from legacy dir: " << std::endl; - std::cerr << key.bytes().toStdString() << std::endl; - } - else - { - d->hiddenService = new Tor::HiddenService(legacyDir, this); - - std::cerr << "Creating new hidden service." << std::endl; - - connect(d->hiddenService, SIGNAL(privateKeyChanged()), this, SLOT(hiddenServicePrivateKeyChanged())) ; - connect(d->hiddenService, SIGNAL(hostnameChanged()), this, SLOT(hiddenServiceHostnameChanged())) ; - } - - Q_ASSERT(d->hiddenService); - connect(d->hiddenService, SIGNAL(statusChanged(int,int)), this, SLOT(hiddenServiceStatusChanged(int,int))); - - // Generally, these are not used, and we bind to localhost and port 0 - // for an automatic (and portable) selection. - - QHostAddress address = QHostAddress::LocalHost; // we only listen from localhost - - quint16 port = 7934;//(quint16)m_settings->read("localListenPort").toInt(); - - std::cerr << "Testing host address: " << address.toString().toStdString() << ":" << port ; - - if (!QTcpServer().listen(address, port)) - { - // XXX error case - std::cerr << " Failed to open incoming socket" << std::endl; - return false; - } - - std::cerr << " OK - Adding hidden service to TorControl." << std::endl; - - //d->hiddenService->addTarget(9878, mIncomingServer->serverAddress(), mIncomingServer->serverPort()); - d->hiddenService->addTarget(9878, QHostAddress::LocalHost,7934); - control()->addHiddenService(d->hiddenService); - - return true ; -} - -void TorManager::hiddenServiceStatusChanged(int old_status,int new_status) -{ - std::cerr << "Hidden service status changed from " << old_status << " to " << new_status << std::endl; -} - -void TorManager::hiddenServicePrivateKeyChanged() -{ - QString key = QString::fromLatin1(d->hiddenService->privateKey().bytes()); - - QFile outfile(d->hiddenServiceDir + QLatin1String("/private_key")) ; - outfile.open( QIODevice::WriteOnly | QIODevice::Text ); - QTextStream s(&outfile); - -#ifdef TO_REMOVE - s << "-----BEGIN RSA PRIVATE KEY-----" << endl; - - for(int i=0;ihiddenServiceDir + QLatin1String("/hostname")) ; - outfile2.open( QIODevice::WriteOnly | QIODevice::Text ); - QTextStream t(&outfile2); - - QString hostname(d->hiddenService->hostname()); - - t << hostname << endl; - outfile2.close(); - - std::cerr << "Hidden service hostname changed: " << hostname.toStdString() << std::endl; -} - -bool TorManager::configurationNeeded() const -{ - return d->configNeeded; -} - -QStringList TorManager::logMessages() const -{ - return d->logMessages; -} - -bool TorManager::hasError() const -{ - return !d->errorMessage.isEmpty(); -} - -QString TorManager::errorMessage() const -{ - return d->errorMessage; -} - -bool TorManager::start() -{ - if (!d->errorMessage.isEmpty()) { - d->errorMessage.clear(); - emit errorChanged(); - } - - SettingsObject settings(QStringLiteral("tor")); - - // If a control port is defined by config or environment, skip launching tor - if (!settings.read("controlPort").isUndefined() || - !qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT")) - { - QHostAddress address(settings.read("controlAddress").toString()); - quint16 port = (quint16)settings.read("controlPort").toInt(); - QByteArray password = settings.read("controlPassword").toString().toLatin1(); - - if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_HOST")) - address = QHostAddress(QString::fromLatin1(qgetenv("TOR_CONTROL_HOST"))); - - if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PORT")) { - bool ok = false; - port = qgetenv("TOR_CONTROL_PORT").toUShort(&ok); - if (!ok) - port = 0; - } - - if (!qEnvironmentVariableIsEmpty("TOR_CONTROL_PASSWD")) - password = qgetenv("TOR_CONTROL_PASSWD"); - - if (!port) { - d->setError(QStringLiteral("Invalid control port settings from environment or configuration")); - return false; - } - - if (address.isNull()) - address = QHostAddress::LocalHost; - - d->control->setAuthPassword(password); - d->control->connect(address, port); - } else { - // Launch a bundled Tor instance - QString executable = d->torExecutablePath(); - - std::cerr << "Executable path: " << executable.toStdString() << std::endl; - - if (executable.isEmpty()) { - d->setError(QStringLiteral("Cannot find tor executable")); - return false; - } - - if (!d->process) { - d->process = new TorProcess(this); - connect(d->process, SIGNAL(stateChanged(int)), d, SLOT(processStateChanged(int))); - connect(d->process, SIGNAL(errorMessageChanged(QString)), d, - SLOT(processErrorChanged(QString))); - connect(d->process, SIGNAL(logMessage(QString)), d, SLOT(processLogMessage(QString))); - } - - if (!QFile::exists(d->dataDir) && !d->createDataDir(d->dataDir)) { - d->setError(QStringLiteral("Cannot write data location: %1").arg(d->dataDir)); - return false; - } - - QString defaultTorrc = d->dataDir + QStringLiteral("default_torrc"); - if (!QFile::exists(defaultTorrc) && !d->createDefaultTorrc(defaultTorrc)) { - d->setError(QStringLiteral("Cannot write data files: %1").arg(defaultTorrc)); - return false; - } - - QFile torrc(d->dataDir + QStringLiteral("torrc")); - if (!torrc.exists() || torrc.size() == 0) { - d->configNeeded = true; - emit configurationNeededChanged(); - } - - d->process->setExecutable(executable); - d->process->setDataDir(d->dataDir); - d->process->setDefaultTorrc(defaultTorrc); - d->process->start(); - } - return true ; -} - -bool TorManager::getProxyServerInfo(QHostAddress& proxy_server_adress,uint16_t& proxy_server_port) -{ - proxy_server_adress = control()->socksAddress(); - proxy_server_port = control()->socksPort(); - - return proxy_server_port > 1023 ; -} - -bool TorManager::getHiddenServiceInfo(QString& service_id,QString& service_onion_address,uint16_t& service_port, QHostAddress& service_target_address,uint16_t& target_port) -{ - QList hidden_services = control()->hiddenServices(); - - if(hidden_services.empty()) - return false ; - - // Only return the first one. - - for(auto it(hidden_services.begin());it!=hidden_services.end();++it) - { - service_onion_address = (*it)->hostname(); - service_id = (*it)->serviceId(); - - for(auto it2((*it)->targets().begin());it2!=(*it)->targets().end();++it2) - { - service_port = (*it2).servicePort ; - service_target_address = (*it2).targetAddress ; - target_port = (*it2).targetPort; - break ; - } - break ; - } - return true ; -} - -void TorManagerPrivate::processStateChanged(int state) -{ - std::cerr << Q_FUNC_INFO << "state: " << state << " passwd=\"" << QString(process->controlPassword()).toStdString() << "\" " << process->controlHost().toString().toStdString() - << ":" << process->controlPort() << std::endl; - if (state == TorProcess::Ready) { - control->setAuthPassword(process->controlPassword()); - control->connect(process->controlHost(), process->controlPort()); - } -} - -void TorManagerPrivate::processErrorChanged(const QString &errorMessage) -{ - std::cerr << "tor error:" << errorMessage.toStdString() << std::endl; - setError(errorMessage); -} - -void TorManagerPrivate::processLogMessage(const QString &message) -{ - std::cerr << "tor:" << message.toStdString() << std::endl; - if (logMessages.size() >= 50) - logMessages.takeFirst(); - logMessages.append(message); -} - -void TorManagerPrivate::controlStatusChanged(int status) -{ - if (status == TorControl::Connected) { - if (!configNeeded) { - // If DisableNetwork is 1, trigger configurationNeeded - connect(control->getConfiguration(QStringLiteral("DisableNetwork")), - SIGNAL(finished()), SLOT(getConfFinished())); - } - - if (process) { - // Take ownership via this control socket - control->takeOwnership(); - } - } -} - -void TorManagerPrivate::getConfFinished() -{ - GetConfCommand *command = qobject_cast(sender()); - if (!command) - return; - - if (command->get("DisableNetwork").toInt() == 1 && !configNeeded) { - configNeeded = true; - emit q->configurationNeededChanged(); - } -} - -QString TorManagerPrivate::torExecutablePath() const -{ - SettingsObject settings(QStringLiteral("tor")); - QString path = settings.read("executablePath").toString(); - - if (!path.isEmpty() && QFile::exists(path)) - return path; - -#ifdef Q_OS_WIN - QString filename(QStringLiteral("/tor/tor.exe")); -#else - QString filename(QStringLiteral("/tor")); -#endif - - path = qApp->applicationDirPath(); - - if (QFile::exists(path + filename)) - return path + filename; - -#ifdef BUNDLED_TOR_PATH - path = QStringLiteral(BUNDLED_TOR_PATH); - if (QFile::exists(path + filename)) - return path + filename; -#endif - -#ifdef __APPLE__ - // on MacOS, try traditional brew installation path - - path = QStringLiteral("/usr/local/opt/tor/bin") ; - - if (QFile::exists(path + filename)) - return path + filename; -#endif - - // Try $PATH - return filename.mid(1); -} - -bool TorManagerPrivate::createDataDir(const QString &path) -{ - QDir dir(path); - return dir.mkpath(QStringLiteral(".")); -} - -bool TorManagerPrivate::createDefaultTorrc(const QString &path) -{ - static const char defaultTorrcContent[] = - "SocksPort auto\n" - "AvoidDiskWrites 1\n" -// "DisableNetwork 1\n" // (cyril) I removed this because it prevents Tor to bootstrap. - "__ReloadTorrcOnSIGHUP 0\n"; - - QFile file(path); - if (!file.open(QIODevice::WriteOnly)) - return false; - if (file.write(defaultTorrcContent) < 0) - return false; - return true; -} - -void TorManagerPrivate::setError(const QString &message) -{ - errorMessage = message; - emit q->errorChanged(); -} - -#include "TorManager.moc" - diff --git a/retroshare-gui/src/TorControl/TorProcess.cpp b/retroshare-gui/src/TorControl/TorProcess.cpp deleted file mode 100644 index ca1aecfe5..000000000 --- a/retroshare-gui/src/TorControl/TorProcess.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* 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_p.h b/retroshare-gui/src/TorControl/TorProcess_p.h deleted file mode 100644 index 9aa2585e3..000000000 --- a/retroshare-gui/src/TorControl/TorProcess_p.h +++ /dev/null @@ -1,79 +0,0 @@ -/* 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 deleted file mode 100644 index fb9c07b26..000000000 --- a/retroshare-gui/src/TorControl/TorSocket.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* 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 deleted file mode 100644 index 0c68f7854..000000000 --- a/retroshare-gui/src/TorControl/TorSocket.h +++ /dev/null @@ -1,97 +0,0 @@ -/* 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/gui/ChatLobbyWidget.cpp b/retroshare-gui/src/gui/ChatLobbyWidget.cpp index d369c9bcf..13fdc4329 100644 --- a/retroshare-gui/src/gui/ChatLobbyWidget.cpp +++ b/retroshare-gui/src/gui/ChatLobbyWidget.cpp @@ -59,6 +59,10 @@ #define COLUMN_COUNT 3 #define COLUMN_DATA 0 +#define COLUMN_NAME_NB_CHAR 30 +#define COLUMN_USER_COUNT_NB_CHAR 4 +#define COLUMN_TOPIC_NB_CHAR 25 + #define ROLE_SORT Qt::UserRole #define ROLE_ID Qt::UserRole + 1 #define ROLE_SUBSCRIBED Qt::UserRole + 2 @@ -88,6 +92,13 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags) { ui.setupUi(this); + int H = QFontMetricsF(ui.lobbyTreeWidget->font()).height(); +#if QT_VERSION < QT_VERSION_CHECK(5,11,0) + int W = QFontMetricsF(ui.lobbyTreeWidget->font()).width("_"); +#else + int W = QFontMetricsF(ui.lobbyTreeWidget->font()).horizontalAdvance("_"); +#endif + m_bProcessSettings = false; myChatLobbyUserNotify = NULL; myInviteYesButton = NULL; @@ -108,6 +119,10 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags) compareRole = new RSTreeWidgetItemCompareRole; compareRole->setRole(COLUMN_NAME, ROLE_SORT); + RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this); + itemDelegate->setSpacing(QSize(W/2, H/4)); + ui.lobbyTreeWidget->setItemDelegate(itemDelegate); + ui.lobbyTreeWidget->setColumnCount(COLUMN_COUNT); ui.lobbyTreeWidget->sortItems(COLUMN_NAME, Qt::AscendingOrder); @@ -159,14 +174,11 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags) ui.lobbyTreeWidget->setColumnHidden(COLUMN_USER_COUNT,true) ; ui.lobbyTreeWidget->setColumnHidden(COLUMN_TOPIC,true) ; ui.lobbyTreeWidget->setSortingEnabled(true) ; - ui.lobbyTreeWidget->setItemDelegateForColumn(COLUMN_NAME, new RSElidedItemDelegate()); - float fact = QFontMetricsF(font()).height()/14.0f; - ui.lobbyTreeWidget->adjustSize(); - ui.lobbyTreeWidget->setColumnWidth(COLUMN_NAME,100*fact); - ui.lobbyTreeWidget->setColumnWidth(COLUMN_USER_COUNT, 50*fact); - ui.lobbyTreeWidget->setColumnWidth(COLUMN_TOPIC, 50*fact); + ui.lobbyTreeWidget->setColumnWidth(COLUMN_NAME,COLUMN_NAME_NB_CHAR*W); + ui.lobbyTreeWidget->setColumnWidth(COLUMN_USER_COUNT, COLUMN_USER_COUNT_NB_CHAR*W); + ui.lobbyTreeWidget->setColumnWidth(COLUMN_TOPIC, COLUMN_TOPIC_NB_CHAR*W); /** Setup the actions for the header context menu */ showUserCountAct= new QAction(headerItem->text(COLUMN_USER_COUNT),this); @@ -181,7 +193,7 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags) ui.splitter->setStretchFactor(1, 1); QList sizes; - sizes << 200*fact << width(); // Qt calculates the right sizes + sizes << ui.lobbyTreeWidget->columnWidth(COLUMN_NAME) << width(); // Qt calculates the right sizes ui.splitter->setSizes(sizes); lobbyChanged(); @@ -194,7 +206,6 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags) // load settings processSettings(true); - int S = QFontMetricsF(font()).height(); QString help_str = tr("\

  Chat Rooms

\

Chat rooms work pretty much like IRC. \ @@ -212,11 +223,11 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags) Note: For the chat rooms to work properly, your computer needs be on time. So check your system clock!\

\ " - ).arg(QString::number(2*S), QString::number(S)) ; + ).arg(QString::number(4*W), QString::number(2*W)) ; - registerHelpButton(ui.helpButton,help_str,"ChatLobbyDialog") ; - - ui.lobbyTreeWidget->setIconSize(QSize(S*1.5,S*1.5)); + registerHelpButton(ui.helpButton,help_str,"ChatLobbyDialog") ; + + ui.lobbyTreeWidget->setIconSize(QSize(H*1.5,H*1.5)); } ChatLobbyWidget::~ChatLobbyWidget() @@ -232,7 +243,7 @@ ChatLobbyWidget::~ChatLobbyWidget() UserNotify *ChatLobbyWidget::createUserNotify(QObject *parent) { myChatLobbyUserNotify = new ChatLobbyUserNotify(parent); - connect(myChatLobbyUserNotify, SIGNAL(countChanged(ChatLobbyId, uint)), this, SLOT(updateNotify(ChatLobbyId, uint))); + connect(myChatLobbyUserNotify, SIGNAL(countChanged(ChatLobbyId,uint)), this, SLOT(updateNotify(ChatLobbyId,uint))); return myChatLobbyUserNotify; } diff --git a/retroshare-gui/src/gui/ChatLobbyWidget.ui b/retroshare-gui/src/gui/ChatLobbyWidget.ui index 5a679189e..8f8e8f29d 100644 --- a/retroshare-gui/src/gui/ChatLobbyWidget.ui +++ b/retroshare-gui/src/gui/ChatLobbyWidget.ui @@ -102,7 +102,7 @@ Qt::NoFocus - + :/icons/help_64.png:/icons/help_64.png @@ -191,6 +191,11 @@
+ + + 11 + + 16 @@ -459,7 +464,7 @@ LineEditClear QLineEdit -
gui/common/LineEditClear.h
+
gui/common/LineEditClear.h
RSTreeWidget @@ -468,7 +473,6 @@ - diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index 6aa4e1ce1..1abdb698b 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -33,15 +33,15 @@ #include #include "gui/settings/rsharesettings.h" -#include "TorControl/TorManager.h" #include "util/misc.h" #include "gui/common/FilesDefs.h" -#include -#include -#include -#include -#include +#include "retroshare/rstor.h" +#include "retroshare/rsidentity.h" +#include "retroshare/rsinit.h" +#include "retroshare/rsnotify.h" +#include "rsserver/rsaccounts.h" +#include "util/rsrandom.h" #include #include @@ -520,7 +520,7 @@ void GenCertDialog::genPerson() bool isHiddenLoc = (ui.nodeType_CB->currentIndex()>0); bool isAutoTor = (ui.nodeType_CB->currentIndex()==1); - if(isAutoTor && !Tor::TorManager::isTorAvailable()) + if(isAutoTor && !RsTor::isTorAvailable()) { QMessageBox::critical(this,tr("Tor is not available"),tr("No Tor executable has been found on your system. You need to install Tor before creating a hidden identity.")) ; return ; diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 2585a286c..a7536b048 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -169,15 +169,18 @@ IdDialog::IdDialog(QWidget *parent) //connect(mCirclesBroadcastBase, SIGNAL(fillDisplay(bool)), this, SLOT(updateCirclesDisplay(bool))); ownItem = new QTreeWidgetItem(); - ownItem->setText(0, tr("My own identities")); + ownItem->setText(RSID_COL_NICKNAME, tr("My own identities")); + ownItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font()); ownItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); // this is in order to prevent displaying a reputaiton icon next to these items. allItem = new QTreeWidgetItem(); - allItem->setText(0, tr("All")); + allItem->setText(RSID_COL_NICKNAME, tr("All")); + allItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font()); allItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); contactsItem = new QTreeWidgetItem(); - contactsItem->setText(0, tr("My contacts")); + contactsItem->setText(RSID_COL_NICKNAME, tr("My contacts")); + contactsItem->setFont(RSID_COL_NICKNAME, ui->idTreeWidget->font()); contactsItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); @@ -366,6 +369,7 @@ IdDialog::IdDialog(QWidget *parent) ui->idTreeWidget->setColumnWidth(RSID_COL_IDTYPE, 18 * fontWidth); ui->idTreeWidget->setColumnWidth(RSID_COL_VOTES, 2 * fontWidth); + ui->idTreeWidget->setItemDelegate(new RSElidedItemDelegate()); ui->idTreeWidget->setItemDelegateForColumn( RSID_COL_NICKNAME, new GxsIdTreeItemDelegate()); @@ -648,15 +652,16 @@ void IdDialog::loadCircles(const std::list& groupInfo) if(!mExternalOtherCircleItem) { mExternalOtherCircleItem = new QTreeWidgetItem(); - mExternalOtherCircleItem->setText(0, tr("Other circles")); - + mExternalOtherCircleItem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Other circles")); + mExternalOtherCircleItem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, ui->treeWidget_membership->font()); ui->treeWidget_membership->addTopLevelItem(mExternalOtherCircleItem); } if(!mExternalBelongingCircleItem ) { mExternalBelongingCircleItem = new QTreeWidgetItem(); - mExternalBelongingCircleItem->setText(0, tr("Circles I belong to")); + mExternalBelongingCircleItem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, tr("Circles I belong to")); + mExternalBelongingCircleItem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME, ui->treeWidget_membership->font()); ui->treeWidget_membership->addTopLevelItem(mExternalBelongingCircleItem); } #endif @@ -732,14 +737,11 @@ void IdDialog::loadCircles(const std::list& groupInfo) item->setToolTip(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,tooltip); - if (am_I_admin) - { - QFont font = item->font(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) ; - font.setBold(true) ; - item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ; - item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ; - item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ; - } + QFont font = ui->treeWidget_membership->font() ; + font.setBold(am_I_admin) ; + item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ; + item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ; + item->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ; // now determine for this circle wether we have pending invites // we add a sub-item to the circle (to show the invite system info) in the following two cases: @@ -854,12 +856,10 @@ void IdDialog::loadCircles(const std::list& groupInfo) subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS, Qt::UserRole, QVariant(it->second)) ; subitem->setData(CIRCLEGROUP_CIRCLE_COL_GROUPID, Qt::UserRole, QString::fromStdString(it->first.toStdString())) ; - //subitem->setIcon(RSID_COL_NICKNAME, QIcon(pixmap)); - - new_sub_items.push_back(subitem); + new_sub_items.push_back(subitem); } - else - subitem = item->child(subitem_index); + else + subitem = item->child(subitem_index); if(invited && !subscrb) { @@ -878,15 +878,12 @@ void IdDialog::loadCircles(const std::list& groupInfo) if(invited && subscrb) subitem->setText(CIRCLEGROUP_CIRCLE_COL_GROUPID, tr("Member")) ; - if (is_own_id) - { - QFont font = subitem->font(CIRCLEGROUP_CIRCLE_COL_GROUPNAME) ; - font.setBold(true) ; - subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ; - subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ; - subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ; - } - } + QFont font = ui->treeWidget_membership->font() ; + font.setBold(is_own_id) ; + subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPNAME,font) ; + subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPID,font) ; + subitem->setFont(CIRCLEGROUP_CIRCLE_COL_GROUPFLAGS,font) ; + } // add all items item->addChildren(new_sub_items); @@ -1389,73 +1386,42 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, uint32_t item_flags = 0; /* do filtering */ - bool ok = false; if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility) - { - if (isLinkedToOwnNode && (accept & RSID_FILTER_YOURSELF)) - { - ok = true; - item_flags |= RSID_FILTER_YOURSELF ; - } + { + if (isLinkedToOwnNode && (accept & RSID_FILTER_YOURSELF)) + item_flags |= RSID_FILTER_YOURSELF ; - if (data.mPgpKnown && (accept & RSID_FILTER_FRIENDS)) - { - ok = true; - item_flags |= RSID_FILTER_FRIENDS ; - } + if (data.mPgpKnown && (accept & RSID_FILTER_FRIENDS)) + item_flags |= RSID_FILTER_FRIENDS ; - if (accept & RSID_FILTER_OTHERS) - { - ok = true; - item_flags |= RSID_FILTER_OTHERS ; - } - } - else if (accept & RSID_FILTER_PSEUDONYMS) - { - ok = true; - item_flags |= RSID_FILTER_PSEUDONYMS ; - } + if (accept & RSID_FILTER_OTHERS) + item_flags |= RSID_FILTER_OTHERS ; + } + else if (accept & RSID_FILTER_PSEUDONYMS) + item_flags |= RSID_FILTER_PSEUDONYMS ; - if (isOwnId && (accept & RSID_FILTER_OWNED_BY_YOU)) - { - ok = true; - item_flags |= RSID_FILTER_OWNED_BY_YOU ; - } + if (isOwnId && (accept & RSID_FILTER_OWNED_BY_YOU)) + item_flags |= RSID_FILTER_OWNED_BY_YOU ; if (isBanned && (accept & RSID_FILTER_BANNED)) - { - ok = true; item_flags |= RSID_FILTER_BANNED ; - } - if (!ok) + if (item_flags == 0) return false; if (!item) - { - item = new TreeWidgetItem(); - } - + item = new TreeWidgetItem(); + item->setText(RSID_COL_NICKNAME, QString::fromUtf8(data.mMeta.mGroupName.c_str()).left(RSID_MAXIMUM_NICKNAME_SIZE)); item->setData(RSID_COL_NICKNAME, Qt::UserRole, QString::fromStdString(data.mMeta.mGroupId.toStdString())); item->setText(RSID_COL_KEYID, QString::fromStdString(data.mMeta.mGroupId.toStdString())); - if(isBanned) - { - //TODO (Phenom): Add qproperty for these text colors in stylesheets - item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QColor(Qt::red)); - item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QColor(Qt::red)); - item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QColor(Qt::red)); - item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QColor(Qt::red)); - } - else - { - item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, QVariant()); - item->setData(RSID_COL_KEYID , Qt::ForegroundRole, QVariant()); - item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, QVariant()); - item->setData(RSID_COL_VOTES , Qt::ForegroundRole, QVariant()); - } + //TODO (Phenom): Add qproperty for these text colors in stylesheets + item->setData(RSID_COL_NICKNAME, Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() ); + item->setData(RSID_COL_KEYID , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() ); + item->setData(RSID_COL_IDTYPE , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() ); + item->setData(RSID_COL_VOTES , Qt::ForegroundRole, isBanned ? QColor(Qt::red) : QVariant() ); item->setData(RSID_COL_KEYID, Qt::UserRole,QVariant(item_flags)) ; item->setTextAlignment(RSID_COL_VOTES, Qt::AlignRight | Qt::AlignVCenter); @@ -1466,16 +1432,9 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, RSID_COL_VOTES,SortRole, static_cast(idd.mReputation.mOverallReputationLevel)); - if(isOwnId) - { - QFont font = item->font(RSID_COL_NICKNAME) ; - - font.setBold(true) ; - item->setFont(RSID_COL_NICKNAME,font) ; - item->setFont(RSID_COL_IDTYPE,font) ; - item->setFont(RSID_COL_KEYID,font) ; - - QString tooltip = tr("This identity is owned by you"); + if(isOwnId) + { + QString tooltip = tr("This identity is owned by you"); if(idd.mFlags & RS_IDENTITY_FLAGS_IS_DEPRECATED) { @@ -1487,10 +1446,16 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, tooltip += tr("\nThis identity has a unsecure fingerprint (It's probably quite old).\nYou should get rid of it now and use a new one.\nThese identities will soon be not supported anymore.") ; } - item->setToolTip(RSID_COL_NICKNAME, tooltip) ; - item->setToolTip(RSID_COL_KEYID, tooltip) ; - item->setToolTip(RSID_COL_IDTYPE, tooltip) ; - } + item->setToolTip(RSID_COL_NICKNAME, tooltip) ; + item->setToolTip(RSID_COL_KEYID, tooltip) ; + item->setToolTip(RSID_COL_IDTYPE, tooltip) ; + } + QFont font = ui->idTreeWidget->font() ; + font.setBold(isOwnId) ; + item->setFont(RSID_COL_NICKNAME,font) ; + item->setFont(RSID_COL_IDTYPE,font) ; + item->setFont(RSID_COL_KEYID,font) ; + //QPixmap pixmap ; // @@ -1501,8 +1466,6 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, // Icon Place Holder item->setIcon(RSID_COL_NICKNAME,FilesDefs::getIconFromQtResourcePath(":/icons/png/anonymous.png")); - QString tooltip; - if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility) { if (data.mPgpKnown) @@ -1513,25 +1476,25 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, item->setToolTip(RSID_COL_IDTYPE,"Verified signature from node "+QString::fromStdString(data.mPgpId.toStdString())) ; - tooltip += tr("Node name:")+" " + QString::fromUtf8(details.name.c_str()) + "\n"; - tooltip += tr("Node Id :")+" " + QString::fromStdString(data.mPgpId.toStdString()) ; + QString tooltip = tr("Node name:")+" " + QString::fromUtf8(details.name.c_str()) + "\n"; + tooltip += tr("Node Id :")+" " + QString::fromStdString(data.mPgpId.toStdString()) ; item->setToolTip(RSID_COL_KEYID,tooltip) ; } else { - QString txt = tr("[Unknown node]"); + QString txt = tr("[Unknown node]"); item->setText(RSID_COL_IDTYPE, txt); - - if(!data.mPgpId.isNull()) - { - item->setToolTip(RSID_COL_IDTYPE,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ; - item->setToolTip(RSID_COL_KEYID,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ; - } - else - { - item->setToolTip(RSID_COL_IDTYPE,tr("Unchecked signature")) ; - item->setToolTip(RSID_COL_KEYID,tr("Unchecked signature")) ; - } + + if(!data.mPgpId.isNull()) + { + item->setToolTip(RSID_COL_IDTYPE,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ; + item->setToolTip(RSID_COL_KEYID,tr("Unverified signature from node ")+QString::fromStdString(data.mPgpId.toStdString())) ; + } + else + { + item->setToolTip(RSID_COL_IDTYPE,tr("Unchecked signature")) ; + item->setToolTip(RSID_COL_KEYID,tr("Unchecked signature")) ; + } } } else @@ -2224,8 +2187,9 @@ void IdDialog::IdListCustomPopupMenu( QPoint ) if(!one_item_owned_by_you) { - QWidget *widget = new QWidget(contextMenu); - widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); + QFrame *widget = new QFrame(contextMenu); + widget->setObjectName("gradFrame"); //Use qss + //widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); // create menu header QHBoxLayout *hbox = new QHBoxLayout(widget); @@ -2233,12 +2197,14 @@ void IdDialog::IdListCustomPopupMenu( QPoint ) hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); - QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); + iconLabel->setObjectName("trans_Icon"); + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); QLabel *textLabel = new QLabel("" + ui->titleBarLabel->text() + "", widget); + textLabel->setObjectName("trans_Text"); hbox->addWidget(textLabel); QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); diff --git a/retroshare-gui/src/gui/Identity/IdDialog.ui b/retroshare-gui/src/gui/Identity/IdDialog.ui index 7b16da166..8e40da52b 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.ui +++ b/retroshare-gui/src/gui/Identity/IdDialog.ui @@ -218,6 +218,11 @@ 0
+ + + 11 + + Qt::CustomContextMenu @@ -1046,6 +1051,11 @@ border-image: url(:/images/closepressed.png)
+ + + 11 + + Qt::CustomContextMenu diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index bf5dc6230..207fc5130 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1689,3 +1690,19 @@ void MainWindow::setCompactStatusMode(bool compact) ratesstatus->setCompactMode(compact); //opModeStatus: TODO Show only ??? } + +Gui_InputDialogReturn MainWindow::guiInputDialog(const QString& windowTitle, const QString& labelText, QLineEdit::EchoMode textEchoMode, bool modal) +{ + + QInputDialog dialog(this); + dialog.setWindowTitle(windowTitle); + dialog.setLabelText(labelText); + dialog.setTextEchoMode(textEchoMode); + dialog.setModal(modal); + + Gui_InputDialogReturn ret; + ret.execReturn = dialog.exec(); + ret.textValue = dialog.textValue(); + + return ret; +} diff --git a/retroshare-gui/src/gui/MainWindow.h b/retroshare-gui/src/gui/MainWindow.h index e34fda42a..b4f3cccd5 100644 --- a/retroshare-gui/src/gui/MainWindow.h +++ b/retroshare-gui/src/gui/MainWindow.h @@ -21,6 +21,7 @@ #ifndef _MainWindow_H #define _MainWindow_H +#include #include #include @@ -74,6 +75,14 @@ class MessengerWindow; class ApplicationWindow; #endif + +struct Gui_InputDialogReturn +{ + int execReturn; + QString textValue; +}; +Q_DECLARE_METATYPE(Gui_InputDialogReturn); + class MainWindow : public RWindow { Q_OBJECT @@ -192,7 +201,7 @@ public: } static bool hiddenmode; - + public slots: void receiveNewArgs(QStringList args); void displayErrorMessage(int,int,const QString&) ; @@ -210,9 +219,35 @@ public slots: void showBandwidthGraph(); void toggleStatusToolTip(bool toggle); + + /** + * @brief Create a QInputDialog. This must be called in MainWindow thread because Widgets must be created in the GUI thread. + * Here an exemple how to call it: + * + * bool sameThread = QThread::currentThread() == qApp->thread(); + * Gui_InputDialogReturn ret; + * qRegisterMetaType("Gui_InputDialogReturn"); + * QMetaObject::invokeMethod( MainWindow::getInstance() + * , "guiInputDialog" + * , sameThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection + * , Q_RETURN_ARG(Gui_InputDialogReturn, ret) + * , Q_ARG(QString, windowTitle) + * , Q_ARG(QString, labelText) + * , Q_ARG(QLineEdit::EchoMode, textEchoMode) + * , Q_ARG(bool, modal) + * ); + * + * @param windowTitle: the window title (caption). + * @param labelText: label's text which describes what needs to be input. + * @param textEchoMode: the echo mode for the text value. + * @param modal: pop up the dialog as modal or modeless. + * @return Gui_InputDialogReturn ( Accepted(1)|Rejected(0), text value for the input dialog) + */ + Gui_InputDialogReturn guiInputDialog(const QString& windowTitle, const QString& labelText, QLineEdit::EchoMode textEchoMode, bool modal); + protected: /** Default Constructor */ - MainWindow(QWidget *parent = 0, Qt::WindowFlags flags = 0); + MainWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); void closeEvent(QCloseEvent *); diff --git a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.cpp b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.cpp index 241c713d1..da47d7596 100644 --- a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.cpp +++ b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.cpp @@ -122,14 +122,14 @@ void BoardPostDisplayWidgetBase::setReadStatus(bool isNew, bool isUnread) void BoardPostDisplayWidget_compact::doExpand(bool e) { #ifdef DEBUG_BOARDPOSTDISPLAYWIDGET - std::cerr << "Expanding" << std::endl; + std::cerr << "Expanding" << std::endl; #endif - if(e) - ui->frame_notes->show(); - else - ui->frame_notes->hide(); + if(e) + ui->frame_notes->show(); + else + ui->frame_notes->hide(); - emit expand(mPost.mMeta.mMsgId,e); + emit expand(mPost.mMeta.mMsgId,e); } void BoardPostDisplayWidgetBase::loadComments(bool e) @@ -144,7 +144,7 @@ void BoardPostDisplayWidgetBase::readToggled() emit changeReadStatusRequested(mPost.mMeta.mMsgId,s); } -void BoardPostDisplayWidgetBase::setup() +void BoardPostDisplayWidgetBase::baseSetup() { // show/hide things based on the view type @@ -166,8 +166,6 @@ void BoardPostDisplayWidgetBase::setup() QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this); connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(handleCopyLinkClicked())); - int S = QFontMetricsF(font()).height() ; - readButton()->setChecked(false); QMenu *menu = new QMenu(); @@ -184,6 +182,7 @@ void BoardPostDisplayWidgetBase::setup() if(redacted) { commentButton()->setDisabled(true); + shareButton()->setDisabled(true); voteUpButton()->setDisabled(true); voteDownButton()->setDisabled(true); fromLabel()->setId(mPost.mMeta.mAuthorId); @@ -196,8 +195,6 @@ void BoardPostDisplayWidgetBase::setup() } else { - QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png"); - QDateTime qtime; qtime.setTime_t(mPost.mMeta.mPublishTs); QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy"); @@ -295,16 +292,7 @@ BoardPostDisplayWidget_compact::BoardPostDisplayWidget_compact(const RsPostedPos : BoardPostDisplayWidgetBase(post,display_flags,parent), ui(new Ui::BoardPostDisplayWidget_compact()) { ui->setupUi(this); - setup(); - - ui->right_VL->addStretch(); - ui->right_VL->setAlignment(Qt::AlignTop); - ui->topLayout->setAlignment(Qt::AlignTop); - ui->arrowsLayout->addStretch(); - ui->arrowsLayout->setAlignment(Qt::AlignTop); - ui->feedFrame_VL->addStretch(); - - adjustSize(); + BoardPostDisplayWidget_compact::setup(); } BoardPostDisplayWidget_compact::~BoardPostDisplayWidget_compact() @@ -314,7 +302,7 @@ BoardPostDisplayWidget_compact::~BoardPostDisplayWidget_compact() void BoardPostDisplayWidget_compact::setup() { - BoardPostDisplayWidgetBase::setup(); + baseSetup(); // show/hide things based on the view type @@ -356,7 +344,7 @@ void BoardPostDisplayWidget_compact::setup() QObject::connect(ui->expandButton, SIGNAL(toggled(bool)), this, SLOT(doExpand(bool))); QTextDocument doc; - doc.setHtml(notes()->text()); + doc.setHtml(BoardPostDisplayWidget_compact::notes()->text()); if(mDisplayFlags & SHOW_NOTES) { @@ -427,16 +415,7 @@ BoardPostDisplayWidget_card::BoardPostDisplayWidget_card(const RsPostedPost& pos : BoardPostDisplayWidgetBase(post,display_flags,parent), ui(new Ui::BoardPostDisplayWidget_card()) { ui->setupUi(this); - setup(); - - ui->right_VL->addStretch(); - ui->right_VL->setAlignment(Qt::AlignTop); - ui->topLayout->setAlignment(Qt::AlignTop); - ui->arrowsLayout->addStretch(); - ui->arrowsLayout->setAlignment(Qt::AlignTop); - ui->feedFrame_VL->addStretch(); - - adjustSize(); + BoardPostDisplayWidget_card::setup(); } BoardPostDisplayWidget_card::~BoardPostDisplayWidget_card() @@ -446,7 +425,7 @@ BoardPostDisplayWidget_card::~BoardPostDisplayWidget_card() void BoardPostDisplayWidget_card::setup() { - BoardPostDisplayWidgetBase::setup(); + baseSetup(); RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId); bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE); @@ -463,7 +442,6 @@ void BoardPostDisplayWidget_card::setup() GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL); // Wiping data - as its been passed to thumbnail. - QPixmap scaledpixmap; if(pixmap.width() > 800){ QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation); ui->pictureLabel->setPixmap(scaledpixmap); @@ -478,10 +456,10 @@ void BoardPostDisplayWidget_card::setup() } QTextDocument doc; - doc.setHtml(notes()->text()); + doc.setHtml(BoardPostDisplayWidget_card::notes()->text()); if(doc.toPlainText().trimmed().isEmpty()) - notes()->hide(); + BoardPostDisplayWidget_card::notes()->hide(); } QToolButton *BoardPostDisplayWidget_card::voteUpButton() { return ui->voteUpButton; } diff --git a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.h b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.h index a6d4a04d6..189f83ae5 100644 --- a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.h +++ b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget.h @@ -62,10 +62,11 @@ public: static const char *DEFAULT_BOARD_IMAGE; -protected slots: +protected: /* GxsGroupFeedItem */ - virtual void setup(); // to be overloaded by the different views + void baseSetup(); + virtual void setup() =0; // to be overloaded by the different views virtual QToolButton *voteUpButton() =0; virtual QToolButton *commentButton() =0; @@ -81,6 +82,7 @@ protected slots: virtual QToolButton *shareButton() =0; virtual QFrame *feedFrame() =0; +protected slots: void loadComments(bool e); void readToggled(); void setReadStatus(bool isNew, bool isUnread) ; diff --git a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_card.ui b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_card.ui index 28ce4fd1d..85eb14906 100644 --- a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_card.ui +++ b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_card.ui @@ -90,7 +90,10 @@ - + + + 0 + 0 @@ -173,6 +176,13 @@ + + + + Qt::Vertical + + +
@@ -277,7 +287,7 @@ - + Qt::Horizontal @@ -340,7 +350,7 @@ - + Qt::Horizontal @@ -401,16 +411,16 @@ :/images/share.png:/images/share.png - - true - Qt::ToolButtonTextBesideIcon + + true + - + Qt::Horizontal @@ -424,10 +434,30 @@
+ + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + diff --git a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_compact.ui b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_compact.ui index f27524f08..8c6e89748 100644 --- a/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_compact.ui +++ b/retroshare-gui/src/gui/Posted/BoardPostDisplayWidget_compact.ui @@ -7,7 +7,7 @@ 0 0 542 - 150 + 151 @@ -90,7 +90,22 @@ - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -161,6 +176,13 @@ + + + + Qt::Vertical + + + @@ -287,7 +309,7 @@ - + Qt::Horizontal @@ -305,7 +327,7 @@ - + @@ -400,7 +422,7 @@ - + Qt::Horizontal @@ -414,6 +436,13 @@ + + + + Qt::Vertical + + + @@ -473,6 +502,13 @@ + + + + Qt::Vertical + + + diff --git a/retroshare-gui/src/gui/Posted/PostedCardView.ui b/retroshare-gui/src/gui/Posted/PostedCardView.ui index a55acad24..dba517263 100644 --- a/retroshare-gui/src/gui/Posted/PostedCardView.ui +++ b/retroshare-gui/src/gui/Posted/PostedCardView.ui @@ -7,7 +7,7 @@ 0 0 614 - 182 + 198 @@ -16,7 +16,7 @@ - + 0 @@ -118,7 +118,7 @@ - + 5 @@ -216,7 +216,7 @@ - + Qt::Horizontal @@ -241,30 +241,27 @@ 0 - - - QFrame::NoFrame QFrame::Plain - + 0 - 3 + 0 - 3 + 0 - 3 + 0 - 3 + 0 @@ -337,26 +334,20 @@ - + Qt::Vertical QSizePolicy::Expanding - - - 20 - 5 - - - + @@ -389,7 +380,7 @@ - + Qt::Horizontal @@ -461,7 +452,7 @@ - + 0 @@ -485,7 +476,7 @@ - + Qt::Horizontal @@ -513,9 +504,9 @@ - - + + diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp index 9c08c8169..ce22fa7f3 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.cpp @@ -41,9 +41,12 @@ #include "gui/common/FilesDefs.h" /* View Page */ -#define VIEW_POST 1 -#define VIEW_IMAGE 2 -#define VIEW_LINK 3 +#define VIEW_POST 0 +#define VIEW_IMAGE 1 +#define VIEW_LINK 2 +/* View Image */ +#define IMG_ATTACH 0 +#define IMG_PICTURE 1 PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGroupId& grpId, const RsGxsId& default_author, QWidget *parent): QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint), @@ -54,7 +57,6 @@ PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGrou Settings->loadWidgetInformation(this); connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createPost())); - connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(ui->addPicButton, SIGNAL(clicked() ), this , SLOT(addPicture())); connect(ui->RichTextEditWidget, SIGNAL(textSizeOk(bool)),ui->postButton, SLOT(setEnabled(bool))); @@ -84,6 +86,7 @@ PostedCreatePostDialog::PostedCreatePostDialog(RsPosted *posted, const RsGxsGrou connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setPage(int))); ui->removeButton->hide(); + ui->stackedWidgetPicture->setCurrentIndex(IMG_ATTACH); /* load settings */ processSettings(true); @@ -202,7 +205,6 @@ void PostedCreatePostDialog::addPicture() // select a picture file if (misc::getOpenFileName(window(), RshareSettings::LASTDIR_IMAGES, tr("Load Picture File"), "Pictures (*.png *.xpm *.jpg *.jpeg *.gif *.webp )", imagefilename)) { - QString encodedImage; QImage image; if (image.load(imagefilename) == false) { fprintf (stderr, "RsHtml::makeEmbeddedImage() - image \"%s\" can't be load\n", imagefilename.toLatin1().constData()); @@ -213,7 +215,7 @@ void PostedCreatePostDialog::addPicture() QImage opt; if(ImageUtil::optimizeSizeBytes(imagebytes, image, opt, 640*480, MAXMESSAGESIZE - 2000)) { //Leave space for other stuff ui->imageLabel->setPixmap(QPixmap::fromImage(opt)); - ui->stackedWidgetPicture->setCurrentIndex(1); + ui->stackedWidgetPicture->setCurrentIndex(IMG_PICTURE); ui->removeButton->show(); } else { imagefilename = ""; @@ -259,45 +261,24 @@ int PostedCreatePostDialog::viewMode() void PostedCreatePostDialog::setPage(int viewMode) { - switch (viewMode) { - case VIEW_POST: - ui->stackedWidget->setCurrentIndex(0); + if( (viewMode < 0) || (viewMode > ui->stackedWidget->count()-1) ) + viewMode = VIEW_POST; - ui->viewPostButton->setChecked(true); - ui->viewImageButton->setChecked(false); - ui->viewLinkButton->setChecked(false); + ui->stackedWidget->setCurrentIndex(viewMode); - break; - case VIEW_IMAGE: - ui->stackedWidget->setCurrentIndex(1); + ui->viewPostButton ->setChecked(viewMode==VIEW_POST); + ui->viewImageButton->setChecked(viewMode==VIEW_IMAGE); + ui->viewLinkButton ->setChecked(viewMode==VIEW_LINK); - ui->viewImageButton->setChecked(true); - ui->viewPostButton->setChecked(false); - ui->viewLinkButton->setChecked(false); - - break; - case VIEW_LINK: - ui->stackedWidget->setCurrentIndex(2); - - ui->viewLinkButton->setChecked(true); - ui->viewPostButton->setChecked(false); - ui->viewImageButton->setChecked(false); - - break; - default: - setPage(VIEW_POST); - return; - } } void PostedCreatePostDialog::on_removeButton_clicked() { imagefilename = ""; imagebytes.clear(); - QPixmap empty; - ui->imageLabel->setPixmap(empty); + ui->imageLabel->setPixmap(QPixmap()); ui->removeButton->hide(); - ui->stackedWidgetPicture->setCurrentIndex(0); + ui->stackedWidgetPicture->setCurrentIndex(IMG_ATTACH); } void PostedCreatePostDialog::reject() diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h index a62fe46c6..48ae3cbd4 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.h @@ -52,7 +52,7 @@ private slots: void addPicture(); void on_removeButton_clicked(); void fileHashingFinished(QList hashedFiles); - void reject(); + void reject() override; //QDialog void setPage(int viewMode); private: diff --git a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui index a9ae69b46..a44f226b1 100644 --- a/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui +++ b/retroshare-gui/src/gui/Posted/PostedCreatePostDialog.ui @@ -143,7 +143,7 @@ - 1 + 0 diff --git a/retroshare-gui/src/gui/Posted/PostedItem.ui b/retroshare-gui/src/gui/Posted/PostedItem.ui index fc316a632..6c6b290f8 100644 --- a/retroshare-gui/src/gui/Posted/PostedItem.ui +++ b/retroshare-gui/src/gui/Posted/PostedItem.ui @@ -16,7 +16,7 @@ - + 1 @@ -82,21 +82,21 @@ QFrame::Plain - + 0 - 3 + 0 - 3 + 0 - 3 + 0 - 3 + 0 @@ -169,26 +169,20 @@ - + Qt::Vertical QSizePolicy::Expanding - - - 20 - 5 - - - + 9 @@ -233,7 +227,7 @@ - + Qt::Vertical @@ -248,7 +242,7 @@ - + 6 @@ -286,7 +280,7 @@ - + 0 @@ -312,7 +306,7 @@ - + 0 @@ -320,7 +314,7 @@ QLayout::SetDefaultConstraint - + 5 @@ -416,7 +410,7 @@ - + Qt::Horizontal @@ -434,7 +428,7 @@ - + 6 @@ -567,7 +561,7 @@ - + Qt::Horizontal @@ -631,9 +625,9 @@ QFrame::Raised - + - + Qt::Horizontal @@ -659,7 +653,7 @@ - + Qt::Horizontal @@ -685,7 +679,7 @@ QFrame::Sunken - + 3 @@ -726,17 +720,17 @@ + + GxsIdLabel + QLabel +
gui/gxs/GxsIdLabel.h
+
ElidedLabel QLabel
gui/common/ElidedLabel.h
1
- - GxsIdLabel - QLabel -
gui/gxs/GxsIdLabel.h
-
ClickableLabel QLabel diff --git a/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.cpp b/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.cpp index 087eebe3c..7dcc767b8 100644 --- a/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.cpp @@ -58,9 +58,7 @@ // number of posts to show at once. #define POSTS_CHUNK_SIZE 25 -/**** - * #define DEBUG_POSTED - ***/ +//#define DEBUG_POSTED static const int POSTED_TABS_POSTS = 1; @@ -87,8 +85,12 @@ std::ostream& operator<<(std::ostream& o,const QSize& s) { return o << s.width() void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { +#ifdef DEBUG_POSTED + if(option.state & QStyle::State_Selected) RS_DBG("Selected"); +#endif + if((option.state & QStyle::State_Selected)) // Avoids double display. The selected widget is never exactly the size of the rendered one, - return; // so when selected, we only draw the selected one. + return; // so when selected, we only draw the selected one. // prepare painter->save(); @@ -98,7 +100,7 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & painter->save(); - painter->fillRect( option.rect, option.palette.background()); + painter->fillRect( option.rect, option.palette.window()); painter->restore(); QPixmap pixmap(option.rect.size()); @@ -109,9 +111,9 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & BoardPostDisplayWidget_compact w(post,displayFlags(post.mMeta.mMsgId),nullptr); w.setFixedSize(option.rect.size()); - w.updateGeometry(); w.adjustSize(); + w.render(&pixmap,QPoint(0,0),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background } else @@ -121,6 +123,7 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & w.setFixedSize(option.rect.size()); w.updateGeometry(); w.adjustSize(); + w.render(&pixmap,QPoint(0,0),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background } @@ -145,6 +148,10 @@ void PostedPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & painter->save(); painter->drawPixmap(option.rect.topLeft(), pixmap /*,.scaled(option.rect.width(),option.rect.width()*w.height()/(float)w.width(),Qt::KeepAspectRatio,Qt::SmoothTransformation)*/); +#ifdef DEBUG_POSTED + painter->drawText(option.rect.bottomLeft(),QString::number(time(nullptr))); + RS_DBG("DisplayMode=", mDisplayMode == BoardPostDisplayWidget_compact::DISPLAY_MODE_COMPACT? "Compact":"Card", " Title:", post.mMeta.mMsgName.c_str()); +#endif painter->restore(); } @@ -193,40 +200,46 @@ uint8_t PostedPostDelegate::displayFlags(const RsGxsMessageId &id) const QWidget *PostedPostDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const { - RsPostedPost post = index.data(Qt::UserRole).value() ; - if(index.column() == RsPostedPostsModel::COLUMN_POSTS) - { - QWidget *w ; + if (!index.isValid()) + return nullptr; - if(mDisplayMode==BoardPostDisplayWidget_compact::DISPLAY_MODE_COMPACT) - w = new BoardPostDisplayWidget_compact(post,displayFlags(post.mMeta.mMsgId),parent); - else - w = new BoardPostDisplayWidget_card(post,displayFlags(post.mMeta.mMsgId),parent); + if(index.column() != RsPostedPostsModel::COLUMN_POSTS) + return nullptr; - QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(voteMsg(RsGxsGrpMsgIdPair,bool))); - QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(expandItem(RsGxsMessageId,bool))); - QObject::connect(w,SIGNAL(commentsRequested(const RsGxsMessageId&,bool)),mPostListWidget,SLOT(openComments(const RsGxsMessageId&))); - QObject::connect(w,SIGNAL(changeReadStatusRequested(const RsGxsMessageId&,bool)),mPostListWidget,SLOT(changeReadStatus(const RsGxsMessageId&,bool))); + QWidget *w ; + RsPostedPost post = index.data(Qt::UserRole).value() ; - // All other interactions with the widget should cause the msg to be set as read. - QObject::connect(w,SIGNAL(thumbnailOpenned()),mPostListWidget,SLOT(markCurrentPostAsRead())); - QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(markCurrentPostAsRead())); - QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(markCurrentPostAsRead())); - QObject::connect(w,SIGNAL(commentsRequested(const RsGxsMessageId&,bool)),mPostListWidget,SLOT(markCurrentPostAsRead())); - QObject::connect(w,SIGNAL(shareButtonClicked()),mPostListWidget,SLOT(markCurrentPostAsRead())); - QObject::connect(w,SIGNAL(copylinkClicked()),mPostListWidget,SLOT(copyMessageLink())); +#ifdef DEBUG_POSTED + RS_DBG("Title:", post.mMeta.mMsgName.c_str()); +#endif - w->setFixedSize(option.rect.size()); - w->adjustSize(); - w->updateGeometry(); - w->adjustSize(); + if(mDisplayMode==BoardPostDisplayWidget_compact::DISPLAY_MODE_COMPACT) + w = new BoardPostDisplayWidget_compact(post,displayFlags(post.mMeta.mMsgId),parent); + else + w = new BoardPostDisplayWidget_card(post,displayFlags(post.mMeta.mMsgId),parent); - return w; - } - else - return NULL; + QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(voteMsg(RsGxsGrpMsgIdPair,bool))); + QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(expandItem(RsGxsMessageId,bool))); + QObject::connect(w,SIGNAL(commentsRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(openComments(RsGxsMessageId))); + QObject::connect(w,SIGNAL(changeReadStatusRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(changeReadStatus(RsGxsMessageId,bool))); + + // All other interactions with the widget should cause the msg to be set as read. + QObject::connect(w,SIGNAL(thumbnailOpenned()),mPostListWidget,SLOT(markCurrentPostAsRead())); + QObject::connect(w,SIGNAL(vote(RsGxsGrpMsgIdPair,bool)),mPostListWidget,SLOT(markCurrentPostAsRead())); + QObject::connect(w,SIGNAL(expand(RsGxsMessageId,bool)),this,SLOT(markCurrentPostAsRead())); + QObject::connect(w,SIGNAL(commentsRequested(RsGxsMessageId,bool)),mPostListWidget,SLOT(markCurrentPostAsRead())); + QObject::connect(w,SIGNAL(shareButtonClicked()),mPostListWidget,SLOT(markCurrentPostAsRead())); + QObject::connect(w,SIGNAL(copylinkClicked()),mPostListWidget,SLOT(copyMessageLink())); + + w->setGeometry(option.rect); + w->adjustSize(); + w->updateGeometry(); + w->adjustSize(); + + return w; } + void PostedPostDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { editor->setGeometry(option.rect); @@ -264,13 +277,11 @@ PostedListWidgetWithModel::PostedListWidgetWithModel(const RsGxsGroupId& postedI connect(ui->nextButton,SIGNAL(clicked()),this,SLOT(nextPosts())); connect(ui->prevButton,SIGNAL(clicked()),this,SLOT(prevPosts())); - connect(ui->postsTree,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(postContextMenu(const QPoint&))); + connect(ui->postsTree,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(postContextMenu(QPoint))); connect(ui->viewModeButton,SIGNAL(clicked()),this,SLOT(switchDisplayMode())); connect(mPostedPostsModel,SIGNAL(boardPostsLoaded()),this,SLOT(postPostLoad())); - QFontMetricsF fm(font()); - /* Setup UI helper */ /* Connect signals */ @@ -374,23 +385,20 @@ void PostedListWidgetWithModel::filterItems(QString text) void PostedListWidgetWithModel::nextPosts() { - ui->postsTree->selectionModel()->clear(); - if(mPostedPostsModel->displayedStartPostIndex() + POSTS_CHUNK_SIZE < mPostedPostsModel->filteredPostsCount()) - { - mPostedPostsModel->setPostsInterval(POSTS_CHUNK_SIZE+mPostedPostsModel->displayedStartPostIndex(),POSTS_CHUNK_SIZE); + if(mPostedPostsModel->displayedStartPostIndex() + POSTS_CHUNK_SIZE < mPostedPostsModel->filteredPostsCount()) + { + mPostedPostsModel->setPostsInterval(POSTS_CHUNK_SIZE+mPostedPostsModel->displayedStartPostIndex(),POSTS_CHUNK_SIZE); updateShowLabel(); - } + } } void PostedListWidgetWithModel::prevPosts() { - ui->postsTree->selectionModel()->clear(); - - if((int)mPostedPostsModel->displayedStartPostIndex() > 0) - { - mPostedPostsModel->setPostsInterval((int)mPostedPostsModel->displayedStartPostIndex()-POSTS_CHUNK_SIZE,POSTS_CHUNK_SIZE); - updateShowLabel(); - } + if((int)mPostedPostsModel->displayedStartPostIndex() > 0) + { + mPostedPostsModel->setPostsInterval((int)mPostedPostsModel->displayedStartPostIndex()-POSTS_CHUNK_SIZE,POSTS_CHUNK_SIZE); + updateShowLabel(); + } } void PostedListWidgetWithModel::showAuthorInPeople() @@ -490,29 +498,30 @@ void PostedListWidgetWithModel::handleEvent_main_thread(std::shared_ptrmPostedEventCode) { - case RsPostedEventCode::NEW_COMMENT: // [[fallthrough]]; - case RsPostedEventCode::NEW_VOTE: // [[fallthrough]]; - { - // special treatment here because the message might be a comment, so we need to refresh the comment tab if openned + case RsPostedEventCode::NEW_COMMENT: [[fallthrough]]; + case RsPostedEventCode::NEW_VOTE: + { + // special treatment here because the message might be a comment, so we need to refresh the comment tab if openned - for(int i=2;itabWidget->count();++i) - { - auto *t = dynamic_cast(ui->tabWidget->widget(i)); + for(int i=2;itabWidget->count();++i) + { + auto *t = dynamic_cast(ui->tabWidget->widget(i)); - if(t->groupId() == e->mPostedGroupId) - t->refresh(); - } - } - case RsPostedEventCode::NEW_MESSAGE: // [[fallthrough]]; - case RsPostedEventCode::NEW_POSTED_GROUP: // [[fallthrough]]; - case RsPostedEventCode::UPDATED_POSTED_GROUP: // [[fallthrough]]; - case RsPostedEventCode::UPDATED_MESSAGE: - case RsPostedEventCode::BOARD_DELETED: - case RsPostedEventCode::SYNC_PARAMETERS_UPDATED: - { + if(t->groupId() == e->mPostedGroupId) + t->refresh(); + } + } + [[clang::fallthrough]]; + case RsPostedEventCode::NEW_MESSAGE: [[fallthrough]]; + case RsPostedEventCode::NEW_POSTED_GROUP: [[fallthrough]]; + case RsPostedEventCode::UPDATED_POSTED_GROUP: [[fallthrough]]; + case RsPostedEventCode::UPDATED_MESSAGE: [[fallthrough]]; + case RsPostedEventCode::BOARD_DELETED: [[fallthrough]]; + case RsPostedEventCode::SYNC_PARAMETERS_UPDATED: + { if(e->mPostedGroupId == groupId()) updateDisplay(true); - } + } default: break; diff --git a/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.ui b/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.ui index 0433fe838..9ed0fc3db 100644 --- a/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.ui +++ b/retroshare-gui/src/gui/Posted/PostedListWidgetWithModel.ui @@ -251,7 +251,7 @@ p, li { white-space: pre-wrap; } Qt::Vertical - QSizePolicy::Preferred + QSizePolicy::Expanding @@ -310,7 +310,7 @@ p, li { white-space: pre-wrap; } - 11 + 25 75 true true diff --git a/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui b/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui index 940d8f895..a731ec3b0 100644 --- a/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui +++ b/retroshare-gui/src/gui/TheWire/PulseAddDialog.ui @@ -35,6 +35,9 @@ + + 0 + 0 @@ -232,9 +235,15 @@ + + 0 + 0 + + 0 + diff --git a/retroshare-gui/src/gui/TheWire/PulseMessage.ui b/retroshare-gui/src/gui/TheWire/PulseMessage.ui index 8af226cea..09b64f780 100644 --- a/retroshare-gui/src/gui/TheWire/PulseMessage.ui +++ b/retroshare-gui/src/gui/TheWire/PulseMessage.ui @@ -14,6 +14,9 @@ Form + + 0 + @@ -53,7 +56,7 @@ MS Sans Serif - 10 + 11 diff --git a/retroshare-gui/src/gui/TheWire/PulseReply.ui b/retroshare-gui/src/gui/TheWire/PulseReply.ui index 3af469f63..3f85dc164 100644 --- a/retroshare-gui/src/gui/TheWire/PulseReply.ui +++ b/retroshare-gui/src/gui/TheWire/PulseReply.ui @@ -52,6 +52,9 @@ 0 + + 1 + @@ -149,6 +152,9 @@ + + 9 + 0 @@ -440,10 +446,22 @@ 0 - 40 + 0 + + 6 + + + 0 + + + 0 + + + 0 + @@ -611,13 +629,19 @@ 0 - 40 + 0 + + 6 + 0 + + 0 + 0 diff --git a/retroshare-gui/src/gui/TorControl/TorControlWindow.cpp b/retroshare-gui/src/gui/TorControl/TorControlWindow.cpp new file mode 100644 index 000000000..6da6b8d7a --- /dev/null +++ b/retroshare-gui/src/gui/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/gui/TorControl/TorControlWindow.h b/retroshare-gui/src/gui/TorControl/TorControlWindow.h new file mode 100644 index 000000000..c4e8ce522 --- /dev/null +++ b/retroshare-gui/src/gui/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/gui/TorControl/TorControlWindow.ui b/retroshare-gui/src/gui/TorControl/TorControlWindow.ui new file mode 100644 index 000000000..602146641 --- /dev/null +++ b/retroshare-gui/src/gui/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/gui/WikiPoos/WikiDialog.ui b/retroshare-gui/src/gui/WikiPoos/WikiDialog.ui index 7a86e6488..056e0a859 100644 --- a/retroshare-gui/src/gui/WikiPoos/WikiDialog.ui +++ b/retroshare-gui/src/gui/WikiPoos/WikiDialog.ui @@ -50,7 +50,7 @@ 510 - + 0 @@ -63,33 +63,48 @@ 0 - - - - - 0 - 150 - + + + + Qt::Vertical - - - - - - - Page Name + + + + 0 + 0 + - - - - Page Id + + + 0 + 150 + - - - - Orig Id + + + + + 0 + 0 + - + + + Page Name + + + + + Page Id + + + + + Orig Id + + + diff --git a/retroshare-gui/src/gui/chat/ChatWidget.cpp b/retroshare-gui/src/gui/chat/ChatWidget.cpp index d4a20fe74..a909e3e27 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.cpp +++ b/retroshare-gui/src/gui/chat/ChatWidget.cpp @@ -381,7 +381,7 @@ void ChatWidget::init(const ChatId &chat_id, const QString &title) hist_chat_type = RS_HISTORY_TYPE_PUBLIC; messageCount = Settings->getPublicChatHistoryCount(); - ui->titleBarFrame->setVisible(false); + ui->headerBFrame->setVisible(false); } if (rsHistory->getEnable(hist_chat_type)) diff --git a/retroshare-gui/src/gui/chat/ChatWidget.ui b/retroshare-gui/src/gui/chat/ChatWidget.ui index 88e501335..879c99d00 100644 --- a/retroshare-gui/src/gui/chat/ChatWidget.ui +++ b/retroshare-gui/src/gui/chat/ChatWidget.ui @@ -32,7 +32,7 @@ QLayout::SetMaximumSize - + 0 @@ -51,7 +51,7 @@ QFrame::Plain - + 2 diff --git a/retroshare-gui/src/gui/common/ElidedLabel.cpp b/retroshare-gui/src/gui/common/ElidedLabel.cpp index c444c8113..e44927df3 100644 --- a/retroshare-gui/src/gui/common/ElidedLabel.cpp +++ b/retroshare-gui/src/gui/common/ElidedLabel.cpp @@ -126,7 +126,11 @@ bool ElidedLabel::paintElidedLine( QPainter* painter, QString plainText to.setWrapMode(wordWrap ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap); textLayout.setTextOption(to); - if (painter) painter->save(); + if (painter) + { + painter->save(); + painter->setFont(useFont); + } textLayout.beginLayout(); forever { //Get new line for text. @@ -220,10 +224,7 @@ bool ElidedLabel::paintElidedLine( QPainter* painter, QString plainText if(width+iTransX+cr.left() <= cr.right()) if (painter) - { - painter->setFont(useFont); painter->drawText(QPoint(iTransX + cr.left(), y + fontMetrics.ascent() + cr.top()), elidedLastLine); - } //Draw button to get ToolTip #if QT_VERSION < QT_VERSION_CHECK(5,11,0) diff --git a/retroshare-gui/src/gui/common/FriendList.cpp b/retroshare-gui/src/gui/common/FriendList.cpp index 531a87f63..303b34356 100644 --- a/retroshare-gui/src/gui/common/FriendList.cpp +++ b/retroshare-gui/src/gui/common/FriendList.cpp @@ -55,6 +55,7 @@ #include "gui/chat/ChatUserNotify.h" #include "gui/connect/ConnectProgressDialog.h" #include "gui/common/ElidedLabel.h" +#include "gui/common/FilesDefs.h" #include "FriendList.h" #include "ui_FriendList.h" @@ -297,8 +298,9 @@ void FriendList::peerTreeWidgetCustomPopupMenu() QMenu *contextMenu = new QMenu(this); - QWidget *widget = new QWidget(contextMenu); - widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); + QFrame *widget = new QFrame(contextMenu); + widget->setObjectName("gradFrame"); //Use qss + //widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); // create menu header QHBoxLayout *hbox = new QHBoxLayout(widget); @@ -306,12 +308,14 @@ void FriendList::peerTreeWidgetCustomPopupMenu() hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); + iconLabel->setObjectName("trans_Icon"); QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); QLabel *textLabel = new QLabel("RetroShare", widget); + textLabel->setObjectName("trans_Text"); hbox->addWidget(textLabel); QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); diff --git a/retroshare-gui/src/gui/common/GroupTreeWidget.cpp b/retroshare-gui/src/gui/common/GroupTreeWidget.cpp index 685c54169..0597a652c 100644 --- a/retroshare-gui/src/gui/common/GroupTreeWidget.cpp +++ b/retroshare-gui/src/gui/common/GroupTreeWidget.cpp @@ -60,16 +60,25 @@ GroupTreeWidget::GroupTreeWidget(QWidget *parent) : connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged())); connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(customContextMenuRequested(QPoint))); - connect(ui->treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*))); + connect(ui->treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); connect(ui->treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(itemActivated(QTreeWidgetItem*,int))); if (!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) { // need signal itemClicked too connect(ui->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(itemActivated(QTreeWidgetItem*,int))); } + int H = QFontMetricsF(ui->treeWidget->font()).height() ; +#if QT_VERSION < QT_VERSION_CHECK(5,11,0) + int W = QFontMetricsF(ui->treeWidget->font()).width("_") ; + int D = QFontMetricsF(ui->treeWidget->font()).width("9999-99-99[]") ; +#else + int W = QFontMetricsF(ui->treeWidget->font()).horizontalAdvance("_") ; + int D = QFontMetricsF(ui->treeWidget->font()).horizontalAdvance("9999-99-99[]") ; +#endif + /* Add own item delegate */ RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this); - itemDelegate->setSpacing(QSize(0, 2)); + itemDelegate->setSpacing(QSize(W/2, H/4)); ui->treeWidget->setItemDelegate(itemDelegate); /* Set compare role for each column */ @@ -87,10 +96,6 @@ GroupTreeWidget::GroupTreeWidget(QWidget *parent) : ui->treeWidget->enableColumnCustomize(true); ui->treeWidget->setColumnCustomizable(GTW_COLUMN_NAME, false); - int S = QFontMetricsF(font()).height() ; - int W = QFontMetricsF(font()).width("_") ; - int D = QFontMetricsF(font()).width("9999-99-99[]") ; - QTreeWidgetItem *headerItem = ui->treeWidget->headerItem(); headerItem->setText(GTW_COLUMN_NAME, tr("Name")); headerItem->setText(GTW_COLUMN_UNREAD, ""); @@ -143,7 +148,7 @@ GroupTreeWidget::GroupTreeWidget(QWidget *parent) : connect(ui->distantSearchLineEdit,SIGNAL(returnPressed()),this,SLOT(distantSearch())) ; - ui->treeWidget->setIconSize(QSize(S*1.8,S*1.8)); + ui->treeWidget->setIconSize(QSize(H*1.8,H*1.8)); } GroupTreeWidget::~GroupTreeWidget() @@ -205,9 +210,9 @@ void GroupTreeWidget::updateColors() int color = item->data(GTW_COLUMN_DATA, ROLE_COLOR).toInt(); if (color >= 0) { - item->setData(GTW_COLUMN_NAME, Qt::TextColorRole, mTextColor[color]); + item->setData(GTW_COLUMN_NAME, Qt::ForegroundRole, mTextColor[color]); } else { - item->setData(GTW_COLUMN_NAME, Qt::TextColorRole, QVariant()); + item->setData(GTW_COLUMN_NAME, Qt::ForegroundRole, QVariant()); } } @@ -246,7 +251,6 @@ void GroupTreeWidget::itemActivated(QTreeWidgetItem *item, int column) QTreeWidgetItem *GroupTreeWidget::addCategoryItem(const QString &name, const QIcon &icon, bool expand, int sortOrder /*= -1*/) { - QFont font; RSTreeWidgetItem *item = new RSTreeWidgetItem(); ui->treeWidget->addTopLevelItem(item); // To get StyleSheet for Items @@ -255,15 +259,16 @@ QTreeWidgetItem *GroupTreeWidget::addCategoryItem(const QString &name, const QIc item->setText(GTW_COLUMN_NAME, name); item->setData(GTW_COLUMN_DATA, ROLE_NAME, name); - font = item->font(GTW_COLUMN_NAME); - font.setBold(true); - item->setFont(GTW_COLUMN_NAME, font); + QFont itFont = item->font(GTW_COLUMN_NAME); + itFont.setBold(true); + itFont.setPointSize(ui->treeWidget->font().pointSize()); //use treeWidget font size defined in ui. + item->setFont(GTW_COLUMN_NAME, itFont); item->setIcon(GTW_COLUMN_NAME, icon); - int S = QFontMetricsF(font).height(); + //int S = QFontMetricsF(itFont).height(); - item->setSizeHint(GTW_COLUMN_NAME, QSize(S*1.9, S*1.9)); - item->setData(GTW_COLUMN_NAME, Qt::TextColorRole, textColorCategory()); + //item->setSizeHint(GTW_COLUMN_NAME, QSize(S*1.9, S*1.9)); //size hint is calculated by item delegate. Use itemDelegate->setSpacing() in constructor. + item->setData(GTW_COLUMN_NAME, Qt::ForegroundRole, textColorCategory()); item->setData(GTW_COLUMN_DATA, ROLE_COLOR, GROUPTREEWIDGET_COLOR_CATEGORY); item->setExpanded(expand); @@ -385,6 +390,7 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList< if (item == NULL) { item = new RSTreeWidgetItem(compareRole); item->setData(GTW_COLUMN_DATA, ROLE_ID, itemInfo.id); + item->setFont(GTW_COLUMN_DATA, ui->treeWidget->font()); //static_cast(item)->setNoDataAsLast(true); //Uncomment this to sort data with QVariant() always at end. categoryItem->addChild(item); } @@ -506,18 +512,18 @@ void GroupTreeWidget::setUnreadCount(QTreeWidgetItem *item, int unreadCount) return; } - QFont font = item->font(GTW_COLUMN_NAME); + QFont itFont = item->font(GTW_COLUMN_NAME); if (unreadCount) { item->setText(GTW_COLUMN_UNREAD, QString::number(unreadCount)); - font.setBold(true); + itFont.setBold(true); } else { item->setText(GTW_COLUMN_UNREAD, ""); - font.setBold(false); + itFont.setBold(false); } item->setData(GTW_COLUMN_UNREAD, ROLE_SORT, unreadCount); - item->setFont(GTW_COLUMN_NAME, font); + item->setFont(GTW_COLUMN_NAME, itFont); } QTreeWidgetItem *GroupTreeWidget::getItemFromId(const QString &id) diff --git a/retroshare-gui/src/gui/common/GroupTreeWidget.ui b/retroshare-gui/src/gui/common/GroupTreeWidget.ui index f925cf8ed..d3e0a14a5 100644 --- a/retroshare-gui/src/gui/common/GroupTreeWidget.ui +++ b/retroshare-gui/src/gui/common/GroupTreeWidget.ui @@ -70,6 +70,11 @@ 0 + + + 11 + + Qt::CustomContextMenu diff --git a/retroshare-gui/src/gui/common/NewFriendList.cpp b/retroshare-gui/src/gui/common/NewFriendList.cpp index d151c6430..37b1511f8 100644 --- a/retroshare-gui/src/gui/common/NewFriendList.cpp +++ b/retroshare-gui/src/gui/common/NewFriendList.cpp @@ -184,6 +184,13 @@ NewFriendList::NewFriendList(QWidget */*parent*/) : /* RsAutoUpdatePage(5000,par { ui->setupUi(this); + int H = QFontMetricsF(ui->peerTreeWidget->font()).height(); +#if QT_VERSION < QT_VERSION_CHECK(5,11,0) + int W = QFontMetricsF(ui->peerTreeWidget->font()).width("_"); +#else + int W = QFontMetricsF(ui->peerTreeWidget->font()).horizontalAdvance("_"); +#endif + ui->filterLineEdit->setPlaceholderText(tr("Search")) ; ui->filterLineEdit->showFilterIcon(); @@ -205,7 +212,9 @@ NewFriendList::NewFriendList(QWidget */*parent*/) : /* RsAutoUpdatePage(5000,par mProxyModel->setFilterRegExp(QRegExp(RsFriendListModel::FilterString)); ui->peerTreeWidget->setModel(mProxyModel); - ui->peerTreeWidget->setItemDelegate(new RSElidedItemDelegate()); + RSElidedItemDelegate *itemDelegate = new RSElidedItemDelegate(this); + itemDelegate->setSpacing(QSize(W/2, H/4)); + ui->peerTreeWidget->setItemDelegate(itemDelegate); ui->peerTreeWidget->setWordWrap(false); /* Add filter actions */ @@ -228,17 +237,13 @@ NewFriendList::NewFriendList(QWidget */*parent*/) : /* RsAutoUpdatePage(5000,par QShortcut *Shortcut = new QShortcut(QKeySequence(Qt::Key_Delete), ui->peerTreeWidget, 0, 0, Qt::WidgetShortcut); connect(Shortcut, SIGNAL(activated()), this, SLOT(removeItem()),Qt::QueuedConnection); - QFontMetricsF fontMetrics(ui->peerTreeWidget->font()); + /* Set initial column width */ + ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_NAME , 22 * W); + ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_IP , 15 * W); + ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_ID , 32 * W); + ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_LAST_CONTACT, 12 * W); - /* Set initial column width */ - int fontWidth = fontMetrics.width("W"); - ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_NAME , 22 * fontWidth); - ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_IP , 15 * fontWidth); - ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_ID , 32 * fontWidth); - ui->peerTreeWidget->setColumnWidth(RsFriendListModel::COLUMN_THREAD_LAST_CONTACT, 12 * fontWidth); - - int avatarHeight = fontMetrics.height() * 2; - ui->peerTreeWidget->setIconSize(QSize(avatarHeight, avatarHeight)); + ui->peerTreeWidget->setIconSize(QSize(H*2, H*2)); mModel->checkInternalData(true); @@ -301,8 +306,9 @@ void NewFriendList::headerContextMenuRequested(QPoint /*p*/) { QMenu displayMenu(tr("Show Items"), this); - QWidget *widget = new QWidget(&displayMenu); - widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); + QFrame *widget = new QFrame(&displayMenu); + widget->setObjectName("gradFrame"); //Use qss + //widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); // create menu header QHBoxLayout *hbox = new QHBoxLayout(widget); @@ -310,12 +316,14 @@ void NewFriendList::headerContextMenuRequested(QPoint /*p*/) hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); + iconLabel->setObjectName("trans_Icon"); QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); QLabel *textLabel = new QLabel("Show/hide...", widget); + textLabel->setObjectName("trans_Text"); hbox->addWidget(textLabel); QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); @@ -564,8 +572,9 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu() QMenu contextMenu(this); - QWidget *widget = new QWidget(&contextMenu); - widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); + QFrame *widget = new QFrame(); + widget->setObjectName("gradFrame"); //Use qss + //widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); // create menu header QHBoxLayout *hbox = new QHBoxLayout(widget); @@ -573,12 +582,14 @@ void NewFriendList::peerTreeWidgetCustomPopupMenu() hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); + iconLabel->setObjectName("trans_Icon"); QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/user/friends24.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); QLabel *textLabel = new QLabel("Friend list", widget); + textLabel->setObjectName("trans_Text"); hbox->addWidget(textLabel); QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); diff --git a/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp b/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp index eb017a033..80f37befc 100644 --- a/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp +++ b/retroshare-gui/src/gui/common/RSElidedItemDelegate.cpp @@ -51,7 +51,7 @@ QTreeView::item:hover, QTreeWidget::item:hover, QListWidget::item:hover{ color: #0000EF; background-color: #FEDCBA; } -QQTreeView::item:selected:hover, TreeWidget::item:selected:hover, QListWidget::item:selected:hover{ +QTreeView::item:selected:hover, QTreeWidget::item:selected:hover, QListWidget::item:selected:hover{ color: #ABCDEF; background-color: #FE0000; } @@ -88,7 +88,12 @@ QSize RSElidedItemDelegate::sizeHint(const QStyleOptionViewItem &option, const Q QSize contSize = ownStyle->sizeFromContents( QStyle::CT_ItemViewItem,&ownOption ,QSize( checkRect.width()+iconRect.width()+textRect.width() - ,qMax(checkRect.height(),qMax(iconRect.height(),textRect.height()))),widget); + , qMax(checkRect.height(),qMax(iconRect.height(),textRect.height())) + ), widget ) ; + + contSize += QSize( 2*spacing().width() + , qMax(checkRect.height(),iconRect.height()) > textRect.height() + ? 0 : 2*spacing().height() ); return contSize; } @@ -97,7 +102,7 @@ inline QColor getImagePixelColor(QImage img, int x, int y) { #if QT_VERSION >= QT_VERSION_CHECK(5,6,0) #ifdef DEBUG_EID_PAINT -// RsDbg() << " RSEID: Found Color " << img.pixelColor(x,y).name(QColor::HexArgb).toStdString() << " at " << x << "," << y << std::endl; + //RsDbg(" RSEID: Found Color ", img.pixelColor(x,y).name(QColor::HexArgb).toStdString(), " at ", x, ",", y); #endif return img.pixelColor(x,y); #else @@ -109,7 +114,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & { if(!index.isValid()) { - RsErr() << __PRETTY_FUNCTION__ << " attempt to draw an invalid index." << std::endl; + RS_ERR(" attempt to draw an invalid index."); return ; } painter->save(); @@ -120,7 +125,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & ownOption.icon = option.icon; #ifdef DEBUG_EID_PAINT - RsDbg() << __PRETTY_FUNCTION__ << std::endl << " RSEID: Enter for item with text:" << ownOption.text.toStdString() << std::endl; + RS_DBG("\n RSEID: Enter for item with text:", ownOption.text.toStdString()); #endif const QWidget* widget = option.widget; @@ -139,16 +144,16 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & ownOption.fontMetrics = QFontMetrics(font); #ifdef DEBUG_EID_PAINT QFontInfo info(font); - RsDbg() << " RSEID: Found font in model:" << info.family().toStdString() << std::endl; + RsDbg(" RSEID: Found font in model:", info.family().toStdString()); #endif } // Get Text color from model if one exists QColor textColor; - if (index.data(Qt::TextColorRole).isValid()) { + if (index.data(Qt::ForegroundRole).isValid()) { //textColor = QColor(index.data(Qt::TextColorRole).toString());//Needs to pass from string else loose RBG format. - textColor = index.data(Qt::TextColorRole).value(); + textColor = index.data(Qt::ForegroundRole).value(); #ifdef DEBUG_EID_PAINT - RsDbg() << " RSEID: Found text color in model:" << textColor.name().toStdString() << std::endl; + RsDbg(" RSEID: Found text color in model:", textColor.name().toStdString()); #endif } // Get Brush from model if one exists @@ -157,7 +162,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & if (index.data(Qt::BackgroundRole).isValid()) { bgBrush = index.data(Qt::BackgroundRole).value(); #ifdef DEBUG_EID_PAINT - RsDbg() << " RSEID: Found bg brush in model:" << bgBrush.color().name().toStdString() << std::endl; + RsDbg(" RSEID: Found bg brush in model:", bgBrush.color().name().toStdString()); #endif } @@ -165,10 +170,10 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & if ( (bgBrush.color().spec()==QColor::Invalid) || (textColor.spec()!=QColor::Invalid) ) { #ifdef DEBUG_EID_PAINT - RsDbg() << " RSEID:" - << ((bgBrush.color().spec()==QColor::Invalid) ? " Brush not defined" : "") - << ((textColor.spec()==QColor::Invalid) ? " Text Color not defined" : "") - << " so get it from base image." << std::endl; + RsDbg( " RSEID:" + , ((bgBrush.color().spec()==QColor::Invalid) ? " Brush not defined" : "") + , ((textColor.spec()==QColor::Invalid) ? " Text Color not defined" : "") + , " so get it from base image."); #endif // QPalette is not updated by StyleSheet all occurs in internal class. (QRenderRule) // https://code.woboq.org/qt5/qtbase/src/widgets/styles/qstylesheetstyle.cpp.html#4138 @@ -179,7 +184,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & if (moSize.width() <= 20) moSize.setWidth(20); #ifdef DEBUG_EID_PAINT - RsDbg() << " RSEID: for item size = " << moSize.width() << "x" << moSize.height() << std::endl; + RsDbg(" RSEID: for item size = ", moSize.width(), "x", moSize.height()); #endif QImage moImg(moSize,QImage::Format_ARGB32); @@ -268,14 +273,14 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & { bgBrush = QBrush(moBGColor); #ifdef DEBUG_EID_PAINT - RsDbg() << " RSEID: bg brush setted to " << moBGColor.name(QColor::HexArgb).toStdString() << std::endl; + RsDbg(" RSEID: bg brush setted to ", moBGColor.name(QColor::HexArgb).toStdString()); #endif } if (textColor.spec()==QColor::Invalid) { textColor = moColor; #ifdef DEBUG_EID_PAINT - RsDbg() << " RSEID: text color setted to " << moColor.name(QColor::HexArgb).toStdString() << std::endl; + RsDbg(" RSEID: text color setted to ", moColor.name(QColor::HexArgb).toStdString()); #endif } } @@ -389,7 +394,7 @@ void RSElidedItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & } painter->restore(); #ifdef DEBUG_EID_PAINT - RsDbg() << " RSEID: Finished" << std::endl; + RsDbg(" RSEID: Finished"); #endif } diff --git a/retroshare-gui/src/gui/common/RSTreeView.cpp b/retroshare-gui/src/gui/common/RSTreeView.cpp index 624021f2f..454ab250e 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.cpp +++ b/retroshare-gui/src/gui/common/RSTreeView.cpp @@ -18,42 +18,73 @@ * * *******************************************************************************/ -#include -#include #include "RSTreeView.h" -RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent) +#include "util/rsdebug.h" + +#include +#include + +//#define DEBUG_RSTREEVIEW + +RSTreeView::RSTreeView(QWidget *parent) + : QTreeView(parent), autoSelect(false) { - setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future. + setMouseTracking(false); // normally the default, but who knows if it's not going to change in the future. } void RSTreeView::wheelEvent(QWheelEvent *e) { - if(e->modifiers() == Qt::ControlModifier) - { - emit zoomRequested(e->delta() > 0); - return; - } - else - QTreeView::wheelEvent(e); + if(e->modifiers() == Qt::ControlModifier) + { + emit zoomRequested(e->angleDelta().y() > 0); + return; + } + else + QTreeView::wheelEvent(e); } void RSTreeView::mouseMoveEvent(QMouseEvent *e) { - QModelIndex idx = indexAt(e->pos()); +#ifdef DEBUG_RSTREEVIEW + RS_DBG(e->localPos().x(), ":", e->localPos().y()); +#endif + if (autoSelect) + { + QModelIndex idx = indexAt(e->pos()); - if(idx.isValid() && idx != selectionModel()->currentIndex()) - selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect); + if(idx.isValid() && idx != selectionModel()->currentIndex()) + { +#ifdef DEBUG_RSTREEVIEW + RS_DBG("Selection changed"); +#endif + selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect); + } + } - QTreeView::mouseMoveEvent(e); + QTreeView::mouseMoveEvent(e); +} + +void RSTreeView::leaveEvent(QEvent *e) +{ +#ifdef DEBUG_RSTREEVIEW + RS_DBG(""); +#endif + if (autoSelect) + { + auto fp = focusPolicy(); + setFocusPolicy(Qt::NoFocus); // To not select first index when resetting current index. + selectionModel()->setCurrentIndex(QModelIndex(),QItemSelectionModel::Clear); // Close editor + setFocusPolicy(fp); + } + + QTreeView::leaveEvent(e); } void RSTreeView::setAutoSelect(bool b) { - if(b) - setMouseTracking(true); - else - setMouseTracking(false); + autoSelect = b; // Keep this because setMouseTracking can be called outside. + setMouseTracking(b); } void RSTreeView::resizeEvent(QResizeEvent *e) diff --git a/retroshare-gui/src/gui/common/RSTreeView.h b/retroshare-gui/src/gui/common/RSTreeView.h index 116ccac31..0ccd34134 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.h +++ b/retroshare-gui/src/gui/common/RSTreeView.h @@ -29,26 +29,34 @@ class RSTreeView : public QTreeView Q_OBJECT public: - RSTreeView(QWidget *parent = 0); + RSTreeView(QWidget *parent = nullptr); + /** + * @brief set Placeholder Text + * @param text + */ void setPlaceholderText(const QString &text); - // Use this to make selection automatic based on mouse position. This is useful to trigger selection and therefore editing mode - // in trees that show editing widgets using a QStyledItemDelegate - - void setAutoSelect(bool b); + /** + * @brief Use this to make selection automatic based on mouse position. + * This is useful to trigger selection and therefore editing mode in trees that show editing widgets using a QStyledItemDelegate. + * @param b + */ + void setAutoSelect(bool b); signals: - void sizeChanged(QSize); - void zoomRequested(bool zoom_or_unzoom); + void sizeChanged(QSize); + void zoomRequested(bool zoom_or_unzoom); protected: virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection + virtual void leaveEvent(QEvent *e) override; // overriding so as to manage auto-selection clear virtual void wheelEvent(QWheelEvent *e) override; // overriding so as to manage zoom - virtual void resizeEvent(QResizeEvent *e) override; + virtual void resizeEvent(QResizeEvent *e) override; virtual void paintEvent(QPaintEvent *event) override; QString placeholderText; + bool autoSelect; }; #endif diff --git a/retroshare-gui/src/gui/common/RSTreeWidget.cpp b/retroshare-gui/src/gui/common/RSTreeWidget.cpp index 09ef481f6..9c91c5f2e 100644 --- a/retroshare-gui/src/gui/common/RSTreeWidget.cpp +++ b/retroshare-gui/src/gui/common/RSTreeWidget.cpp @@ -253,8 +253,9 @@ QMenu *RSTreeWidget::createStandardContextMenu(QMenu *contextMenu) } if(!mContextMenuActions.isEmpty() || !mContextMenuMenus.isEmpty() || mEnableColumnCustomize) { - QWidget *widget = new QWidget(contextMenu); - widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); + QFrame *widget = new QFrame(contextMenu); + widget->setObjectName("gradFrame"); //Use qss + //widget->setStyleSheet( ".QWidget{background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,stop:0 #FEFEFE, stop:1 #E8E8E8); border: 1px solid #CCCCCC;}"); // create menu header QHBoxLayout *hbox = new QHBoxLayout(widget); @@ -262,12 +263,14 @@ QMenu *RSTreeWidget::createStandardContextMenu(QMenu *contextMenu) hbox->setSpacing(6); QLabel *iconLabel = new QLabel(widget); - QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/settings.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); + iconLabel->setObjectName("trans_Icon"); + QPixmap pix = FilesDefs::getPixmapFromQtResourcePath(":/images/settings.png").scaledToHeight(QFontMetricsF(iconLabel->font()).height()*1.5); iconLabel->setPixmap(pix); iconLabel->setMaximumSize(iconLabel->frameSize().height() + pix.height(), pix.width()); hbox->addWidget(iconLabel); QLabel *textLabel = new QLabel("" + tr("Tree View Options") + "", widget); + textLabel->setObjectName("trans_Text"); hbox->addWidget(textLabel); QSpacerItem *spacerItem = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); diff --git a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp index c3463a79b..15456bbd5 100755 --- a/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp +++ b/retroshare-gui/src/gui/connect/ConnectFriendWizard.cpp @@ -39,6 +39,7 @@ #include "ConnectFriendWizard.h" #include "ui_ConnectFriendWizard.h" #include "gui/common/PeerDefs.h" +#include "gui/connect/ConfCertDialog.h" #include "gui/notifyqt.h" #include "gui/common/GroupDefs.h" #include "gui/msgs/MessageComposer.h" @@ -572,6 +573,16 @@ void ConnectFriendWizard::initializePage(int id) ui->ipEdit->setText(s); ui->signersEdit->setPlainText(ts); + ui->knownIpLabel->setHidden(peerDetails.ipAddressList.empty()); + ui->knownIpEdit->setHidden(peerDetails.ipAddressList.empty()); + { + QString ipList; + for(auto& it : peerDetails.ipAddressList) + ipList.append(QString::fromStdString(it) + "\n"); + + ui->knownIpEdit->setPlainText(ipList); + } + fillGroups(this, ui->groupComboBox, groupId); if(peerDetails.isHiddenNode) @@ -587,15 +598,16 @@ void ConnectFriendWizard::initializePage(int id) } if(mIsShortInvite) { - ui->nameEdit->setText(tr("[Unknown]")); - ui->addKeyToKeyring_CB->setChecked(false); - ui->addKeyToKeyring_CB->setEnabled(false); + if(ui->nameEdit->text().isEmpty()) + ui->nameEdit->setText(tr("[Unknown]")); + ui->addKeyToKeyring_CB->setChecked(false); + ui->addKeyToKeyring_CB->setEnabled(false); ui->signersEdit->hide(); ui->signersLabel->hide(); - ui->signGPGCheckBox->setChecked(false); - ui->signGPGCheckBox->setEnabled(false); - ui->acceptNoSignGPGCheckBox->setChecked(true); - ui->acceptNoSignGPGCheckBox->setEnabled(false); + ui->signGPGCheckBox->setChecked(false); + ui->signGPGCheckBox->setEnabled(false); + ui->acceptNoSignGPGCheckBox->setChecked(true); + ui->acceptNoSignGPGCheckBox->setEnabled(false); } ui->ipEdit->setTextInteractionFlags(Qt::TextSelectableByMouse); @@ -856,30 +868,30 @@ void ConnectFriendWizard::cleanFriendCert() { bool certValid = false; QString errorMsg ; + QString certDetail; std::string cert = ui->friendCertEdit->toPlainText().toUtf8().constData(); if (cert.empty()) { - ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png")); ui->friendCertCleanLabel->setToolTip(""); - ui->friendCertCleanLabel->setStyleSheet(""); errorMsg = tr(""); } else { std::string cleanCert; uint32_t error_code; + RsPeerDetails details; - if (rsPeers->cleanCertificate(cert, cleanCert, mIsShortInvite, error_code)) - { + if (rsPeers->cleanCertificate(cert, cleanCert, mIsShortInvite, error_code, details)) + { certValid = true; if (cert != cleanCert) - { + { QTextCursor textCursor = ui->friendCertEdit->textCursor(); whileBlocking(ui->friendCertEdit)->setPlainText(QString::fromUtf8(cleanCert.c_str())); whileBlocking(ui->friendCertEdit)->setTextCursor(textCursor); - ui->friendCertCleanLabel->setStyleSheet(""); + certDetail = ConfCertDialog::getCertificateDescription(details,false,mIsShortInvite,!details.ipAddressList.empty()); } if (mIsShortInvite) @@ -887,7 +899,7 @@ void ConnectFriendWizard::cleanFriendCert() else errorMsg = tr("Valid certificate") ; - ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png")); + ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png")); } else { if (error_code > 0) { switch (error_code) { @@ -903,16 +915,17 @@ void ConnectFriendWizard::cleanFriendCert() default: errorMsg = tr("Not a valid Retroshare certificate!") ; - ui->friendCertCleanLabel->setStyleSheet("QLabel#friendCertCleanLabel {border: 1px solid #DCDC41; border-radius: 6px; background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FFFFD7, stop:1 #FFFFB2);}"); } } - ui->friendCertCleanLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png")); } } - ui->friendCertCleanLabel->setPixmap(certValid ? FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png") : FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png")); - ui->friendCertCleanLabel->setToolTip(errorMsg); + ui->friendCertCleanLabel->setPixmap(certValid ? FilesDefs::getPixmapFromQtResourcePath(":/images/accepted16.png") : FilesDefs::getPixmapFromQtResourcePath(":/images/delete.png")); + ui->friendCertCleanLabel->setToolTip("

" + errorMsg + (certValid ? "\n" + certDetail : "") + "

"); ui->friendCertCleanLabel->setText(errorMsg); + ui->friendCertCleanLabel->setProperty("WrongValue", !certValid && !errorMsg.isEmpty()); + ui->friendCertCleanLabel->style()->unpolish(ui->friendCertCleanLabel); + ui->friendCertCleanLabel->style()->polish( ui->friendCertCleanLabel); ui->TextPage->setComplete(certValid); } diff --git a/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui b/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui index f6ac10b5e..65b736762 100644 --- a/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui +++ b/retroshare-gui/src/gui/connect/ConnectFriendWizard.ui @@ -590,7 +590,7 @@ - Email + ProfilId
@@ -629,7 +629,7 @@ - Signers + Signers:
@@ -675,6 +675,16 @@
+ + + + Known IP: + + + + + +
diff --git a/retroshare-gui/src/gui/feeds/MsgItem.cpp b/retroshare-gui/src/gui/feeds/MsgItem.cpp index 66ae5f49b..e0b3d2bf0 100644 --- a/retroshare-gui/src/gui/feeds/MsgItem.cpp +++ b/retroshare-gui/src/gui/feeds/MsgItem.cpp @@ -31,6 +31,7 @@ #include "gui/common/AvatarDefs.h" #include "gui/common/FilesDefs.h" #include "gui/notifyqt.h" +#include "util/qtthreadsutils.h" #include #include @@ -59,12 +60,13 @@ MsgItem::MsgItem(FeedHolder *parent, uint32_t feedId, const std::string &msgId, //connect( gotoButton, SIGNAL( clicked( void ) ), this, SLOT( gotoHome ( void ) ) ); /* specific ones */ - connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), this, SLOT(checkMessageReadStatus())); connect( playButton, SIGNAL( clicked( void ) ), this, SLOT( playMedia ( void ) ) ); connect( deleteButton, SIGNAL( clicked( void ) ), this, SLOT( deleteMsg ( void ) ) ); connect( replyButton, SIGNAL( clicked( void ) ), this, SLOT( replyMsg ( void ) ) ); connect( sendinviteButton, SIGNAL( clicked( void ) ), this, SLOT( sendInvite ( void ) ) ); + mEventHandlerId = 0; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS ); expandFrame->hide(); info_Frame_Invite->hide(); @@ -73,6 +75,58 @@ MsgItem::MsgItem(FeedHolder *parent, uint32_t feedId, const std::string &msgId, updateItem(); } +MsgItem::~MsgItem() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); +} + +void MsgItem::handleEvent_main_thread(std::shared_ptr event) +{ + if(event->mType != RsEventType::MAIL_STATUS) { + return; + } + + const RsMailStatusEvent *fe = dynamic_cast(event.get()); + if (!fe) { + return; + } + + switch (fe->mMailStatusEventCode) { + case RsMailStatusEventCode::MESSAGE_CHANGED: + if (fe->mChangedMsgIds.find(mMsgId) != fe->mChangedMsgIds.end()) { + MessageInfo msgInfo; + + if (!rsMail->getMessage(mMsgId, msgInfo)) { + removeItem(); + break; + } + + if (!mCloseOnRead) { + break; + } + + if (msgInfo.msgflags & RS_MSG_NEW) { + /* Message status is still "new" */ + break; + } + + removeItem(); + } + break; + case RsMailStatusEventCode::MESSAGE_REMOVED: + if (fe->mChangedMsgIds.find(mMsgId) != fe->mChangedMsgIds.end()) { + removeItem(); + } + break; + case RsMailStatusEventCode::MESSAGE_SENT: + case RsMailStatusEventCode::NEW_MESSAGE: + case RsMailStatusEventCode::TAG_CHANGED: + case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK: + case RsMailStatusEventCode::SIGNATURE_FAILED: + break; + } +} + void MsgItem::updateItemStatic() { /* fill in */ @@ -246,7 +300,6 @@ void MsgItem::doExpand(bool open) mCloseOnRead = false; rsMail->MessageRead(mMsgId, false); - mCloseOnRead = true; } else { @@ -332,26 +385,6 @@ void MsgItem::toggle() expand(expandFrame->isHidden()); } -void MsgItem::checkMessageReadStatus() -{ - if (!mCloseOnRead) { - return; - } - - MessageInfo msgInfo; - if (!rsMail->getMessage(mMsgId, msgInfo)) { - std::cerr << "MsgItem::checkMessageReadStatus() Couldn't find Msg" << std::endl; - return; - } - - if (msgInfo.msgflags & RS_MSG_NEW) { - /* Message status is still "new" */ - return; - } - - removeItem(); -} - void MsgItem::sendInvite() { MessageInfo mi; diff --git a/retroshare-gui/src/gui/feeds/MsgItem.h b/retroshare-gui/src/gui/feeds/MsgItem.h index 32824ca23..2cb8645ba 100644 --- a/retroshare-gui/src/gui/feeds/MsgItem.h +++ b/retroshare-gui/src/gui/feeds/MsgItem.h @@ -24,6 +24,7 @@ #include "ui_MsgItem.h" #include "FeedItem.h" #include +#include class FeedHolder; class SubFileItem; @@ -35,6 +36,7 @@ class MsgItem : public FeedItem, private Ui::MsgItem public: /** Default Constructor */ MsgItem(FeedHolder *parent, uint32_t feedId, const std::string &msgId, bool isHome); + virtual ~MsgItem(); void updateItemStatic(); @@ -46,6 +48,7 @@ protected: private: void fillExpandFrame(); + void handleEvent_main_thread(std::shared_ptr event); private slots: /* default stuff */ @@ -56,7 +59,6 @@ private slots: void deleteMsg(); void replyMsg(); void sendInvite(); - void checkMessageReadStatus(); void updateItem(); @@ -66,6 +68,7 @@ private: bool mIsHome; bool mCloseOnRead; + RsEventsHandlerId_t mEventHandlerId; std::list mFileItems; }; diff --git a/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp b/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp index 071f794ff..fddf2c8d5 100644 --- a/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp +++ b/retroshare-gui/src/gui/msgs/MessageUserNotify.cpp @@ -22,13 +22,20 @@ #include "MessageUserNotify.h" #include "gui/notifyqt.h" #include "gui/MainWindow.h" +#include "util/qtthreadsutils.h" #include "gui/msgs/MessageInterface.h" MessageUserNotify::MessageUserNotify(QObject *parent) : UserNotify(parent) { - connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), this, SLOT(updateIcon())); + mEventHandlerId = 0; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS ); +} + +MessageUserNotify::~MessageUserNotify() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); } bool MessageUserNotify::hasSetting(QString *name, QString *group) @@ -72,3 +79,28 @@ void MessageUserNotify::iconClicked() { MainWindow::showWindow(MainWindow::Messages); } + +void MessageUserNotify::handleEvent_main_thread(std::shared_ptr event) +{ + if(event->mType != RsEventType::MAIL_STATUS) { + return; + } + + const RsMailStatusEvent *fe = dynamic_cast(event.get()); + if (!fe) { + return; + } + + switch (fe->mMailStatusEventCode) { + case RsMailStatusEventCode::NEW_MESSAGE: + case RsMailStatusEventCode::MESSAGE_CHANGED: + case RsMailStatusEventCode::MESSAGE_REMOVED: + updateIcon(); + break; + case RsMailStatusEventCode::MESSAGE_SENT: + case RsMailStatusEventCode::TAG_CHANGED: + case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK: + case RsMailStatusEventCode::SIGNATURE_FAILED: + break; + } +} diff --git a/retroshare-gui/src/gui/msgs/MessageUserNotify.h b/retroshare-gui/src/gui/msgs/MessageUserNotify.h index 58b30e8fc..f9b7458ab 100644 --- a/retroshare-gui/src/gui/msgs/MessageUserNotify.h +++ b/retroshare-gui/src/gui/msgs/MessageUserNotify.h @@ -21,6 +21,7 @@ #ifndef MESSAGEUSERNOTIFY_H #define MESSAGEUSERNOTIFY_H +#include #include "gui/common/UserNotify.h" class MessageUserNotify : public UserNotify @@ -29,6 +30,7 @@ class MessageUserNotify : public UserNotify public: MessageUserNotify(QObject *parent = 0); + virtual ~MessageUserNotify(); virtual bool hasSetting(QString *name, QString *group) override; @@ -41,6 +43,11 @@ private: virtual QString getNotifyMessage(bool plural) override; virtual void iconClicked() override; + + void handleEvent_main_thread(std::shared_ptr event); + +private: + RsEventsHandlerId_t mEventHandlerId; }; #endif // MESSAGEUSERNOTIFY_H diff --git a/retroshare-gui/src/gui/msgs/MessageWidget.cpp b/retroshare-gui/src/gui/msgs/MessageWidget.cpp index e614eeca0..d05d17446 100644 --- a/retroshare-gui/src/gui/msgs/MessageWidget.cpp +++ b/retroshare-gui/src/gui/msgs/MessageWidget.cpp @@ -46,6 +46,7 @@ #include "util/HandleRichText.h" #include "util/DateTime.h" #include "util/QtVersion.h" +#include "util/qtthreadsutils.h" #include #include @@ -159,9 +160,6 @@ MessageWidget::MessageWidget(bool controlled, QWidget *parent, Qt::WindowFlags f viewsource->setShortcut(QKeySequence("CTRL+O")); connect(viewsource, SIGNAL(triggered()), this, SLOT(viewSource())); - connect(NotifyQt::getInstance(), SIGNAL(messagesTagsChanged()), this, SLOT(messagesTagsChanged())); - connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), this, SLOT(messagesChanged())); - ui.imageBlockWidget->addButtonAction(tr("Load images always for this message"), this, SLOT(loadImagesAlways()), true); ui.msgText->setImageBlockWidget(ui.imageBlockWidget); @@ -211,6 +209,9 @@ MessageWidget::MessageWidget(bool controlled, QWidget *parent, Qt::WindowFlags f ui.dateText-> setText(""); ui.info_Frame_Invite->hide(); + + mEventHandlerId = 0; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS ); } MessageWidget::~MessageWidget() @@ -218,6 +219,47 @@ MessageWidget::~MessageWidget() if (isControlled == false) { processSettings("MessageWidget", false); } + + rsEvents->unregisterEventsHandler(mEventHandlerId); +} + +void MessageWidget::handleEvent_main_thread(std::shared_ptr event) +{ + if(event->mType != RsEventType::MAIL_STATUS) { + return; + } + + const RsMailStatusEvent *fe = dynamic_cast(event.get()); + if (!fe) { + return; + } + + switch (fe->mMailStatusEventCode) { + case RsMailStatusEventCode::MESSAGE_REMOVED: + if (fe->mChangedMsgIds.find(currMsgId) != fe->mChangedMsgIds.end()) { + if (isControlled) { + /* processed by MessagesDialog */ + return; + } + + /* messages was removed */ + if (isWindow) { + window()->close(); + } else { + deleteLater(); + } + } + break; + case RsMailStatusEventCode::TAG_CHANGED: + messagesTagsChanged(); + break; + case RsMailStatusEventCode::MESSAGE_SENT: + case RsMailStatusEventCode::MESSAGE_CHANGED: + case RsMailStatusEventCode::NEW_MESSAGE: + case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK: + case RsMailStatusEventCode::SIGNATURE_FAILED: + break; + } } void MessageWidget::connectAction(enumActionType actionType, QToolButton* button) @@ -407,25 +449,6 @@ void MessageWidget::messagesTagsChanged() showTagLabels(); } -void MessageWidget::messagesChanged() -{ - if (isControlled) { - /* processed by MessagesDialog */ - return; - } - - /* test Message */ - MessageInfo msgInfo; - if (rsMail->getMessage(currMsgId, msgInfo) == false) { - /* messages was removed */ - if (isWindow) { - window()->close(); - } else { - deleteLater(); - } - } -} - void MessageWidget::clearTagLabels() { /* clear all tags */ diff --git a/retroshare-gui/src/gui/msgs/MessageWidget.h b/retroshare-gui/src/gui/msgs/MessageWidget.h index 5acf627f6..12438e55c 100644 --- a/retroshare-gui/src/gui/msgs/MessageWidget.h +++ b/retroshare-gui/src/gui/msgs/MessageWidget.h @@ -22,6 +22,7 @@ #define _MESSAGEWIDGET_H #include +#include #include "ui_MessageWidget.h" class QToolButton; @@ -75,7 +76,6 @@ private slots: void msgfilelistWidgetCostumPopupMenu(QPoint); void messagesTagsChanged(); - void messagesChanged(); void togglefileview(bool noUpdate = false); void getcurrentrecommended(); @@ -93,11 +93,14 @@ private: void showTagLabels(); void setToolbarButtonStyle(Qt::ToolButtonStyle style); + void handleEvent_main_thread(std::shared_ptr event); + bool isControlled; bool isWindow; std::string currMsgId; unsigned int currMsgFlags; bool expandFiles; + RsEventsHandlerId_t mEventHandlerId; QList tagLabels; diff --git a/retroshare-gui/src/gui/msgs/MessagesDialog.cpp b/retroshare-gui/src/gui/msgs/MessagesDialog.cpp index ffc108a53..c5c3c543c 100644 --- a/retroshare-gui/src/gui/msgs/MessagesDialog.cpp +++ b/retroshare-gui/src/gui/msgs/MessagesDialog.cpp @@ -268,9 +268,6 @@ MessagesDialog::MessagesDialog(QWidget *parent) registerHelpButton(ui.helpButton,help_str,"MessagesDialog") ; - connect(NotifyQt::getInstance(), SIGNAL(messagesChanged()), mMessageModel, SLOT(updateMessages())); - connect(NotifyQt::getInstance(), SIGNAL(messagesTagsChanged()), this, SLOT(messagesTagsChanged())); - connect(ui.filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); connect(ui.filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterColumnChanged(int))); @@ -294,6 +291,9 @@ MessagesDialog::MessagesDialog(QWidget *parent) mEventHandlerId=0; rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_STATUS ); + + mTagEventHandlerId = 0; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [this,event]() { handleTagEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_TAG ); } void MessagesDialog::handleEvent_main_thread(std::shared_ptr event) @@ -310,9 +310,34 @@ void MessagesDialog::handleEvent_main_thread(std::shared_ptr even case RsMailStatusEventCode::MESSAGE_SENT: case RsMailStatusEventCode::MESSAGE_REMOVED: case RsMailStatusEventCode::NEW_MESSAGE: + case RsMailStatusEventCode::MESSAGE_CHANGED: + case RsMailStatusEventCode::TAG_CHANGED: + mMessageModel->updateMessages(); updateMessageSummaryList(); break; - default: + case RsMailStatusEventCode::MESSAGE_RECEIVED_ACK: + case RsMailStatusEventCode::SIGNATURE_FAILED: + break; + } +} + +void MessagesDialog::handleTagEvent_main_thread(std::shared_ptr event) +{ + if (event->mType != RsEventType::MAIL_TAG) { + return; + } + + const RsMailTagEvent *fe = dynamic_cast(event.get()); + if (!fe) { + return; + } + + switch (fe->mMailTagEventCode) { + case RsMailTagEventCode::TAG_ADDED: + case RsMailTagEventCode::TAG_CHANGED: + case RsMailTagEventCode::TAG_REMOVED: + fillQuickView(); + mMessageModel->updateMessages(); break; } } @@ -324,6 +349,12 @@ void MessagesDialog::preModelUpdate() mTmpSavedSelectedIds.clear(); getSelectedMessages(mTmpSavedSelectedIds); + mTmpSavedCurrentId.clear(); + const QModelIndex& m = ui.messageTreeWidget->currentIndex(); + if (m.isValid()) { + mTmpSavedCurrentId = m.sibling(m.row(), RsMessageModel::COLUMN_THREAD_MSGID).data(RsMessageModel::MsgIdRole).toString(); + } + std::cerr << "Pre-change: saving selection for " << mTmpSavedSelectedIds.size() << " indexes" << std::endl; } @@ -341,6 +372,13 @@ void MessagesDialog::postModelUpdate() } ui.messageTreeWidget->selectionModel()->select(sel,QItemSelectionModel::SelectCurrent); + + if (!mTmpSavedCurrentId.isEmpty()) { + QModelIndex index = mMessageProxyModel->mapFromSource(mMessageModel->getIndexOfMessage(mTmpSavedCurrentId.toStdString())); + if (index.isValid()) { + ui.messageTreeWidget->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select); + } + } } void MessagesDialog::sortColumn(int col,Qt::SortOrder so) @@ -356,6 +394,9 @@ MessagesDialog::~MessagesDialog() { // save settings processSettings(false); + + rsEvents->unregisterEventsHandler(mEventHandlerId); + rsEvents->unregisterEventsHandler(mTagEventHandlerId); } UserNotify *MessagesDialog::createUserNotify(QObject *parent) @@ -921,13 +962,6 @@ void MessagesDialog::changeQuickView(int newrow) mMessageProxyModel->setFilterRegExp(QRegExp(RsMessageModel::FilterString)); // this triggers the update of the proxy model } -void MessagesDialog::messagesTagsChanged() -{ - fillQuickView(); - mMessageModel->updateMessages(); -} - - // click in messageTreeWidget void MessagesDialog::currentChanged(const QModelIndex& new_proxy_index,const QModelIndex& /*old_proxy_index*/) { diff --git a/retroshare-gui/src/gui/msgs/MessagesDialog.h b/retroshare-gui/src/gui/msgs/MessagesDialog.h index 497c7a06d..e2c3f4c6f 100644 --- a/retroshare-gui/src/gui/msgs/MessagesDialog.h +++ b/retroshare-gui/src/gui/msgs/MessagesDialog.h @@ -66,7 +66,6 @@ protected: public slots: //void insertMessages(); - void messagesTagsChanged(); void messageRemoved(); void preModelUpdate(); void postModelUpdate(); @@ -112,6 +111,7 @@ private slots: private: void handleEvent_main_thread(std::shared_ptr event); + void handleTagEvent_main_thread(std::shared_ptr event); void updateInterface(); @@ -159,9 +159,11 @@ private: Ui::MessagesDialog ui; QList mTmpSavedSelectedIds; + QString mTmpSavedCurrentId; QModelIndex lastSelectedIndex; RsEventsHandlerId_t mEventHandlerId; + RsEventsHandlerId_t mTagEventHandlerId; }; #endif diff --git a/retroshare-gui/src/gui/msgs/TagsMenu.cpp b/retroshare-gui/src/gui/msgs/TagsMenu.cpp index 357c37325..8ef4f7d93 100644 --- a/retroshare-gui/src/gui/msgs/TagsMenu.cpp +++ b/retroshare-gui/src/gui/msgs/TagsMenu.cpp @@ -30,6 +30,7 @@ #include "gui/common/TagDefs.h" #include "gui/settings/NewTag.h" #include "gui/notifyqt.h" +#include "util/qtthreadsutils.h" #include "gui/msgs/MessageInterface.h" @@ -46,11 +47,18 @@ TagsMenu::TagsMenu(const QString &title, QWidget *parent) : QMenu (title, parent) { connect(this, SIGNAL(triggered (QAction*)), this, SLOT(tagTriggered(QAction*))); - connect(NotifyQt::getInstance(), SIGNAL(messagesTagsChanged()), this, SLOT(fillTags())); + + mEventHandlerId = 0; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mEventHandlerId, RsEventType::MAIL_TAG ); fillTags(); } +TagsMenu::~TagsMenu() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); +} + void TagsMenu::paintEvent(QPaintEvent *e) { QMenu::paintEvent(e); @@ -89,6 +97,26 @@ void TagsMenu::paintEvent(QPaintEvent *e) } } +void TagsMenu::handleEvent_main_thread(std::shared_ptr event) +{ + if (event->mType != RsEventType::MAIL_TAG) { + return; + } + + const RsMailTagEvent *fe = dynamic_cast(event.get()); + if (!fe) { + return; + } + + switch (fe->mMailTagEventCode) { + case RsMailTagEventCode::TAG_ADDED: + case RsMailTagEventCode::TAG_CHANGED: + case RsMailTagEventCode::TAG_REMOVED: + fillTags(); + break; + } +} + void TagsMenu::fillTags() { clear(); diff --git a/retroshare-gui/src/gui/msgs/TagsMenu.h b/retroshare-gui/src/gui/msgs/TagsMenu.h index 279638aae..a3f2cdd55 100644 --- a/retroshare-gui/src/gui/msgs/TagsMenu.h +++ b/retroshare-gui/src/gui/msgs/TagsMenu.h @@ -24,6 +24,7 @@ #include #include +#include class TagsMenu : public QMenu { @@ -31,6 +32,7 @@ class TagsMenu : public QMenu public: TagsMenu(const QString &title, QWidget *parent); + virtual ~TagsMenu(); void activateActions(std::list& tagIds); @@ -42,8 +44,14 @@ protected: virtual void paintEvent(QPaintEvent *e); private slots: - void fillTags(); void tagTriggered(QAction *action); + +private: + void handleEvent_main_thread(std::shared_ptr event); + void fillTags(); + +private: + RsEventsHandlerId_t mEventHandlerId; }; #endif diff --git a/retroshare-gui/src/gui/notifyqt.cpp b/retroshare-gui/src/gui/notifyqt.cpp index 7bb1b6cf2..380f2382c 100644 --- a/retroshare-gui/src/gui/notifyqt.cpp +++ b/retroshare-gui/src/gui/notifyqt.cpp @@ -21,12 +21,6 @@ #include "gui/common/FilesDefs.h" #include -#include -#include -#include -//#include -#include - #include "notifyqt.h" #include #include @@ -55,6 +49,13 @@ #include "retroshare/rsplugin.h" +#include +#include +#include +//#include +#include +#include + /***** * #define NOTIFY_DEBUG ****/ @@ -223,37 +224,51 @@ bool NotifyQt::askForPassword(const std::string& title, const std::string& key_d { RsAutoUpdatePage::lockAllEvents() ; - QInputDialog dialog; + QString windowTitle; if (title == "") { - dialog.setWindowTitle(tr("Passphrase required")); + windowTitle = tr("Passphrase required"); } else if (title == "AuthSSLimpl::SignX509ReqWithGPG()") { - dialog.setWindowTitle(tr("You need to sign your node's certificate.")); + windowTitle = tr("You need to sign your node's certificate."); } else if (title == "p3IdService::service_CreateGroup()") { - dialog.setWindowTitle(tr("You need to sign your forum/chatrooms identity.")); + windowTitle = tr("You need to sign your forum/chatrooms identity."); } else { - dialog.setWindowTitle(QString::fromStdString(title)); + windowTitle = QString::fromStdString(title); } - dialog.setLabelText((prev_is_bad ? QString("%1

").arg(tr("Wrong password !")) : QString()) + QString("%1
Profile: %2\n").arg(tr("Please enter your Retroshare passphrase"), QString::fromUtf8(key_details.c_str()))); - dialog.setTextEchoMode(QLineEdit::Password); - dialog.setModal(true); + QString labelText = ( prev_is_bad ? QString("%1

").arg(tr("Wrong password !")) : QString() ) + + QString("%1
Profile: %2\n") + .arg( tr("Please enter your Retroshare passphrase") + , QString::fromUtf8(key_details.c_str()) ); + QLineEdit::EchoMode textEchoMode = QLineEdit::Password; + bool modal = true; - int ret = dialog.exec(); + bool sameThread = QThread::currentThread() == qApp->thread(); + Gui_InputDialogReturn ret; + qRegisterMetaType("Gui_InputDialogReturn"); + QMetaObject::invokeMethod( MainWindow::getInstance() + , "guiInputDialog" + , sameThread ? Qt::DirectConnection : Qt::BlockingQueuedConnection + , Q_RETURN_ARG(Gui_InputDialogReturn, ret) + , Q_ARG(QString, windowTitle) + , Q_ARG(QString, labelText) + , Q_ARG(QLineEdit::EchoMode, textEchoMode) + , Q_ARG(bool, modal) + ); - cancelled = false ; + cancelled = false ; RsAutoUpdatePage::unlockAllEvents() ; - if (ret == QDialog::Rejected) { - password.clear() ; - cancelled = true ; - return true ; - } + if (ret.execReturn == QDialog::Rejected) { + password.clear() ; + cancelled = true ; + return true ; + } - if (ret == QDialog::Accepted) { - password = dialog.textValue().toUtf8().constData(); - return true; - } + if (ret.execReturn == QDialog::Accepted) { + password = ret.textValue.toUtf8().constData(); + return true; + } return false; } @@ -548,18 +563,6 @@ void NotifyQt::notifyListChange(int list, int type) break; case NOTIFY_LIST_SEARCHLIST: break; - case NOTIFY_LIST_MESSAGELIST: -#ifdef NOTIFY_DEBUG - std::cerr << "received msg changed" << std::endl ; -#endif - emit messagesChanged() ; - break; - case NOTIFY_LIST_MESSAGE_TAGS: -#ifdef NOTIFY_DEBUG - std::cerr << "received msg tags changed" << std::endl ; -#endif - emit messagesTagsChanged(); - break; case NOTIFY_LIST_CHANNELLIST: break; case NOTIFY_LIST_TRANSFERLIST: @@ -648,8 +651,6 @@ void NotifyQt::notifyListPreChange(int list, int /*type*/) break; case NOTIFY_LIST_SEARCHLIST: break; - case NOTIFY_LIST_MESSAGELIST: - break; case NOTIFY_LIST_CHANNELLIST: break; case NOTIFY_LIST_TRANSFERLIST: @@ -682,7 +683,6 @@ void NotifyQt::UpdateGUI() // the gui is running, then they get updated by callbacks. if(!already_updated) { - emit messagesChanged() ; emit neighboursChanged(); emit configChanged(); diff --git a/retroshare-gui/src/gui/notifyqt.h b/retroshare-gui/src/gui/notifyqt.h index 069ae8056..f4fc063c7 100644 --- a/retroshare-gui/src/gui/notifyqt.h +++ b/retroshare-gui/src/gui/notifyqt.h @@ -109,8 +109,6 @@ class NotifyQt: public QObject, public NotifyClient void lobbyListChanged() const ; void chatLobbyEvent(qulonglong,int,const RsGxsId&,const QString&) ; void neighboursChanged() const ; - void messagesChanged() const ; - void messagesTagsChanged() const; void configChanged() const ; void logInfoChanged(const QString&) const ; void chatStatusChanged(const ChatId&,const QString&) const ; diff --git a/retroshare-gui/src/gui/qss/stylesheet/Standard_Dark.qss b/retroshare-gui/src/gui/qss/stylesheet/Standard_Dark.qss index f2097d0be..536a14061 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/Standard_Dark.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/Standard_Dark.qss @@ -2230,6 +2230,15 @@ PlotWidget { /* RetroShare specific part */ /**********************************/ +/**** Fix QPushButton ****/ + +QPushButton, +QPushButton:disabled, +QPushButton:checked { + padding-left: 6px; + padding-right: 6px; +} + /**** Fix QTreeView Items ****/ QTreeView::item, @@ -2248,13 +2257,13 @@ QTreeView:branch:selected:!active { /**** Fix QSplitter ****/ QSplitter { - background-color: #19232D; + background-color: rgba(0,0,0,0); } - QSplitter::handle { - background-color: #455364; - border: 0px solid #455364; - border-radius: 4px; + background-color: rgba(0,0,0,0); + border: 0px; + border-radius: 2px; + padding: 0px; } @@ -2286,7 +2295,7 @@ QFrame#bottomFrame,/* Frame used at the bottom of dialog*/ QFrame#toasterFrame,/* Frame used in Toasters*/ QFrame#toolBarFrame {/* Frame used for buttons*/ background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #19232D, stop:1 #32414B); - border: 1px solid #CCCCCC; + border: 1px solid #455364; } QFrame#toolBarFrame > LineEditClear { background-color: #29333D; @@ -2316,7 +2325,7 @@ QFrame#titleBarFrame { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #455364, stop: 0.5 #54687A,stop: 0.6 #44586A, stop:1 #455364); border: 0px; } -QFrame[objectName^="#headerBFrame"] { +QFrame[objectName^="headerBFrame"] { border: 1px; border-radius: 4px; } diff --git a/retroshare-gui/src/gui/qss/stylesheet/Standard_Light.qss b/retroshare-gui/src/gui/qss/stylesheet/Standard_Light.qss index 85f601e3e..a7f1e6c8c 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/Standard_Light.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/Standard_Light.qss @@ -2230,6 +2230,15 @@ PlotWidget { /* RetroShare specific part */ /**********************************/ +/**** Fix QPushButton ****/ + +QPushButton, +QPushButton:disabled, +QPushButton:checked { + padding-left: 6px; + padding-right: 6px; +} + /**** Fix QTreeView Items ****/ QTreeView::item, @@ -2248,13 +2257,13 @@ QTreeView:branch:selected:!active { /**** Fix QSplitter ****/ QSplitter { - background-color: white; + background-color: rgba(0,0,0,0); } - QSplitter::handle { - background-color: #C9CDD0; - border: 0px solid #C9CDD0; - border-radius: 4px; + background-color: rgba(0,0,0,0); + border: 0px; + border-radius: 2px; + padding: 0px; } @@ -2307,25 +2316,34 @@ QLabel#avatarLabel{ } -/* HeaderFrame & TitleBarFrame */ +/* HeaderFrame */ QFrame[objectName^="headerFrame"], QFrame[objectName^="headerBFrame"], -QToolBar#headerToolBar, -QFrame#titleBarFrame { +QToolBar#headerToolBar { background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD); border: 0px; } -QFrame[objectName^="#headerBFrame"] { +QFrame[objectName^="headerBFrame"] { border: 1px; border-radius: 4px; } QFrame[objectName^="headerFrame"] > *:!hover, -QFrame[objectName^="headerBFrame"] > *:!hover, -QFrame#titleBarFrame > *:!hover { +QFrame[objectName^="headerBFrame"] > *:!hover { background: transparent; color: white; } + +/* TitleBarFrame */ + +QFrame#titleBarFrame { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #F0F8FD, stop:1 #E6F2FD); + border: 0px; +} +QFrame#titleBarFrame > *:!hover { + background: transparent; + color: black; +} QFrame#titleBarFrame QComboBox, QFrame#titleBarFrame QLineEdit, QFrame#titleBarFrame QTextEdit { diff --git a/retroshare-gui/src/gui/qss/stylesheet/default.qss b/retroshare-gui/src/gui/qss/stylesheet/default.qss index 40d0b06ea..d1730bfef 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/default.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/default.qss @@ -418,31 +418,3 @@ OpModeStatus[opMode="Minimal"] { [WrongValue="true"] { background-color: #FF8080; } - -/* HeaderFrame & TitleBarFrame */ - -QFrame[objectName^="headerFrame"], -QFrame[objectName^="headerBFrame"] { - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD); - border: 1px; - border-radius: 4px; -} -QFrame[objectName^="#headerBFrame"] { - border: 1px; - border-radius: 4px; -} -QFrame[objectName^="headerFrame"] > *:!hover, -QFrame[objectName^="headerBFrame"] > *:!hover{ - background: transparent; - color: white; -} - -QFrame#toolBarFrame, QFrame#toolBarFrameTop { - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8); - border: 1px solid #CCCCCC; -} - -ChatWidget QFrame#titleBarFrame { - background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8); - border: 1px solid #CCCCCC; -} diff --git a/retroshare-gui/src/gui/settings/MessagePage.cpp b/retroshare-gui/src/gui/settings/MessagePage.cpp index 91a24ab02..ff302317d 100644 --- a/retroshare-gui/src/gui/settings/MessagePage.cpp +++ b/retroshare-gui/src/gui/settings/MessagePage.cpp @@ -27,6 +27,7 @@ #include "gui/common/TagDefs.h" #include #include "NewTag.h" +#include "util/qtthreadsutils.h" MessagePage::MessagePage(QWidget * parent, Qt::WindowFlags flags) : ConfigPage(parent, flags) @@ -54,10 +55,14 @@ MessagePage::MessagePage(QWidget * parent, Qt::WindowFlags flags) connect(ui.loadEmbeddedImages, SIGNAL(toggled(bool)), this,SLOT(updateLoadEmbededImages() )); connect(ui.openComboBox, SIGNAL(currentIndexChanged(int)),this,SLOT(updateMsgOpen() )); connect(ui.emoticonscheckBox, SIGNAL(toggled(bool)), this,SLOT(updateLoadEmoticons() )); + + mTagEventHandlerId = 0; + rsEvents->registerEventsHandler( [this](std::shared_ptr event) { RsQThreadUtils::postToObject( [this,event]() { handleEvent_main_thread(event); }); }, mTagEventHandlerId, RsEventType::MAIL_TAG ); } MessagePage::~MessagePage() { + rsEvents->unregisterEventsHandler(mTagEventHandlerId); delete(m_pTags); } @@ -134,6 +139,27 @@ MessagePage::load() fillTags(); } +void MessagePage::handleEvent_main_thread(std::shared_ptr event) +{ + if (event->mType != RsEventType::MAIL_TAG) { + return; + } + + const RsMailTagEvent *fe = dynamic_cast(event.get()); + if (!fe) { + return; + } + + switch (fe->mMailTagEventCode) { + case RsMailTagEventCode::TAG_ADDED: + case RsMailTagEventCode::TAG_CHANGED: + case RsMailTagEventCode::TAG_REMOVED: + rsMail->getMessageTagTypes(*m_pTags); + fillTags(); + break; + } +} + // fill tags void MessagePage::fillTags() { diff --git a/retroshare-gui/src/gui/settings/MessagePage.h b/retroshare-gui/src/gui/settings/MessagePage.h index f3a4c12a7..774d31b95 100644 --- a/retroshare-gui/src/gui/settings/MessagePage.h +++ b/retroshare-gui/src/gui/settings/MessagePage.h @@ -62,11 +62,13 @@ private slots: void updateLoadEmoticons(); private: + void handleEvent_main_thread(std::shared_ptr event); void fillTags(); /* Pointer for not include of rsmsgs.h */ MsgTagType *m_pTags; std::list m_changedTagIds; + RsEventsHandlerId_t mTagEventHandlerId; Ui::MessagePage ui; }; diff --git a/retroshare-gui/src/gui/statusbar/torstatus.cpp b/retroshare-gui/src/gui/statusbar/torstatus.cpp index f046f09cc..fdde22337 100644 --- a/retroshare-gui/src/gui/statusbar/torstatus.cpp +++ b/retroshare-gui/src/gui/statusbar/torstatus.cpp @@ -28,11 +28,10 @@ #include "retroshare/rsconfig.h" #include "retroshare/rsinit.h" #include "retroshare/rspeers.h" +#include "retroshare/rstor.h" #include #include "util/misc.h" -#include "TorControl/TorManager.h" -#include "TorControl/TorControl.h" #include "gui/common/FilesDefs.h" #include @@ -92,8 +91,8 @@ void TorStatus::getTorStatus() if(RsAccounts::isTorAuto()) { // get Tor status - int tor_control_status = Tor::TorManager::instance()->control()->status(); - int torstatus = Tor::TorManager::instance()->control()->torStatus(); + RsTorConnectivityStatus tor_control_status = RsTor::torConnectivityStatus(); + RsTorStatus torstatus = RsTor::torStatus(); QString tor_control_status_str,torstatus_str ; bool tor_control_ok ; @@ -101,30 +100,31 @@ void TorStatus::getTorStatus() 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 ; - } + case RsTorConnectivityStatus::ERROR : tor_control_ok = false ; tor_control_status_str = "Error" ; break ; + case RsTorConnectivityStatus::NOT_CONNECTED: tor_control_ok = false ; tor_control_status_str = "Not connected" ; break ; + case RsTorConnectivityStatus::CONNECTING: tor_control_ok = false ; tor_control_status_str = "Connecting" ; break ; + case RsTorConnectivityStatus::AUTHENTICATING: tor_control_ok = false ; tor_control_status_str = "Authenticating" ; break ; + case RsTorConnectivityStatus::AUTHENTICATED: tor_control_ok = false ; tor_control_status_str = "Connected" ; break ; + case RsTorConnectivityStatus::HIDDEN_SERVICE_READY: tor_control_ok = true ; tor_control_status_str = "Hidden service ready" ; break ; + } switch(torstatus) { default: - case Tor::TorControl::TorUnknown: torstatus_str = "Unknown" ; break ; - case Tor::TorControl::TorOffline: torstatus_str = "Tor offline" ; break ; - case Tor::TorControl::TorReady: torstatus_str = "Tor ready" ; break ; + case RsTorStatus::UNKNOWN: torstatus_str = "Unknown" ; break ; + case RsTorStatus::OFFLINE: torstatus_str = "Tor offline" ; break ; + case RsTorStatus::READY: torstatus_str = "Tor ready" ; break ; } #define MIN_RS_NET_SIZE 10 - if(torstatus == Tor::TorControl::TorOffline || !online || !tor_control_ok) + if(torstatus == RsTorStatus::OFFLINE || !online || !tor_control_ok) { // RED - some issue. torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/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) + else if(torstatus == RsTorStatus::READY && online && tor_control_ok) { torstatusLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/tor-on.png").scaledToHeight(1.5*S,Qt::SmoothTransformation)); torstatusLabel->setToolTip( text + tr("Tor is OK")); @@ -141,12 +141,12 @@ void TorStatus::getTorStatus() if(!_updated) { RsPeerDetails pd; - uint32_t hiddentype; - if (rsPeers->getPeerDetails(rsPeers->getOwnId(), pd)) { + uint32_t hiddentype = RS_HIDDEN_TYPE_UNKNOWN; + + if (rsPeers->getPeerDetails(rsPeers->getOwnId(), pd)) + { if(pd.netMode == RS_NETMODE_HIDDEN) - { hiddentype = pd.hiddenType; - } } std::string proxyaddr; uint16_t proxyport; diff --git a/retroshare-gui/src/main.cpp b/retroshare-gui/src/main.cpp index cfb9932ca..82d78d2b2 100644 --- a/retroshare-gui/src/main.cpp +++ b/retroshare-gui/src/main.cpp @@ -66,7 +66,7 @@ CrashStackTrace gCrashStackTrace; # include "gui/settings/JsonApiPage.h" #endif // RS_JSONAPI -#include "TorControl/TorManager.h" +#include "retroshare/rstor.h" #include "TorControl/TorControlWindow.h" #include "retroshare/rsidentity.h" @@ -99,10 +99,6 @@ __declspec(dllexport) __cdecl BOOL _OPENSSL_isservice(void) #endif #endif -/*** WINDOWS DON'T LIKE THIS - REDEFINES VER numbers. -#include -****/ - #include #include #include @@ -399,24 +395,23 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); { // 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/") ; + std::string tor_hidden_service_dir = RsAccounts::AccountDirectory() + "/hidden_service/" ; - Tor::TorManager *torManager = Tor::TorManager::instance(); - torManager->setTorDataDirectory(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 + RsTor::setTorDataDirectory(Rshare::dataDirectory().toStdString() + "/tor/"); + RsTor::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())) ; + RsDirUtil::checkCreateDirectory(std::string(tor_hidden_service_dir)) ; - torManager->setupHiddenService(); + //RsTor::setupHiddenService(); - if(! torManager->start() || torManager->hasError()) + if(! RsTor::start() || RsTor::hasError()) { - QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+torManager->errorMessage()) ; + QMessageBox::critical(NULL,QObject::tr("Cannot start Tor Manager!"),QObject::tr("Tor cannot be started on your system: \n\n")+QString::fromStdString(RsTor::errorMessage())) ; return 1 ; } { - TorControlDialog tcd(torManager) ; + TorControlDialog tcd; QString error_msg ; tcd.show(); @@ -460,30 +455,29 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); { // Tor works with viable hidden service. Let's use it! - QString service_id ; - QString onion_address ; + std::string service_id ; + std::string onion_address ; uint16_t service_port ; uint16_t service_target_port ; uint16_t proxy_server_port ; - QHostAddress service_target_address ; - QHostAddress proxy_server_address ; + std::string service_target_address ; + std::string proxy_server_address ; - Tor::TorManager *torManager = Tor::TorManager::instance(); - torManager->getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port); - torManager->getProxyServerInfo(proxy_server_address,proxy_server_port) ; + RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port); + RsTor::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 << " onion address : " << onion_address << std::endl; + std::cerr << " service_id : " << service_id << 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 << " target address : " << service_target_address << std::endl; - std::cerr << "Setting proxy server to " << service_target_address.toString().toStdString() << ":" << service_target_port << std::endl; + std::cerr << "Setting proxy server to " << service_target_address << ":" << 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) ; + rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address, service_target_port); + rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address, service_port); + rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address,proxy_server_port) ; } Rshare::initPlugins(); @@ -597,6 +591,10 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO); RsGxsUpdateBroadcast::cleanup(); #endif + if (is_auto_tor) { + RsTor::stop(); + } + RsControl::instance()->rsGlobalShutDown(); delete(soundManager); diff --git a/retroshare-gui/src/qss/retroclassic.qss b/retroshare-gui/src/qss/retroclassic.qss new file mode 100644 index 000000000..eb45b3379 --- /dev/null +++ b/retroshare-gui/src/qss/retroclassic.qss @@ -0,0 +1,21 @@ +/* HeaderFrame & TitleBarFrame */ + +QFrame[objectName^="headerFrame"] { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD); +} +QFrame[objectName^="headerBFrame"] { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9BDBF9, stop:1 #1592CD); + border: 1px; + border-radius: 4px; +} +QFrame[objectName^="headerFrame"] > *:!hover, +QFrame[objectName^="headerBFrame"] > *:!hover{ + background: transparent; + color: white; +} + +QFrame[objectName^="toolBarFrame"], +QFrame[objectName^="toolBarFrameTop"] { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #FEFEFE, stop:1 #E8E8E8); + border: 1px solid #CCCCCC; +} diff --git a/retroshare-gui/src/release/skin/Bliss/maxButton.png b/retroshare-gui/src/release/skin/Bliss/maxButton.png deleted file mode 100644 index 4d83bcd35..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/maxButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/maxButton1.png b/retroshare-gui/src/release/skin/Bliss/maxButton1.png deleted file mode 100644 index a916ba483..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/maxButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/maxButton2.png b/retroshare-gui/src/release/skin/Bliss/maxButton2.png deleted file mode 100644 index aaba43674..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/maxButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/minButton.png b/retroshare-gui/src/release/skin/Bliss/minButton.png deleted file mode 100644 index 7f1553a6e..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/minButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/minButton1.png b/retroshare-gui/src/release/skin/Bliss/minButton1.png deleted file mode 100644 index a3c564cdd..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/minButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/minButton2.png b/retroshare-gui/src/release/skin/Bliss/minButton2.png deleted file mode 100644 index 01af810b0..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/minButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/quiButton.png b/retroshare-gui/src/release/skin/Bliss/quiButton.png deleted file mode 100644 index 6a657ae1a..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/quiButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/quiButton1.png b/retroshare-gui/src/release/skin/Bliss/quiButton1.png deleted file mode 100644 index 174c7b2ee..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/quiButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/quiButton2.png b/retroshare-gui/src/release/skin/Bliss/quiButton2.png deleted file mode 100644 index f379e9431..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/quiButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/sl.png b/retroshare-gui/src/release/skin/Bliss/sl.png deleted file mode 100644 index 851821b6a..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/sl.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/so.png b/retroshare-gui/src/release/skin/Bliss/so.png deleted file mode 100644 index 6497639b3..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/so.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/sr.png b/retroshare-gui/src/release/skin/Bliss/sr.png deleted file mode 100644 index 6689549b3..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/sr.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Bliss/su.png b/retroshare-gui/src/release/skin/Bliss/su.png deleted file mode 100644 index 9862742f0..000000000 Binary files a/retroshare-gui/src/release/skin/Bliss/su.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Glassy/maxButton.png b/retroshare-gui/src/release/skin/Glassy/maxButton.png deleted file mode 100644 index 469d21e40..000000000 Binary files a/retroshare-gui/src/release/skin/Glassy/maxButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Glassy/minButton.png b/retroshare-gui/src/release/skin/Glassy/minButton.png deleted file mode 100644 index 40c6ba78c..000000000 Binary files a/retroshare-gui/src/release/skin/Glassy/minButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Glassy/quiButton.png b/retroshare-gui/src/release/skin/Glassy/quiButton.png deleted file mode 100644 index da6390211..000000000 Binary files a/retroshare-gui/src/release/skin/Glassy/quiButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Glassy/sl.png b/retroshare-gui/src/release/skin/Glassy/sl.png deleted file mode 100644 index f259a74c7..000000000 Binary files a/retroshare-gui/src/release/skin/Glassy/sl.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Glassy/so.png b/retroshare-gui/src/release/skin/Glassy/so.png deleted file mode 100644 index 9b554663d..000000000 Binary files a/retroshare-gui/src/release/skin/Glassy/so.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Glassy/sr.png b/retroshare-gui/src/release/skin/Glassy/sr.png deleted file mode 100644 index f259a74c7..000000000 Binary files a/retroshare-gui/src/release/skin/Glassy/sr.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Glassy/su.png b/retroshare-gui/src/release/skin/Glassy/su.png deleted file mode 100644 index 0e96fdbed..000000000 Binary files a/retroshare-gui/src/release/skin/Glassy/su.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Green/maxButton.png b/retroshare-gui/src/release/skin/Green/maxButton.png deleted file mode 100644 index 73f1fc27d..000000000 Binary files a/retroshare-gui/src/release/skin/Green/maxButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Green/minButton.png b/retroshare-gui/src/release/skin/Green/minButton.png deleted file mode 100644 index 1c9adaa6c..000000000 Binary files a/retroshare-gui/src/release/skin/Green/minButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Green/quiButton.png b/retroshare-gui/src/release/skin/Green/quiButton.png deleted file mode 100644 index 6134015ef..000000000 Binary files a/retroshare-gui/src/release/skin/Green/quiButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Green/sl.png b/retroshare-gui/src/release/skin/Green/sl.png deleted file mode 100644 index 06af00e82..000000000 Binary files a/retroshare-gui/src/release/skin/Green/sl.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Green/so.png b/retroshare-gui/src/release/skin/Green/so.png deleted file mode 100644 index 661ffd761..000000000 Binary files a/retroshare-gui/src/release/skin/Green/so.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Green/sr.png b/retroshare-gui/src/release/skin/Green/sr.png deleted file mode 100644 index 7f62f1c0b..000000000 Binary files a/retroshare-gui/src/release/skin/Green/sr.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Green/su.png b/retroshare-gui/src/release/skin/Green/su.png deleted file mode 100644 index 8e04d5077..000000000 Binary files a/retroshare-gui/src/release/skin/Green/su.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Night Vision/maxButton.png b/retroshare-gui/src/release/skin/Night Vision/maxButton.png deleted file mode 100644 index cbb27c3cb..000000000 Binary files a/retroshare-gui/src/release/skin/Night Vision/maxButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Night Vision/minButton.png b/retroshare-gui/src/release/skin/Night Vision/minButton.png deleted file mode 100644 index 5892a982c..000000000 Binary files a/retroshare-gui/src/release/skin/Night Vision/minButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Night Vision/quiButton.png b/retroshare-gui/src/release/skin/Night Vision/quiButton.png deleted file mode 100644 index 1443caa9d..000000000 Binary files a/retroshare-gui/src/release/skin/Night Vision/quiButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Night Vision/sl.png b/retroshare-gui/src/release/skin/Night Vision/sl.png deleted file mode 100644 index acc9db3d1..000000000 Binary files a/retroshare-gui/src/release/skin/Night Vision/sl.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Night Vision/so.png b/retroshare-gui/src/release/skin/Night Vision/so.png deleted file mode 100644 index 900e051a9..000000000 Binary files a/retroshare-gui/src/release/skin/Night Vision/so.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Night Vision/sr.png b/retroshare-gui/src/release/skin/Night Vision/sr.png deleted file mode 100644 index 0d0f296ec..000000000 Binary files a/retroshare-gui/src/release/skin/Night Vision/sr.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Night Vision/su.png b/retroshare-gui/src/release/skin/Night Vision/su.png deleted file mode 100644 index 32b195e0d..000000000 Binary files a/retroshare-gui/src/release/skin/Night Vision/su.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/maxButton.png b/retroshare-gui/src/release/skin/Vista/maxButton.png deleted file mode 100644 index cbb27c3cb..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/maxButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/maxButton1.png b/retroshare-gui/src/release/skin/Vista/maxButton1.png deleted file mode 100644 index 1bed27a49..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/maxButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/maxButton2.png b/retroshare-gui/src/release/skin/Vista/maxButton2.png deleted file mode 100644 index a9ece6da2..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/maxButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/minButton.png b/retroshare-gui/src/release/skin/Vista/minButton.png deleted file mode 100644 index 5892a982c..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/minButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/minButton1.png b/retroshare-gui/src/release/skin/Vista/minButton1.png deleted file mode 100644 index 79207e5df..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/minButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/minButton2.png b/retroshare-gui/src/release/skin/Vista/minButton2.png deleted file mode 100644 index b52bdb299..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/minButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/quiButton.png b/retroshare-gui/src/release/skin/Vista/quiButton.png deleted file mode 100644 index 1443caa9d..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/quiButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/quiButton1.png b/retroshare-gui/src/release/skin/Vista/quiButton1.png deleted file mode 100644 index 008b25e2e..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/quiButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/quiButton2.png b/retroshare-gui/src/release/skin/Vista/quiButton2.png deleted file mode 100644 index 3e79d9f08..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/quiButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/sl.png b/retroshare-gui/src/release/skin/Vista/sl.png deleted file mode 100644 index 05628496b..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/sl.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/so.png b/retroshare-gui/src/release/skin/Vista/so.png deleted file mode 100644 index d9472686c..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/so.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/sr.png b/retroshare-gui/src/release/skin/Vista/sr.png deleted file mode 100644 index 60e8e091f..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/sr.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/Vista/su.png b/retroshare-gui/src/release/skin/Vista/su.png deleted file mode 100644 index c62254cfb..000000000 Binary files a/retroshare-gui/src/release/skin/Vista/su.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/bg.png b/retroshare-gui/src/release/skin/VistaAlpha/bg.png deleted file mode 100644 index f7ead614a..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/bg.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/maxButton.png b/retroshare-gui/src/release/skin/VistaAlpha/maxButton.png deleted file mode 100644 index bb1c53ee4..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/maxButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/maxButton1.png b/retroshare-gui/src/release/skin/VistaAlpha/maxButton1.png deleted file mode 100644 index 1bed27a49..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/maxButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/maxButton2.png b/retroshare-gui/src/release/skin/VistaAlpha/maxButton2.png deleted file mode 100644 index a9ece6da2..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/maxButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/minButton.png b/retroshare-gui/src/release/skin/VistaAlpha/minButton.png deleted file mode 100644 index 379bfa4cb..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/minButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/minButton1.png b/retroshare-gui/src/release/skin/VistaAlpha/minButton1.png deleted file mode 100644 index 79207e5df..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/minButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/minButton2.png b/retroshare-gui/src/release/skin/VistaAlpha/minButton2.png deleted file mode 100644 index b52bdb299..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/minButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/quiButton.png b/retroshare-gui/src/release/skin/VistaAlpha/quiButton.png deleted file mode 100644 index e3882e9ff..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/quiButton.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/quiButton1.png b/retroshare-gui/src/release/skin/VistaAlpha/quiButton1.png deleted file mode 100644 index 008b25e2e..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/quiButton1.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/quiButton2.png b/retroshare-gui/src/release/skin/VistaAlpha/quiButton2.png deleted file mode 100644 index 3e79d9f08..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/quiButton2.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/sl.png b/retroshare-gui/src/release/skin/VistaAlpha/sl.png deleted file mode 100644 index 82deb022e..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/sl.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/so.png b/retroshare-gui/src/release/skin/VistaAlpha/so.png deleted file mode 100644 index 677255b01..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/so.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/sr.png b/retroshare-gui/src/release/skin/VistaAlpha/sr.png deleted file mode 100644 index 22f2e74fe..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/sr.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/VistaAlpha/su.png b/retroshare-gui/src/release/skin/VistaAlpha/su.png deleted file mode 100644 index 6f19cdc00..000000000 Binary files a/retroshare-gui/src/release/skin/VistaAlpha/su.png and /dev/null differ diff --git a/retroshare-gui/src/release/skin/skin.dat b/retroshare-gui/src/release/skin/skin.dat deleted file mode 100644 index 7a501621c..000000000 --- a/retroshare-gui/src/release/skin/skin.dat +++ /dev/null @@ -1,7 +0,0 @@ -[Skin] -Blur=false -Hintergrundfarbe=@Variant(\0\0\0\x43\x1\xff\xff\xd5\xd5\xd5\xd5\xd5\xd5\0\0) -SkinOn=true -Skinpfad=skin/Vista/ -Titelfarbe=@Variant(\0\0\0\x43\x1\xff\xff\0\0\0\0\0\0\0\0) -Titelschrift=@Variant(\0\0\0@\0\0\0\x1a\0\x43\0o\0m\0i\0\x63\0 \0S\0\x61\0n\0s\0 \0M\0S@$\0\0\0\0\0\0\xff\xff\xff\xff\x5\x1\0K\x10) diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index f6d061057..4c6ec6dc8 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -363,46 +363,6 @@ wikipoos { ################################### HEADERS & SOURCES ############################# -# Tor controller - -HEADERS += TorControl/AddOnionCommand.h \ - TorControl/AuthenticateCommand.h \ - TorControl/CryptoKey.h \ - TorControl/GetConfCommand.h \ - TorControl/HiddenService.h \ - TorControl/PendingOperation.h \ - TorControl/ProtocolInfoCommand.h \ - TorControl/SecureRNG.h \ - TorControl/SetConfCommand.h \ - TorControl/Settings.h \ - TorControl/StrUtil.h \ - TorControl/TorControl.h \ - TorControl/TorControlCommand.h \ - TorControl/TorControlSocket.h \ - TorControl/TorManager.h \ - TorControl/TorProcess.h \ - TorControl/TorProcess_p.h \ - TorControl/TorSocket.h \ - TorControl/Useful.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 \ diff --git a/retroshare-gui/src/retroshare-process.nsi b/retroshare-gui/src/retroshare-process.nsi deleted file mode 100644 index 685f90214..000000000 --- a/retroshare-gui/src/retroshare-process.nsi +++ /dev/null @@ -1,502 +0,0 @@ -; Script generated with the Venis Install Wizard & modified by defnax -; This script need the http://nsis.sourceforge.net/Processes_plug-in -; This script need the http://nsis.sourceforge.net/KillProcDLL_plug-in - -; Define your application name -!define APPNAME "RetroShare" -!define VERSION "0.4.13d" -!define APPNAMEANDVERSION "${APPNAME} ${VERSION}" - - -; Main Install settings -Name "${APPNAMEANDVERSION}" -InstallDir "$PROGRAMFILES\RetroShare" -InstallDirRegKey HKLM "Software\${APPNAME}" "" -OutFile "RetroShare_${VERSION}_setup.exe" -BrandingText "${APPNAMEANDVERSION}" -; Use compression -SetCompressor LZMA - -VAR KILLEXENAME - - -; Modern interface settings -!include Sections.nsh -!include "MUI.nsh" - -;Interface Settings -!define MUI_ABORTWARNING -;!define MUI_HEADERIMAGE -;!define MUI_HEADERIMAGE_BITMAP "retroshare.bmp" ; optional - -# MUI defines -!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" -!define MUI_FINISHPAGE_NOAUTOCLOSE -!define MUI_LICENSEPAGE_RADIOBUTTONS -!define MUI_COMPONENTSPAGE_SMALLDESC -!define MUI_FINISHPAGE_LINK "Visit the RetroShare forum for the latest news and support" -!define MUI_FINISHPAGE_LINK_LOCATION "http://retroshare.sourceforge.net/forum/" -!define MUI_FINISHPAGE_RUN "$INSTDIR\RetroShare.exe" -!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_TEXT changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED -!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" -!define MUI_UNFINISHPAGE_NOAUTOCLOSE -!define MUI_LANGDLL_REGISTRY_ROOT HKLM -!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY} -!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage - -; !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIt is recommended that you close all other applications before starting Setup. This will make it possible to update relevant system files without havinf to reboot your computer. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - -;!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - - -; Defines the un-/installer logo of RetroShare -!insertmacro MUI_DEFAULT MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" -!insertmacro MUI_DEFAULT MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-uninstall.bmp" - -; Set languages (first is default language) -!insertmacro MUI_RESERVEFILE_LANGDLL -ReserveFile "${NSISDIR}\Plugins\AdvSplash.dll" - -;-------------------------------- -;Configuration - - - ;!insertmacro MUI_RESERVEFILE_SPECIALBITMAP - - LicenseLangString myLicenseData 1033 "license\license.txt" - LicenseLangString myLicenseData 1031 "license\license-GER.txt" - LicenseLangString myLicenseData 1036 "license\license-FR.txt" - LicenseLangString myLicenseData 1055 "license\license-TR.txt" - LicenseLangString myLicenseData 2052 "license\license.txt" - LicenseLangString myLicenseData 1045 "license\license.txt" - - LicenseData $(myLicenseData) - -# Installer pages -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)" -!insertmacro MUI_PAGE_COMPONENTS -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES - -# Installer languages -!define MUI_LANGDLL_ALLLANGUAGES - -!insertmacro MUI_LANGUAGE English -!insertmacro MUI_LANGUAGE German -!insertmacro MUI_LANGUAGE French -!insertmacro MUI_LANGUAGE Turkish -!insertmacro MUI_LANGUAGE SimpChinese -!insertmacro MUI_LANGUAGE Polish - - ;Component-selection page - ;Titles - - LangString sec_main ${LANG_ENGLISH} "Program Files" - LangString sec_data ${LANG_ENGLISH} "Program Skins" - LangString sec_shortcuts ${LANG_ENGLISH} "Shortcuts" - LangString sec_link ${LANG_ENGLISH} "File Association" - LangString sec_autostart ${LANG_ENGLISH} "Auto Startup" - LangString DESC_sec_main ${LANG_ENGLISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_ENGLISH} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_ENGLISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_ENGLISH} "Associate RetroShare with .pqi file extension" - LangString DESC_sec_autostart ${LANG_ENGLISH} "Auto-Run and Login at Startup" - LangString LANGUAGEID ${LANG_ENGLISH} "1033" - - LangString sec_main ${LANG_FRENCH} "RetroShare" - LangString sec_data ${LANG_FRENCH} "Programme de Skins" - LangString sec_shortcuts ${LANG_FRENCH} "Raccourcis" - LangString sec_link ${LANG_FRENCH} "RetroShare fichiers Association" - LangString sec_startmenu ${LANG_FRENCH} "Raccourcis du menu Démarrer" - LangString sec_autostart ${LANG_FRENCH} "Démarrage automatique" - LangString DESC_sec_main ${LANG_FRENCH} "Installe les fichiers du programme." - LangString DESC_sec_data ${LANG_FRENCH} "Installe RetroShare Skins" - LangString DESC_sec_startmenu ${LANG_FRENCH} "Crée les raccourcis du menu Démarrer" - LangString DESC_sec_shortcuts ${LANG_FRENCH} "Crée une icône sur le bureau." - LangString DESC_sec_link ${LANG_FRENCH} "Associate RetroShare with .pqi file extension" - LangString DESC_sec_autostart ${LANG_FRENCH} "Run and Auto-connexion au démarrage" - LangString LANGUAGEID ${LANG_FRENCH} "1036" - - LangString sec_main ${LANG_GERMAN} "Programmdateien" - LangString sec_data ${LANG_GERMAN} "Skins f�r das Programm" - LangString sec_shortcuts ${LANG_GERMAN} "Shortcuts" - LangString sec_link ${LANG_GERMAN} "Dateiverkn�pfungen" - LangString sec_autostart ${LANG_GERMAN} "Auto Startup" - LangString DESC_sec_main ${LANG_GERMAN} "Installiert die erforderlichen Programmdateien." - LangString DESC_sec_data ${LANG_GERMAN} "Installiert RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_GERMAN} "Erstellt eine RetroShare Verkn�pfung im Startmen�, Desktop oder im Schnellstarter." - LangString DESC_sec_link ${LANG_GERMAN} "RetroShare mit .pqi Dateien verkn�pfen" - LangString DESC_sec_autostart ${LANG_GERMAN} "Beim Neustart automatisch RetroShare starten und sich anmelden" - LangString LANGUAGEID ${LANG_GERMAN} "1031" - - LangString sec_main ${LANG_TURKISH} "Program Dosyalar�" - LangString sec_data ${LANG_TURKISH} "Program Skinleri" - LangString sec_shortcuts ${LANG_TURKISH} "Shortcut'lar" - LangString sec_link ${LANG_TURKISH} ".pqi Dosya Kaydet" - LangString sec_autostart ${LANG_TURKISH} "Otomatik calistir ve baglan" - LangString DESC_sec_main ${LANG_TURKISH} "Program dosyalar�n� kurar." - LangString DESC_sec_data ${LANG_TURKISH} "RetroShare Skin'leri kurar" - LangString DESC_sec_shortcuts ${TURKISH} "Shortcut yap Start menu , Desktop veya Quicklaunchbar icin." - LangString DESC_sec_link ${LANG_TURKISH} "RetroShare .pqi almas� i�in kaydettirir" - LangString DESC_sec_autostart ${LANG_TURKISH} "Isletim sistemi acildiginda Otomatik olarak calistir ve baglan" - LangString LANGUAGEID ${LANG_TURKISH} "1055" - - LangString sec_main ${LANG_SIMPCHINESE} "程序文件" - LangString sec_data ${LANG_SIMPCHINESE} "程序皮肤" - LangString sec_shortcuts ${LANG_SIMPCHINESE} "快捷方式" - LangString sec_link ${LANG_SIMPCHINESE} "RetroShare文件关联" - LangString sec_autostart ${LANG_SIMPCHINESE} "自动启动" - LangString DESC_sec_main ${LANG_SIMPCHINESE} "安装RetroShare程序" - LangString DESC_sec_data ${LANG_SIMPCHINESE} "安装RetroShare皮肤" - LangString DESC_sec_shortcuts ${LANG_SIMPCHINESE} "建RetroShare快捷方式" - LangString DESC_sec_link ${LANG_SIMPCHINESE} "关联.pqi扩展名" - LangString DESC_sec_autostart ${LANG_SIMPCHINESE} "启动时自动运行和登录" - LangString LANGUAGEID ${LANG_SIMPCHINESE} "2052" - - LangString sec_main ${LANG_POLISH} "Pliki programu" - LangString sec_data ${LANG_POLISH} "Skórki" - LangString sec_shortcuts ${LANG_POLISH} "Skróty" - LangString sec_link ${LANG_POLISH} "Skojarz pliki" - LangString sec_autostart ${LANG_POLISH} "Automatyczne uruchamianie" - LangString DESC_sec_main ${LANG_POLISH} "Instaluje pliki programu RetroShare" - LangString DESC_sec_data ${LANG_POLISH} "Instaluje skórki programu RetroShare" - LangString DESC_sec_shortcuts ${LANG_POLISH} "Utwórz ikony skrótów na pulpicie, w menu start oraz na pasku szybkiego uruchamiania." - LangString DESC_sec_link ${LANG_POLISH} "Skojarz pliki o rozszerzeniu .pqi z RetroShare" - LangString DESC_sec_autostart ${LANG_POLISH} "Uruchom i zaloguj podczas startu systemu" - LangString LANGUAGEID ${LANG_POLISH} "1045" - - -!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - -; CloseRetroShare: this will in a loop send the RetroShare window the WM_CLOSE -; message until it does not find a valid RetroShare window -; - -!macro CloseRetroShare UN -Function ${UN}CloseRetroShare - Push $0 - - IntFmt $R4 "%u" 0 - - goto skip - killloop: - DetailPrint "Killing RetroShare" - KillProcDLL::KillProc "RetroShare.exe" - loop: - Sleep 1000 - IntOp $R4 $R4 + 1 - IntCmp $R4 5 done - skip: - DetailPrint "Looking for running copies of RetroShare" - - ;Push "wxWindowClassNR" # the wcn - ;Push "RetroShare" # the known part of the wt - ;Call ${UN}EnhancedFindWindow - ;Pop $0 # will contain the window's handle - ;Pop $1 # will containg the full wcn - # both will containg "failed", if no matching wcn was found - - ;StrCmp $0 "failed" done - ;StrCmp $0 "0" done - ;DetailPrint "Stopping RetroShare" - ;SendMessage $0 16 0 0 # WM_CLOSE == 16 - Goto loop - done: - - IntFmt $R4 "%u" 0 - Processes::FindProcess "RetroShare.exe" - StrCmp $R0 "1" killloop reallydone - - reallydone: - Pop $0 -FunctionEnd -!macroend - - -!macro CheckForIt UN -Function ${UN}CheckForIt - - Processes::FindProcess $KILLEXENAME - StrCmp $R0 "1" foundit didntfindit - - foundit: - MessageBox MB_OKCANCEL "You must quit ${APPNAME} ($KILLEXENAME) \ - before installing this version.$\r$\nPlease quit it and press \ - OK to continue." IDOK tryagain - Abort - - tryagain: - - Sleep 2000 - Processes::FindProcess $KILLEXENAME - StrCmp $R0 "1" stillthere didntfindit - - stillthere: - MessageBox MB_OKCANCEL "There is still a copy of ${APPNAME} \ - ($KILLEXENAME) running.$\r$\nPress OK to force-quit the application, \ - or Cancel to exit." IDOK killit - Abort - - killit: - KillProcDLL::KillProc $KILLEXENAME - Sleep 1000 - - didntfindit: - -FunctionEnd -!macroend - -!macro QuitIt UN -Function ${UN}QuitIt - - # try nicely first - Call ${UN}CloseRetroShare - - # kill all the old ones - StrCpy $KILLEXENAME "RetroShare.exe" - Call ${UN}CheckForIt - StrCpy $KILLEXENAME "" -FunctionEnd -!macroend - - -; This function automatically uninstalls older versions. -; It is largely copied from: -; http://nsis.sourceforge.net/archive/viewpage.php?pageid=326 - - -Section $(sec_main) sec_main - - ;Set Section required - SectionIn RO - - ; Set Section properties - SetOverwrite on - - ; Clears previous error logs - Delete "$INSTDIR\*.log" - - ; Set Section Files and Shortcuts - SetOutPath "$INSTDIR\" - File /r "release\RetroShare.exe" - File /r "D:\Qt\2009.02\mingw\bin\mingwm10.dll" - File /r "D:\Qt\2009.02\qt\bin\QtCore4.dll" - File /r "D:\Qt\2009.02\qt\bin\QtGui4.dll" - File /r "D:\Qt\2009.02\qt\bin\QtNetwork4.dll" - File /r "D:\Qt\2009.02\qt\bin\QtXml4.dll" - File /r "D:\Qt\2009.02\qt\bin\QtScript4.dll" - File /r "pthreadGCE2.dll" - File /r "pthreadGC2d.dll" - File /r "changelog.txt" - - -SectionEnd - -Section $(sec_data) sec_data - - ; Set Section properties - SetOverwrite on - - ; Set Section Files and Shortcuts - SetOutPath "$APPDATA\RetroShare\" - ;File /r "data\*" - - ; We're not ready for external skins... - ; Set Section qss need to remove svn path - SetOutPath "$INSTDIR\qss\" - File /r qss\*.* - - ; Set Section skin - ; SetOutPath "$INSTDIR\skin\" - ; File /r release\skin\*.* - - ; Add emoticons - SetOutPath "$INSTDIR\emoticons\" - File /r emoticons\*.* - - ; Add Chat Style - SetOutPath "$INSTDIR\style\" - File /r style\*.* - -SectionEnd - -Section $(sec_link) sec_link - ; Delete any existing keys - - - ; Write the file association - WriteRegStr HKCR .pqi "" retroshare - WriteRegStr HKCR retroshare "" "PQI File" - WriteRegBin HKCR retroshare EditFlags 00000100 - WriteRegStr HKCR "retroshare\shell" "" open - WriteRegStr HKCR "retroshare\shell\open\command" "" `"$INSTDIR\RetroShare.exe" "%1"` - -SectionEnd - -SectionGroup $(sec_shortcuts) sec_shortcuts -Section StartMenu SEC0001 - - SetOutPath "$INSTDIR" - CreateDirectory "$SMPROGRAMS\${APPNAME}" - CreateShortCut "$SMPROGRAMS\${APPNAME}\$(^UninstallLink).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 - CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd - -Section Desktop SEC0002 - - - CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd - -Section Quicklaunchbar SEC0003 - - - CreateShortCut "$QUICKLAUNCH\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd -SectionGroupEnd - -;Section $(sec_autostart) sec_autostart - -; WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" "$INSTDIR\${APPNAME}.exe -a" - -;SectionEnd - -Section $(sec_autostart) sec_autostart - - CreateShortCut "$SMSTARTUP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 -SectionEnd - - -Section -FinishSection - - WriteRegStr HKLM "Software\${APPNAME}" "" "$INSTDIR" - WriteRegStr HKLM "Software\${APPNAME}" "Version" "${VERSION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\uninstall.exe" - WriteUninstaller "$INSTDIR\uninstall.exe" - -SectionEnd - - - -;-------------------------------- -;Descriptions - -!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${sec_main} $(DESC_sec_main) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_data} $(DESC_sec_data) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_shortcuts} $(DESC_sec_shortcuts) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_link} $(DESC_sec_link) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_autostart} $(DESC_sec_autostart) -!insertmacro MUI_FUNCTION_DESCRIPTION_END - - -;!insertmacro EnhancedFindWindow "" -;!insertmacro EnhancedFindWindow "un." - -!insertmacro CloseRetroShare "" -!insertmacro CloseRetroShare "un." - -!insertmacro CheckForIt "" -!insertmacro CheckForIt "un." - -!insertmacro QuitIt "" -!insertmacro QuitIt "un." - -;Uninstall section -Section "Uninstall" - - Call un.QuitIt - - ; Remove file association registry keys - DeleteRegKey HKCR .pqi - DeleteRegKey HKCR retroshare - - ; Remove program/uninstall regsitry keys - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" - DeleteRegKey HKLM SOFTWARE\${APPNAME} - - DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" - - ; Remove files and uninstaller - Delete $INSTDIR\RetroShare.exe - Delete $INSTDIR\*.dll - Delete $INSTDIR\*.dat - Delete $INSTDIR\*.txt - Delete $INSTDIR\*.ini - Delete $INSTDIR\*.log - - Delete $INSTDIR\uninstall.exe - - ; Remove the kadc.ini file. - ; Don't remove the directory, otherwise - ; we lose the XPGP keys. - ; Should make this an option though... - Delete "$APPDATA\${APPNAME}\kadc.ini" - Delete "$APPDATA\${APPNAME}\*.cfg" - Delete "$APPDATA\${APPNAME}\*.conf" - Delete "$APPDATA\${APPNAME}\*.log-save" - Delete "$APPDATA\${APPNAME}\*.log" - Delete "$APPDATA\${APPNAME}\*.failed" - - RMDir /r "$APPDATA\${APPNAME}\cache" - RMDir /r "$APPDATA\${APPNAME}\Partials" - - ; Remove shortcuts, if any - Delete "$SMPROGRAMS\${APPNAME}\*.*" - - ; Remove desktop shortcut - Delete "$DESKTOP\${APPNAME}.lnk" - - ; Remove Quicklaunch shortcut - Delete "$QUICKLAUNCH\${APPNAME}.lnk" - - ; Remove Autstart - Delete "$SMSTARTUP\${APPNAME}.lnk" - - ; Remove directories used - RMDir "$SMPROGRAMS\${APPNAME}" - RMDir /r "$INSTDIR" - RMDir /r "$INSTDIR\qss" - RMDir /r "$INSTDIR\emoticons" - RMDir /r "$INSTDIR\style" - -SectionEnd - -Function .onInit - - InitPluginsDir - Push $R1 - File /oname=$PLUGINSDIR\spltmp.bmp "gui\images\splash.bmp" - advsplash::show 1200 1000 1000 -1 $PLUGINSDIR\spltmp - Pop $R1 - Pop $R1 - !insertmacro MUI_LANGDLL_DISPLAY - -FunctionEnd - - -# Installer Language Strings -# TODO Update the Language Strings with the appropriate translations. - -LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall" -LangString ^UninstallLink ${LANG_GERMAN} "Deinstallieren" -LangString ^UninstallLink ${LANG_TURKISH} "Kald�r" -LangString ^UninstallLink ${LANG_FRENCH} "Désinstaller" -LangString ^UninstallLink ${LANG_SIMPCHINESE} "卸载" -LangString ^UninstallLink ${LANG_POLISH} "Odinstaluj" - - - - - -; eof diff --git a/retroshare-gui/src/retroshare-ultramodern.nsi b/retroshare-gui/src/retroshare-ultramodern.nsi deleted file mode 100644 index d224cc8f1..000000000 --- a/retroshare-gui/src/retroshare-ultramodern.nsi +++ /dev/null @@ -1,501 +0,0 @@ -; Script generated with the Venis Install Wizard & modified by defnax - -; Define your application name -!define APPNAME "RetroShare" -!define VERSION "0.5.1 4049" -!define APPNAMEANDVERSION "${APPNAME} ${VERSION}" -!define QTBASE "D:\qt\2010.01" - -; Main Install settings -Name "${APPNAMEANDVERSION}" -InstallDir "$PROGRAMFILES\RetroShare" -InstallDirRegKey HKLM "Software\${APPNAME}" "" -OutFile "RetroShare_${VERSION}_setup_ultramodern.exe" -BrandingText "${APPNAMEANDVERSION}" -; Use compression -SetCompressor /SOLID LZMA - -; Modern interface settings -!include Sections.nsh -!include "UMUI.nsh" - -;Interface Settings -!define MUI_ABORTWARNING -;!define MUI_HEADERIMAGE -;!define MUI_HEADERIMAGE_BITMAP "retroshare.bmp" ; optional - -# MUI defines -!define MUI_ICON "${NSISDIR}\Contrib\Graphics\UltraModernUI\Icon.ico" -!define MUI_FINISHPAGE_NOAUTOCLOSE -!define MUI_LICENSEPAGE_RADIOBUTTONS -!define MUI_COMPONENTSPAGE_SMALLDESC -!define MUI_FINISHPAGE_LINK "Visit the RetroShare forum for the latest news and support" -!define MUI_FINISHPAGE_LINK_LOCATION "http://retroshare.sourceforge.net/forum/" -!define MUI_FINISHPAGE_RUN "$INSTDIR\RetroShare.exe" -!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_TEXT changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED -!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\UltraModernUI\UnIcon.ico" -!define MUI_UNFINISHPAGE_NOAUTOCLOSE -!define MUI_LANGDLL_REGISTRY_ROOT HKLM -!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY} -!define UMUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage - -;!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIt is recommended that you close all other applications before starting Setup. This will make it possible to update relevant system files without havinf to reboot your computer. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - -;!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - - -; Defines the un-/installer logo of RetroShare -!insertmacro MUI_DEFAULT MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" -!insertmacro MUI_DEFAULT MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-uninstall.bmp" - -; Set languages (first is default language) -!insertmacro MUI_RESERVEFILE_LANGDLL -ReserveFile "${NSISDIR}\Plugins\AdvSplash.dll" - -;-------------------------------- -;Configuration - - - ;!insertmacro MUI_RESERVEFILE_SPECIALBITMAP - - LicenseLangString myLicenseData 1030 "license\license.txt" - LicenseLangString myLicenseData 1033 "license\license.txt" - LicenseLangString myLicenseData 1031 "license\license-GER.txt" - LicenseLangString myLicenseData 1036 "license\license-FR.txt" - LicenseLangString myLicenseData 1055 "license\license-TR.txt" - LicenseLangString myLicenseData 2052 "license\license.txt" - LicenseLangString myLicenseData 1045 "license\license.txt" - LicenseLangString myLicenseData 1041 "license\license.txt" - LicenseLangString myLicenseData 1042 "license\license.txt" - LicenseLangString myLicenseData 1049 "license\license.txt" - LicenseLangString myLicenseData 1053 "license\license.txt" - - LicenseData $(myLicenseData) - -# Installer pages -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)" -!insertmacro MUI_PAGE_COMPONENTS -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES - -# Installer languages -!define MUI_LANGDLL_ALLLANGUAGES - -!insertmacro MUI_LANGUAGE Danish -!insertmacro MUI_LANGUAGE English -!insertmacro MUI_LANGUAGE French -!insertmacro MUI_LANGUAGE German -!insertmacro MUI_LANGUAGE Japanese -!insertmacro MUI_LANGUAGE Korean -!insertmacro MUI_LANGUAGE Polish -!insertmacro MUI_LANGUAGE Russian -!insertmacro MUI_LANGUAGE Swedish -!insertmacro MUI_LANGUAGE SimpChinese -!insertmacro MUI_LANGUAGE Turkish - - - - ;Component-selection page - ;Titles - - LangString sec_main ${LANG_ENGLISH} "Program Files" - LangString sec_data ${LANG_ENGLISH} "Program Skins" - LangString sec_shortcuts ${LANG_ENGLISH} "Shortcuts" - LangString sec_link ${LANG_ENGLISH} "File Association" - LangString sec_autostart ${LANG_ENGLISH} "Auto Startup" - LangString DESC_sec_main ${LANG_ENGLISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_ENGLISH} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_ENGLISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_ENGLISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_ENGLISH} "1033" - - - LangString sec_main ${LANG_FRENCH} "RetroShare" - LangString sec_data ${LANG_FRENCH} "Programme de Skins" - LangString sec_shortcuts ${LANG_FRENCH} "Raccourcis" - LangString sec_link ${LANG_FRENCH} "RetroShare fichiers Association" - LangString sec_startmenu ${LANG_FRENCH} "Raccourcis du menu Démarrer" - LangString sec_autostart ${LANG_FRENCH} "Démarrage automatique" - LangString DESC_sec_main ${LANG_FRENCH} "Installe les fichiers du programme." - LangString DESC_sec_data ${LANG_FRENCH} "Installe RetroShare Skins" - LangString DESC_sec_startmenu ${LANG_FRENCH} "Crée les raccourcis du menu Démarrer" - LangString DESC_sec_shortcuts ${LANG_FRENCH} "Crée une icône sur le bureau." - LangString DESC_sec_link ${LANG_FRENCH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_FRENCH} "1036" - - - LangString sec_main ${LANG_GERMAN} "Programmdateien" - LangString sec_data ${LANG_GERMAN} "Skins fuer das Programm" - LangString sec_shortcuts ${LANG_GERMAN} "Shortcuts" - LangString sec_link ${LANG_GERMAN} "Dateiverknuepfungen" - LangString sec_autostart ${LANG_GERMAN} "Auto Startup" - LangString DESC_sec_main ${LANG_GERMAN} "Installiert die erforderlichen Programmdateien." - LangString DESC_sec_data ${LANG_GERMAN} "Installiert RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_GERMAN} "Erstellt eine RetroShare Verkn�pfung im Startmen�, Desktop oder im Schnellstarter." - LangString DESC_sec_link ${LANG_GERMAN} "RetroShare mit .rsc Dateien verkn�pfen" - LangString LANGUAGEID ${LANG_GERMAN} "1031" - - LangString sec_main ${LANG_TURKISH} "Program Dosyalar�" - LangString sec_data ${LANG_TURKISH} "Program Skinleri" - LangString sec_shortcuts ${LANG_TURKISH} "Shortcut'lar" - LangString sec_link ${LANG_TURKISH} ".rsc Dosya Kaydet" - LangString sec_autostart ${LANG_TURKISH} "Otomatik calistir ve baglan" - LangString DESC_sec_main ${LANG_TURKISH} "Program dosyalar�n� kurar." - LangString DESC_sec_data ${LANG_TURKISH} "RetroShare Skin'leri kurar" - LangString DESC_sec_shortcuts ${TURKISH} "Shortcut yap Start menu , Desktop veya Quicklaunchbar icin." - LangString DESC_sec_link ${LANG_TURKISH} "RetroShare .rsc almas� i�in kaydettirir" - LangString LANGUAGEID ${LANG_TURKISH} "1055" - - LangString sec_main ${LANG_SIMPCHINESE} "程序文件" - LangString sec_data ${LANG_SIMPCHINESE} "程序皮肤" - LangString sec_shortcuts ${LANG_SIMPCHINESE} "快捷方式" - LangString sec_link ${LANG_SIMPCHINESE} "RetroShare文件关联" - LangString sec_autostart ${LANG_SIMPCHINESE} "自动启动" - LangString DESC_sec_main ${LANG_SIMPCHINESE} "安装RetroShare程序" - LangString DESC_sec_data ${LANG_SIMPCHINESE} "安装RetroShare皮肤" - LangString DESC_sec_shortcuts ${LANG_SIMPCHINESE} "建RetroShare快捷方式" - LangString DESC_sec_link ${LANG_SIMPCHINESE} "关联.rsc扩" - LangString LANGUAGEID ${LANG_SIMPCHINESE} "2052" - - LangString sec_main ${LANG_POLISH} "Pliki programu" - LangString sec_data ${LANG_POLISH} "Skórki" - LangString sec_shortcuts ${LANG_POLISH} "Skróty" - LangString sec_link ${LANG_POLISH} "Skojarz pliki" - LangString sec_autostart ${LANG_POLISH} "Automatyczne uruchamianie" - LangString DESC_sec_main ${LANG_POLISH} "Instaluje pliki programu RetroShare" - LangString DESC_sec_data ${LANG_POLISH} "Instaluje skórki programu RetroShare" - LangString DESC_sec_shortcuts ${LANG_POLISH} "Utwórz ikony skrótów na pulpicie, w menu start oraz na pasku szybkiego uruchamiania." - LangString DESC_sec_link ${LANG_POLISH} "Skojarz pliki o rozszerzeniu .rsc z RetroShare" - LangString LANGUAGEID ${LANG_POLISH} "1045" - - LangString sec_main ${LANG_DANISH} "Program Files" - LangString sec_data ${LANG_DANISH} "Program Skins" - LangString sec_shortcuts ${LANG_DANISH} "Shortcuts" - LangString sec_link ${LANG_DANISH} "File Association" - LangString sec_autostart ${LANG_DANISH} "Auto Startup" - LangString DESC_sec_main ${LANG_DANISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_DANISH} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_DANISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_DANISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_DANISH} "1030" - - LangString sec_main ${LANG_RUSSIAN} "Program Files" - LangString sec_data ${LANG_RUSSIAN} "Program Skins" - LangString sec_shortcuts ${LANG_RUSSIAN} "Shortcuts" - LangString sec_link ${LANG_RUSSIAN} "File Association" - LangString sec_autostart ${LANG_RUSSIAN} "Auto Startup" - LangString DESC_sec_main ${LANG_RUSSIAN} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_RUSSIAN} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_RUSSIAN} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_RUSSIAN} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_RUSSIAN} "1049" - - LangString sec_main ${LANG_SWEDISH} "Program Files" - LangString sec_data ${LANG_SWEDISH} "Program Skins" - LangString sec_shortcuts ${LANG_SWEDISH} "Shortcuts" - LangString sec_link ${LANG_SWEDISH} "File Association" - LangString sec_autostart ${LANG_SWEDISH} "Auto Startup" - LangString DESC_sec_main ${LANG_SWEDISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_SWEDISH} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_SWEDISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_SWEDISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_SWEDISH} "1053" - - LangString sec_main ${LANG_JAPANESE} "Program Files" - LangString sec_data ${LANG_JAPANESE} "Program Skins" - LangString sec_shortcuts ${LANG_JAPANESE} "Shortcuts" - LangString sec_link ${LANG_JAPANESE} "File Association" - LangString sec_autostart ${LANG_JAPANESE} "Auto Startup" - LangString DESC_sec_main ${LANG_JAPANESE} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_JAPANESE} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_JAPANESE} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_JAPANESE} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_JAPANESE} "1041" - - LangString sec_main ${LANG_KOREAN} "Program Files" - LangString sec_data ${LANG_KOREAN} "Program Skins" - LangString sec_shortcuts ${LANG_KOREAN} "Shortcuts" - LangString sec_link ${LANG_KOREAN} "File Association" - LangString sec_autostart ${LANG_KOREAN} "Auto Startup" - LangString DESC_sec_main ${LANG_KOREAN} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_KOREAN} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_KOREAN} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_KOREAN} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_KOREAN} "1042" - - -!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - -Section $(sec_main) sec_main - - ;Set Section required - SectionIn RO - - ; Set Section properties - SetOverwrite on - - ; Clears previous error logs - Delete "$INSTDIR\*.log" - - ; Set Section Files and Shortcuts - SetOutPath "$INSTDIR\" - File /r "release\RetroShare.exe" - File /r "..\..\retroshare-nogui\src\release\retroshare-nogui.exe" - File /r "D:\Qt\2010.01\mingw\bin\mingwm10.dll" - File /r "D:\Qt\2010.01\qt\bin\QtCore4.dll" - File /r "D:\Qt\2010.01\qt\bin\QtGui4.dll" - File /r "D:\Qt\2010.01\qt\bin\QtNetwork4.dll" - File /r "D:\Qt\2010.01\qt\bin\QtXml4.dll" - File /r "D:\Qt\2010.01\qt\bin\QtScript4.dll" - File /r "D:\Qt\2010.01\qt\bin\libgcc_s_dw2-1.dll" - File /r "D:\Qt\2010.01\qt\plugins\imageformats" - File /r "D:\Development\miniupnpc-1.3\miniupnpc.dll" - File /r ${QTBASE}\qt\qt_*.qm - File /r "release\pthreadGC2d.dll" - File /r "release\libgpg-error-0.dll" - File /r "release\libgpgme-11.dll" - File /r "changelog.txt" - File /r /x Data "release\bdboot.txt" - - -SectionEnd - -Section $(sec_data) sec_data - - ; Set Section properties - SetOverwrite on - - ; Set Section Files and Shortcuts - SetOutPath "$APPDATA\RetroShare\" - ;File /r "data\*" - - ; Set Section Plugins - SetOutPath "$APPDATA\RetroShare\plugins\" - ;File /r "plugins\" - - ; Set Section qss and exclude svn - SetOutPath "$INSTDIR\qss\" - File /r /x .svn qss\*.* - - ; Set Section sounds and exclude svn - SetOutPath "$INSTDIR\sounds\" - File /r /x .svn sounds\*.* - - ; Set Section skin - ; SetOutPath "$INSTDIR\skin\" - ; File /r release\skin\*.* - - ; Add emoticons - ;SetOutPath "$INSTDIR\emoticons\" - ;File /r emoticons\*.* - - ; Add Chat Style - ;SetOutPath "$INSTDIR\style\" - ;File /r style\*.* - -SectionEnd - -; These are the programs that are needed by RetroShare. -Section -Prerequisites - SetOutPath $INSTDIR\Prerequisites - MessageBox MB_YESNO "$(InstallGPG4WIN)" /SD IDYES IDNO leave - File "Prerequisites\gpg4win-1.1.4.exe" - ExecWait "$INSTDIR\Prerequisites\gpg4win-1.1.4.exe" - - leave: -SectionEnd - -Section $(sec_link) sec_link - ; Delete any existing keys - - - ; Write the file association - WriteRegStr HKCR .rsc "" retroshare - WriteRegStr HKCR retroshare "" "RSC File" - WriteRegBin HKCR retroshare EditFlags 00000100 - WriteRegStr HKCR "retroshare\shell" "" open - WriteRegStr HKCR "retroshare\shell\open\command" "" `"$INSTDIR\RetroShare.exe" "%1"` - -SectionEnd - -SectionGroup $(sec_shortcuts) sec_shortcuts -Section StartMenu SEC0001 - - SetOutPath "$INSTDIR" - CreateDirectory "$SMPROGRAMS\${APPNAME}" - CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - CreateShortCut "$SMPROGRAMS\${APPNAME}\$(^UninstallLink).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 - -SectionEnd - -Section Desktop SEC0002 - - - CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd - -Section Quicklaunchbar SEC0003 - - - CreateShortCut "$QUICKLAUNCH\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd -SectionGroupEnd - -;Section $(sec_autostart) sec_autostart - -; WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" "$INSTDIR\${APPNAME}.exe -a" - -;SectionEnd - -;Section $(sec_autostart) sec_autostart - -; CreateShortCut "$SMSTARTUP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 -;SectionEnd - - -Section -FinishSection - - WriteRegStr HKLM "Software\${APPNAME}" "" "$INSTDIR" - WriteRegStr HKLM "Software\${APPNAME}" "Version" "${VERSION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\uninstall.exe" - WriteUninstaller "$INSTDIR\uninstall.exe" - -SectionEnd - - - -;-------------------------------- -;Descriptions - -!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${sec_main} $(DESC_sec_main) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_data} $(DESC_sec_data) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_shortcuts} $(DESC_sec_shortcuts) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_link} $(DESC_sec_link) - ;!insertmacro MUI_DESCRIPTION_TEXT ${sec_autostart} $(DESC_sec_autostart) -!insertmacro MUI_FUNCTION_DESCRIPTION_END - -;Uninstall section -Section "Uninstall" - - ; Remove file association registry keys - DeleteRegKey HKCR .rsc - DeleteRegKey HKCR retroshare - - ; Remove program/uninstall regsitry keys - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" - DeleteRegKey HKLM SOFTWARE\${APPNAME} - - DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" - - ; Remove files and uninstaller - Delete $INSTDIR\RetroShare.exe - Delete $INSTDIR\*.dll - Delete $INSTDIR\*.dat - Delete $INSTDIR\*.txt - Delete $INSTDIR\*.ini - Delete $INSTDIR\*.log - - Delete $INSTDIR\uninstall.exe - - ; Remove the kadc.ini file. - ; Don't remove the directory, otherwise - ; we lose the XPGP keys. - ; Should make this an option though... - Delete "$APPDATA\${APPNAME}\kadc.ini" - Delete "$APPDATA\${APPNAME}\*.cfg" - Delete "$APPDATA\${APPNAME}\*.conf" - Delete "$APPDATA\${APPNAME}\*.log-save" - Delete "$APPDATA\${APPNAME}\*.log" - Delete "$APPDATA\${APPNAME}\*.failed" - - RMDir /r "$APPDATA\${APPNAME}\cache" - RMDir /r "$APPDATA\${APPNAME}\Partials" - - ; Remove shortcuts, if any - Delete "$SMPROGRAMS\${APPNAME}\*.*" - - ; Remove desktop shortcut - Delete "$DESKTOP\${APPNAME}.lnk" - - ; Remove Quicklaunch shortcut - Delete "$QUICKLAUNCH\${APPNAME}.lnk" - - ; Remove Autostart - ;Delete "$SMSTARTUP\${APPNAME}.lnk" - - ; Remove directories used - RMDir "$SMPROGRAMS\${APPNAME}" - RMDir /r "$INSTDIR" - RMDir /r "$INSTDIR\qss" - RMDir /r "$INSTDIR\emoticons" - RMDir /r "$INSTDIR\style" - RMDir /r "$INSTDIR\translations" - -SectionEnd - -Function .onInit - - InitPluginsDir - Push $R1 - File /oname=$PLUGINSDIR\spltmp.bmp "gui\images\splash.bmp" - advsplash::show 1200 1000 1000 -1 $PLUGINSDIR\spltmp - Pop $R1 - Pop $R1 - !insertmacro MUI_LANGDLL_DISPLAY - - - -FunctionEnd - - -# Installer Language Strings -# TODO Update the Language Strings with the appropriate translations. - -LangString InstallGPG4WIN ${LANG_ENGLISH} "Install Gpg4win ? Gpg4win is required for RetroShare!" -LangString InstallGPG4WIN ${LANG_GERMAN} "Installiere Gpg4win ? Gpg4win ist erforderlich fuer RetroShare!" -LangString InstallGPG4WIN ${LANG_TURKISH} "Gpg4win Yükle? Gpg4win RetroShare için gerekli!" -LangString InstallGPG4WIN ${LANG_FRENCH} "RetroShare a besoin de GPG4win pour fonctionner. Lancer l'installation de GPG4win?" -LangString InstallGPG4WIN ${LANG_SIMPCHINESE} "Install Gpg4win ? Gpg4win是需要Retroshare!" -LangString InstallGPG4WIN ${LANG_POLISH} "Install Gpg4win ? Gpg4win wymagane jest Retroshare!" -LangString InstallGPG4WIN ${LANG_DANISH} "Installer Gpg4win? Gpg4win er nødvendig for RetroShare!" -LangString InstallGPG4WIN ${LANG_JAPANESE} "Install Gpg4win ? Gpg4win is required for RetroShare!" -LangString InstallGPG4WIN ${LANG_KOREAN} "Install Gpg4win ? Gpg4win is required for RetroShare!" -LangString InstallGPG4WIN ${LANG_RUSSIAN} "Install Gpg4win ? Gpg4win is required for RetroShare!" -LangString InstallGPG4WIN ${LANG_SWEDISH} "Installera Gpg4win? Gpg4win krävs för RetroShare!" - -LangString FINISHPAGELINK ${LANG_ENGLISH} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_GERMAN} "Besuche RetroShare Support Forum " -LangString FINISHPAGELINK ${LANG_TURKISH} "Destek için Retroshare foruma ziyaret" -LangString FINISHPAGELINK ${LANG_FRENCH} "Consultez le forum RetroShare pour vous tenir au courant des dernieres modifications, et obtenir de l'aide." -LangString FINISHPAGELINK ${LANG_SIMPCHINESE} "帮助论坛" -LangString FINISHPAGELINK ${LANG_POLISH} "Odwiedź forum RetroShare do najświeższych informacji i wsparcia" -LangString FINISHPAGELINK ${LANG_DANISH} "Besøg RetroShare fora for de seneste nyheder og støtte" -LangString FINISHPAGELINK ${LANG_JAPANESE} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_KOREAN} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_RUSSIAN} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_SWEDISH} "Besök RetroShare forum för de senaste nyheterna och stöd" - -LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall" -LangString ^UninstallLink ${LANG_GERMAN} "Deinstallieren" -LangString ^UninstallLink ${LANG_TURKISH} "Kald�r" -LangString ^UninstallLink ${LANG_FRENCH} "Désinstaller" -LangString ^UninstallLink ${LANG_SIMPCHINESE} "卸载" -LangString ^UninstallLink ${LANG_POLISH} "Odinstaluj" -LangString ^UninstallLink ${LANG_DANISH} "Afinstaller" -LangString ^UninstallLink ${LANG_JAPANESE} "Uninstall" -LangString ^UninstallLink ${LANG_KOREAN} "Uninstall" -LangString ^UninstallLink ${LANG_RUSSIAN} "Uninstall" -LangString ^UninstallLink ${LANG_SWEDISH} "Avinstallera" - - -; eof diff --git a/retroshare-gui/src/retroshare.in b/retroshare-gui/src/retroshare.in deleted file mode 100644 index 994d26d7c..000000000 --- a/retroshare-gui/src/retroshare.in +++ /dev/null @@ -1,594 +0,0 @@ -; Script generated with the Venis Install Wizard & modified by defnax - -; Define your application name -!define APPNAME "RetroShare" -!define VERSION "0.5.4e" -!define REVISION "$WCREV$" -!define APPNAMEANDVERSION "${APPNAME} ${VERSION} ${REVISION}" -!define QTBASE "d:\qt\2010.05" -!define RSBASE "d:\Development\retroshare\retroshare-gui\" - -; Main Install settings -Name "${APPNAMEANDVERSION}" -InstallDir "$PROGRAMFILES\RetroShare" -InstallDirRegKey HKLM "Software\${APPNAME}" "" -OutFile "RetroShare_${VERSION}_${REVISION}_setup.exe" -BrandingText "${APPNAMEANDVERSION}" -; Use compression -SetCompressor /SOLID LZMA - -; Modern interface settings -!include Sections.nsh -!include "MUI.nsh" - -;Interface Settings -!define MUI_ABORTWARNING -;!define MUI_HEADERIMAGE -;!define MUI_HEADERIMAGE_BITMAP "retroshare.bmp" ; optional - -# MUI defines -!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" -!define MUI_FINISHPAGE_NOAUTOCLOSE -!define MUI_LICENSEPAGE_RADIOBUTTONS -!define MUI_COMPONENTSPAGE_SMALLDESC -!define MUI_FINISHPAGE_LINK "$(FINISHPAGELINK)" -!define MUI_FINISHPAGE_LINK_LOCATION "http://retroshare.sourceforge.net/forum/" -!define MUI_FINISHPAGE_RUN "$INSTDIR\RetroShare.exe" -!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_TEXT changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED -!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" -!define MUI_UNFINISHPAGE_NOAUTOCLOSE -!define MUI_LANGDLL_REGISTRY_ROOT HKLM -!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY} -!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage - -;!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIt is recommended that you close all other applications before starting Setup. This will make it possible to update relevant system files without havinf to reboot your computer. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - -;!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - - -; Defines the un-/installer logo of RetroShare -!insertmacro MUI_DEFAULT MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" -!insertmacro MUI_DEFAULT MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-uninstall.bmp" - -; Set languages (first is default language) -!insertmacro MUI_RESERVEFILE_LANGDLL -ReserveFile "${NSISDIR}\Plugins\AdvSplash.dll" - -;-------------------------------- -;Configuration - - - ;!insertmacro MUI_RESERVEFILE_SPECIALBITMAP - - LicenseLangString myLicenseData 1030 "license\license.txt" - LicenseLangString myLicenseData 1031 "license\license-GER.txt" - LicenseLangString myLicenseData 1032 "license\license-GR.txt" - LicenseLangString myLicenseData 1033 "license\license.txt" - LicenseLangString myLicenseData 1034 "license\license-SP.txt" - LicenseLangString myLicenseData 1036 "license\license-FR.txt" - LicenseLangString myLicenseData 1040 "license\license-IT.txt" - LicenseLangString myLicenseData 1041 "license\license.txt" - LicenseLangString myLicenseData 1042 "license\license.txt" - LicenseLangString myLicenseData 1045 "license\license.txt" - LicenseLangString myLicenseData 1046 "license\license-PT_BR.txt" - LicenseLangString myLicenseData 1049 "license\license.txt" - LicenseLangString myLicenseData 1053 "license\license.txt" - LicenseLangString myLicenseData 1055 "license\license-TR.txt" - LicenseLangString myLicenseData 2052 "license\license.txt" - - LicenseData $(myLicenseData) - -# Installer pages -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)" -!insertmacro MUI_PAGE_COMPONENTS -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES - -# Installer languages -!define MUI_LANGDLL_ALLLANGUAGES - -!insertmacro MUI_LANGUAGE English -!insertmacro MUI_LANGUAGE Danish -!insertmacro MUI_LANGUAGE French -!insertmacro MUI_LANGUAGE German -!insertmacro MUI_LANGUAGE Greek -!insertmacro MUI_LANGUAGE Italian -!insertmacro MUI_LANGUAGE Japanese -!insertmacro MUI_LANGUAGE Korean -!insertmacro MUI_LANGUAGE Polish -!insertmacro MUI_LANGUAGE PortugueseBR -!insertmacro MUI_LANGUAGE Russian -!insertmacro MUI_LANGUAGE Swedish -!insertmacro MUI_LANGUAGE SimpChinese -!insertmacro MUI_LANGUAGE Spanish -!insertmacro MUI_LANGUAGE Turkish - - - ;Component-selection page - ;Titles - - LangString sec_main ${LANG_ENGLISH} "Program Files" - LangString sec_data ${LANG_ENGLISH} "Program Skins" - LangString sec_shortcuts ${LANG_ENGLISH} "Shortcuts" - LangString sec_plugins ${LANG_ENGLISH} "Plugins" - LangString sec_link ${LANG_ENGLISH} "File Association" - LangString sec_autostart ${LANG_ENGLISH} "Auto Startup" - LangString DESC_sec_main ${LANG_ENGLISH} "Installs the RetroShare program files." - LangString DESC_sec_plugins ${LANG_ENGLISH} "Installs the RetroShare plugins." - LangString DESC_sec_data ${LANG_ENGLISH} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_ENGLISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_ENGLISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_ENGLISH} "1033" - - LangString sec_main ${LANG_FRENCH} "RetroShare" - LangString sec_data ${LANG_FRENCH} "Programme de Skins" - LangString sec_plugins ${LANG_FRENCH} "Plugins" - LangString sec_shortcuts ${LANG_FRENCH} "Raccourcis" - LangString sec_link ${LANG_FRENCH} "Association de fichiers" - LangString sec_startmenu ${LANG_FRENCH} "Raccourcis du menu Démarrer" - LangString sec_autostart ${LANG_FRENCH} "Démarrage automatique" - LangString DESC_sec_main ${LANG_FRENCH} "Installation des fichiers du programme." - LangString DESC_sec_data ${LANG_FRENCH} "Installation des Skins de RetroShare" - LangString DESC_sec_plugins ${LANG_FRENCH} "Installation des extensions de RetroShare " - LangString DESC_sec_startmenu ${LANG_FRENCH} "Création des raccourcis du menu Démarrer" - LangString DESC_sec_shortcuts ${LANG_FRENCH} "Création de l'icône sur le bureau." - LangString DESC_sec_link ${LANG_FRENCH} "Associer RetroShare avec l'extension de fichier .rsc" - LangString LANGUAGEID ${LANG_FRENCH} "1036" - - LangString sec_main ${LANG_GERMAN} "Programme" - LangString sec_data ${LANG_GERMAN} "Programm Skins" - LangString sec_plugins ${LANG_GERMAN} "Plug-ins" - LangString sec_shortcuts ${LANG_GERMAN} "Verknüpfungen" - LangString sec_link ${LANG_GERMAN} "Dateiverknüpfungen" - LangString sec_autostart ${LANG_GERMAN} "Autostart" - LangString DESC_sec_main ${LANG_GERMAN} "Installiert die RetroShare Programmdateien." - LangString DESC_sec_data ${LANG_GERMAN} "Installiert RetroShare Skins" - LangString DESC_sec_plugins ${LANG_GERMAN} "Installiert die RetroShare Erweiterungen." - LangString DESC_sec_shortcuts ${LANG_GERMAN} "RetroShare Verknüpfung im Startmenüe, Desktop oder im Schnellstarter erstellen." - LangString DESC_sec_link ${LANG_GERMAN} "RetroShare mit .rsc Dateiendung verknüpfen" - LangString LANGUAGEID ${LANG_GERMAN} "1031" - - LangString sec_main ${LANG_TURKISH} "Program Dosyalar?" - LangString sec_data ${LANG_TURKISH} "Program Skinleri" - LangString sec_shortcuts ${LANG_TURKISH} "Shortcut'lar" - LangString sec_plugins ${LANG_TURKISH} "Eklentiler" - LangString sec_link ${LANG_TURKISH} ".rsc Dosya Kaydet" - LangString sec_autostart ${LANG_TURKISH} "Otomatik calistir ve baglan" - LangString DESC_sec_main ${LANG_TURKISH} "Program dosyalar?n? kurar." - LangString DESC_sec_data ${LANG_TURKISH} "RetroShare Skin'leri kurar" - LangString DESC_sec_plugins ${LANG_TURKISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${TURKISH} "Shortcut yap Start menu , Desktop veya Quicklaunchbar icin." - LangString DESC_sec_link ${LANG_TURKISH} "RetroShare .rsc almas? i?in kaydettirir" - LangString LANGUAGEID ${LANG_TURKISH} "1055" - - LangString sec_main ${LANG_SIMPCHINESE} "程序文件" - LangString sec_data ${LANG_SIMPCHINESE} "程序皮肤" - LangString sec_plugins ${LANG_SIMPCHINESE} "Plugins" - LangString sec_shortcuts ${LANG_SIMPCHINESE} "快捷方式" - LangString sec_link ${LANG_SIMPCHINESE} "RetroShare文件关联" - LangString sec_autostart ${LANG_SIMPCHINESE} "自动启动" - LangString DESC_sec_main ${LANG_SIMPCHINESE} "安装RetroShare程序" - LangString DESC_sec_data ${LANG_SIMPCHINESE} "安装RetroShare皮肤" - LangString DESC_sec_plugins ${LANG_SIMPCHINESE} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_SIMPCHINESE} "建RetroShare快捷方式" - LangString DESC_sec_link ${LANG_SIMPCHINESE} "关联.rsc扩展名" - LangString LANGUAGEID ${LANG_SIMPCHINESE} "2052" - - LangString sec_main ${LANG_POLISH} "Pliki programu" - LangString sec_data ${LANG_POLISH} "Skórki" - LangString sec_plugins ${LANG_POLISH} "Plugins" - LangString sec_shortcuts ${LANG_POLISH} "Skróty" - LangString sec_link ${LANG_POLISH} "Skojarz pliki" - LangString sec_autostart ${LANG_POLISH} "Automatyczne uruchamianie" - LangString DESC_sec_main ${LANG_POLISH} "Instaluje pliki programu RetroShare" - LangString DESC_sec_data ${LANG_POLISH} "Instaluje skórki programu RetroShare" - LangString DESC_sec_plugins ${LANG_POLISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_POLISH} "Utwórz ikony skrótów na pulpicie, w menu start oraz na pasku szybkiego uruchamiania." - LangString DESC_sec_link ${LANG_POLISH} "Skojarz pliki o rozszerzeniu .rsc z RetroShare" - LangString LANGUAGEID ${LANG_POLISH} "1045" - - LangString sec_main ${LANG_DANISH} "Program Files" - LangString sec_data ${LANG_DANISH} "Program Skins" - LangString sec_plugins ${LANG_DANISH} "Plugins" - LangString sec_shortcuts ${LANG_DANISH} "Shortcuts" - LangString sec_link ${LANG_DANISH} "File Association" - LangString sec_autostart ${LANG_DANISH} "Auto Startup" - LangString DESC_sec_main ${LANG_DANISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_DANISH} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_DANISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_DANISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_DANISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_DANISH} "1030" - - LangString sec_main ${LANG_RUSSIAN} "Program Files" - LangString sec_data ${LANG_RUSSIAN} "Program Skins" - LangString sec_plugins ${LANG_RUSSIAN} "Plugins" - LangString sec_shortcuts ${LANG_RUSSIAN} "Shortcuts" - LangString sec_link ${LANG_RUSSIAN} "File Association" - LangString sec_autostart ${LANG_RUSSIAN} "Auto Startup" - LangString DESC_sec_main ${LANG_RUSSIAN} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_RUSSIAN} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_RUSSIAN} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_RUSSIAN} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_RUSSIAN} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_RUSSIAN} "1049" - - LangString sec_main ${LANG_SWEDISH} "Program Files" - LangString sec_data ${LANG_SWEDISH} "Program Skins" - LangString sec_plugins ${LANG_SWEDISH} "Plugins" - LangString sec_shortcuts ${LANG_SWEDISH} "Shortcuts" - LangString sec_link ${LANG_SWEDISH} "File Association" - LangString sec_autostart ${LANG_SWEDISH} "Auto Startup" - LangString DESC_sec_main ${LANG_SWEDISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_SWEDISH} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_SWEDISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_SWEDISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_SWEDISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_SWEDISH} "1053" - - LangString sec_main ${LANG_SPANISH} "Archivos de programa" - LangString sec_data ${LANG_SPANISH} "Estilos del programa" - LangString sec_plugins ${LANG_SPANISH} "Plugins" - LangString sec_shortcuts ${LANG_SPANISH} "Accesos directos" - LangString sec_link ${LANG_SPANISH} "Asociación de archivos" - LangString sec_autostart ${LANG_SPANISH} "Inicio automático" - LangString DESC_sec_main ${LANG_SPANISH} "Instala los archivos del programa RetroShare." - LangString DESC_sec_data ${LANG_SPANISH} "Instala los estilos para RetroShare" - LangString DESC_sec_plugins ${LANG_SPANISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_SPANISH} "Crear iconos de acceso directo de RetroShare." - LangString DESC_sec_link ${LANG_SPANISH} "Asociar RetroShare con la extensión de archivo .rsc" - LangString LANGUAGEID ${LANG_SPANISH} "1034" - - LangString sec_main ${LANG_ITALIAN} "Programmi" - LangString sec_data ${LANG_ITALIAN} "Temi del Programma" - LangString sec_plugins ${LANG_ITALIAN} "Plugins" - LangString sec_shortcuts ${LANG_ITALIAN} "Icone" - LangString sec_link ${LANG_ITALIAN} "Associazione dei File" - LangString sec_autostart ${LANG_ITALIAN} "Esecuzione Automatica" - LangString DESC_sec_main ${LANG_ITALIAN} "Installare i file programma di RetroShare." - LangString DESC_sec_data ${LANG_ITALIAN} "Installare i temi di RetroShare" - LangString DESC_sec_plugins ${LANG_ITALIAN} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_ITALIAN} "Creare le icone per avviare RetroShare." - LangString DESC_sec_link ${LANG_ITALIAN} "Associa a RetroShare i file con estensione .rsc" - LangString LANGUAGEID ${LANG_ITALIAN} "1040" - - LangString sec_main ${LANG_GREEK} "Program Files" - LangString sec_data ${LANG_GREEK} "Program Skins" - LangString sec_plugins ${LANG_GREEK} "Plugins" - LangString sec_shortcuts ${LANG_GREEK} "Shortcuts" - LangString sec_link ${LANG_GREEK} "File Association" - LangString sec_autostart ${LANG_GREEK} "Auto Startup" - LangString DESC_sec_main ${LANG_GREEK} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_GREEK} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_GREEK} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_GREEK} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_GREEK} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_GREEK} "1032" - - LangString sec_main ${LANG_PORTUGUESEBR} "Program Files" - LangString sec_data ${LANG_PORTUGUESEBR} "Program Skins" - LangString sec_plugins ${LANG_PORTUGUESEBR} "Plugins" - LangString sec_shortcuts ${LANG_PORTUGUESEBR} "Shortcuts" - LangString sec_link ${LANG_PORTUGUESEBR} "File Association" - LangString sec_autostart ${LANG_PORTUGUESEBR} "Auto Startup" - LangString DESC_sec_main ${LANG_PORTUGUESEBR} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_PORTUGUESEBR} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_PORTUGUESEBR} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_PORTUGUESEBR} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_PORTUGUESEBR} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_PORTUGUESEBR} "1046" - - LangString sec_main ${LANG_JAPANESE} "Program Files" - LangString sec_data ${LANG_JAPANESE} "Program Skins" - LangString sec_plugins ${LANG_JAPANESE} "Plugins" - LangString sec_shortcuts ${LANG_JAPANESE} "Shortcuts" - LangString sec_link ${LANG_JAPANESE} "File Association" - LangString sec_autostart ${LANG_JAPANESE} "Auto Startup" - LangString DESC_sec_main ${LANG_JAPANESE} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_JAPANESE} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_JAPANESE} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_JAPANESE} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_JAPANESE} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_JAPANESE} "1041" - - LangString sec_main ${LANG_KOREAN} "Program Files" - LangString sec_data ${LANG_KOREAN} "Program Skins" - LangString sec_plugins ${LANG_KOREAN} "Plugins" - LangString sec_shortcuts ${LANG_KOREAN} "Shortcuts" - LangString sec_link ${LANG_KOREAN} "File Association" - LangString sec_autostart ${LANG_KOREAN} "Auto Startup" - LangString DESC_sec_main ${LANG_KOREAN} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_KOREAN} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_KOREAN} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_KOREAN} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_KOREAN} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_KOREAN} "1042" - - -!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - -Section $(sec_main) sec_main - - ;Set Section required - SectionIn RO - - ; Set Section properties - SetOverwrite on - - ; Clears previous error logs - Delete "$INSTDIR\*.log" - - ; Set Section Files and Shortcuts - SetOutPath "$INSTDIR\" - File /r "release\RetroShare.exe" - File /r "..\..\retroshare-nogui\src\release\retroshare-nogui.exe" - File /r "${QTBASE}\mingw\bin\mingwm10.dll" - File /r "${QTBASE}\qt\bin\QtCore4.dll" - File /r "${QTBASE}\qt\bin\QtGui4.dll" - File /r "${QTBASE}\qt\bin\QtNetwork4.dll" - File /r "${QTBASE}\qt\bin\QtMultimedia4.dll" - File /r "${QTBASE}\qt\bin\QtXml4.dll" - File /r "${QTBASE}\qt\bin\QtScript4.dll" - File /r "${QTBASE}\qt\bin\QtSvg4.dll" - File /r "${QTBASE}\qt\bin\libgcc_s_dw2-1.dll" - File /r "${QTBASE}\qt\plugins\imageformats" - File /r ${QTBASE}\qt\qt_*.qm - File /r ${RSBASE}\src\qt_*.qm - File /r "release\pthreadGC2d.dll" - File /r "d:\Development\lib\libminiupnpc-1.3\miniupnpc.dll" - File /r "changelog.txt" - File /r /x Data "release\bdboot.txt" - - -SectionEnd - -Section $(sec_data) sec_data - - ; Set Section properties - SetOverwrite on - - ; Set Section Files and Shortcuts - SetOutPath "$APPDATA\RetroShare\" - ;File /r "data\*" - - ; Set Section qss and exclude svn - SetOutPath "$INSTDIR\qss\" - File /r /x .svn qss\*.* - - ; Set Section sounds and exclude svn - SetOutPath "$INSTDIR\sounds\" - File /r /x .svn sounds\*.* - - ; Set Section skin - ; SetOutPath "$INSTDIR\skin\" - ; File /r release\skin\*.* - - ; Add emoticons - ;SetOutPath "$INSTDIR\emoticons\" - ;File /r emoticons\*.* - - ; Add Chat Style - SetOutPath "$APPDATA\RetroShare\stylesheets\" - File /r gui\qss\chat\Bubble - File /r gui\qss\chat\Bubble_Compact - - -SectionEnd - -SectionGroup $(sec_plugins) sec_plugins -Section Voip SEC001 - - ; Set Section properties - SetOverwrite on - - ; Set Section Plugins - SetOutPath "$APPDATA\RetroShare\extensions\" - - File /r "..\..\plugins\VOIP\release\VOIP.dll" - -SectionEnd - -Section FeedReader SEC002 - - ; Set Section properties - SetOverwrite on - - ; Set Section Plugins - SetOutPath "$APPDATA\RetroShare\extensions\" - - File /r "..\..\plugins\FeedReader\release\FeedReader.dll" - -SectionEnd -SectionGroupEnd - -Section $(sec_link) sec_link - ; Delete any existing keys - - - ; Write the file association - WriteRegStr HKCR .rsc "" retroshare - WriteRegStr HKCR retroshare "" "RSC File" - WriteRegBin HKCR retroshare EditFlags 00000100 - WriteRegStr HKCR "retroshare\shell" "" open - WriteRegStr HKCR "retroshare\shell\open\command" "" `"$INSTDIR\RetroShare.exe" "%1"` - -SectionEnd - -SectionGroup $(sec_shortcuts) sec_shortcuts -Section StartMenu SEC0001 - - SetOutPath "$INSTDIR" - CreateDirectory "$SMPROGRAMS\${APPNAME}" - CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - CreateShortCut "$SMPROGRAMS\${APPNAME}\$(^UninstallLink).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 - -SectionEnd - -Section Desktop SEC0002 - - - CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd - -Section Quicklaunchbar SEC0003 - - - CreateShortCut "$QUICKLAUNCH\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd -SectionGroupEnd - -;Section $(sec_autostart) sec_autostart - -; WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" "$INSTDIR\${APPNAME}.exe -a" - -;SectionEnd - -;Section $(sec_autostart) sec_autostart - -; CreateShortCut "$SMSTARTUP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 -;SectionEnd - - -Section -FinishSection - - WriteRegStr HKLM "Software\${APPNAME}" "" "$INSTDIR" - WriteRegStr HKLM "Software\${APPNAME}" "Version" "${VERSION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\uninstall.exe" - WriteUninstaller "$INSTDIR\uninstall.exe" - -SectionEnd - - - -;-------------------------------- -;Descriptions - -!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${sec_main} $(DESC_sec_main) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_data} $(DESC_sec_data) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_plugins} $(DESC_sec_plugins) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_shortcuts} $(DESC_sec_shortcuts) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_link} $(DESC_sec_link) - ;!insertmacro MUI_DESCRIPTION_TEXT ${sec_autostart} $(DESC_sec_autostart) -!insertmacro MUI_FUNCTION_DESCRIPTION_END - -;Uninstall section -Section "Uninstall" - - ; Remove file association registry keys - DeleteRegKey HKCR .rsc - DeleteRegKey HKCR retroshare - - ; Remove program/uninstall regsitry keys - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" - DeleteRegKey HKLM SOFTWARE\${APPNAME} - - DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" - - ; Remove files and uninstaller - Delete $INSTDIR\RetroShare.exe - Delete $INSTDIR\*.dll - Delete $INSTDIR\*.dat - Delete $INSTDIR\*.txt - Delete $INSTDIR\*.ini - Delete $INSTDIR\*.log - - Delete $INSTDIR\uninstall.exe - - ; Remove the kadc.ini file. - ; Don't remove the directory, otherwise - ; we lose the XPGP keys. - ; Should make this an option though... - Delete "$APPDATA\${APPNAME}\kadc.ini" - Delete "$APPDATA\${APPNAME}\*.cfg" - Delete "$APPDATA\${APPNAME}\*.conf" - Delete "$APPDATA\${APPNAME}\*.log-save" - Delete "$APPDATA\${APPNAME}\*.log" - Delete "$APPDATA\${APPNAME}\*.failed" - - RMDir /r "$APPDATA\${APPNAME}\cache" - RMDir /r "$APPDATA\${APPNAME}\Partials" - - ; Remove shortcuts, if any - Delete "$SMPROGRAMS\${APPNAME}\*.*" - - ; Remove desktop shortcut - Delete "$DESKTOP\${APPNAME}.lnk" - - ; Remove Quicklaunch shortcut - Delete "$QUICKLAUNCH\${APPNAME}.lnk" - - ; Remove Autostart - ;Delete "$SMSTARTUP\${APPNAME}.lnk" - - ; Remove directories used - RMDir "$SMPROGRAMS\${APPNAME}" - RMDir /r "$INSTDIR" - RMDir /r "$INSTDIR\qss" - RMDir /r "$INSTDIR\emoticons" - RMDir /r "$INSTDIR\style" - RMDir /r "$INSTDIR\translations" - -SectionEnd - -Function .onInit - - InitPluginsDir - Push $R1 - File /oname=$PLUGINSDIR\spltmp.bmp "gui\images\splash.bmp" - advsplash::show 1200 1000 1000 -1 $PLUGINSDIR\spltmp - Pop $R1 - Pop $R1 - !insertmacro MUI_LANGDLL_DISPLAY - - - -FunctionEnd - - -# Installer Language Strings -# TODO Update the Language Strings with the appropriate translations. - - -LangString FINISHPAGELINK ${LANG_ENGLISH} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_GERMAN} "Das RetroShare Support-Forum besuchen, um Neuigkeiten und Unterstützung zu erfahren" -LangString FINISHPAGELINK ${LANG_TURKISH} "Destek için Retroshare foruma ziyaret et" -LangString FINISHPAGELINK ${LANG_FRENCH} "Consultez le forum RetroShare pour vous tenir au courant des dernieres modifications, et obtenir de l'aide." -LangString FINISHPAGELINK ${LANG_SIMPCHINESE} "帮助论坛" -LangString FINISHPAGELINK ${LANG_POLISH} "Odwiedź forum RetroShare do najświeższych informacji i wsparcia" -LangString FINISHPAGELINK ${LANG_DANISH} "Besøg RetroShare fora for de seneste nyheder og støtte" -LangString FINISHPAGELINK ${LANG_JAPANESE} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_KOREAN} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_RUSSIAN} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_SWEDISH} "Besök RetroShare forum för de senaste nyheterna och stöd" -LangString FINISHPAGELINK ${LANG_SPANISH} "Visite los foros de RetroShare para las últimas noticias y soporte" -LangString FINISHPAGELINK ${LANG_ITALIAN} "Visita i forum di RetroShare per le ultime novità ed il supporto" -LangString FINISHPAGELINK ${LANG_GREEK} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_PORTUGUESEBR} "Visit the RetroShare forums for the latest news and support" - -LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall" -LangString ^UninstallLink ${LANG_GERMAN} "Deinstallieren" -LangString ^UninstallLink ${LANG_TURKISH} "Kald�r" -LangString ^UninstallLink ${LANG_FRENCH} "Désinstaller" -LangString ^UninstallLink ${LANG_SIMPCHINESE} "卸载" -LangString ^UninstallLink ${LANG_POLISH} "Odinstaluj" -LangString ^UninstallLink ${LANG_DANISH} "Afinstaller" -LangString ^UninstallLink ${LANG_JAPANESE} "Uninstall" -LangString ^UninstallLink ${LANG_KOREAN} "Uninstall" -LangString ^UninstallLink ${LANG_RUSSIAN} "Uninstall" -LangString ^UninstallLink ${LANG_SWEDISH} "Avinstallera" -LangString ^UninstallLink ${LANG_SPANISH} "Desinstalar" -LangString ^UninstallLink ${LANG_ITALIAN} "Disinstallare" -LangString ^UninstallLink ${LANG_GREEK} "Απεγκατάσταση" -LangString ^UninstallLink ${LANG_PORTUGUESEBR} "Desinstalar" - -; eof diff --git a/retroshare-gui/src/retroshare.nsi b/retroshare-gui/src/retroshare.nsi deleted file mode 100644 index 9a604b44a..000000000 --- a/retroshare-gui/src/retroshare.nsi +++ /dev/null @@ -1,595 +0,0 @@ -; Script generated with the Venis Install Wizard & modified by defnax - -; Define your application name -!define APPNAME "RetroShare" -!define VERSION "0.5.5a" -!define REVISION "6725" -!define APPNAMEANDVERSION "${APPNAME} ${VERSION} ${REVISION}" -!define QTBASE "h:\qt\2010.05" -!define RSBASE "h:\Development\retroshare\retroshare-gui\" - -; Main Install settings -Name "${APPNAMEANDVERSION}" -InstallDir "$PROGRAMFILES\RetroShare" -InstallDirRegKey HKLM "Software\${APPNAME}" "" -OutFile "RetroShare_${VERSION}_${REVISION}_setup.exe" -BrandingText "${APPNAMEANDVERSION}" -; Use compression -SetCompressor /SOLID LZMA - -; Modern interface settings -!include Sections.nsh -!include "MUI.nsh" - -;Interface Settings -!define MUI_ABORTWARNING -;!define MUI_HEADERIMAGE -;!define MUI_HEADERIMAGE_BITMAP "retroshare.bmp" ; optional - -# MUI defines -!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\orange-install.ico" -!define MUI_FINISHPAGE_NOAUTOCLOSE -!define MUI_LICENSEPAGE_RADIOBUTTONS -!define MUI_COMPONENTSPAGE_SMALLDESC -!define MUI_FINISHPAGE_LINK "$(FINISHPAGELINK)" -!define MUI_FINISHPAGE_LINK_LOCATION "http://retroshare.sourceforge.net/forum/" -!define MUI_FINISHPAGE_RUN "$INSTDIR\RetroShare.exe" -!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_TEXT changelog.txt -!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED -!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\orange-uninstall.ico" -!define MUI_UNFINISHPAGE_NOAUTOCLOSE -!define MUI_LANGDLL_REGISTRY_ROOT HKLM -!define MUI_LANGDLL_REGISTRY_KEY ${REGKEY} -!define MUI_LANGDLL_REGISTRY_VALUENAME InstallerLanguage - -;!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIt is recommended that you close all other applications before starting Setup. This will make it possible to update relevant system files without havinf to reboot your computer. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - -;!define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of RetroShare. \r\n\r\nIMPORTANT: Ensure that RetroShare is NOT RUNNING before continuing (you can exit from the taskbar menu), otherwise the installer cannot update the executables, and the installation will fail. \r\n\r\nClick Next to continue. " - - -; Defines the un-/installer logo of RetroShare -!insertmacro MUI_DEFAULT MUI_WELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange.bmp" -!insertmacro MUI_DEFAULT MUI_UNWELCOMEFINISHPAGE_BITMAP "${NSISDIR}\Contrib\Graphics\Wizard\orange-uninstall.bmp" - -; Set languages (first is default language) -!insertmacro MUI_RESERVEFILE_LANGDLL -ReserveFile "${NSISDIR}\Plugins\AdvSplash.dll" - -;-------------------------------- -;Configuration - - - ;!insertmacro MUI_RESERVEFILE_SPECIALBITMAP - - LicenseLangString myLicenseData 1030 "license\license.txt" - LicenseLangString myLicenseData 1031 "license\license-GER.txt" - LicenseLangString myLicenseData 1032 "license\license-GR.txt" - LicenseLangString myLicenseData 1033 "license\license.txt" - LicenseLangString myLicenseData 1034 "license\license-SP.txt" - LicenseLangString myLicenseData 1036 "license\license-FR.txt" - LicenseLangString myLicenseData 1040 "license\license-IT.txt" - LicenseLangString myLicenseData 1041 "license\license.txt" - LicenseLangString myLicenseData 1042 "license\license.txt" - LicenseLangString myLicenseData 1045 "license\license.txt" - LicenseLangString myLicenseData 1046 "license\license-PT_BR.txt" - LicenseLangString myLicenseData 1049 "license\license.txt" - LicenseLangString myLicenseData 1053 "license\license.txt" - LicenseLangString myLicenseData 1055 "license\license-TR.txt" - LicenseLangString myLicenseData 2052 "license\license.txt" - - LicenseData $(myLicenseData) - -# Installer pages -!insertmacro MUI_PAGE_WELCOME -!insertmacro MUI_PAGE_LICENSE "$(myLicenseData)" -!insertmacro MUI_PAGE_COMPONENTS -!insertmacro MUI_PAGE_DIRECTORY -!insertmacro MUI_PAGE_INSTFILES -!insertmacro MUI_PAGE_FINISH -!insertmacro MUI_UNPAGE_CONFIRM -!insertmacro MUI_UNPAGE_INSTFILES - -# Installer languages -!define MUI_LANGDLL_ALLLANGUAGES - -!insertmacro MUI_LANGUAGE English -!insertmacro MUI_LANGUAGE Danish -!insertmacro MUI_LANGUAGE French -!insertmacro MUI_LANGUAGE German -!insertmacro MUI_LANGUAGE Greek -!insertmacro MUI_LANGUAGE Italian -!insertmacro MUI_LANGUAGE Japanese -!insertmacro MUI_LANGUAGE Korean -!insertmacro MUI_LANGUAGE Polish -!insertmacro MUI_LANGUAGE PortugueseBR -!insertmacro MUI_LANGUAGE Russian -!insertmacro MUI_LANGUAGE Swedish -!insertmacro MUI_LANGUAGE SimpChinese -!insertmacro MUI_LANGUAGE Spanish -!insertmacro MUI_LANGUAGE Turkish - - - ;Component-selection page - ;Titles - - LangString sec_main ${LANG_ENGLISH} "Program Files" - LangString sec_data ${LANG_ENGLISH} "Program Skins" - LangString sec_shortcuts ${LANG_ENGLISH} "Shortcuts" - LangString sec_plugins ${LANG_ENGLISH} "Plugins" - LangString sec_link ${LANG_ENGLISH} "File Association" - LangString sec_autostart ${LANG_ENGLISH} "Auto Startup" - LangString DESC_sec_main ${LANG_ENGLISH} "Installs the RetroShare program files." - LangString DESC_sec_plugins ${LANG_ENGLISH} "Installs the RetroShare plugins." - LangString DESC_sec_data ${LANG_ENGLISH} "Installs RetroShare Skins" - LangString DESC_sec_shortcuts ${LANG_ENGLISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_ENGLISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_ENGLISH} "1033" - - LangString sec_main ${LANG_FRENCH} "RetroShare" - LangString sec_data ${LANG_FRENCH} "Programme de Skins" - LangString sec_plugins ${LANG_FRENCH} "Plugins" - LangString sec_shortcuts ${LANG_FRENCH} "Raccourcis" - LangString sec_link ${LANG_FRENCH} "Association de fichiers" - LangString sec_startmenu ${LANG_FRENCH} "Raccourcis du menu Démarrer" - LangString sec_autostart ${LANG_FRENCH} "Démarrage automatique" - LangString DESC_sec_main ${LANG_FRENCH} "Installation des fichiers du programme." - LangString DESC_sec_data ${LANG_FRENCH} "Installation des Skins de RetroShare" - LangString DESC_sec_plugins ${LANG_FRENCH} "Installation des extensions de RetroShare " - LangString DESC_sec_startmenu ${LANG_FRENCH} "Création des raccourcis du menu Démarrer" - LangString DESC_sec_shortcuts ${LANG_FRENCH} "Création de l'icône sur le bureau." - LangString DESC_sec_link ${LANG_FRENCH} "Associer RetroShare avec l'extension de fichier .rsc" - LangString LANGUAGEID ${LANG_FRENCH} "1036" - - LangString sec_main ${LANG_GERMAN} "Programme" - LangString sec_data ${LANG_GERMAN} "Programm Skins" - LangString sec_plugins ${LANG_GERMAN} "Plug-ins" - LangString sec_shortcuts ${LANG_GERMAN} "Verknüpfungen" - LangString sec_link ${LANG_GERMAN} "Dateiverknüpfungen" - LangString sec_autostart ${LANG_GERMAN} "Autostart" - LangString DESC_sec_main ${LANG_GERMAN} "Installiert die RetroShare Programmdateien." - LangString DESC_sec_data ${LANG_GERMAN} "Installiert RetroShare Skins" - LangString DESC_sec_plugins ${LANG_GERMAN} "Installiert die RetroShare Erweiterungen." - LangString DESC_sec_shortcuts ${LANG_GERMAN} "RetroShare Verknüpfung im Startmenüe, Desktop oder im Schnellstarter erstellen." - LangString DESC_sec_link ${LANG_GERMAN} "RetroShare mit .rsc Dateiendung verknüpfen" - LangString LANGUAGEID ${LANG_GERMAN} "1031" - - LangString sec_main ${LANG_TURKISH} "Program Dosyalar?" - LangString sec_data ${LANG_TURKISH} "Program Skinleri" - LangString sec_shortcuts ${LANG_TURKISH} "Shortcut'lar" - LangString sec_plugins ${LANG_TURKISH} "Eklentiler" - LangString sec_link ${LANG_TURKISH} ".rsc Dosya Kaydet" - LangString sec_autostart ${LANG_TURKISH} "Otomatik calistir ve baglan" - LangString DESC_sec_main ${LANG_TURKISH} "Program dosyalar?n? kurar." - LangString DESC_sec_data ${LANG_TURKISH} "RetroShare Skin'leri kurar" - LangString DESC_sec_plugins ${LANG_TURKISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${TURKISH} "Shortcut yap Start menu , Desktop veya Quicklaunchbar icin." - LangString DESC_sec_link ${LANG_TURKISH} "RetroShare .rsc almas? i?in kaydettirir" - LangString LANGUAGEID ${LANG_TURKISH} "1055" - - LangString sec_main ${LANG_SIMPCHINESE} "程序文件" - LangString sec_data ${LANG_SIMPCHINESE} "程序皮肤" - LangString sec_plugins ${LANG_SIMPCHINESE} "Plugins" - LangString sec_shortcuts ${LANG_SIMPCHINESE} "快捷方式" - LangString sec_link ${LANG_SIMPCHINESE} "RetroShare文件关联" - LangString sec_autostart ${LANG_SIMPCHINESE} "自动启动" - LangString DESC_sec_main ${LANG_SIMPCHINESE} "安装RetroShare程序" - LangString DESC_sec_data ${LANG_SIMPCHINESE} "安装RetroShare皮肤" - LangString DESC_sec_plugins ${LANG_SIMPCHINESE} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_SIMPCHINESE} "建RetroShare快捷方式" - LangString DESC_sec_link ${LANG_SIMPCHINESE} "关联.rsc扩展名" - LangString LANGUAGEID ${LANG_SIMPCHINESE} "2052" - - LangString sec_main ${LANG_POLISH} "Pliki programu" - LangString sec_data ${LANG_POLISH} "Skórki" - LangString sec_plugins ${LANG_POLISH} "Plugins" - LangString sec_shortcuts ${LANG_POLISH} "Skróty" - LangString sec_link ${LANG_POLISH} "Skojarz pliki" - LangString sec_autostart ${LANG_POLISH} "Automatyczne uruchamianie" - LangString DESC_sec_main ${LANG_POLISH} "Instaluje pliki programu RetroShare" - LangString DESC_sec_data ${LANG_POLISH} "Instaluje skórki programu RetroShare" - LangString DESC_sec_plugins ${LANG_POLISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_POLISH} "Utwórz ikony skrótów na pulpicie, w menu start oraz na pasku szybkiego uruchamiania." - LangString DESC_sec_link ${LANG_POLISH} "Skojarz pliki o rozszerzeniu .rsc z RetroShare" - LangString LANGUAGEID ${LANG_POLISH} "1045" - - LangString sec_main ${LANG_DANISH} "Program Files" - LangString sec_data ${LANG_DANISH} "Program Skins" - LangString sec_plugins ${LANG_DANISH} "Plugins" - LangString sec_shortcuts ${LANG_DANISH} "Shortcuts" - LangString sec_link ${LANG_DANISH} "File Association" - LangString sec_autostart ${LANG_DANISH} "Auto Startup" - LangString DESC_sec_main ${LANG_DANISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_DANISH} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_DANISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_DANISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_DANISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_DANISH} "1030" - - LangString sec_main ${LANG_RUSSIAN} "Program Files" - LangString sec_data ${LANG_RUSSIAN} "Program Skins" - LangString sec_plugins ${LANG_RUSSIAN} "Plugins" - LangString sec_shortcuts ${LANG_RUSSIAN} "Shortcuts" - LangString sec_link ${LANG_RUSSIAN} "File Association" - LangString sec_autostart ${LANG_RUSSIAN} "Auto Startup" - LangString DESC_sec_main ${LANG_RUSSIAN} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_RUSSIAN} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_RUSSIAN} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_RUSSIAN} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_RUSSIAN} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_RUSSIAN} "1049" - - LangString sec_main ${LANG_SWEDISH} "Program Files" - LangString sec_data ${LANG_SWEDISH} "Program Skins" - LangString sec_plugins ${LANG_SWEDISH} "Plugins" - LangString sec_shortcuts ${LANG_SWEDISH} "Shortcuts" - LangString sec_link ${LANG_SWEDISH} "File Association" - LangString sec_autostart ${LANG_SWEDISH} "Auto Startup" - LangString DESC_sec_main ${LANG_SWEDISH} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_SWEDISH} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_SWEDISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_SWEDISH} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_SWEDISH} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_SWEDISH} "1053" - - LangString sec_main ${LANG_SPANISH} "Archivos de programa" - LangString sec_data ${LANG_SPANISH} "Estilos del programa" - LangString sec_plugins ${LANG_SPANISH} "Plugins" - LangString sec_shortcuts ${LANG_SPANISH} "Accesos directos" - LangString sec_link ${LANG_SPANISH} "Asociación de archivos" - LangString sec_autostart ${LANG_SPANISH} "Inicio automático" - LangString DESC_sec_main ${LANG_SPANISH} "Instala los archivos del programa RetroShare." - LangString DESC_sec_data ${LANG_SPANISH} "Instala los estilos para RetroShare" - LangString DESC_sec_plugins ${LANG_SPANISH} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_SPANISH} "Crear iconos de acceso directo de RetroShare." - LangString DESC_sec_link ${LANG_SPANISH} "Asociar RetroShare con la extensión de archivo .rsc" - LangString LANGUAGEID ${LANG_SPANISH} "1034" - - LangString sec_main ${LANG_ITALIAN} "Programmi" - LangString sec_data ${LANG_ITALIAN} "Temi del Programma" - LangString sec_plugins ${LANG_ITALIAN} "Plugins" - LangString sec_shortcuts ${LANG_ITALIAN} "Icone" - LangString sec_link ${LANG_ITALIAN} "Associazione dei File" - LangString sec_autostart ${LANG_ITALIAN} "Esecuzione Automatica" - LangString DESC_sec_main ${LANG_ITALIAN} "Installare i file programma di RetroShare." - LangString DESC_sec_data ${LANG_ITALIAN} "Installare i temi di RetroShare" - LangString DESC_sec_plugins ${LANG_ITALIAN} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_ITALIAN} "Creare le icone per avviare RetroShare." - LangString DESC_sec_link ${LANG_ITALIAN} "Associa a RetroShare i file con estensione .rsc" - LangString LANGUAGEID ${LANG_ITALIAN} "1040" - - LangString sec_main ${LANG_GREEK} "Program Files" - LangString sec_data ${LANG_GREEK} "Program Skins" - LangString sec_plugins ${LANG_GREEK} "Plugins" - LangString sec_shortcuts ${LANG_GREEK} "Shortcuts" - LangString sec_link ${LANG_GREEK} "File Association" - LangString sec_autostart ${LANG_GREEK} "Auto Startup" - LangString DESC_sec_main ${LANG_GREEK} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_GREEK} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_GREEK} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_GREEK} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_GREEK} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_GREEK} "1032" - - LangString sec_main ${LANG_PORTUGUESEBR} "Program Files" - LangString sec_data ${LANG_PORTUGUESEBR} "Program Skins" - LangString sec_plugins ${LANG_PORTUGUESEBR} "Plugins" - LangString sec_shortcuts ${LANG_PORTUGUESEBR} "Shortcuts" - LangString sec_link ${LANG_PORTUGUESEBR} "File Association" - LangString sec_autostart ${LANG_PORTUGUESEBR} "Auto Startup" - LangString DESC_sec_main ${LANG_PORTUGUESEBR} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_PORTUGUESEBR} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_PORTUGUESEBR} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_PORTUGUESEBR} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_PORTUGUESEBR} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_PORTUGUESEBR} "1046" - - LangString sec_main ${LANG_JAPANESE} "Program Files" - LangString sec_data ${LANG_JAPANESE} "Program Skins" - LangString sec_plugins ${LANG_JAPANESE} "Plugins" - LangString sec_shortcuts ${LANG_JAPANESE} "Shortcuts" - LangString sec_link ${LANG_JAPANESE} "File Association" - LangString sec_autostart ${LANG_JAPANESE} "Auto Startup" - LangString DESC_sec_main ${LANG_JAPANESE} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_JAPANESE} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_JAPANESE} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_JAPANESE} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_JAPANESE} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_JAPANESE} "1041" - - LangString sec_main ${LANG_KOREAN} "Program Files" - LangString sec_data ${LANG_KOREAN} "Program Skins" - LangString sec_plugins ${LANG_KOREAN} "Plugins" - LangString sec_shortcuts ${LANG_KOREAN} "Shortcuts" - LangString sec_link ${LANG_KOREAN} "File Association" - LangString sec_autostart ${LANG_KOREAN} "Auto Startup" - LangString DESC_sec_main ${LANG_KOREAN} "Installs the RetroShare program files." - LangString DESC_sec_data ${LANG_KOREAN} "Installs RetroShare Skins" - LangString DESC_sec_plugins ${LANG_KOREAN} "Installs the RetroShare plugins." - LangString DESC_sec_shortcuts ${LANG_KOREAN} "Create RetroShare shortcut icons." - LangString DESC_sec_link ${LANG_KOREAN} "Associate RetroShare with .rsc file extension" - LangString LANGUAGEID ${LANG_KOREAN} "1042" - - -!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - -Section $(sec_main) sec_main - - ;Set Section required - SectionIn RO - - ; Set Section properties - SetOverwrite on - - ; Clears previous error logs - Delete "$INSTDIR\*.log" - - ; Set Section Files and Shortcuts - SetOutPath "$INSTDIR\" - File /r "release\RetroShare.exe" - File /r "..\..\retroshare-nogui\src\release\retroshare-nogui.exe" - File /r "${QTBASE}\mingw\bin\mingwm10.dll" - File /r "${QTBASE}\qt\bin\QtCore4.dll" - File /r "${QTBASE}\qt\bin\QtGui4.dll" - File /r "${QTBASE}\qt\bin\QtNetwork4.dll" - File /r "${QTBASE}\qt\bin\QtMultimedia4.dll" - File /r "${QTBASE}\qt\bin\QtXml4.dll" - File /r "${QTBASE}\qt\bin\QtScript4.dll" - File /r "${QTBASE}\qt\bin\QtSvg4.dll" - File /r "${QTBASE}\qt\bin\libgcc_s_dw2-1.dll" - File /r "${QTBASE}\qt\plugins\imageformats" - File /r ${QTBASE}\qt\qt_*.qm - File /r ${RSBASE}\src\qt_*.qm - File /r "release\pthreadGC2d.dll" - File /r "h:\Development\lib\libminiupnpc-1.3\miniupnpc.dll" - File /r "changelog.txt" - File /r /x Data "release\bdboot.txt" - - -SectionEnd - -Section $(sec_data) sec_data - - ; Set Section properties - SetOverwrite on - - ; Set Section Files and Shortcuts - SetOutPath "$APPDATA\RetroShare\" - ;File /r "data\*" - - ; Set Section qss and exclude svn - SetOutPath "$INSTDIR\qss\" - File /r /x .svn qss\*.* - - ; Set Section sounds and exclude svn - SetOutPath "$INSTDIR\sounds\" - File /r /x .svn sounds\*.* - - ; Set Section skin - ; SetOutPath "$INSTDIR\skin\" - ; File /r release\skin\*.* - - ; Add emoticons - ;SetOutPath "$INSTDIR\emoticons\" - ;File /r emoticons\*.* - - ; Add Chat Style - SetOutPath "$APPDATA\RetroShare\stylesheets\" - File /r gui\qss\chat\Bubble - File /r gui\qss\chat\Bubble_Compact - - -SectionEnd - -SectionGroup $(sec_plugins) sec_plugins -Section Voip SEC001 - - ; Set Section properties - SetOverwrite on - - ; Set Section Plugins - SetOutPath "$APPDATA\RetroShare\extensions\" - - File /r "..\..\plugins\VOIP\release\VOIP.dll" - -SectionEnd - -Section FeedReader SEC002 - - ; Set Section properties - SetOverwrite on - - ; Set Section Plugins - SetOutPath "$APPDATA\RetroShare\extensions\" - - File /r "..\..\plugins\FeedReader\release\FeedReader.dll" - -SectionEnd - -SectionGroupEnd - -Section $(sec_link) sec_link - ; Delete any existing keys - - - ; Write the file association - WriteRegStr HKCR .rsc "" retroshare - WriteRegStr HKCR retroshare "" "RSC File" - WriteRegBin HKCR retroshare EditFlags 00000100 - WriteRegStr HKCR "retroshare\shell" "" open - WriteRegStr HKCR "retroshare\shell\open\command" "" `"$INSTDIR\RetroShare.exe" "%1"` - -SectionEnd - -SectionGroup $(sec_shortcuts) sec_shortcuts -Section StartMenu SEC0001 - - SetOutPath "$INSTDIR" - CreateDirectory "$SMPROGRAMS\${APPNAME}" - CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - CreateShortCut "$SMPROGRAMS\${APPNAME}\$(^UninstallLink).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0 - -SectionEnd - -Section Desktop SEC0002 - - - CreateShortCut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd - -Section Quicklaunchbar SEC0003 - - - CreateShortCut "$QUICKLAUNCH\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 - -SectionEnd -SectionGroupEnd - -;Section $(sec_autostart) sec_autostart - -; WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" "$INSTDIR\${APPNAME}.exe -a" - -;SectionEnd - -;Section $(sec_autostart) sec_autostart - -; CreateShortCut "$SMSTARTUP\${APPNAME}.lnk" "$INSTDIR\RetroShare.exe" "" "$INSTDIR\RetroShare.exe" 0 -;SectionEnd - - -Section -FinishSection - - WriteRegStr HKLM "Software\${APPNAME}" "" "$INSTDIR" - WriteRegStr HKLM "Software\${APPNAME}" "Version" "${VERSION}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\uninstall.exe" - WriteUninstaller "$INSTDIR\uninstall.exe" - -SectionEnd - - - -;-------------------------------- -;Descriptions - -!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN - !insertmacro MUI_DESCRIPTION_TEXT ${sec_main} $(DESC_sec_main) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_data} $(DESC_sec_data) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_plugins} $(DESC_sec_plugins) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_shortcuts} $(DESC_sec_shortcuts) - !insertmacro MUI_DESCRIPTION_TEXT ${sec_link} $(DESC_sec_link) - ;!insertmacro MUI_DESCRIPTION_TEXT ${sec_autostart} $(DESC_sec_autostart) -!insertmacro MUI_FUNCTION_DESCRIPTION_END - -;Uninstall section -Section "Uninstall" - - ; Remove file association registry keys - DeleteRegKey HKCR .rsc - DeleteRegKey HKCR retroshare - - ; Remove program/uninstall regsitry keys - DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" - DeleteRegKey HKLM SOFTWARE\${APPNAME} - - DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "RetroRun" - - ; Remove files and uninstaller - Delete $INSTDIR\RetroShare.exe - Delete $INSTDIR\*.dll - Delete $INSTDIR\*.dat - Delete $INSTDIR\*.txt - Delete $INSTDIR\*.ini - Delete $INSTDIR\*.log - - Delete $INSTDIR\uninstall.exe - - ; Remove the kadc.ini file. - ; Don't remove the directory, otherwise - ; we lose the XPGP keys. - ; Should make this an option though... - Delete "$APPDATA\${APPNAME}\kadc.ini" - Delete "$APPDATA\${APPNAME}\*.cfg" - Delete "$APPDATA\${APPNAME}\*.conf" - Delete "$APPDATA\${APPNAME}\*.log-save" - Delete "$APPDATA\${APPNAME}\*.log" - Delete "$APPDATA\${APPNAME}\*.failed" - - RMDir /r "$APPDATA\${APPNAME}\cache" - RMDir /r "$APPDATA\${APPNAME}\Partials" - - ; Remove shortcuts, if any - Delete "$SMPROGRAMS\${APPNAME}\*.*" - - ; Remove desktop shortcut - Delete "$DESKTOP\${APPNAME}.lnk" - - ; Remove Quicklaunch shortcut - Delete "$QUICKLAUNCH\${APPNAME}.lnk" - - ; Remove Autostart - ;Delete "$SMSTARTUP\${APPNAME}.lnk" - - ; Remove directories used - RMDir "$SMPROGRAMS\${APPNAME}" - RMDir /r "$INSTDIR" - RMDir /r "$INSTDIR\qss" - RMDir /r "$INSTDIR\emoticons" - RMDir /r "$INSTDIR\style" - RMDir /r "$INSTDIR\translations" - -SectionEnd - -Function .onInit - - InitPluginsDir - Push $R1 - File /oname=$PLUGINSDIR\spltmp.bmp "gui\images\splash.bmp" - advsplash::show 1200 1000 1000 -1 $PLUGINSDIR\spltmp - Pop $R1 - Pop $R1 - !insertmacro MUI_LANGDLL_DISPLAY - - - -FunctionEnd - - -# Installer Language Strings -# TODO Update the Language Strings with the appropriate translations. - - -LangString FINISHPAGELINK ${LANG_ENGLISH} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_GERMAN} "Das RetroShare Support-Forum besuchen, um Neuigkeiten und Unterstützung zu erfahren" -LangString FINISHPAGELINK ${LANG_TURKISH} "Destek için Retroshare foruma ziyaret et" -LangString FINISHPAGELINK ${LANG_FRENCH} "Consultez le forum RetroShare pour vous tenir au courant des dernieres modifications, et obtenir de l'aide." -LangString FINISHPAGELINK ${LANG_SIMPCHINESE} "帮助论坛" -LangString FINISHPAGELINK ${LANG_POLISH} "Odwiedź forum RetroShare do najświeższych informacji i wsparcia" -LangString FINISHPAGELINK ${LANG_DANISH} "Besøg RetroShare fora for de seneste nyheder og støtte" -LangString FINISHPAGELINK ${LANG_JAPANESE} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_KOREAN} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_RUSSIAN} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_SWEDISH} "Besök RetroShare forum för de senaste nyheterna och stöd" -LangString FINISHPAGELINK ${LANG_SPANISH} "Visite los foros de RetroShare para las últimas noticias y soporte" -LangString FINISHPAGELINK ${LANG_ITALIAN} "Visita i forum di RetroShare per le ultime novità ed il supporto" -LangString FINISHPAGELINK ${LANG_GREEK} "Visit the RetroShare forums for the latest news and support" -LangString FINISHPAGELINK ${LANG_PORTUGUESEBR} "Visit the RetroShare forums for the latest news and support" - -LangString ^UninstallLink ${LANG_ENGLISH} "Uninstall" -LangString ^UninstallLink ${LANG_GERMAN} "Deinstallieren" -LangString ^UninstallLink ${LANG_TURKISH} "Kald�r" -LangString ^UninstallLink ${LANG_FRENCH} "Désinstaller" -LangString ^UninstallLink ${LANG_SIMPCHINESE} "卸载" -LangString ^UninstallLink ${LANG_POLISH} "Odinstaluj" -LangString ^UninstallLink ${LANG_DANISH} "Afinstaller" -LangString ^UninstallLink ${LANG_JAPANESE} "Uninstall" -LangString ^UninstallLink ${LANG_KOREAN} "Uninstall" -LangString ^UninstallLink ${LANG_RUSSIAN} "Uninstall" -LangString ^UninstallLink ${LANG_SWEDISH} "Avinstallera" -LangString ^UninstallLink ${LANG_SPANISH} "Desinstalar" -LangString ^UninstallLink ${LANG_ITALIAN} "Disinstallare" -LangString ^UninstallLink ${LANG_GREEK} "Απεγκατάσταση" -LangString ^UninstallLink ${LANG_PORTUGUESEBR} "Desinstalar" - -; eof diff --git a/retroshare-service/src/android/AndroidManifest.xml b/retroshare-service/src/android/AndroidManifest.xml index d41a72995..ecf8ba78a 100644 --- a/retroshare-service/src/android/AndroidManifest.xml +++ b/retroshare-service/src/android/AndroidManifest.xml @@ -2,9 +2,9 @@ - + @@ -14,59 +14,81 @@ - - - - - - - - - - - - - + + + + + - + + + + - - - - +]]> + + + + + + + + + + + + + + + +]]> + + + + + + + + + + + + + @@ -76,17 +98,11 @@ ++ features based on the dependencies of the application. ++ Remove the comment if you do not require these default features. --> +]]> - + - - - - - - - - - + --> +]]> diff --git a/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java b/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java index 283fd10cc..3f2e3f375 100644 --- a/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java +++ b/retroshare-service/src/android/src/org/retroshare/service/AppUpdatedReceiver.java @@ -19,6 +19,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +/* package org.retroshare.service; import android.content.BroadcastReceiver; @@ -36,3 +37,4 @@ public class AppUpdatedReceiver extends BroadcastReceiver RetroShareServiceAndroid.start(context); } } +*/ \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/AssetHelper.java b/retroshare-service/src/android/src/org/retroshare/service/AssetHelper.java new file mode 120000 index 000000000..60da18398 --- /dev/null +++ b/retroshare-service/src/android/src/org/retroshare/service/AssetHelper.java @@ -0,0 +1 @@ +../../../../../../../libretroshare/src/rs_android/org/retroshare/service/AssetHelper.java \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java b/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java index 89c09c241..75bfbc063 100644 --- a/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java +++ b/retroshare-service/src/android/src/org/retroshare/service/BootCompletedReceiver.java @@ -19,6 +19,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +/* package org.retroshare.service; import android.content.BroadcastReceiver; @@ -35,3 +36,4 @@ public class BootCompletedReceiver extends BroadcastReceiver RetroShareServiceAndroid.start(context); } } +*/ \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/ErrorConditionWrap.java b/retroshare-service/src/android/src/org/retroshare/service/ErrorConditionWrap.java new file mode 120000 index 000000000..1f90a62c6 --- /dev/null +++ b/retroshare-service/src/android/src/org/retroshare/service/ErrorConditionWrap.java @@ -0,0 +1 @@ +../../../../../../../libretroshare/src/rs_android/org/retroshare/service/ErrorConditionWrap.java \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java deleted file mode 100644 index 3cea999bf..000000000 --- a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * RetroShare - * Copyright (C) 2016-2018 Gioacchino Mazzurco - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - * SPDX-FileCopyrightText: Retroshare Team - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package org.retroshare.service; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.Intent; - -import org.qtproject.qt5.android.bindings.QtService; - -public class RetroShareServiceAndroid extends QtService -{ - public static void start(Context ctx) - { - ctx.startService(new Intent(ctx, RetroShareServiceAndroid.class)); - } - - public static void stop(Context ctx) - { - ctx.stopService(new Intent(ctx, RetroShareServiceAndroid.class)); - } - - public static boolean isRunning(Context ctx) - { - ActivityManager manager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE); - for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) - if (RetroShareServiceAndroid.class.getName().equals(service.service.getClassName())) - return true; - return false; - } -} diff --git a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java new file mode 120000 index 000000000..480e21ac4 --- /dev/null +++ b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceAndroid.java @@ -0,0 +1 @@ +../../../../../../../libretroshare/src/rs_android/org/retroshare/service/RetroShareServiceAndroid.java \ No newline at end of file diff --git a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java index d1e1f78b7..dafe10301 100644 --- a/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java +++ b/retroshare-service/src/android/src/org/retroshare/service/RetroShareServiceControlActivity.java @@ -1,6 +1,6 @@ /* * RetroShare - * Copyright (C) 2016-2018 Gioacchino Mazzurco + * Copyright (C) 2016-2021 Gioacchino Mazzurco * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -50,7 +50,11 @@ public class RetroShareServiceControlActivity extends Activity else { button.setText("Starting..."); - RetroShareServiceAndroid.start(RetroShareServiceControlActivity.this); + RetroShareServiceAndroid.start( + RetroShareServiceControlActivity.this, + RetroShareServiceAndroid.DEFAULT_JSON_API_PORT, + RetroShareServiceAndroid.DEFAULT_JSON_API_BINDING_ADDRESS + ); serviceStarting = true; serviceStopping = false; } diff --git a/retroshare-service/src/retroshare-service-android.cc b/retroshare-service/src/retroshare-service-android.cc new file mode 100644 index 000000000..30204c8fd --- /dev/null +++ b/retroshare-service/src/retroshare-service-android.cc @@ -0,0 +1,29 @@ +/* + * RetroShare Service Android + * Copyright (C) 2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * SPDX-FileCopyrightText: Retroshare Team + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include "rs_android/rsjni.hpp" + +extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* _reserved) +{ + RS_DBG(vm); + return JNI_OnLoad_retroshare(vm, _reserved); +} diff --git a/retroshare-service/src/retroshare-service.cc b/retroshare-service/src/retroshare-service.cc index e80d468e1..51888d25a 100644 --- a/retroshare-service/src/retroshare-service.cc +++ b/retroshare-service/src/retroshare-service.cc @@ -1,6 +1,7 @@ /* * RetroShare Service - * Copyright (C) 2016-2019 Gioacchino Mazzurco + * Copyright (C) 2016-2021 Gioacchino Mazzurco + * Copyright (C) 2021 Asociación Civil Altermundi * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -19,39 +20,33 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -#include "util/stacktrace.h" -#include "util/argstream.h" -#include "util/rskbdinput.h" -#include "retroshare/rsinit.h" - -#ifdef RS_JSONAPI -#include "retroshare/rsjsonapi.h" - -#ifdef RS_WEBUI -#include "retroshare/rswebui.h" -#endif -#endif - -static CrashStackTrace gCrashStackTrace; #include #include #include #include -#ifdef __ANDROID__ -# include -# include -# include -# include - -# include "util/androiddebug.h" -#endif // def __ANDROID__ - +#include "util/stacktrace.h" +#include "util/argstream.h" +#include "util/rskbdinput.h" +#include "util/rsdir.h" +#include "retroshare/rsinit.h" +#include "retroshare/rstor.h" +#include "retroshare/rspeers.h" #include "retroshare/rsinit.h" #include "retroshare/rsiface.h" #include "util/rsdebug.h" +#ifdef RS_JSONAPI +# include "retroshare/rsjsonapi.h" + +# ifdef RS_WEBUI +# include "retroshare/rswebui.h" +# endif // def RS_WEBUI +#endif // def RS_JSONAPI + +static CrashStackTrace gCrashStackTrace; + #ifdef RS_SERVICE_TERMINAL_LOGIN class RsServiceNotify: public NotifyClient { @@ -74,9 +69,6 @@ public: }; #endif // def RS_SERVICE_TERMINAL_LOGIN -#ifdef __ANDROID__ -void signalHandler(int /*signal*/) { QCoreApplication::exit(0); } -#else static std::atomic keepRunning(true); static int receivedSignal = 0; @@ -87,16 +79,10 @@ void signalHandler(int signal) receivedSignal = signal; keepRunning = false; } -#endif // def __ANDROID__ int main(int argc, char* argv[]) { -#ifdef __ANDROID__ - AndroidStdIOCatcher dbg; (void) dbg; - QAndroidService app(argc, argv); -#endif // def __ANDROID__ - signal(SIGINT, signalHandler); signal(SIGTERM, signalHandler); #ifdef SIGBREAK @@ -128,7 +114,7 @@ int main(int argc, char* argv[]) RsConfigOptions conf; #ifdef RS_JSONAPI - conf.jsonApiPort = RsJsonApi::DEFAULT_PORT; // enable JSonAPI by default + conf.jsonApiPort = RsJsonApi::DEFAULT_PORT; // enable JSON API by default #ifdef RS_WEBUI std::string webui_base_directory = RsWebUi::DEFAULT_BASE_DIRECTORY; #endif @@ -311,6 +297,35 @@ int main(int argc, char* argv[]) << std::endl; return -result; } + + if(RsAccounts::isTorAuto()) + { + + std::cerr << "(II) Hidden service is ready:" << std::endl; + + std::string service_id ; + std::string onion_address ; + uint16_t service_port ; + uint16_t service_target_port ; + uint16_t proxy_server_port ; + std::string service_target_address ; + std::string proxy_server_address ; + + RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,service_target_port); + RsTor::getProxyServerInfo(proxy_server_address,proxy_server_port) ; + + std::cerr << " onion address : " << onion_address << std::endl; + std::cerr << " service_id : " << service_id << 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 << std::endl; + + std::cerr << "Setting proxy server to " << service_target_address << ":" << service_target_port << std::endl; + + rsPeers->setLocalAddress(rsPeers->getOwnId(), service_target_address, service_target_port); + rsPeers->setHiddenNode(rsPeers->getOwnId(), onion_address, service_port); + rsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, proxy_server_address,proxy_server_port) ; + } } #endif // def RS_SERVICE_TERMINAL_LOGIN @@ -323,22 +338,10 @@ int main(int argc, char* argv[]) } #endif -#ifdef __ANDROID__ - rsControl->setShutdownCallback(QCoreApplication::exit); - - QObject::connect( - &app, &QCoreApplication::aboutToQuit, - [](){ - if(RsControl::instance()->isReady()) - RsControl::instance()->rsGlobalShutDown(); } ); - - return app.exec(); -#else // def __ANDROID__ rsControl->setShutdownCallback([&](int){keepRunning = false;}); while(keepRunning) std::this_thread::sleep_for(std::chrono::milliseconds(500)); return 0; -#endif } diff --git a/retroshare-service/src/retroshare-service.pro b/retroshare-service/src/retroshare-service.pro index cbd1b9d13..82eda34c6 100644 --- a/retroshare-service/src/retroshare-service.pro +++ b/retroshare-service/src/retroshare-service.pro @@ -1,6 +1,6 @@ # RetroShare service qmake build script # -# Copyright (C) 2018-2019, Gioacchino Mazzurco +# Copyright (C) 2018-2021, Gioacchino Mazzurco # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the @@ -22,9 +22,6 @@ TARGET = retroshare-service -QT += core -QT -= gui - !include("../../libretroshare/src/use_libretroshare.pri"):error("Including") SOURCES += retroshare-service.cc @@ -32,8 +29,6 @@ SOURCES += retroshare-service.cc ################################# Linux ########################################## android-* { - QT += androidextras - ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android DISTFILES += android/AndroidManifest.xml \ @@ -45,6 +40,9 @@ android-* { android/build.gradle \ android/gradle/wrapper/gradle-wrapper.properties \ android/gradlew.bat + + SOURCES -= retroshare-service.cc + SOURCES += retroshare-service-android.cc } diff --git a/retroshare.pri b/retroshare.pri index f16220c68..01b5d4fd7 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -821,5 +821,4 @@ contains(RS_UPNP_LIB, upnp):DEFINES*=RS_USE_LIBUPNP isEmpty(BIN_DIR) : BIN_DIR = $${RS_BIN_DIR} isEmpty(INC_DIR) : INC_DIR = $${RS_INCLUDE_DIR} isEmpty(LIBDIR) : LIBDIR = $${QMAKE_LIBDIR} -isEmpty(DATA_DIR) : DATA_DIR = $${RS_DATA_DIR} isEmpty(PLUGIN_DIR): PLUGIN_DIR= $${RS_PLUGIN_DIR} diff --git a/supportlibs/jni.hpp b/supportlibs/jni.hpp new file mode 160000 index 000000000..66f73a6aa --- /dev/null +++ b/supportlibs/jni.hpp @@ -0,0 +1 @@ +Subproject commit 66f73a6aa82367d6ba23e7e842f95dfb33c451d6 diff --git a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc index 925e90be2..442a25cc1 100644 --- a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc +++ b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.cc @@ -134,11 +134,11 @@ const RsPgpId& rs_nxs_test::RsDummyPgpUtils::getPGPOwnId() { return mOwnId; } -RsPgpId rs_nxs_test::RsDummyPgpUtils::getPGPId(const RsPeerId& /*sslid*/) { +RsPgpId rs_nxs_test::RsDummyPgpUtils::getPgpId(const RsPeerId& /*sslid*/) { return RsPgpId().random(); } -bool rs_nxs_test::RsDummyPgpUtils::getGPGAllList(std::list& /*ids*/) { +bool rs_nxs_test::RsDummyPgpUtils::getPgpAllList(std::list& /*ids*/) { return true; } diff --git a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h index aee8f989e..6e993a658 100644 --- a/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h +++ b/tests/unittests/libretroshare/gxs/nxs_test/nxsdummyservices.h @@ -164,8 +164,8 @@ namespace rs_nxs_test virtual ~RsDummyPgpUtils(){} const RsPgpId &getPGPOwnId() ; - RsPgpId getPGPId(const RsPeerId& sslid) ; - bool getGPGAllList(std::list &ids) ; + RsPgpId getPgpId(const RsPeerId& sslid) ; + bool getPgpAllList(std::list &ids) ; bool getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) const; bool parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const; diff --git a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc index 2d274897a..49e440e5e 100644 --- a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc +++ b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.cc @@ -24,7 +24,7 @@ FakePgpAuxUtils::FakePgpAuxUtils(const RsPeerId& ownId) { - mOwnId = getPGPId(ownId); + mOwnId = getPgpId(ownId); addPeerIdToPgpList(ownId); } @@ -39,7 +39,7 @@ void FakePgpAuxUtils::addPeerListToPgpList(const std::list &ids) void FakePgpAuxUtils::addPeerIdToPgpList(const RsPeerId &id) { - RsPgpId pgpId = getPGPId(id); + RsPgpId pgpId = getPgpId(id); if (mPgpList.end() == std::find(mPgpList.begin(), mPgpList.end(), pgpId)) { mPgpList.push_back(pgpId); @@ -51,7 +51,7 @@ const RsPgpId & FakePgpAuxUtils::getPGPOwnId() return mOwnId; } -RsPgpId FakePgpAuxUtils::getPGPId(const RsPeerId& sslid) +RsPgpId FakePgpAuxUtils::getPgpId(const RsPeerId& sslid) { /* convert an sslId */ std::string idstring = sslid.toStdString(); @@ -95,7 +95,7 @@ bool FakePgpAuxUtils::VerifySignBin(const void* /*data*/, uint32_t /*len*/, unsi return true; } -bool FakePgpAuxUtils::getGPGAllList(std::list &ids) +bool FakePgpAuxUtils::getPgpAllList(std::list &ids) { ids = mPgpList; return true; diff --git a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h index 63b20b85d..f866164a6 100644 --- a/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h +++ b/tests/unittests/libretroshare/services/gxs/FakePgpAuxUtils.h @@ -28,7 +28,7 @@ public: FakePgpAuxUtils(const RsPeerId& ownId); virtual const RsPgpId &getPGPOwnId(); - virtual RsPgpId getPGPId(const RsPeerId& sslid); + virtual RsPgpId getPgpId(const RsPeerId& sslid); virtual bool getKeyFingerprint(const RsPgpId& id,PGPFingerprintType& fp) const; virtual bool parseSignature(unsigned char *sign, unsigned int signlen, RsPgpId& issuer) const; @@ -36,7 +36,7 @@ public: virtual void addPeerListToPgpList(const std::list &ids); virtual void addPeerIdToPgpList(const RsPeerId &id); - virtual bool getGPGAllList(std::list &ids); + virtual bool getPgpAllList(std::list &ids); private: RsPgpId mOwnId; std::list mPgpList;