Merge branch 'RetroShare:master' into picturepreview-fileattachments-forums

This commit is contained in:
defnax 2024-11-15 21:45:51 +01:00 committed by GitHub
commit 26fececb2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
493 changed files with 71323 additions and 74304 deletions

20
.gitignore vendored
View file

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: (C) 2004-2019 Retroshare Team <contact@retroshare.cc>
# SPDX-FileCopyrightText: (C) 2004-2022 Retroshare Team <contact@retroshare.cc>
# SPDX-License-Identifier: CC0-1.0
*.o
@ -16,4 +16,22 @@ Thumbs.db
.kdev4
*.kdev4
.qmake.stash
!supportlibs/libsam3/Makefile
# QtCreator cruft
*CMakeLists.txt.user*
# Build artifacts
/jsonapi-generator/src/jsonapi-generator
/jsonapi-generator/src/jsonapi-generator-doxygen-final.conf
/jsonapi-generator/src/jsonapi-includes.inl
/jsonapi-generator/src/jsonapi-wrappers.inl
/jsonapi-generator/src/xml/
/retroshare-friendserver/src/retroshare-friendserver
/retroshare-gui/src/include/
/retroshare-gui/src/retroshare
/retroshare-gui/src/temp/
/retroshare-service/src/retroshare-service
/*.tar.?z

View file

@ -1,7 +1,7 @@
image: docker:stable
image: docker:latest
services:
- docker:stable-dind
- docker:dind
stages:
- build
@ -18,6 +18,7 @@ variables:
build-ubuntu-test-image:
stage: build
script:
- docker --version
- >
docker login "$CI_REGISTRY"
--username "$CI_REGISTRY_USER"
@ -35,6 +36,7 @@ build-ubuntu-test-image:
test-ubuntu:
stage: test
script:
- docker --version
- >
docker login "$CI_REGISTRY"
--username "$CI_REGISTRY_USER"
@ -67,43 +69,3 @@ test-ubuntu:
docker exec retroshare
curl --verbose http://127.0.0.1:9092/rsMsgs/getChatLobbyList | jq
- docker container stop retroshare
build-android-arm-apk:
stage: build
script:
- mkdir Dockercontext
- >
docker build -t retroshare:android_arm_latest
$($CI_PROJECT_DIR/build_scripts/GitlabCI/getRepoArgs.sh)
--build-arg RS_SERVICE_QMAKE_EXTRA_OPTS="$RS_SERVICE_QMAKE_EXTRA_OPTS"
--file $CI_PROJECT_DIR/build_scripts/GitlabCI/Android.Dockerfile
Dockercontext
# see https://stackoverflow.com/a/59055906
- >
docker cp
$(docker create --rm retroshare:android_arm_latest):/retroshare-service-android-build/android-build/build/outputs/apk/debug/android-build-debug.apk
$CI_PROJECT_DIR/RetroShare_Android_Service-arm.apk
artifacts:
paths:
- RetroShare_Android_Service-arm.apk
# Use separate runner to avoid no space left on device
build-android-arm64-apk:
stage: build
script:
- mkdir Dockercontext
- >
docker build -t retroshare:android_arm64_latest
$($CI_PROJECT_DIR/build_scripts/GitlabCI/getRepoArgs.sh)
--build-arg ANDROID_PLATFORM_VER=21
--build-arg ANDROID_NDK_ARCH=arm64
--build-arg RS_SERVICE_QMAKE_EXTRA_OPTS="$RS_SERVICE_QMAKE_EXTRA_OPTS"
--file $CI_PROJECT_DIR/build_scripts/GitlabCI/Android.Dockerfile
Dockercontext
- >
docker cp
$(docker create --rm retroshare:android_arm64_latest):/retroshare-service-android-build/android-build/build/outputs/apk/debug/android-build-debug.apk
$CI_PROJECT_DIR/RetroShare_Android_Service-arm64.apk
artifacts:
paths:
- RetroShare_Android_Service-arm64.apk

8
.gitmodules vendored
View file

@ -10,7 +10,8 @@
[submodule "supportlibs/udp-discovery-cpp"]
path = supportlibs/udp-discovery-cpp
url = https://github.com/truvorskameikin/udp-discovery-cpp.git
branch = develop
branch = master
# develop branch was removed we were using it at commit f3a3103a6c52e5707629e8d0a7e279a7758fe845
[submodule "supportlibs/rapidjson"]
path = supportlibs/rapidjson
url = https://github.com/Tencent/rapidjson.git
@ -30,5 +31,8 @@
branch = master
[submodule "libretroshare"]
path = libretroshare
url = ../libretroshare
url = https://github.com/RetroShare/libretroshare.git
branch = master
[submodule "retroshare-webui"]
path = retroshare-webui
url = https://github.com/RetroShare/RSNewWebUI.git

View file

@ -9,9 +9,10 @@ RetroShare provides file sharing, chat, messages, forums, channels and more.
.Build Status
|===============================================================================
|GNU/Linux, Android (via Gitlab CI) | image:https://gitlab.com/RetroShare/RetroShare/badges/master/pipeline.svg[link="https://gitlab.com/RetroShare/RetroShare/-/commits/master",title="pipeline status"]
|GNU/Linux (via Gitlab CI) | image:https://gitlab.com/RetroShare/RetroShare/badges/master/pipeline.svg[link="https://gitlab.com/RetroShare/RetroShare/-/commits/master",title="pipeline status"]
|Windows (via AppVeyor) | image:https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true[link="https://ci.appveyor.com/project/RetroShare58622/retroshare"]
|macOS, (via Travis CI) | image:https://app.travis-ci.com/RetroShare/RetroShare.svg?branch=master[link="https://app.travis-ci.com/github/RetroShare/RetroShare"]
|libretroshare GNU/Linux, Android (via Gitlab CI) | image:https://gitlab.com/RetroShare/libretroshare/badges/master/pipeline.svg[link="https://gitlab.com/RetroShare/libretroshare/-/commits/master",title="pipeline status"]
|===============================================================================
== Get the source

View file

@ -68,6 +68,13 @@ retroshare_plugins {
plugins.target = plugins
}
rs_webui {
SUBDIRS += retroshare-webui
retroshare-webui.file = retroshare-webui/webui.pro
retroshare-webui.target = rs_webui
retroshare-webui.depends = retroshare_gui
}
wikipoos {
SUBDIRS += pegmarkdown
pegmarkdown.file = supportlibs/pegmarkdown/pegmarkdown.pro

View file

@ -258,10 +258,10 @@ after_build:
# artifacts configuration #
#---------------------------------#
#artifacts:
# - path: '**\*.exe'
# - path: filelist.txt
# - path: $(RS_DEPLOY)
artifacts:
- path: '**\*.exe'
- path: filelist.txt
- path: $(RS_DEPLOY)
#
# # pushing a single file

View file

@ -12,16 +12,26 @@
## To run the container
# docker run -it -p 127.0.0.1:9092:9092 "${CI_REGISTRY_IMAGE}" retroshare-service --jsonApiPort 9092 --jsonApiBindAddress 0.0.0.0
FROM ubuntu
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
ENV APT_UNAT="--assume-yes --quiet"
RUN apt-get update -y && apt-get upgrade -y -qq && \
apt-get install -y -qq build-essential cimg-dev libssl-dev libbz2-dev \
libsqlite3-dev \
libsqlcipher-dev libupnp-dev pkg-config libz-dev \
qt5-default libxapian-dev qttools5-dev doxygen rapidjson-dev \
git cmake curl
RUN apt-get update $APT_UNAT && \
apt-get upgrade --show-upgraded $APT_UNAT && \
apt-get clean $APT_UNAT && \
apt-get install --no-install-recommends $APT_UNAT \
bash build-essential cimg-dev libssl-dev libbz2-dev \
libminiupnpc-dev \
libsqlite3-dev libsqlcipher-dev \
pkg-config libz-dev \
libxapian-dev doxygen rapidjson-dev \
git cmake curl python3
## Avoid git cloning spuriously failing with
# server certificate verification failed. CAfile: none CRLfile: none
RUN apt-get install --no-install-recommends $APT_UNAT --reinstall \
ca-certificates
RUN git clone --depth 1 https://github.com/aetilius/pHash.git && \
rm -rf pHash-build && mkdir pHash-build && cd pHash-build && \
@ -30,15 +40,23 @@ RUN git clone --depth 1 https://github.com/aetilius/pHash.git && \
rm -rf pHash-build pHash
ARG FRESHCLONE=0
ARG REPO_URL=https://gitlab.com/RetroShare/RetroShare.git
ARG REPO_URL=https://github.com/RetroShare/RetroShare.git
ARG REPO_BRANCH=master
ARG REPO_DEPTH="--depth 2000"
RUN git clone $REPO_DEPTH $REPO_URL -b $REPO_BRANCH && cd RetroShare && \
git fetch --tags && cd ..
RUN mkdir RetroShare-build && cd RetroShare-build && \
qmake ../RetroShare \
CONFIG+=no_retroshare_plugins \
CONFIG+=retroshare_service CONFIG+=no_retroshare_gui \
CONFIG+=rs_jsonapi CONFIG+=rs_deep_search && \
(make -j$(nproc) || make -j$(nproc) || make) && make install && \
RUN git clone $REPO_DEPTH $REPO_URL -b $REPO_BRANCH && \
cd RetroShare && \
git fetch --tags && \
git submodule update --init \
libbitdht/ libretroshare/ openpgpsdk/ retroshare-webui/ \
supportlibs/restbed/ && \
cd supportlibs/restbed/ && \
git submodule update --init \
dependency/asio/ dependency/kashmir/ && \
cd ../../../
RUN \
mkdir RetroShare-build && cd RetroShare-build && \
cmake -B. -S../RetroShare/retroshare-service \
-DRS_FORUM_DEEP_INDEX=ON -DRS_JSON_API=ON -DRS_WEBUI=ON && \
make -j$(nproc) && make install && \
cd .. && rm -rf RetroShare-build

View file

@ -2,17 +2,19 @@ FROM registry.gitlab.com/retroshare/retroshare:base
RUN apt-get update -y && apt-get upgrade -y
ARG REPO_URL=https://gitlab.com/RetroShare/RetroShare.git
ARG REPO_URL=https://github.com/RetroShare/RetroShare.git
ARG REPO_BRANCH=master
RUN \
cd RetroShare && git remote add testing $REPO_URL && \
git fetch --tags testing $REPO_BRANCH && \
git reset --hard testing/$REPO_BRANCH && \
git submodule update --init \
libbitdht/ libretroshare/ openpgpsdk/ retroshare-webui/ && \
git --no-pager log --max-count 1
RUN \
mkdir RetroShare-build && cd RetroShare-build && \
qmake ../RetroShare CONFIG+=no_retroshare_gui \
CONFIG+=retroshare_service \
CONFIG+=rs_jsonapi CONFIG+=rs_deep_search && \
(make -j$(nproc) || make -j$(nproc) || make) && make install && \
cmake -B. -S../RetroShare/retroshare-service \
-DRS_FORUM_DEEP_INDEX=ON -DRS_JSON_API=ON -DRS_WEBUI=ON \
-DRS_WARN_DEPRECATED=OFF -DRS_WARN_LESS=ON && \
make -j$(nproc) && make install && \
cd .. && rm -rf RetroShare-build

@ -1 +1 @@
Subproject commit df16cb915465d058c75277678799ce4dadeae287
Subproject commit 0a3997cc1355b2c848161dca015b7e2df039707b

View file

@ -30,32 +30,22 @@ In GitHub Desktop -> Clone Repository -> URL
Add Repository URL: https://github.com/RetroShare/RetroShare.git and Clone
## ***Choose if you use MacPort or HomeBrew***
## ***Get XCode & MacOSX SDK***
### MacPort Installation
Install XCode following this guide: [XCode](http://guide.macports.org/#installing.xcode)
Install MacPort and XCode following this guide: [MacPort and XCode](http://guide.macports.org/#installing.xcode)
To identify the correct version of Xcode to install, you need to know which OS you are running. Go to the [x] menu -> "About This Mac" and read the macOS version number.
Start XCode to get it updated and to able C compiler to create executables.
If you are running the macOS Catalina >= 10.15, you can install Xcode directly from App Store using the instructions below.
#### Install libraries
You can find older versions of Xcode at [Apple Developer Downloads](https://developer.apple.com/downloads/). Find the appropriate .xip file for your macOS version
$ sudo port -v selfupdate
$ sudo port install openssl
$ sudo port install miniupnpc
$ sudo port install libmicrohttpd
To install from App Store:
For VOIP Plugin:
Select [x] menu - > "App Store…".
Search for Xcode. Download and install.
$ sudo port install speex-devel
$ sudo port install opencv
$ sudo port install ffmpeg
Get Your OSX SDK if missing: [MacOSX-SDKs](https://github.com/phracker/MacOSX-SDKs)
### HOMEBREW Installation
Install HomeBrew following this guide: [HomeBrew](http://brew.sh/)
Once Xcode has installed, you must drag the XCode icon into your Applications folder. After you have done this, open Xcode from the Applications folder by double-clicking on the icon and then follow the remaining instructions below.
Install XCode command line developer tools:
@ -63,14 +53,42 @@ Install XCode command line developer tools:
Start XCode to get it updated and to able C compiler to create executables.
Get Your MacOSX SDK if missing: [MacOSX-SDKs](https://github.com/phracker/MacOSX-SDKs)
## ***Choose if you use MacPort or HomeBrew***
### MacPort Installation
Install MacPort following this guide: [MacPort](http://guide.macports.org/#installing.xcode)
#### Install libraries
$ sudo port -v selfupdate
$ sudo port install openssl
$ sudo port install miniupnpc
For VOIP Plugin:
$ sudo port install speex-devel
$ sudo port install opencv
$ sudo port install ffmpeg
### HOMEBREW Installation
Install HomeBrew following this guide: [HomeBrew](http://brew.sh/)
#### Install libraries
$ brew install openssl
$ brew install miniupnpc
$ brew install libmicrohttpd
$ brew install rapidjson
$ brew install sqlcipher
#### Install CMake
$ brew install cmake
If you have error in linking, run this:
$sudo chown -R $(whoami) /usr/local/lib/pkgconfig
@ -85,8 +103,7 @@ For VOIP Plugin:
For FeedReader Plugin:
$ brew install libxslt
Get Your OSX SDK if missing: [MacOSX-SDKs](https://github.com/phracker/MacOSX-SDKs)
$ brew install libxml2
## Last Settings
@ -104,11 +121,8 @@ In QtCreator Projects -> Build -> Build Settings -> Build Steps -> Add Additiona
## Set your Mac OS SDK version
Edit RetroShare.pro
CONFIG += c++14 rs_macos11.1
and then retroshare.pri
Edit retroshare.pri and set your installed sdk version example for 11.1 -> rs_macos11.1 (line 135:)
macx:CONFIG *= rs_macos11.1
rs_macos10.8:CONFIG -= rs_macos11.1
@ -122,7 +136,7 @@ and then retroshare.pri
## Link Include & Libraries
Edit your retroshare.pri and add to macx-* section
When required edit your retroshare.pri macx-* section, check if the Include and Lib path are correct (macx-* section)
INCLUDEPATH += "/usr/local/opt/openssl/include"
QMAKE_LIBDIR += "/usr/local/opt/openssl/lib"
@ -131,13 +145,21 @@ Edit your retroshare.pri and add to macx-* section
alternative via Terminal
$ qmake INCLUDEPATH+="/usr/local/opt/openssl/include" QMAKE_LIBDIR+="/usr/local/opt/openssl/lib" QMAKE_LIBDIR+="/usr/local/opt/sqlcipher/lib" QMAKE_LIBDIR+="/usr/local/opt/miniupnpc/lib"
$ qmake
INCLUDEPATH+="/usr/local/opt/openssl/include" \
QMAKE_LIBDIR+="/usr/local/opt/openssl/lib" \
QMAKE_LIBDIR+="/usr/local/opt/sqlcipher/lib" \
QMAKE_LIBDIR+="/usr/local/opt/miniupnpc/lib" \
CONFIG+=rs_autologin \
CONFIG+=rs_use_native_dialogs \
CONFIG+=release \
..
For FeedReader Plugin:
INCLUDEPATH += "/usr/local/opt/libxml2/include/libxml2"
For building RetroShare with plugins:
With plugins:
$ qmake \
INCLUDEPATH+="/usr/local/opt/openssl/include" QMAKE_LIBDIR+="/usr/local/opt/openssl/lib" \
@ -159,13 +181,30 @@ For building RetroShare with plugins:
You can now compile RetroShare into Qt Creator or with Terminal
cd retroshare
qmake; make
$ cd /path/to/retroshare
$ qmake ..
$ make
You can change Target and SDK in *./retroshare.pri:82* changing value of QMAKE_MACOSX_DEPLOYMENT_TARGET and QMAKE_MAC_SDK
You can find the compiled application at *./retroshare/retroshare-gui/src/retroshare.app*
## Issues
If you have issues with openssl (Undefined symbols for architecture x86_64) try to add to *~/.profile* file this or via Terminal
export PATH="/usr/local/opt/openssl/bin:$PATH"
export LDFLAGS="-L/usr/local/opt/openssl/lib"
export CPPFLAGS="-I/usr/local/opt/openssl/include"
export PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig"
For Qt Creator -> QtCreator Projects -> Build -> Build Settings -> Build Steps -> Add Additional arguments:
LDFLAGS="-L/usr/local/opt/openssl/lib"
CPPFLAGS="-I/usr/local/opt/openssl/include"
## Copy Plugins
$ cp \
@ -173,3 +212,13 @@ You can find the compiled application at *./retroshare/retroshare-gui/src/retros
./plugins/VOIP/lib/libVOIP.dylib \
./plugins/RetroChess/lib/libRetroChess.dylib \
./retroshare-gui/src/RetroShare.app/Contents/Resources/
### Compile Retroshare-Service & Webui with CMake
before you can compile overwrite the file "asio/include/asio/detail/config.hpp" here is a fix for macos [
asio fix](https://github.com/chriskohlhoff/asio/commit/68df16d560c68944809bb2947360fe8035e9ae0a)
$ cd retroshare-service
$ mkdir build-dir
$ cd build-dir
$ cmake -DRS_WEBUI=ON -DCMAKE_BUILD_TYPE=Release ..
$ make

View file

@ -0,0 +1,50 @@
#!/bin/sh
APP="RetroShare"
RSVERSION="0.6.7a"
QTVERSION="Qt-5.14.1"
# Install the 7z to create dmg archives.
#brew list p7zip || brew install p7zip
# Package your app
echo "Packaging retroshare..."
#cd ${project_dir}/build/macOS/clang/x86_64/release/
cd retroshare-gui/src/
# Remove build directories that you don't want to deploy
rm -rf moc
rm -rf obj
rm -rf qrc
# This sets the CFBundleVersion & CFBundleShortVersionString string
/usr/libexec/PlistBuddy -c "Delete :CFBundleGetInfoString" retroshare.app/Contents/Info.plist
/usr/libexec/PlistBuddy -c "Add :CFBundleVersion string $RSVERSION" retroshare.app/Contents/Info.plist
/usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string $RSVERSION" retroshare.app/Contents/Info.plist
# This automatically creates retroshare.dmg
echo "Creating dmg archive..."
macdeployqt retroshare.app -dmg
DATE=`date +"%m-%d-%Y"`
MACVERSION=`sw_vers -productVersion`
#RSVERSION=`git describe --abbrev=0 --tags`
GITHEAD=`git rev-parse --short HEAD`
mv $APP.dmg "$APP-$RSVERSION-$GITHEAD-$DATE-MacOS-$MACVERSION-$QTVERSION.dmg"
# You can use the appdmg command line app to create your dmg file if
# you want to use a custom background and icon arrangement. I'm still
# working on this for my apps, myself. If you want to do this, you'll
# remove the -dmg option above.
# appdmg json-path YourApp_${TRAVIS_TAG}.dmg
# Copy other project files
cp "../../libbitdht/src/bitdht/bdboot.txt" "retroshare.app/Contents/Resources/"
cp "../../plugins/FeedReader/lib/libFeedReader.dylib" "retroshare.app/Contents/Resources/"
cp -R "sounds" "retroshare.app/Contents/Resources/sounds"
# cp "${project_dir}/README.md" "README.md"
# cp "${project_dir}/LICENSE" "LICENSE"
# cp "${project_dir}/Qt License" "Qt License"

View file

@ -32,6 +32,16 @@ if not "%ParamNoupdate%"=="1" (
if "%ParamIndexing%"=="1" %EnvMSYS2Cmd% "pacman --noconfirm --needed -S mingw-w64-%RsMSYS2Architecture%-xapian-core mingw-w64-%RsMSYS2Architecture%-libvorbis mingw-w64-%RsMSYS2Architecture%-flac mingw-w64-%RsMSYS2Architecture%-taglib"
)
:: Fix webui compilation (TODO: remove when whole RS switched to cmake)
if "%ParamWebui%"=="1" (
pushd "%SourcePath%"
copy "%SourcePath%\libretroshare\src\jsonapi\jsonapi-generator-doxygen.conf" "%SourcePath%\jsonapi-generator\src\jsonapi-generator-doxygen.conf" %Quite%
copy "%SourcePath%\libretroshare\src\jsonapi\async-method-wrapper-template.cpp.tmpl" "%SourcePath%\jsonapi-generator\src\async-method-wrapper-template.cpp.tmpl" %Quite%
copy "%SourcePath%\libretroshare\src\jsonapi\method-wrapper-template.cpp.tmpl" "%SourcePath%\jsonapi-generator\src\method-wrapper-template.cpp.tmpl" %Quite%
git update-index --assume-unchanged "jsonapi-generator\src\jsonapi-generator-doxygen.conf" "jsonapi-generator\src\async-method-wrapper-template.cpp.tmpl" "jsonapi-generator\src\method-wrapper-template.cpp.tmpl"
popd
)
:: Initialize environment
call "%~dp0env.bat" %*
if errorlevel 2 exit /B 2
@ -66,6 +76,7 @@ echo %RS_QMAKE_CONFIG% > buildinfo.txt
echo %RsBuildConfig% >> buildinfo.txt
echo %RsArchitecture% >> buildinfo.txt
echo Qt %QtVersion% >> buildinfo.txt
echo %RsToolchain% >> buildinfo.txt
echo %RsCompiler% >> buildinfo.txt
call "%ToolsPath%\msys2-path.bat" "%SourcePath%" MSYS2SourcePath
@ -86,11 +97,6 @@ title Build - %SourceName%-%RsBuildConfig% [make]
%EnvMSYS2Cmd% "make -j %CoreCount%"
if errorlevel 1 goto error
:: Webui
if "%ParamWebui%"=="1" (
call "%~dp0..\tools\webui.bat"
)
:error
popd

View file

@ -1,6 +1,4 @@
:: Process commandline parameter
set Param32=0
set Param64=0
set ParamRelease=0
set ParamDebug=0
set ParamAutologin=0
@ -9,17 +7,40 @@ set ParamTor=0
set ParamWebui=0
set ParamClang=0
set ParamIndexing=0
set ParamFriendserver=0
set ParamNoupdate=0
set CoreCount=%NUMBER_OF_PROCESSORS%
set RS_QMAKE_CONFIG=
set RsToolchain=
set tcc=0
:parameter_loop
if "%~1" NEQ "" (
for /f "tokens=1,2 delims==" %%a in ("%~1") do (
if "%%~a"=="32" (
set Param32=1
set RsToolchain=mingw32
set /A tcc=tcc+1
) else if "%%~a"=="64" (
set Param64=1
set RsToolchain=mingw64
set /A tcc=tcc+1
) else if "%%~a"=="mingw32" (
set RsToolchain=mingw32
set /A tcc=tcc+1
) else if "%%~a"=="mingw64" (
set RsToolchain=mingw64
set /A tcc=tcc+1
) else if "%%~a"=="ucrt64" (
set RsToolchain=ucrt64
set /A tcc=tcc+1
) else if "%%~a"=="clang64" (
set RsToolchain=clang64
set /A tcc=tcc+1
) else if "%%~a"=="clang32" (
set RsToolchain=clang32
set /A tcc=tcc+1
) else if "%%~a"=="clangarm64" (
set RsToolchain=clangarm64
set /A tcc=tcc+1
) else if "%%~a"=="release" (
set ParamRelease=1
) else if "%%~a"=="debug" (
@ -38,6 +59,8 @@ if "%~1" NEQ "" (
set ParamClang=1
) else if "%%~a"=="indexing" (
set ParamIndexing=1
) else if "%%~a"=="friendserver" (
set ParamFriendserver=1
) else if "%%~a"=="noupdate" (
set ParamNoupdate=1
) else if "%%~a"=="CONFIG+" (
@ -52,22 +75,37 @@ if "%~1" NEQ "" (
goto parameter_loop
)
if "%Param32%"=="1" (
if "%Param64%"=="1" (
echo.
echo 32-bit or 64-bit?
if %tcc% NEQ 1 (
echo Multiple or no toolchain specified
goto :usage
)
set RsBit=32
if "%RsToolchain%"=="mingw32" (
set RsArchitecture=x86
set RsMSYS2Architecture=i686
)
if "%Param64%"=="1" (
set RsBit=64
set MSYSTEM=MINGW32
) else if "%RsToolchain%"=="mingw64" (
set RsArchitecture=x64
set RsMSYS2Architecture=x86_64
set MSYSTEM=MINGW64
) else if "%RsToolchain%"=="ucrt64" (
set RsArchitecture=x64
set RsMSYS2Architecture=ucrt-x86_64
set MSYSTEM=UCRT64
) else if "%RsToolchain%"=="clang64" (
set RsArchitecture=x64
set RsMSYS2Architecture=clang-x86_64
set MSYSTEM=CLANG64
set ParamClang=1
) else if "%RsToolchain%"=="clang32" (
set RsArchitecture=x86
set RsMSYS2Architecture=clang-i686
set MSYSTEM=CLANG32
set ParamClang=1
) else if "%RsToolchain%"=="clangarm64" (
set RsArchitecture=arm64
set RsMSYS2Architecture=clang-aarch64
set MSYSTEM=CLANGARM64
)
if "%ParamClang%"=="1" (
@ -76,8 +114,6 @@ if "%ParamClang%"=="1" (
set RsCompiler=GCC
)
if "%RsBit%"=="" goto :usage
if "%ParamRelease%"=="1" (
if "%ParamDebug%"=="1" (
echo.
@ -104,14 +140,21 @@ if "%ParamIndexing%"=="1" (
set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% "CONFIG+=rs_deep_channels_index" "CONFIG+=rs_deep_files_index" "CONFIG+=rs_deep_files_index_ogg" "CONFIG+=rs_deep_files_index_flac" "CONFIG+=rs_deep_files_index_taglib"
)
if "%ParamFriendserver%"=="1" (
set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% "CONFIG+=rs_efs"
)
exit /B 0
:usage
echo.
echo Usage: 32^|64 release^|debug [autologin plugins webui singlethread clang indexing noupdate] ["CONFIG+=..."]
echo Usage: 32^|64^|other release^|debug [autologin plugins webui singlethread clang indexing friendserver noupdate] ["CONFIG+=..."]
echo.
echo Mandatory parameter
echo 32^|64 32-bit or 64-bit Version
echo 32^|64 32-bit or 64-bit version (same as mingw32 or mingw64)
echo Or you can specify any other toolchain supported by msys2:
echo mingw32^|mingw64^|clang32^|clang64^|ucrt64^|clangarm64
echo More info: https://www.msys2.org/docs/environments
echo release^|debug Build release or debug version
echo.
echo Optional parameter (need clean when changed)
@ -121,6 +164,7 @@ echo webui Enable JsonAPI and pack webui files
echo singlethread Use only 1 thread for building
echo clang Use clang compiler instead of GCC
echo indexing Build with deep channel and file indexing support
echo friendserver Enable friendserver support
echo noupdate Skip updating the libraries
echo "CONFIG+=..." Enable some extra features, you can find the almost complete list in retroshare.pri
echo.

View file

@ -2,8 +2,6 @@ call "%~dp0env-base.bat" %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
set MSYSTEM=MINGW%RsBit%
set BuildPath=%EnvRootPath%\builds
set DeployPath=%EnvRootPath%\deploy
@ -14,13 +12,13 @@ if not exist "%DeployPath%" mkdir "%DeployPath%"
call "%ToolsPath%\get-qt-version.bat" QtVersion
if "%QtVersion%"=="" %cecho% error "Cannot get Qt version." & exit /B 1
set RsMinGWPath=%EnvMSYS2BasePath%\mingw%RsBit%
set RsMinGWPath=%EnvMSYS2BasePath%\%RsToolchain%
set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%RsArchitecture%-%RsCompiler%-%RsBuildConfig%
set RsDeployPath=%DeployPath%\Qt-%QtVersion%%RsType%-%RsArchitecture%-%RsCompiler%-%RsBuildConfig%
set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%RsToolchain%-%RsCompiler%-%RsBuildConfig%
set RsDeployPath=%DeployPath%\Qt-%QtVersion%%RsType%-%RsToolchain%-%RsCompiler%-%RsBuildConfig%
set RsPackPath=%DeployPath%
set RsArchiveAdd=
set RsWebuiPath=%RootPath%\%SourceName%-webui
set RsWebuiBuildPath=%RsBuildPath%\retroshare-webui\webui
if not exist "%~dp0env-mod.bat" goto no_mod
call "%~dp0env-mod.bat"

View file

@ -105,6 +105,9 @@ copy "%RsBuildPath%\retroshare-nogui\src\%RsBuildConfig%\retroshare*-nogui.exe"
copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite%
copy "%RsBuildPath%\supportlibs\cmark\build\src\libcmark.dll" "%RsDeployPath%" %Quite%
if exist "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" copy "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" "%RsDeployPath%" %Quite%
if exist "%RsBuildPath%\retroshare-friendserver\src\%RsBuildConfig%\retroshare-friendserver.exe" (
copy "%RsBuildPath%\retroshare-friendserver\src\%RsBuildConfig%\retroshare-friendserver.exe" "%RsDeployPath%" %Quite%
)
echo copy extensions
for /D %%D in ("%RsBuildPath%\plugins\*") do (
@ -184,12 +187,12 @@ echo copy buildinfo.txt
copy "%RsBuildPath%\buildinfo.txt" "%RsDeployPath%" %Quite%
if "%ParamWebui%"=="1" (
if exist "%RsWebuiPath%\webui" (
if exist "%RsWebuiBuildPath%" (
echo copy webui
mkdir "%RsDeployPath%\webui"
xcopy /S "%RsWebuiPath%\webui" "%RsDeployPath%\webui" %Quite%
xcopy /S "%RsWebuiBuildPath%" "%RsDeployPath%\webui" %Quite%
) else (
%cecho% error "Webui is enabled, but no webui data found at %RsWebuiPath%\webui"
%cecho% error "Webui is enabled, but no webui data found at %RsWebuiBuildPath%"
goto error
)
)

View file

@ -16,7 +16,13 @@ if "%~1"=="clean" (
goto exit
)
if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
set MSYS2Version=20231026
set MSYS2Install=msys2-base-x86_64-%MSYS2Version%.sfx.exe
set MSYS2Url=https://github.com/msys2/msys2-installer/releases/download/%MSYS2Version:~0,4%-%MSYS2Version:~4,2%-%MSYS2Version:~6,2%/%MSYS2Install%
set MSYS2UnpackPath=%EnvMSYS2Path%\msys64
if exist "%MSYS2UnpackPath%\usr\bin\pacman.exe" (
if "%~1"=="reinstall" (
choice /M "Found existing MSYS2 version. Do you want to proceed?"
if !ERRORLEVEL!==2 goto exit
@ -25,29 +31,26 @@ if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
)
)
if "%MSYS2Architecture%"=="i686" set MSYS2Version=20210705
if "%MSYS2Architecture%"=="x86_64" set MSYS2Version=20210725
set MSYS2Install=msys2-base-%MSYS2Architecture%-%MSYS2Version%.tar.xz
set MSYS2Url=https://repo.msys2.org/distrib/%MSYS2Architecture%/%MSYS2Install%
if exist "%MSYS2UnpackPath%" (
%cecho% info "Remove previous MSYS2 version"
call "%ToolsPath%\remove-dir.bat" "%EnvMSYS2Path%"
call "%ToolsPath%\remove-dir.bat" "%MSYS2UnpackPath%"
)
%cecho% info "Download installation files"
%cecho% info "Download MSYS2 installation files"
if not exist "%EnvDownloadPath%\%MSYS2Install%" call "%ToolsPath%\download-file.bat" "%MSYS2Url%" "%EnvDownloadPath%\%MSYS2Install%"
if not exist "%EnvDownloadPath%\%MSYS2Install%" %cecho% error "Cannot download MSYS" & goto error
%cecho% info "Unpack MSYS2"
"%EnvSevenZipExe%" x -so "%EnvDownloadPath%\%MSYS2Install%" | "%EnvSevenZipExe%" x -y -si -ttar -o"%EnvMSYS2Path%"
"%EnvDownloadPath%\%MSYS2Install%" -y -o"%EnvMSYS2Path%"
set MSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh
set MSYS2SH=%MSYS2UnpackPath%\usr\bin\sh
%cecho% info "Initialize MSYS2"
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
"%MSYS2SH%" -lc "pacman --noconfirm -Sy"
"%MSYS2SH%" -lc "pacman --noconfirm -Su"
call "%EnvMSYS2Path%\msys%MSYS2Base%\autorebase.bat"
call "%MSYS2UnpackPath%\autorebase.bat"
:exit
endlocal

View file

@ -34,7 +34,7 @@ if not exist "%EnvToolsPath%\cecho.exe" (
if not exist "%EnvDownloadPath%\%cCEhoInstall%" echo Cannot download cecho installation& goto error
echo Unpack cecho
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%" -y -bso0
copy "%EnvTempPath%\cecho.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"

View file

@ -2,18 +2,13 @@
setlocal
if exist "%~dp0msys2\msys32" call :update 32
if exist "%~dp0msys2\msys64" call :update 64
if not exist "%~dp0msys2\msys64" goto :EOF
goto :EOF
set MSYS2SH=%~dp0msys2\msys64\usr\bin\sh
:update
set MSYS2SH=%~dp0msys2\msys%~1\usr\bin\sh
echo Update MSYS2 %~1
echo Update MSYS2
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
"%MSYS2SH%" -lc "pacman --noconfirm -Su"
:exit
endlocal
goto :EOF

View file

@ -1,22 +0,0 @@
setlocal
echo.
echo === webui
echo.
title Build webui
if not exist "%RsWebuiPath%" (
echo Checking out webui source into %RsWebuiPath%
if not "%ParamNoupdate%"=="1" (
%EnvMSYS2Cmd% "pacman --noconfirm --needed -S git"
)
git clone https://github.com/RetroShare/RSNewWebUI.git "%RsWebuiPath%"
) else (
echo Webui source found at %RsWebuiPath%
)
pushd "%RsWebuiPath%\webui-src\make-src"
%EnvMSYS2Cmd% "sh build.sh"
popd
endlocal

View file

@ -1,18 +1,18 @@
ZLIB_VERSION=1.2.3
BZIP2_VERSION=1.0.6
MINIUPNPC_VERSION=2.0
OPENSSL_VERSION=1.1.1h
ZLIB_VERSION=1.2.11
BZIP2_VERSION=1.0.8
MINIUPNPC_VERSION=2.2.3
OPENSSL_VERSION=1.1.1w
SPEEX_VERSION=1.2.0
SPEEXDSP_VERSION=1.2rc3
LIBXML2_VERSION=2.9.7
LIBXSLT_VERSION=1.1.32
CURL_VERSION=7.58.0
SPEEXDSP_VERSION=1.2.0
LIBXML2_VERSION=2.9.12
LIBXSLT_VERSION=1.1.34
CURL_VERSION=7.81.0
TCL_VERSION=8.6.10
SQLCIPHER_VERSION=4.4.0
LIBMICROHTTPD_VERSION=0.9.59
FFMPEG_VERSION=3.4
SQLCIPHER_VERSION=4.5.0
LIBMICROHTTPD_VERSION=0.9.75
FFMPEG_VERSION=4.4
RAPIDJSON_VERSION=1.1.0
XAPIAN_VERSION=1.4.7
XAPIAN_VERSION=1.4.19
DOWNLOAD_PATH?=download
BUILD_PATH=build
@ -56,8 +56,7 @@ $(BUILD_PATH)/zlib-$(ZLIB_VERSION): $(DOWNLOAD_PATH)/zlib-$(ZLIB_VERSION).tar.gz
rm -r -f $(BUILD_PATH)/zlib-*
tar xvf $(DOWNLOAD_PATH)/zlib-$(ZLIB_VERSION).tar.gz
# build
cd zlib-$(ZLIB_VERSION) && ./configure
#cd zlib-$(ZLIB_VERSION) && make install prefix="`pwd`/../$(BUILD_PATH)"
cd zlib-$(ZLIB_VERSION) && make -f win32/Makefile.gcc libz.a
cd zlib-$(ZLIB_VERSION) && make
# copy files
mkdir -p $(BUILD_PATH)/zlib-$(ZLIB_VERSION).tmp/include
@ -72,7 +71,7 @@ $(BUILD_PATH)/zlib-$(ZLIB_VERSION): $(DOWNLOAD_PATH)/zlib-$(ZLIB_VERSION).tar.gz
bzip2: $(BUILD_PATH)/bzip2-$(BZIP2_VERSION)
$(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz:
wget https://sourceforge.net/projects/bzip2/files/bzip2-$(BZIP2_VERSION).tar.gz/download -O $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz
wget https://www.sourceware.org/pub/bzip2/bzip2-$(BZIP2_VERSION).tar.gz -O $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz
$(BUILD_PATH)/bzip2-$(BZIP2_VERSION): $(DOWNLOAD_PATH)/bzip2-$(BZIP2_VERSION).tar.gz
# prepare
@ -100,10 +99,10 @@ $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION): $(DOWNLOAD_PATH)/miniupnpc-$(MINIU
rm -r -f $(BUILD_PATH)/miniupnpc-*
tar xvf $(DOWNLOAD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tar.gz
# build
cd miniupnpc-$(MINIUPNPC_VERSION) && export CC=gcc && export PATH=.:$$PATH && make -f Makefile.mingw init libminiupnpc.a miniupnpc.dll
cd miniupnpc-$(MINIUPNPC_VERSION) && export CC=gcc && export PATH=.:$$PATH && make -f Makefile.mingw libminiupnpc.a miniupnpc.dll
# copy files
mkdir -p $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/include/miniupnpc
cp miniupnpc-$(MINIUPNPC_VERSION)/*.h $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/include/miniupnpc/
cp miniupnpc-$(MINIUPNPC_VERSION)/include/*.h $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/include/miniupnpc/
mkdir -p $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/lib
cp miniupnpc-$(MINIUPNPC_VERSION)/miniupnpc.lib $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/lib/
mkdir -p $(BUILD_PATH)/miniupnpc-$(MINIUPNPC_VERSION).tmp/bin
@ -314,7 +313,7 @@ $(BUILD_PATH)/ffmpeg-$(FFMPEG_VERSION): $(DOWNLOAD_PATH)/ffmpeg-$(FFMPEG_VERSION
rm -r -f $(BUILD_PATH)/ffmpeg-*
tar xvf $(DOWNLOAD_PATH)/ffmpeg-$(FFMPEG_VERSION).tar.gz
# build
cd ffmpeg-$(FFMPEG_VERSION) && ./configure --disable-shared --enable-static --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-ffserver --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages --disable-yasm --disable-everything --enable-encoder=mpeg4 --enable-decoder=mpeg4 --prefix="`pwd`/../$(BUILD_PATH)/ffmpeg-$(FFMPEG_VERSION).tmp"
cd ffmpeg-$(FFMPEG_VERSION) && ./configure --disable-shared --enable-static --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages --disable-yasm --disable-everything --enable-encoder=mpeg4 --enable-decoder=mpeg4 --prefix="`pwd`/../$(BUILD_PATH)/ffmpeg-$(FFMPEG_VERSION).tmp"
cd ffmpeg-$(FFMPEG_VERSION) && make install
# cleanup
rm -r -f ffmpeg-$(FFMPEG_VERSION)

View file

@ -13,11 +13,11 @@ call "%~dp0build-libs\build-libs.bat"
if errorlevel 1 %cecho% error "Failed to build libraries." & exit /B %ERRORLEVEL%
%cecho% info "Build %SourceName%"
call "%~dp0build\build.bat" release autologin jsonapi plugins nativedialogs
call "%~dp0build\build.bat" release autologin webui plugins nativedialogs service
if errorlevel 1 %cecho% error "Failed to build %SourceName%." & exit /B %ERRORLEVEL%
%cecho% info "Pack %SourceName%"
call "%~dp0build\pack.bat" release plugins
call "%~dp0build\pack.bat" release webui plugins service
if errorlevel 1 %cecho% error "Failed to pack %SourceName%." & exit /B %ERRORLEVEL%
%cecho% info "Build installer"

View file

@ -9,7 +9,7 @@ call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
:: Initialize environment
call "%~dp0env.bat" release
call "%~dp0env.bat" installer release
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@ -40,6 +40,7 @@ set NSIS_PARAM=%NSIS_PARAM% /DARCHITECTURE="%GCCArchitecture%"
set NSIS_PARAM=%NSIS_PARAM% /DDATE="%RsDate%"
if exist "%EnvTorPath%\Tor\tor.exe" set NSIS_PARAM=%NSIS_PARAM% /DTORDIR="%EnvTorPath%\Tor"
if exist "%RsWebuiBuildPath%" set NSIS_PARAM=%NSIS_PARAM% /DWEBUIDIR="%RsWebuiBuildPath%"
:: Get compiled version
call "%ToolsPath%\get-rs-version.bat" "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare.exe" RsVersion

View file

@ -9,7 +9,7 @@ call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
:: Initialize environment
call "%~dp0env.bat" %*
call "%~dp0env.bat" build %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@ -49,11 +49,15 @@ echo.
title Build - %SourceName%-%RsBuildConfig% [qmake]
set RS_QMAKE_CONFIG=%RsBuildConfig% no_rs_cppwarning
set RS_QMAKE_CONFIG=%RsBuildConfig%
if "%ParamAutologin%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_autologin
if "%ParamJsonApi%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_jsonapi
if "%ParamWebui%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_webui
if "%ParamPlugins%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% retroshare_plugins
if "%ParamUseNativeDialogs%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_use_native_dialogs
if "%ParamService%" NEQ "1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% no_retroshare_service
if "%ParamFriendServer%" NEQ "1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% no_retroshare_friendserver
if "%ParamEmbeddedFriendServer%"=="1" set RS_QMAKE_CONFIG=%RS_QMAKE_CONFIG% rs_efs
qmake "%SourcePath%\RetroShare.pro" -r -spec win32-g++ "CONFIG+=%RS_QMAKE_CONFIG%" "EXTERNAL_LIB_DIR=%BuildLibsPath%\libs"
if errorlevel 1 goto error

View file

@ -8,7 +8,7 @@ if errorlevel 1 goto error_env
call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
call "%~dp0env.bat" %*
call "%~dp0env.bat" clean %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env

View file

@ -4,11 +4,18 @@ set ParamDebug=0
set ParamAutologin=0
set ParamPlugins=0
set ParamJsonApi=0
set ParamWebui=0
set ParamService=0
set ParamFriendServer=0
set ParamEmbeddedFriendServer=0
set ParamUseNativeDialogs=0
set ParamTor=0
set NonInteractive=0
set CoreCount=%NUMBER_OF_PROCESSORS%
set Module=%~1
shift /1
:parameter_loop
if "%~1" NEQ "" (
for /f "tokens=1,2 delims==" %%a in ("%~1") do (
@ -20,6 +27,15 @@ if "%~1" NEQ "" (
set ParamAutologin=1
) else if "%%~a"=="jsonapi" (
set ParamJsonApi=1
) else if "%%~a"=="webui" (
set ParamJsonApi=1
set ParamWebui=1
) else if "%%~a"=="service" (
set ParamService=1
) else if "%%~a"=="friendserver" (
set ParamFriendServer=1
) else if "%%~a"=="embedded-friendserver" (
set ParamEmbeddedFriendServer=1
) else if "%%~a"=="plugins" (
set ParamPlugins=1
) else if "%%~a"=="tor" (
@ -32,7 +48,7 @@ if "%~1" NEQ "" (
set ParamUseNativeDialogs=1
) else (
echo.
echo Unknown parameter %1
echo Unknown parameter %1 for %Module%
goto :usage
)
)
@ -89,6 +105,7 @@ set RsBuildPath=%BuildPath%\Qt-%QtVersion%-%GCCArchitecture%-%RsBuildConfig%
set RsDeployPath=%DeployPath%\Qt-%QtVersion%-%GCCArchitecture%%RsType%-%RsBuildConfig%
set RsPackPath=%DeployPath%
set RsArchiveAdd=
set RsWebuiBuildPath=%RsBuildPath%\retroshare-webui\webui
if not exist "%~dp0env-mod.bat" goto no_mod
call "%~dp0env-mod.bat"
@ -104,19 +121,31 @@ echo.
echo Mandatory parameter
echo release^|debug Build release or debug version
echo.
echo Optional parameter (need clean when changed)
if "%Module%"=="build" (
echo Optional parameter ^(need clean when changed^)
echo autologin Build with autologin
echo jsonapi Build with jsonapi
echo webui Build with jsonapi and webui
echo service Build service
echo friendserver Build Friend Server
echo embedded-friendserver Build with embedded Friend Server
echo plugins Build plugins
echo nativedialogs Build with native dialogs
echo.
echo Optional parameter
echo singlethread Use only 1 thread for building
echo.
echo Parameter for pack
echo tor Pack tor version
echo.
echo Parameter for git-log
)
if "%Module%"=="pack" (
echo Optional parameter
echo webui Pack webui
echo service Pack service
echo friendserver Pack Friend Server ^(needs Tor^)
echo tor Pack Tor version
echo plugins Pack plugins
)
if "%Module%"=="git-log" (
echo Optional parameter
echo non-interactive Non-interactive mode
)
echo.
exit /B 2

View file

@ -8,7 +8,7 @@ if errorlevel 1 goto error_env
call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
call "%~dp0env.bat" %*
call "%~dp0env.bat" git-log %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@ -88,6 +88,10 @@ echo %RsRef%>"%RsLastRefFile%"
exit /B %ERRORLEVEL%
:error
%cecho% error "\n%~n0 failed\n"
exit /B 1
:error_env
echo Failed to initialize environment.
endlocal

View file

@ -11,7 +11,7 @@ call "%EnvPath%\env.bat"
if errorlevel 1 goto error_env
:: Initialize environment
call "%~dp0env.bat" %*
call "%~dp0env.bat" pack %*
if errorlevel 2 exit /B 2
if errorlevel 1 goto error_env
@ -95,9 +95,23 @@ copy nul "%RsDeployPath%\portable" %Quite%
echo copy binaries
copy "%RsBuildPath%\retroshare-gui\src\%RsBuildConfig%\retroshare*.exe" "%RsDeployPath%" %Quite%
copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite%
if exist "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" copy "%RsBuildPath%\libretroshare\src\lib\retroshare.dll" "%RsDeployPath%" %Quite%
if "%ParamService%"=="1" (
copy "%RsBuildPath%\retroshare-service\src\%RsBuildConfig%\retroshare*-service.exe" "%RsDeployPath%" %Quite%
if errorlevel 1 %cecho% error "Service not found"& goto error
)
if "%ParamFriendServer%"=="1" (
if "%ParamTor%"=="1" (
copy "%RsBuildPath%\retroshare-friendserver\src\%RsBuildConfig%\retroshare-friendserver.exe" "%RsDeployPath%" %Quite%
if errorlevel 1 %cecho% error "Friend Server not found"& goto error
) else (
%cecho% error "Friend Server needs Tor"
goto error
)
)
echo copy extensions
if "%ParamPlugins%"=="1" (
for /D %%D in ("%RsBuildPath%\plugins\*") do (
@ -167,10 +181,11 @@ copy "%SourcePath%\libbitdht\src\bitdht\bdboot.txt" "%RsDeployPath%" %Quite%
echo copy changelog.txt
copy "%RsBuildPath%\changelog.txt" "%RsDeployPath%" %Quite%
if exist "%SourcePath%\libresapi\src\webui" (
if defined ParamWebui (
echo copy webui
mkdir "%RsDeployPath%\webui"
xcopy /S "%SourcePath%\libresapi\src\webui" "%RsDeployPath%\webui" %Quite%
xcopy /S "%RsWebuiBuildPath%" "%RsDeployPath%\webui" %Quite%
if errorlevel 1 %cecho% error "WebUi not found"& goto error
)
if "%ParamTor%"=="1" (

View file

@ -5,7 +5,6 @@
call "%~dp0env.bat"
if errorlevel 1 goto error_env
rem openssl x86 doesn't compile with mingw64 x64
:: Get gcc versions
call "%ToolsPath%\get-gcc-version.bat" GCCVersion GCCArchitecture
if "%GCCVersion%"=="" %cecho% error "Cannot get gcc version." & exit /B 1
@ -23,11 +22,12 @@ if "%GCCArchitecture%"=="x64" (
)
set EnvMSYS2Path=%EnvRootPath%\msys2
set EnvMSYS2BasePath=%EnvMSYS2Path%\msys64
call "%~dp0tools\prepare-msys2.bat" %1
if errorlevel 1 exit /B %ERRORLEVEL%
set EnvMSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh.exe
set EnvMSYS2SH=%EnvMSYS2BasePath%\usr\bin\sh.exe
if not exist "%EnvMSYS2SH%" if errorlevel 1 goto error_env
set EnvMSYS2Cmd="%EnvMSYS2SH%" -lc

View file

@ -28,6 +28,9 @@ if errorlevel 1 exit /B %ERRORLEVEL%
set PATH=%EnvToolsPath%\MinGit\cmd;%EnvToolsPath%\cmake\bin;%PATH%
set HOME=%EnvToolsPath%\MinGit\home
:: Add Doxygen to PATH
set PATH=%EnvToolsPath%\doxygen;%PATH%
exit /B 0
:error_env

View file

@ -16,7 +16,15 @@ if "%~1"=="clean" (
goto exit
)
if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
set MSYS2Version=20231026
set MSYS2Install=msys2-base-x86_64-%MSYS2Version%.sfx.exe
set MSYS2Url=https://github.com/msys2/msys2-installer/releases/download/%MSYS2Version:~0,4%-%MSYS2Version:~4,2%-%MSYS2Version:~6,2%/%MSYS2Install%
set MSYS2UnpackPath=%EnvMSYS2Path%\msys64
set CMakeInstall=cmake-3.19.0-win32-x86.zip
set CMakeUrl=https://github.com/Kitware/CMake/releases/download/v3.19.0/%CMakeInstall%
if exist "%MSYS2UnpackPath%\usr\bin\pacman.exe" (
if "%~1"=="reinstall" (
choice /M "Found existing MSYS2 version. Do you want to proceed?"
if !ERRORLEVEL!==2 goto exit
@ -25,21 +33,12 @@ if exist "%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\pacman.exe" (
)
)
if "%MSYS2Architecture%"=="i686" set MSYS2Version=20210705
if "%MSYS2Architecture%"=="x86_64" set MSYS2Version=20210725
set MSYS2Install=msys2-base-%MSYS2Architecture%-%MSYS2Version%.tar.xz
set MSYS2Url=https://repo.msys2.org/distrib/%MSYS2Architecture%/%MSYS2Install%
set CMakeInstall=cmake-3.19.0-win32-x86.zip
set CMakeUrl=https://github.com/Kitware/CMake/releases/download/v3.19.0/%CMakeInstall%
set CMakeUnpackPath=%EnvMSYS2Path%\msys%MSYS2Base%
if exist "%EnvMSYS2Path%\msys%MSYS2Base%" (
if exist "%MSYS2UnpackPath%" (
%cecho% info "Remove previous MSYS2 version"
call "%ToolsPath%\remove-dir.bat" "%EnvMSYS2Path%\msys%MSYS2Base%"
call "%ToolsPath%\remove-dir.bat" "%MSYS2UnpackPath%"
)
%cecho% info "Download installation files"
%cecho% info "Download MSYS2 installation files"
if not exist "%EnvDownloadPath%\%MSYS2Install%" call "%ToolsPath%\download-file.bat" "%MSYS2Url%" "%EnvDownloadPath%\%MSYS2Install%"
if not exist "%EnvDownloadPath%\%MSYS2Install%" %cecho% error "Cannot download MSYS" & goto error
@ -47,31 +46,32 @@ if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\download-file.
if not exist "%EnvDownloadPath%\%CMakeInstall%" %cecho% error "Cannot download CMake" & goto error
%cecho% info "Unpack MSYS2"
"%EnvSevenZipExe%" x -so "%EnvDownloadPath%\%MSYS2Install%" | "%EnvSevenZipExe%" x -y -si -ttar -o"%EnvMSYS2Path%"
"%EnvDownloadPath%\%MSYS2Install%" -y -o"%EnvMSYS2Path%"
%cecho% info "Unpack CMake"
"%EnvSevenZipExe%" x -o"%CMakeUnpackPath%" "%EnvDownloadPath%\%CMakeInstall%"
"%EnvSevenZipExe%" x -o"%MSYS2UnpackPath%" "%EnvDownloadPath%\%CMakeInstall%" -y -bso0
%cecho% info "Install CMake"
set CMakeVersion=
for /D %%F in (%CMakeUnpackPath%\cmake*) do set CMakeVersion=%%~nxF
for /D %%F in (%MSYS2UnpackPath%\cmake*) do set CMakeVersion=%%~nxF
if "%CMakeVersion%"=="" %cecho% error "CMake version not found." & goto :exit
%cecho% info "Found CMake version %CMakeVersion%"
set FoundProfile=
for /f "tokens=3" %%F in ('find /c /i "%CMakeVersion%" "%EnvMSYS2Path%\msys%MSYS2Base%\etc\profile"') do set FoundProfile=%%F
for /f "tokens=3" %%F in ('find /c /i "%CMakeVersion%" "%MSYS2UnpackPath%\etc\profile"') do set FoundProfile=%%F
if "%FoundProfile%"=="0" (
echo export PATH="${PATH}:/%CMakeVersion%/bin">>"%EnvMSYS2Path%\msys%MSYS2Base%\etc\profile"
echo export PATH="${PATH}:/%CMakeVersion%/bin">>"%MSYS2UnpackPath%\etc\profile"
)
set MSYS2SH=%EnvMSYS2Path%\msys%MSYS2Base%\usr\bin\sh
set MSYS2SH=%MSYS2UnpackPath%\usr\bin\sh
%cecho% info "Initialize MSYS2"
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
"%MSYS2SH%" -lc "pacman --noconfirm -Sy"
"%MSYS2SH%" -lc "pacman --noconfirm -Su"
call "%EnvMSYS2Path%\msys%MSYS2Base%\autorebase.bat"
call "%MSYS2UnpackPath%\autorebase.bat"
:exit
endlocal

View file

@ -16,6 +16,9 @@ set NSISInstallPath=%EnvToolsPath%\NSIS
set MinGitInstall=MinGit-2.28.0-32-bit.zip
set MinGitUrl=https://github.com/git-for-windows/git/releases/download/v2.28.0.windows.1/%MinGitInstall%
set MinGitInstallPath=%EnvToolsPath%\MinGit
set DoxygenInstall=doxygen-1.9.6.windows.x64.bin.zip
set DoxygenUrl=https://github.com/doxygen/doxygen/releases/download/Release_1_9_6/%DoxygenInstall%
set DoxygenInstallPath=%EnvToolsPath%\doxygen
set CMakeVersion=cmake-3.19.0-win32-x86
set CMakeInstall=%CMakeVersion%.zip
set CMakeUrl=https://github.com/Kitware/CMake/releases/download/v3.19.0/%CMakeInstall%
@ -50,7 +53,7 @@ if not exist "%EnvToolsPath%\cecho.exe" (
if not exist "%EnvDownloadPath%\%cCEhoInstall%" echo Cannot download cecho installation& goto error
echo Unpack cecho
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CEchoInstall%" -y -bso0
copy "%EnvTempPath%\cecho.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@ -66,7 +69,7 @@ if not exist "%EnvToolsPath%\depends.exe" (
if not exist "%EnvDownloadPath%\%DependsInstall%" %cecho% error "Cannot download Dependendy Walker installation" & goto error
%cecho% info "Unpack Dependency Walker"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%DependsInstall%"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%DependsInstall%" -y -bso0
copy "%EnvTempPath%\*" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@ -82,7 +85,7 @@ if not exist "%EnvToolsPath%\cut.exe" (
if not exist "%EnvDownloadPath%\%UnixToolsInstall%" %cecho% error "Cannot download Unix Tools installation" & goto error
%cecho% info "Unpack Unix Tools"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%" -y -bso0
copy "%EnvTempPath%\cut.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@ -98,7 +101,7 @@ if not exist "%EnvToolsPath%\sed.exe" (
if not exist "%EnvDownloadPath%\%UnixToolsInstall%" %cecho% error "Cannot download Unix Tools installation" & goto error
%cecho% info "Unpack Unix Tools"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%UnixToolsInstall%" -y -bso0
copy "%EnvTempPath%\sed.exe" "%EnvToolsPath%"
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
@ -118,7 +121,7 @@ if not exist "%NSISInstallPath%\nsis.exe" (
if not exist "%EnvDownloadPath%\%NSISInstall%" %cecho% error "Cannot download NSIS installation" & goto error
%cecho% info "Unpack NSIS"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%NSISInstall%"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%NSISInstall%" -y -bso0
if not exist "%NSISInstallPath%" mkdir "%NSISInstallPath%"
xcopy /s "%EnvTempPath%" "%NSISInstallPath%"
@ -132,7 +135,20 @@ if not exist "%MinGitInstallPath%\cmd\git.exe" (
if not exist "%EnvDownloadPath%\%MinGitInstall%" %cecho% error "Cannot download MinGit installation" & goto error
%cecho% info "Unpack MinGit"
"%EnvSevenZipExe%" x -o"%MinGitInstallPath%" "%EnvDownloadPath%\%MinGitInstall%"
"%EnvSevenZipExe%" x -o"%MinGitInstallPath%" "%EnvDownloadPath%\%MinGitInstall%" -y -bso0
)
if not exist "%EnvDownloadPath%\%DoxygenInstall%" call "%ToolsPath%\remove-dir.bat" "%DoxygenInstallPath%"
if not exist "%DoxygenInstallPath%\doxygen.exe" (
if exist "%DoxygenInstallPath%" call "%ToolsPath%\remove-dir.bat" "%DoxygenInstallPath%"
%cecho% info "Download Doxygen installation"
if not exist "%EnvDownloadPath%\%DoxygenInstall%" call "%ToolsPath%\download-file.bat" "%DoxygenUrl%" "%EnvDownloadPath%\%DoxygenInstall%"
if not exist "%EnvDownloadPath%\%DoxygenInstall%" %cecho% error "Cannot download doxygen installation" & goto error
%cecho% info "Unpack Doxygen"
"%EnvSevenZipExe%" x -o"%DoxygenInstallPath%" "%EnvDownloadPath%\%DoxygenInstall%" -y -bso0
)
if not exist "%EnvDownloadPath%\%CMakeInstall%" call "%ToolsPath%\remove-dir.bat" "%CMakeInstallPath%"
@ -147,7 +163,7 @@ if not exist "%CMakeInstallPath%\bin\cmake.exe" (
if not exist "%EnvDownloadPath%\%CMakeInstall%" %cecho% error "Cannot download CMake installation" & goto error
%cecho% info "Unpack CMake"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CMakeInstall%"
"%EnvSevenZipExe%" x -o"%EnvTempPath%" "%EnvDownloadPath%\%CMakeInstall%" -y -bso0
move "%EnvTempPath%\%CMakeVersion%" "%CMakeInstallPath%"
@ -160,7 +176,7 @@ mkdir "%EnvTempPath%"
call "%ToolsPath%\download-file.bat" "%TorDownloadIndexUrl%" "%EnvTempPath%\index.html"
if not exist "%EnvTempPath%\index.html" %cecho% error "Cannot download Tor installation" & goto error
for /F "tokens=1,2 delims= " %%A in ('%EnvSedExe% -r -n -e"s/.*href=\"^(.*^)^(tor-win32.*\.zip^)\".*/\2 \1\2/p" "%EnvTempPath%\index.html"') do set TorInstall=%%A& set TorDownloadUrl=%TorProjectUrl%%%B
for /F "tokens=1,2 delims= " %%A in ('%EnvSedExe% -r -n -e"s/.*href=\"^(.*^)^(tor-.*windows-i686.*\.tar\.gz^)\".*/\2 \1\2/p" "%EnvTempPath%\index.html"') do set TorInstall=%%A& set TorDownloadUrl=%%B
call "%ToolsPath%\remove-dir.bat" "%EnvTempPath%"
if "%TorInstall%"=="" %cecho% error "Cannot download Tor installation" & goto error
if "%TorDownloadUrl%"=="" %cecho% error "Cannot download Tor installation" & goto error
@ -173,7 +189,7 @@ if not exist "%EnvTorPath%\Tor\tor.exe" (
if not exist "%EnvDownloadPath%\%TorInstall%" %cecho% error "Cannot download Tor installation" & goto error
%cecho% info "Unpack Tor"
"%EnvSevenZipExe%" x -o"%EnvTorPath%" "%EnvDownloadPath%\%TorInstall%"
"%EnvSevenZipExe%" x -so "%EnvDownloadPath%\%TorInstall%" | "%EnvSevenZipExe%" x -si -ttar -o"%EnvTorPath%" -y -bso0
)
:exit

View file

@ -2,18 +2,13 @@
setlocal
if exist "%~dp0msys2\msys32" call :update 32
if exist "%~dp0msys2\msys64" call :update 64
if not exist "%~dp0msys2\msys64" goto :EOF
goto :EOF
set MSYS2SH=%~dp0msys2\msys64\usr\bin\sh
:update
set MSYS2SH=%~dp0msys2\msys%~1\usr\bin\sh
echo Update MSYS2 %~1
echo Update MSYS2
"%MSYS2SH%" -lc "yes | pacman --noconfirm -Syuu msys2-keyring"
"%MSYS2SH%" -lc "pacman --noconfirm -Su"
:exit
endlocal
goto :EOF

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Instal·la ${APPNAME} i els components necessaris."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "Pells"
!insertmacro LANG_STRING Section_Data_Desc "Instal·la pells."
!insertmacro LANG_STRING Section_Shortcuts "Icones d'accés directe"

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Installiert ${APPNAME} und die benötigten Komponenten."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installiert Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installiert WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installiert Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installiert Friend Server."
!insertmacro LANG_STRING Section_Data "Skins"
!insertmacro LANG_STRING Section_Data_Desc "Skins installieren."
!insertmacro LANG_STRING Section_Shortcuts "Verknüpfungssymbole"

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Installs ${APPNAME} and required components."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "Skins"
!insertmacro LANG_STRING Section_Data_Desc "Installs skins."
!insertmacro LANG_STRING Section_Shortcuts "Shortcut icons"

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Instala ${APPNAME} y los componentes requeridos."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "Coberturas (skins)"
!insertmacro LANG_STRING Section_Data_Desc "Instalar coberturas"
!insertmacro LANG_STRING Section_Shortcuts "Iconos de accesos directos"

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Installe ${APPNAME} et les composants requis."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "Habillages"
!insertmacro LANG_STRING Section_Data_Desc "Installe des habillages."
!insertmacro LANG_STRING Section_Shortcuts "Icônes de raccourci"

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Instaluje ${APPNAME} oraz wymagane komponenty."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "Skórki"
!insertmacro LANG_STRING Section_Data_Desc "Instaluje skórki."
!insertmacro LANG_STRING Section_Shortcuts "Ikony skrótów"

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Установка ${APPNAME} и необходимых компонентов."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "Оболочки"
!insertmacro LANG_STRING Section_Data_Desc "Установка оболочек."
!insertmacro LANG_STRING Section_Shortcuts "Ярлыки"

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "${APPNAME} ve gerekli bileşenleri kurar."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "Temalar"
!insertmacro LANG_STRING Section_Data_Desc "Tema yükleyin."
!insertmacro LANG_STRING Section_Shortcuts "Kısayol simgeleri"

View file

@ -22,6 +22,48 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Section_WebUI</name>
<message>
<source>WebUI</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Section_WebUI_Desc</name>
<message>
<source>Installs WebUI.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Section_Service</name>
<message>
<source>Service</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Section_Service_Desc</name>
<message>
<source>Installs Service.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Section_FriendServer</name>
<message>
<source>Friend Server</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Section_FriendServer_Desc</name>
<message>
<source>Installs Friend Server.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Section_Data</name>
<message>

View file

@ -2,6 +2,12 @@
!insertmacro LANG_STRING Section_Main_Desc "Installs ${APPNAME} and required components."
!insertmacro LANG_STRING Section_Tor "Tor"
!insertmacro LANG_STRING Section_Tor_Desc "Installs Tor."
!insertmacro LANG_STRING Section_WebUI "WebUI"
!insertmacro LANG_STRING Section_WebUI_Desc "Installs WebUI."
!insertmacro LANG_STRING Section_Service "Service"
!insertmacro LANG_STRING Section_Service_Desc "Installs Service."
!insertmacro LANG_STRING Section_FriendServer "Friend Server"
!insertmacro LANG_STRING Section_FriendServer_Desc "Installs Friend Server."
!insertmacro LANG_STRING Section_Data "皮肤"
!insertmacro LANG_STRING Section_Data_Desc "安装皮肤"
!insertmacro LANG_STRING Section_Shortcuts "快捷方式图标"

View file

@ -60,6 +60,9 @@
!define /date DATE "%Y%m%d"
!endif
# Service
${!defineifexist} SERVICE_EXISTS "${RELEASEDIR}\retroshare-service\src\release\retroshare-service.exe"
# Tor
!ifdef TORDIR
${!defineifexist} TOR_EXISTS "${TORDIR}\tor.exe"
@ -68,6 +71,20 @@ ${!defineifexist} TOR_EXISTS "${TORDIR}\tor.exe"
!endif
!endif
# WebUI
!ifdef WEBUIDIR
${!defineifexist} WEBUI_EXISTS "${WEBUIDIR}\index.html"
!ifndef WEBUI_EXISTS
!error "WebUI files not found"
!endif
!endif
# Friend Server
!ifdef TOR_EXISTS
# Add Friend Server with Tor only
#${!defineifexist} FRIENDSERVER_EXISTS "${RELEASEDIR}\retroshare-friendserver\src\release\retroshare-friendserver.exe"
!endif
# Application name and version
!define APPNAME "RetroShare"
!define APPNAMEANDVERSION "${APPNAME} ${VERSION}"
@ -193,7 +210,6 @@ Section $(Section_Main) Section_Main
; Main binaries
SetOutPath "$INSTDIR"
File "${RELEASEDIR}\retroshare-gui\src\release\retroshare.exe"
File "${RELEASEDIR}\retroshare-service\src\release\retroshare-service.exe"
File /nonfatal "${RELEASEDIR}\libretroshare\src\lib\retroshare.dll"
; Qt binaries
@ -282,11 +298,35 @@ Section $(Section_Main) Section_Main
File /r "${SOURCEDIR}\retroshare-gui\src\license\*.*"
SectionEnd
# Service
!ifdef SERVICE_EXISTS
Section /o $(Section_Service) Section_Service
SetOutPath "$INSTDIR"
File "${RELEASEDIR}\retroshare-service\src\release\retroshare-service.exe"
SectionEnd
!endif
# Friend Server
!ifdef FRIENDSERVER_EXISTS
Section /o $(Section_FriendServer) Section_FriendServer
SetOutPath "$INSTDIR"
File "${RELEASEDIR}\retroshare-friendserver\src\release\retroshare-friendserver.exe"
SectionEnd
!endif
# Tor
!ifdef TOR_EXISTS
Section /o $(Section_Tor) Section_Tor
SetOutPath "$INSTDIR\tor"
File /r "${TORDIR}\*"
File "${TORDIR}\*"
SectionEnd
!endif
# WebUI
!ifdef WEBUI_EXISTS
Section /o $(Section_WebUI) Section_WebUI
SetOutPath "$INSTDIR\webui"
File /r "${WEBUIDIR}\*"
SectionEnd
!endif
@ -355,6 +395,22 @@ Section $(Section_StartMenu) Section_StartMenu
CreateDirectory "$SMPROGRAMS\${APPNAME}"
CreateShortCut "$SMPROGRAMS\${APPNAME}\$(Link_Uninstall).lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\retroshare.exe" "" "$INSTDIR\retroshare.exe" 0
!ifdef SERVICE_EXISTS
SectionGetFlags ${Section_Service} $0
IntOp $0 $0 & ${SF_SELECTED}
${If} $0 == ${SF_SELECTED}
CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME} Service.lnk" "$INSTDIR\retroshare-service.exe" "" "$INSTDIR\retroshare-service.exe" 0
${EndIf}
!endif
!ifdef FRIENDSERVER_EXISTS
SectionGetFlags ${Section_FriendServer} $0
IntOp $0 $0 & ${SF_SELECTED}
${If} $0 == ${SF_SELECTED}
CreateShortCut "$SMPROGRAMS\${APPNAME}\${APPNAME} Friend Server.lnk" "$INSTDIR\retroshare-friendserver.exe" "" "$INSTDIR\retroshare-friendserver.exe" 0
${EndIf}
!endif
SectionEnd
Section $(Section_Desktop) Section_Desktop
@ -407,6 +463,9 @@ SectionEnd
; !insertmacro MUI_DESCRIPTION_TEXT ${Section_Link} $(Section_Link_Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${Section_AutoStart} $(Section_AutoStart_Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${Section_Tor} $(Section_Tor_Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${Section_WebUI} $(Section_WebUI_Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${Section_Service} $(Section_Service_Desc)
!insertmacro MUI_DESCRIPTION_TEXT ${Section_FriendServer} $(Section_FriendServer_Desc)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
# Uninstall
@ -461,6 +520,26 @@ Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
FunctionEnd
!ifdef FRIENDSERVER_EXISTS
Function .onSelChange
SectionGetFlags ${Section_FriendServer} $0
IntOp $0 $0 & ${SF_SELECTED}
${If} $0 == ${SF_SELECTED}
# Activate Tor and set readonly
SectionGetFlags ${Section_Tor} $1
IntOp $1 $1 | ${SF_SELECTED}
IntOp $1 $1 | ${SF_RO}
SectionSetFlags ${Section_Tor} $1
${Else}
# Remove readonly from Tor
SectionGetFlags ${Section_Tor} $1
IntOp $2 ${SF_RO} ~
IntOp $1 $1 & $2
SectionSetFlags ${Section_Tor} $1
${EndIf}
FunctionEnd
!endif
# Installation mode
Function RequireAdmin

View file

@ -18,6 +18,8 @@ copy nul %logfile% > nul
pushd %~1
set Percent=%%
set last=HEAD
for /f %%t in ('git tag --sort=-taggerdate --merged ^| findstr v') do (
echo generating changelog for !last!..%%t
@ -30,7 +32,7 @@ for /f %%t in ('git tag --sort=-taggerdate --merged ^| findstr v') do (
rem echo !last! ---^> %%t >> %logfile%
echo ----------------------------------------------- >> %logfile%
echo. >> %logfile%
git log %%t..!last! --no-merges "--pretty=format:%%h %%ai %%<(10,trunc)%%an %%s" >> %logfile%
git log %%t..!last! --no-merges "--pretty=format:!Percent!h !Percent!ai !Percent!<(10,trunc)!Percent!an !Percent!s" >> %logfile%
echo. >> %logfile%
echo. >> %logfile%
set last=%%t

View file

@ -0,0 +1,20 @@
#!/bin/bash
function git_del_tag()
{
mTag=$1
for mRemote in $(git remote); do
echo "Attempting tag $mTag removal from remote $mRemote"
GIT_TERMINAL_PROMPT=0 git push $mRemote :$mTag || true
done
git tag --delete $mTag
}
for mModule in . build_scripts/OBS/ libbitdht/ libretroshare/ openpgpsdk/ retroshare-webui/ ; do
pushd $mModule
git_del_tag v0.6.7a
git tag --list | grep untagged | while read mTag; do git_del_tag $mTag ; done
popd
done

@ -1 +1 @@
Subproject commit 659423769541169457c41f71c8a038e2d64ba079
Subproject commit 2ddc86fb575a61170f4c06a00152e3e7dc74c8f4

@ -1 +1 @@
Subproject commit a7a430008b76e53727598c4d13106e7ce95221d7
Subproject commit 402f32eda026c3ec3e429b5fb842e87ebd985d73

View file

@ -44,6 +44,7 @@ SOURCES = FeedReaderPlugin.cpp \
gui/FeedReaderFeedNotify.cpp \
gui/FeedReaderUserNotify.cpp \
gui/FeedReaderFeedItem.cpp \
gui/FeedTreeWidget.cpp \
util/CURLWrapper.cpp \
util/XMLWrapper.cpp \
util/HTMLWrapper.cpp \
@ -64,6 +65,7 @@ HEADERS = FeedReaderPlugin.h \
gui/FeedReaderFeedNotify.h \
gui/FeedReaderUserNotify.h \
gui/FeedReaderFeedItem.h \
gui/FeedTreeWidget.h \
util/CURLWrapper.h \
util/XMLWrapper.h \
util/HTMLWrapper.h \
@ -121,6 +123,11 @@ win32 {
#Have to reorder libs, else got /libs/lib/libcrypto.a(bio_lib.o):bio_lib.c:(.text+0x0): multiple definition of `BIO_new'
LIBS = -lcurl -lxml2 -lz -lxslt -lws2_32 -lwldap32 -lssl -lcrypto -lgdi32 $${LIBS}
isEmpty(QMAKE_SH) {
# MinGW
LIBS += -lcrypt32
}
# Check for msys2
!isEmpty(PREFIX_MSYS2) {
message(Use msys2 xml2.)

View file

@ -92,7 +92,7 @@ void FeedReaderPlugin::setInterfaces(RsPlugInInterfaces &interfaces)
{
mInterfaces = interfaces;
mFeedReader = new p3FeedReader(mPlugInHandler, mInterfaces.mGxsForums);
mFeedReader = new p3FeedReader(mPlugInHandler, mInterfaces.mGxsForums, mInterfaces.mPosted);
rsFeedReader = mFeedReader;
mNotify = new FeedReaderNotify();

View file

@ -30,9 +30,11 @@
#include "gui/common/UIStateHelper.h"
#include <retroshare/rsgxsforums.h>
#include <retroshare/rsposted.h>
#include <iostream>
#define TOKEN_TYPE_FORUM_GROUPS 1
#define TOKEN_TYPE_POSTED_GROUPS 2
AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent)
: QDialog(parent, Qt::Window), mFeedReader(feedReader), mNotify(notify), ui(new Ui::AddFeedDialog)
@ -47,9 +49,12 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
mStateHelper->addWidget(TOKEN_TYPE_FORUM_GROUPS, ui->forumComboBox, UISTATE_LOADING_DISABLED);
mStateHelper->addWidget(TOKEN_TYPE_FORUM_GROUPS, ui->buttonBox->button(QDialogButtonBox::Ok), UISTATE_LOADING_DISABLED);
mStateHelper->addWidget(TOKEN_TYPE_POSTED_GROUPS, ui->postedComboBox, UISTATE_LOADING_DISABLED);
mStateHelper->addWidget(TOKEN_TYPE_POSTED_GROUPS, ui->buttonBox->button(QDialogButtonBox::Ok), UISTATE_LOADING_DISABLED);
/* Setup TokenQueue */
mTokenQueue = new TokenQueue(rsGxsForums->getTokenService(), this);
mForumTokenQueue = new TokenQueue(rsGxsForums->getTokenService(), this);
mPostedTokenQueue = new TokenQueue(rsPosted->getTokenService(), this);
/* Connect signals */
connect(ui->buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(createFeed()));
@ -59,18 +64,23 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
connect(ui->useStandardStorageTimeCheckBox, SIGNAL(toggled(bool)), this, SLOT(useStandardStorageTimeToggled()));
connect(ui->useStandardUpdateInterval, SIGNAL(toggled(bool)), this, SLOT(useStandardUpdateIntervalToggled()));
connect(ui->useStandardProxyCheckBox, SIGNAL(toggled(bool)), this, SLOT(useStandardProxyToggled()));
connect(ui->typeForumRadio, SIGNAL(toggled(bool)), this, SLOT(typeForumToggled()));
connect(ui->typeForumCheckBox, SIGNAL(toggled(bool)), this, SLOT(typeForumToggled()));
connect(ui->typePostedCheckBox, SIGNAL(toggled(bool)), this, SLOT(typePostedToggled()));
connect(ui->typeLocalCheckBox, SIGNAL(toggled(bool)), this, SLOT(typeLocalToggled()));
connect(ui->postedFirstImageCheckBox, SIGNAL(toggled(bool)), this, SLOT(postedFirstImageToggled()));
connect(ui->previewButton, SIGNAL(clicked()), this, SLOT(preview()));
/* currently only for local feeds */
connect(ui->saveCompletePageCheckBox, SIGNAL(toggled(bool)), this, SLOT(denyForumToggled()));
connect(ui->saveCompletePageCheckBox, SIGNAL(toggled(bool)), this, SLOT(denyForumAndPostedToggled()));
connect(ui->urlLineEdit, SIGNAL(textChanged(QString)), this, SLOT(validate()));
connect(ui->nameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(validate()));
connect(ui->useInfoFromFeedCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
connect(ui->typeLocalRadio, SIGNAL(toggled(bool)), this, SLOT(validate()));
connect(ui->typeForumRadio, SIGNAL(toggled(bool)), this, SLOT(validate()));
connect(ui->typeLocalCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
connect(ui->typeForumCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
connect(ui->forumComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(validate()));
connect(ui->typePostedCheckBox, SIGNAL(toggled(bool)), this, SLOT(validate()));
connect(ui->postedComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(validate()));
connect(ui->clearCachePushButton, SIGNAL(clicked()), this, SLOT(clearMessageCache()));
@ -79,13 +89,24 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
ui->activatedCheckBox->setChecked(true);
mStateHelper->setWidgetEnabled(ui->forumComboBox, false);
mStateHelper->setWidgetEnabled(ui->postedComboBox, false);
ui->useInfoFromFeedCheckBox->setChecked(true);
ui->updateForumInfoCheckBox->setEnabled(false);
ui->updateForumInfoCheckBox->setChecked(true);
ui->updatePostedInfoCheckBox->setEnabled(false);
ui->updatePostedInfoCheckBox->setChecked(true);
ui->postedFirstImageCheckBox->setEnabled(false);
ui->postedFirstImageCheckBox->setChecked(false);
ui->postedOnlyImageCheckBox->setEnabled(false);
ui->postedOnlyImageCheckBox->setChecked(false);
ui->postedShinkImageCheckBox->setEnabled(false);
ui->postedShinkImageCheckBox->setChecked(true);
ui->useAuthenticationCheckBox->setChecked(false);
ui->useStandardStorageTimeCheckBox->setChecked(true);
ui->useStandardUpdateInterval->setChecked(true);
ui->useStandardProxyCheckBox->setChecked(true);
ui->embedImagesCheckBox->setChecked(true);
ui->saveCompletePageCheckBox->setEnabled(false);
/* not yet supported */
ui->authenticationGroupBox->setEnabled(false);
@ -98,6 +119,9 @@ AddFeedDialog::AddFeedDialog(RsFeedReader *feedReader, FeedReaderNotify *notify,
/* fill own forums */
requestForumGroups();
/* fill own posted */
requestPostedGroups();
validate();
ui->urlLineEdit->setFocus();
@ -112,7 +136,8 @@ AddFeedDialog::~AddFeedDialog()
processSettings(false);
delete(ui);
delete(mTokenQueue);
delete(mForumTokenQueue);
delete(mPostedTokenQueue);
}
void AddFeedDialog::processSettings(bool load)
@ -161,18 +186,66 @@ void AddFeedDialog::useStandardProxyToggled()
void AddFeedDialog::typeForumToggled()
{
bool checked = ui->typeForumRadio->isChecked();
bool checked = ui->typeForumCheckBox->isChecked();
mStateHelper->setWidgetEnabled(ui->forumComboBox, checked);
ui->updateForumInfoCheckBox->setEnabled(checked);
if (checked) {
ui->typeLocalCheckBox->setChecked(false);
}
}
void AddFeedDialog::denyForumToggled()
void AddFeedDialog::postedFirstImageToggled()
{
bool checked = ui->postedFirstImageCheckBox->isChecked();
ui->postedOnlyImageCheckBox->setEnabled(checked);
if (!checked) {
ui->postedOnlyImageCheckBox->setChecked(false);
}
}
void AddFeedDialog::typePostedToggled()
{
bool checked = ui->typePostedCheckBox->isChecked();
mStateHelper->setWidgetEnabled(ui->postedComboBox, checked);
ui->updatePostedInfoCheckBox->setEnabled(checked);
ui->postedFirstImageCheckBox->setEnabled(checked);
ui->postedShinkImageCheckBox->setEnabled(checked);
if (checked) {
ui->typeLocalCheckBox->setChecked(false);
}else {
ui->postedFirstImageCheckBox->setChecked(false);
}
}
void AddFeedDialog::typeLocalToggled()
{
bool checked = ui->typeLocalCheckBox->isChecked();
if (checked) {
mStateHelper->setWidgetEnabled(ui->forumComboBox, false);
mStateHelper->setWidgetEnabled(ui->postedComboBox, false);
ui->typeForumCheckBox->setChecked(false);
ui->typePostedCheckBox->setChecked(false);
ui->saveCompletePageCheckBox->setEnabled(true);
} else {
ui->saveCompletePageCheckBox->setEnabled(false);
ui->saveCompletePageCheckBox->setChecked(false);
}
}
void AddFeedDialog::denyForumAndPostedToggled()
{
if (ui->saveCompletePageCheckBox->isChecked()) {
ui->typeForumRadio->setEnabled(false);
ui->typeLocalRadio->setChecked(true);
ui->typeForumCheckBox->setEnabled(false);
ui->typeForumCheckBox->setChecked(false);
ui->typePostedCheckBox->setEnabled(false);
ui->typePostedCheckBox->setChecked(false);
ui->typeLocalCheckBox->setChecked(true);
} else {
ui->typeForumRadio->setEnabled(true);
ui->typeForumCheckBox->setEnabled(true);
ui->typePostedCheckBox->setEnabled(true);
}
}
@ -189,11 +262,15 @@ void AddFeedDialog::validate()
ui->previewButton->setEnabled(ok);
if (!ui->typeLocalRadio->isChecked() && !ui->typeForumRadio->isChecked()) {
if (!ui->typeLocalCheckBox->isChecked() && !ui->typeForumCheckBox->isChecked() && !ui->typePostedCheckBox->isChecked()) {
ok = false;
}
if (ui->typeForumRadio->isChecked() && ui->forumComboBox->itemData(ui->forumComboBox->currentIndex()).toString().isEmpty()) {
if (ui->typeForumCheckBox->isChecked() && ui->forumComboBox->itemData(ui->forumComboBox->currentIndex()).toString().isEmpty()) {
ok = false;
}
if (ui->typePostedCheckBox->isChecked() && ui->postedComboBox->itemData(ui->postedComboBox->currentIndex()).toString().isEmpty()) {
ok = false;
}
@ -224,20 +301,32 @@ bool AddFeedDialog::fillFeed(uint32_t feedId)
ui->urlLineEdit->setText(QString::fromUtf8(feedInfo.url.c_str()));
ui->useInfoFromFeedCheckBox->setChecked(feedInfo.flag.infoFromFeed);
ui->updateForumInfoCheckBox->setChecked(feedInfo.flag.updateForumInfo);
ui->updatePostedInfoCheckBox->setChecked(feedInfo.flag.updatePostedInfo);
ui->postedFirstImageCheckBox->setChecked(feedInfo.flag.postedFirstImage);
ui->postedOnlyImageCheckBox->setChecked(feedInfo.flag.postedOnlyImage);
ui->postedShinkImageCheckBox->setChecked(feedInfo.flag.postedShrinkImage);
ui->activatedCheckBox->setChecked(!feedInfo.flag.deactivated);
ui->embedImagesCheckBox->setChecked(feedInfo.flag.embedImages);
ui->saveCompletePageCheckBox->setChecked(feedInfo.flag.saveCompletePage);
ui->descriptionPlainTextEdit->setPlainText(QString::fromUtf8(feedInfo.description.c_str()));
if (feedInfo.flag.forum || feedInfo.flag.posted) {
if (feedInfo.flag.forum) {
mStateHelper->setWidgetEnabled(ui->forumComboBox, true);
ui->typeForumRadio->setChecked(true);
ui->saveCompletePageCheckBox->setEnabled(false);
ui->typeForumCheckBox->setChecked(true);
setActiveForumId(feedInfo.forumId);
}
if (feedInfo.flag.posted) {
mStateHelper->setWidgetEnabled(ui->postedComboBox, true);
ui->typePostedCheckBox->setChecked(true);
setActivePostedId(feedInfo.postedId);
}
ui->saveCompletePageCheckBox->setEnabled(false);
} else {
ui->typeLocalRadio->setChecked(true);
ui->typeLocalCheckBox->setChecked(true);
mStateHelper->setWidgetEnabled(ui->forumComboBox, false);
}
@ -286,6 +375,21 @@ void AddFeedDialog::setActiveForumId(const std::string &forumId)
}
}
void AddFeedDialog::setActivePostedId(const std::string &postedId)
{
if (mStateHelper->isLoading(TOKEN_TYPE_POSTED_GROUPS)) {
mFillPostedId = postedId;
return;
}
int index = ui->postedComboBox->findData(QString::fromStdString(postedId));
if (index >= 0) {
ui->postedComboBox->setCurrentIndex(index);
} else {
ui->postedComboBox->setCurrentIndex(0);
}
}
void AddFeedDialog::getFeedInfo(FeedInfo &feedInfo)
{
feedInfo.parentId = mParentId;
@ -294,18 +398,28 @@ void AddFeedDialog::getFeedInfo(FeedInfo &feedInfo)
feedInfo.url = ui->urlLineEdit->text().toUtf8().constData();
feedInfo.flag.infoFromFeed = ui->useInfoFromFeedCheckBox->isChecked();
feedInfo.flag.updateForumInfo = ui->updateForumInfoCheckBox->isChecked() && ui->updateForumInfoCheckBox->isEnabled();
feedInfo.flag.updatePostedInfo = ui->updatePostedInfoCheckBox->isChecked() && ui->updatePostedInfoCheckBox->isEnabled();
feedInfo.flag.postedFirstImage = ui->postedFirstImageCheckBox->isChecked() && ui->postedFirstImageCheckBox->isEnabled();
feedInfo.flag.postedOnlyImage = ui->postedOnlyImageCheckBox->isChecked() && ui->postedOnlyImageCheckBox->isEnabled();
feedInfo.flag.postedShrinkImage = ui->postedShinkImageCheckBox->isChecked() && ui->postedShinkImageCheckBox->isEnabled();
feedInfo.flag.deactivated = !ui->activatedCheckBox->isChecked();
feedInfo.flag.embedImages = ui->embedImagesCheckBox->isChecked();
feedInfo.flag.saveCompletePage = ui->saveCompletePageCheckBox->isChecked();
feedInfo.description = ui->descriptionPlainTextEdit->toPlainText().toUtf8().constData();
feedInfo.flag.forum = ui->typeForumRadio->isChecked();
feedInfo.flag.forum = ui->typeForumCheckBox->isChecked();
if (feedInfo.flag.forum) {
feedInfo.forumId = ui->forumComboBox->itemData(ui->forumComboBox->currentIndex()).toString().toStdString();
}
feedInfo.flag.posted = ui->typePostedCheckBox->isChecked();
if (feedInfo.flag.posted) {
feedInfo.postedId = ui->postedComboBox->itemData(ui->postedComboBox->currentIndex()).toString().toStdString();
}
feedInfo.flag.authentication = ui->useAuthenticationCheckBox->isChecked();
feedInfo.user = ui->userLineEdit->text().toUtf8().constData();
feedInfo.password = ui->passwordLineEdit->text().toUtf8().constData();
@ -340,12 +454,12 @@ void AddFeedDialog::createFeed()
if (mFeedId == 0) {
/* add new feed */
RsFeedAddResult result = mFeedReader->addFeed(feedInfo, mFeedId);
RsFeedResult result = mFeedReader->addFeed(feedInfo, mFeedId);
if (FeedReaderStringDefs::showError(this, result, tr("Create feed"), tr("Cannot create feed."))) {
return;
}
} else {
RsFeedAddResult result = mFeedReader->setFeed(mFeedId, feedInfo);
RsFeedResult result = mFeedReader->setFeed(mFeedId, feedInfo);
if (FeedReaderStringDefs::showError(this, result, tr("Edit feed"), tr("Cannot change feed."))) {
return;
}
@ -382,10 +496,10 @@ void AddFeedDialog::requestForumGroups()
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
mTokenQueue->cancelActiveRequestTokens(TOKEN_TYPE_FORUM_GROUPS);
mForumTokenQueue->cancelActiveRequestTokens(TOKEN_TYPE_FORUM_GROUPS);
uint32_t token;
mTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, TOKEN_TYPE_FORUM_GROUPS);
mForumTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, TOKEN_TYPE_FORUM_GROUPS);
}
void AddFeedDialog::loadForumGroups(const uint32_t &token)
@ -416,9 +530,50 @@ void AddFeedDialog::loadForumGroups(const uint32_t &token)
}
}
void AddFeedDialog::requestPostedGroups()
{
mStateHelper->setLoading(TOKEN_TYPE_POSTED_GROUPS, true);
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
mPostedTokenQueue->cancelActiveRequestTokens(TOKEN_TYPE_POSTED_GROUPS);
uint32_t token;
mPostedTokenQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, TOKEN_TYPE_POSTED_GROUPS);
}
void AddFeedDialog::loadPostedGroups(const uint32_t &token)
{
std::vector<RsPostedGroup> groups;
rsPosted->getGroupData(token, groups);
ui->postedComboBox->clear();
for (std::vector<RsPostedGroup>::iterator it = groups.begin(); it != groups.end(); ++it) {
const RsPostedGroup &group = *it;
/* show only own posted */
if (IS_GROUP_PUBLISHER(group.mMeta.mSubscribeFlags) && IS_GROUP_ADMIN(group.mMeta.mSubscribeFlags) && !group.mMeta.mAuthorId.isNull()) {
ui->postedComboBox->addItem(QString::fromUtf8(group.mMeta.mGroupName.c_str()), QString::fromStdString(group.mMeta.mGroupId.toStdString()));
}
}
/* insert empty item */
ui->postedComboBox->insertItem(0, "", "");
ui->postedComboBox->setCurrentIndex(0);
mStateHelper->setLoading(TOKEN_TYPE_POSTED_GROUPS, false);
if (!mFillPostedId.empty()) {
setActivePostedId(mFillPostedId);
mFillPostedId.clear();
}
}
void AddFeedDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req)
{
if (queue == mTokenQueue)
if (queue == mForumTokenQueue)
{
/* now switch on req */
switch(req.mUserType)
@ -433,4 +588,20 @@ void AddFeedDialog::loadRequest(const TokenQueue *queue, const TokenRequest &req
}
}
if (queue == mPostedTokenQueue)
{
/* now switch on req */
switch(req.mUserType)
{
case TOKEN_TYPE_POSTED_GROUPS:
loadPostedGroups(req.mToken);
break;
default:
std::cerr << "AddFeedDialog::loadRequest() ERROR: INVALID TYPE";
std::cerr << std::endl;
}
}
}

View file

@ -53,7 +53,10 @@ private slots:
void useStandardUpdateIntervalToggled();
void useStandardProxyToggled();
void typeForumToggled();
void denyForumToggled();
void postedFirstImageToggled();
void typePostedToggled();
void typeLocalToggled();
void denyForumAndPostedToggled();
void validate();
void createFeed();
void preview();
@ -63,9 +66,12 @@ private:
void processSettings(bool load);
void getFeedInfo(FeedInfo &feedInfo);
void setActiveForumId(const std::string &forumId);
void setActivePostedId(const std::string &postedId);
void requestForumGroups();
void loadForumGroups(const uint32_t &token);
void requestPostedGroups();
void loadPostedGroups(const uint32_t &token);
private:
RsFeedReader *mFeedReader;
@ -73,13 +79,15 @@ private:
uint32_t mFeedId;
uint32_t mParentId;
std::string mFillForumId;
std::string mFillPostedId;
RsFeedTransformationType mTransformationType;
std::list<std::string> mXPathsToUse;
std::list<std::string> mXPathsToRemove;
std::string mXslt;
TokenQueue *mTokenQueue;
TokenQueue *mForumTokenQueue;
TokenQueue *mPostedTokenQueue;
UIStateHelper *mStateHelper;
Ui::AddFeedDialog *ui;

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1068</width>
<height>880</height>
<width>739</width>
<height>653</height>
</rect>
</property>
<property name="windowTitle">
@ -45,48 +45,6 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gradFrame_GL">
<item row="3" column="0">
<widget class="QGroupBox" name="typeGroupBox">
<property name="title">
<string>Type</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="typeForumRadio">
<property name="text">
<string>Forum</string>
</property>
</widget>
</item>
<item>
<widget class="RSComboBox" name="forumComboBox"/>
</item>
</layout>
</item>
<item>
<widget class="QRadioButton" name="typeLocalRadio">
<property name="text">
<string>Local Feed</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0">
<widget class="QGroupBox" name="authenticationGroupBox">
<property name="title">
@ -309,7 +267,6 @@
<property name="font">
<font>
<pointsize>11</pointsize>
<weight>75</weight>
<italic>true</italic>
<bold>true</bold>
</font>
@ -324,7 +281,44 @@
</item>
</layout>
</item>
<item row="4" column="0">
<item row="3" column="1">
<widget class="QGroupBox" name="flagGroupBox">
<property name="title">
<string>Misc</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="activatedCheckBox">
<property name="text">
<string>Activated</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useInfoFromFeedCheckBox">
<property name="text">
<string>Use name and description from feed</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="embedImagesCheckBox">
<property name="text">
<string>Embed images</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="saveCompletePageCheckBox">
<property name="text">
<string>Save complete web page (experimental for local feeds)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="1">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Transformation</string>
@ -357,26 +351,49 @@
</layout>
</widget>
</item>
<item row="3" column="1" rowspan="2">
<widget class="QGroupBox" name="flagGroupBox">
<item row="3" column="0" rowspan="3">
<widget class="QGroupBox" name="typeGroupBox">
<property name="title">
<string>Misc</string>
<string>Type</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="typeForumCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="activatedCheckBox">
<property name="text">
<string>Activated</string>
<string>Forum</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useInfoFromFeedCheckBox">
<item row="0" column="1">
<widget class="RSComboBox" name="forumComboBox"/>
</item>
<item row="8" column="0">
<widget class="QCheckBox" name="typeLocalCheckBox">
<property name="text">
<string>Use name and description from feed</string>
<string>Local Feed</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="updateForumInfoCheckBox">
<property name="text">
@ -384,20 +401,50 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="embedImagesCheckBox">
</layout>
</item>
<item row="4" column="1">
<widget class="RSComboBox" name="postedComboBox"/>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="typePostedCheckBox">
<property name="text">
<string>Embed images</string>
<string>Board</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="saveCompletePageCheckBox">
<item row="6" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="updatePostedInfoCheckBox">
<property name="text">
<string>Save complete web page (experimental for local feeds)</string>
<string>Update board information</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="postedOnlyImageCheckBox">
<property name="text">
<string>Only image</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="postedFirstImageCheckBox">
<property name="text">
<string>Use first image as board image</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="postedShinkImageCheckBox">
<property name="text">
<string>Shrink image</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
@ -423,22 +470,32 @@
<tabstop>urlLineEdit</tabstop>
<tabstop>nameLineEdit</tabstop>
<tabstop>descriptionPlainTextEdit</tabstop>
<tabstop>typeForumRadio</tabstop>
<tabstop>typeForumCheckBox</tabstop>
<tabstop>forumComboBox</tabstop>
<tabstop>updateForumInfoCheckBox</tabstop>
<tabstop>typePostedCheckBox</tabstop>
<tabstop>postedComboBox</tabstop>
<tabstop>updatePostedInfoCheckBox</tabstop>
<tabstop>postedShinkImageCheckBox</tabstop>
<tabstop>postedFirstImageCheckBox</tabstop>
<tabstop>postedOnlyImageCheckBox</tabstop>
<tabstop>typeLocalCheckBox</tabstop>
<tabstop>activatedCheckBox</tabstop>
<tabstop>useInfoFromFeedCheckBox</tabstop>
<tabstop>updateForumInfoCheckBox</tabstop>
<tabstop>embedImagesCheckBox</tabstop>
<tabstop>saveCompletePageCheckBox</tabstop>
<tabstop>previewButton</tabstop>
<tabstop>useAuthenticationCheckBox</tabstop>
<tabstop>userLineEdit</tabstop>
<tabstop>passwordLineEdit</tabstop>
<tabstop>useStandardStorageTimeCheckBox</tabstop>
<tabstop>clearCachePushButton</tabstop>
<tabstop>storageTimeSpinBox</tabstop>
<tabstop>useStandardUpdateInterval</tabstop>
<tabstop>updateIntervalSpinBox</tabstop>
<tabstop>useStandardProxyCheckBox</tabstop>
<tabstop>proxyAddressLineEdit</tabstop>
<tabstop>proxyPortSpinBox</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections/>

View file

@ -23,6 +23,7 @@
#include <QInputDialog>
#include <QPainter>
#include <QMessageBox>
#include <QBuffer>
#include "FeedReaderDialog.h"
#include "FeedReaderMessageWidget.h"
@ -35,6 +36,8 @@
#include "gui/settings/rsharesettings.h"
#include "gui/notifyqt.h"
#include "FeedReaderUserNotify.h"
#include "gui/Posted/PostedCreatePostDialog.h"
#include "util/imageutil.h"
#include "interface/rsFeedReader.h"
#include "retroshare/rsiface.h"
@ -55,6 +58,8 @@
#define ROLE_FEED_ERROR Qt::UserRole + 9
#define ROLE_FEED_DEACTIVATED Qt::UserRole + 10
const int MAXMESSAGESIZE = RsSerialiser::MAX_SERIAL_SIZE - 70000;
FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *notify, QWidget *parent)
: MainPage(parent), mFeedReader(feedReader), mNotify(notify), ui(new Ui::FeedReaderDialog)
{
@ -66,6 +71,7 @@ FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *n
mMessageWidget = NULL;
connect(mNotify, &FeedReaderNotify::feedChanged, this, &FeedReaderDialog::feedChanged, Qt::QueuedConnection);
connect(mNotify, &FeedReaderNotify::optimizeImage, this, &FeedReaderDialog::optimizeImage, Qt::QueuedConnection);
connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged()));
@ -84,9 +90,16 @@ FeedReaderDialog::FeedReaderDialog(RsFeedReader *feedReader, FeedReaderNotify *n
connect(ui->feedAddButton, SIGNAL(clicked()), this, SLOT(newFeed()));
connect(ui->feedProcessButton, SIGNAL(clicked()), this, SLOT(processFeed()));
connect(ui->feedTreeWidget, SIGNAL(feedReparent(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(feedTreeReparent(QTreeWidgetItem*,QTreeWidgetItem*)));
mFeedCompareRole = new RSTreeWidgetItemCompareRole;
mFeedCompareRole->setRole(COLUMN_FEED_NAME, ROLE_FEED_SORT);
/* enable drag and drop */
ui->feedTreeWidget->setAcceptDrops(true);
ui->feedTreeWidget->setDragEnabled(true);
ui->feedTreeWidget->setDragDropMode(QAbstractItemView::InternalMove);
/* initialize root item */
mRootItem = new QTreeWidgetItem(ui->feedTreeWidget);
QString name = tr("Message Folders");
@ -395,6 +408,9 @@ void FeedReaderDialog::updateFeeds(uint32_t parentId, QTreeWidgetItem *parentIte
mOpenFeedIds->removeAt(index);
}
}
} else {
/* disable drop */
item->setFlags(item->flags() & ~Qt::ItemIsDropEnabled);
}
}
@ -594,6 +610,55 @@ void FeedReaderDialog::feedChanged(uint32_t feedId, int type)
calculateFeedItems();
}
void FeedReaderDialog::optimizeImage()
{
while (true) {
FeedReaderOptimizeImageTask *optimizeImageTask = mFeedReader->getOptimizeImageTask();
if (!optimizeImageTask) {
return;
}
optimizeImageTask->mResult = false;
QImage image;
if (image.loadFromData(optimizeImageTask->mImage.data(), optimizeImageTask->mImage.size())) {
QByteArray optimizedImageData;
std::string optimizedImageMimeType;
QImage imageOptDummy;
switch (optimizeImageTask->mType) {
case FeedReaderOptimizeImageTask::POSTED:
if (PostedCreatePostDialog::optimizeImage(image, optimizedImageData, imageOptDummy)) {
optimizedImageMimeType = "image/jpeg";
optimizeImageTask->mResult = true;
}
break;
case FeedReaderOptimizeImageTask::SIZE:
if (ImageUtil::optimizeSizeBytes(optimizedImageData, image, imageOptDummy, "JPG", 0, MAXMESSAGESIZE)) {
optimizedImageMimeType = "image/jpg";
optimizeImageTask->mResult = true;
}
break;
}
if (optimizeImageTask->mResult) {
if (optimizedImageData.size() < (ssize_t) optimizeImageTask->mImage.size()) {
/* use optimized image */
optimizeImageTask->mImageResult.assign(optimizedImageData.begin(), optimizedImageData.end());
optimizeImageTask->mMimeTypeResult = optimizedImageMimeType;
} else {
/* use original image */
optimizeImageTask->mImageResult = optimizeImageTask->mImage;
optimizeImageTask->mMimeTypeResult = optimizeImageTask->mMimeType;
}
}
}
mFeedReader->setOptimizeImageTaskResult(optimizeImageTask);
}
}
FeedReaderMessageWidget *FeedReaderDialog::feedMessageWidget(uint32_t id)
{
int tabCount = ui->messageTabWidget->count();
@ -738,7 +803,7 @@ void FeedReaderDialog::newFolder()
if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) {
uint32_t feedId;
RsFeedAddResult result = mFeedReader->addFolder(currentFeedId(), dialog.textValue().toUtf8().constData(), feedId);
RsFeedResult result = mFeedReader->addFolder(currentFeedId(), dialog.textValue().toUtf8().constData(), feedId);
FeedReaderStringDefs::showError(this, result, tr("Create folder"), tr("Cannot create folder."));
}
}
@ -791,7 +856,7 @@ void FeedReaderDialog::editFeed()
dialog.setTextValue(item->data(COLUMN_FEED_DATA, ROLE_FEED_NAME).toString());
if (dialog.exec() == QDialog::Accepted && !dialog.textValue().isEmpty()) {
RsFeedAddResult result = mFeedReader->setFolder(feedId, dialog.textValue().toUtf8().constData());
RsFeedResult result = mFeedReader->setFolder(feedId, dialog.textValue().toUtf8().constData());
FeedReaderStringDefs::showError(this, result, tr("Create folder"), tr("Cannot create folder."));
}
} else {
@ -832,3 +897,30 @@ void FeedReaderDialog::processFeed()
mFeedReader->processFeed(feedId);
}
void FeedReaderDialog::feedTreeReparent(QTreeWidgetItem *item, QTreeWidgetItem *newParent)
{
if (!item || ! newParent) {
return;
}
uint32_t feedId = item->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toUInt();
uint32_t parentId = newParent->data(COLUMN_FEED_DATA, ROLE_FEED_ID).toUInt();
if (feedId == 0) {
return;
}
RsFeedResult result = mFeedReader->setParent(feedId, parentId);
if (FeedReaderStringDefs::showError(this, result, tr("Move feed"), tr("Cannot move feed."))) {
return;
}
bool expanded = item->isExpanded();
item->parent()->removeChild(item);
newParent->addChild(item);
item->setExpanded(expanded);
newParent->setExpanded(true);
calculateFeedItems();
}

View file

@ -61,6 +61,7 @@ private slots:
void editFeed();
void activateFeed();
void processFeed();
void feedTreeReparent(QTreeWidgetItem *item, QTreeWidgetItem *newParent);
void messageTabCloseRequested(int index);
void messageTabChanged(int index);
@ -68,6 +69,7 @@ private slots:
/* FeedReaderNotify */
void feedChanged(uint32_t feedId, int type);
void optimizeImage();
private:
uint32_t currentFeedId();

View file

@ -150,7 +150,7 @@
</widget>
</item>
<item row="1" column="0">
<widget class="RSTreeWidget" name="feedTreeWidget">
<widget class="FeedTreeWidget" name="feedTreeWidget">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@ -186,9 +186,9 @@
</widget>
<customwidgets>
<customwidget>
<class>RSTreeWidget</class>
<class>FeedTreeWidget</class>
<extends>QTreeWidget</extends>
<header>gui/common/RSTreeWidget.h</header>
<header>gui/FeedTreeWidget.h</header>
</customwidget>
<customwidget>
<class>RSTabWidget</class>

View file

@ -25,6 +25,7 @@
#include <QDesktopServices>
#include <QTimer>
#include <QPainter>
#include <QMimeData>
#include "FeedReaderMessageWidget.h"
#include "ui_FeedReaderMessageWidget.h"
@ -36,8 +37,14 @@
#include "gui/settings/rsharesettings.h"
#include "util/HandleRichText.h"
#include "util/QtVersion.h"
#include "gui/Posted/PostedCreatePostDialog.h"
#include "gui/gxsforums/CreateGxsForumMsg.h"
#include "gui/common/FilesDefs.h"
#include "util/imageutil.h"
#include "retroshare/rsiface.h"
#include "retroshare/rsgxsforums.h"
#include "retroshare/rsposted.h"
#define COLUMN_MSG_COUNT 4
#define COLUMN_MSG_TITLE 0
@ -83,6 +90,12 @@ FeedReaderMessageWidget::FeedReaderMessageWidget(uint32_t feedId, RsFeedReader *
connect(ui->msgRemoveButton, SIGNAL(clicked()), this, SLOT(removeMsg()));
connect(ui->feedProcessButton, SIGNAL(clicked()), this, SLOT(processFeed()));
/* Set initial size the splitter */
QList<int> sizes;
sizes << 250 << width(); // Qt calculates the right sizes
ui->pictureSplitter->setSizes(sizes);
ui->pictureSplitter->hide();
// create timer for navigation
mTimer = new QTimer(this);
mTimer->setInterval(300);
@ -115,6 +128,10 @@ FeedReaderMessageWidget::FeedReaderMessageWidget(uint32_t feedId, RsFeedReader *
ui->filterLineEdit->addFilter(QIcon(), tr("Author"), COLUMN_MSG_AUTHOR, tr("Search Author"));
ui->filterLineEdit->setCurrentFilter(COLUMN_MSG_TITLE);
/* add image actions */
ui->attachmentLabel->addContextMenuAction(ui->actionAttachmentCopyLinkLocation);
connect(ui->actionAttachmentCopyLinkLocation, &QAction::triggered, this, &FeedReaderMessageWidget::attachmentCopyLinkLocation);
/* load settings */
processSettings(true);
@ -175,6 +192,7 @@ void FeedReaderMessageWidget::processSettings(bool load)
// state of splitter
ui->msgSplitter->restoreState(Settings->value("msgSplitter").toByteArray());
ui->pictureSplitter->restoreState(Settings->value("pictureSplitter").toByteArray());
} else {
// save settings
@ -183,6 +201,7 @@ void FeedReaderMessageWidget::processSettings(bool load)
// state of splitter
Settings->setValue("msgSplitter", ui->msgSplitter->saveState());
Settings->setValue("pictureSplitter", ui->pictureSplitter->saveState());
}
Settings->endGroup();
@ -245,9 +264,19 @@ void FeedReaderMessageWidget::setFeedId(uint32_t feedId)
ui->msgReadAllButton->setEnabled(false);
ui->msgTreeWidget->setPlaceholderText("");
} else {
if (mFeedInfo.flag.forum) {
if (mFeedInfo.flag.forum || mFeedInfo.flag.posted) {
ui->msgReadAllButton->setEnabled(false);
if (mFeedInfo.flag.forum && mFeedInfo.flag.posted) {
ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the forum and the board"));
} else {
if (mFeedInfo.flag.forum) {
ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the forum"));
}
if (mFeedInfo.flag.posted) {
ui->msgTreeWidget->setPlaceholderText(tr("The messages will be added to the board"));
}
}
} else {
ui->msgReadAllButton->setEnabled(true);
ui->msgTreeWidget->setPlaceholderText("");
@ -340,6 +369,20 @@ void FeedReaderMessageWidget::msgTreeCustomPopupMenu(QPoint /*point*/)
action = contextMnu.addAction(QIcon(""), tr("Remove"), this, SLOT(removeMsg()));
action->setEnabled(!selectedItems.empty());
if (selectedItems.size() == 1 && (mFeedReader->forums() || mFeedReader->posted())) {
contextMnu.addSeparator();
if (mFeedReader->forums()) {
QMenu *menu = contextMnu.addMenu(tr("Add to forum"));
connect(menu, SIGNAL(aboutToShow()), this, SLOT(fillForumMenu()));
}
if (mFeedReader->posted()) {
QMenu *menu = contextMnu.addMenu(tr("Add to board"));
connect(menu, SIGNAL(aboutToShow()), this, SLOT(fillPostedMenu()));
}
}
contextMnu.addSeparator();
action = contextMnu.addAction(QIcon(""), tr("Retransform"), this, SLOT(retransformMsg()));
@ -571,6 +614,21 @@ void FeedReaderMessageWidget::msgItemChanged()
mTimer->start();
}
void FeedReaderMessageWidget::clearMessage()
{
ui->msgTitle->clear();
// ui->msgLink->clear();
ui->msgText->clear();
ui->msgTextSplitter->clear();
ui->attachmentLabel->clear();
ui->linkButton->setEnabled(false);
ui->msgText->show();
ui->pictureSplitter->hide();
ui->actionAttachmentCopyLinkLocation->setData(QVariant());
ui->actionAttachmentCopyLinkLocation->setEnabled(false);
}
void FeedReaderMessageWidget::updateCurrentMessage()
{
mTimer->stop();
@ -580,10 +638,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
std::string msgId = currentMsgId();
if (mFeedId == 0 || msgId.empty()) {
ui->msgTitle->clear();
// ui->msgLink->clear();
ui->msgText->clear();
ui->linkButton->setEnabled(false);
clearMessage();
ui->msgReadButton->setEnabled(false);
ui->msgUnreadButton->setEnabled(false);
@ -594,10 +649,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
QTreeWidgetItem *item = ui->msgTreeWidget->currentItem();
if (!item) {
/* there is something wrong */
ui->msgTitle->clear();
// ui->msgLink->clear();
ui->msgText->clear();
ui->linkButton->setEnabled(false);
clearMessage();
ui->msgReadButton->setEnabled(false);
ui->msgUnreadButton->setEnabled(false);
@ -612,10 +664,7 @@ void FeedReaderMessageWidget::updateCurrentMessage()
/* get msg */
FeedMsgInfo msgInfo;
if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) {
ui->msgTitle->clear();
// ui->msgLink->clear();
ui->msgText->clear();
ui->linkButton->setEnabled(false);
clearMessage();
return;
}
@ -635,9 +684,41 @@ void FeedReaderMessageWidget::updateCurrentMessage()
setMsgAsReadUnread(row, setToReadOnActive);
}
ui->actionAttachmentCopyLinkLocation->setData(QVariant());
ui->actionAttachmentCopyLinkLocation->setEnabled(false);
if (!msgInfo.attachment.empty()) {
QByteArray imageData((char*) msgInfo.attachment.data(), msgInfo.attachment.size());
QPixmap pixmap;
if (pixmap.loadFromData(imageData)) {
ui->attachmentLabel->setPixmap(pixmap);
ui->pictureSplitter->show();
ui->msgText->hide();
} else {
ui->pictureSplitter->hide();
ui->msgText->show();
}
if (!msgInfo.attachmentLink.empty()) {
ui->actionAttachmentCopyLinkLocation->setData(QString::fromUtf8(msgInfo.attachmentLink.c_str()));
ui->actionAttachmentCopyLinkLocation->setEnabled(true);
}
} else {
ui->pictureSplitter->hide();
ui->msgText->show();
}
QString msgTxt = RsHtml().formatText(ui->msgText->document(), QString::fromUtf8((msgInfo.descriptionTransformed.empty() ? msgInfo.description : msgInfo.descriptionTransformed).c_str()), RSHTML_FORMATTEXT_EMBED_LINKS);
if (ui->pictureSplitter->isVisible()) {
ui->msgTextSplitter->setHtml(msgTxt);
ui->msgText->clear();
} else {
ui->msgText->setHtml(msgTxt);
ui->msgTextSplitter->clear();
ui->attachmentLabel->clear();
}
ui->msgTitle->setText(QString::fromUtf8(msgInfo.title.c_str()));
ui->linkButton->setEnabled(!msgInfo.link.empty());
@ -714,11 +795,11 @@ void FeedReaderMessageWidget::toggleMsgText()
void FeedReaderMessageWidget::toggleMsgText_internal()
{
if (ui->expandButton->isChecked()) {
ui->msgText->setVisible(true);
ui->horizontalLayoutWidget->setVisible(true);
ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png")));
ui->expandButton->setToolTip(tr("Hide"));
} else {
ui->msgText->setVisible(false);
ui->horizontalLayoutWidget->setVisible(false);
ui->expandButton->setIcon(QIcon(QString(":/images/edit_add24.png")));
ui->expandButton->setToolTip(tr("Expand"));
}
@ -840,3 +921,123 @@ void FeedReaderMessageWidget::openLinkMsg()
QDesktopServices::openUrl(QUrl(link));
}
void FeedReaderMessageWidget::fillForumMenu()
{
QMenu *menu = dynamic_cast<QMenu*>(sender()) ;
if (!menu) {
return;
}
disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(fillForumMenu()));
std::vector<RsGxsForumGroup> groups;
if (mFeedReader->getForumGroups(groups, true)) {
for (std::vector<RsGxsForumGroup>::iterator it = groups.begin(); it != groups.end(); ++it) {
const RsGxsForumGroup &group = *it;
QAction *action = menu->addAction(QString::fromUtf8(group.mMeta.mGroupName.c_str()), this, SLOT(addToForum()));
action->setData(QString::fromUtf8(group.mMeta.mGroupId.toStdString().c_str()));
}
}
}
void FeedReaderMessageWidget::fillPostedMenu()
{
QMenu *menu = dynamic_cast<QMenu*>(sender()) ;
if (!menu) {
return;
}
disconnect(menu, SIGNAL(aboutToShow()), this, SLOT(fillPostedMenu()));
std::vector<RsPostedGroup> groups;
if (mFeedReader->getPostedGroups(groups, true)) {
for (std::vector<RsPostedGroup>::iterator it = groups.begin(); it != groups.end(); ++it) {
const RsPostedGroup &group = *it;
QAction *action = menu->addAction(QString::fromUtf8(group.mMeta.mGroupName.c_str()), this, SLOT(addToPosted()));
action->setData(QString::fromUtf8(group.mMeta.mGroupId.toStdString().c_str()));
}
}
}
void FeedReaderMessageWidget::addToForum()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QString id = action->data().toString();
if (id.isEmpty()) {
return;
}
QList<QTreeWidgetItem*> selectedItems = ui->msgTreeWidget->selectedItems();
if (selectedItems.size() != 1) {
return;
}
std::string msgId = selectedItems[0]->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString();
FeedMsgInfo msgInfo;
if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) {
return;
}
RsGxsGroupId forumId(id.toStdString());
CreateGxsForumMsg *msgDialog = new CreateGxsForumMsg(forumId, RsGxsMessageId(), RsGxsMessageId(), RsGxsId()) ;
msgDialog->setSubject(QString::fromUtf8(msgInfo.title.c_str()));
msgDialog->insertPastedText(QString::fromUtf8(msgInfo.description.c_str()));
msgDialog->show();
}
void FeedReaderMessageWidget::addToPosted()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QString id = action->data().toString();
if (id.isEmpty()) {
return;
}
QList<QTreeWidgetItem*> selectedItems = ui->msgTreeWidget->selectedItems();
if (selectedItems.size() != 1) {
return;
}
std::string msgId = selectedItems[0]->data(COLUMN_MSG_DATA, ROLE_MSG_ID).toString().toStdString();
FeedMsgInfo msgInfo;
if (!mFeedReader->getMsgInfo(mFeedId, msgId, msgInfo)) {
return;
}
RsGxsGroupId postedId(id.toStdString());
PostedCreatePostDialog *msgDialog = new PostedCreatePostDialog(mFeedReader->posted(), postedId);
msgDialog->setTitle(QString::fromUtf8(msgInfo.title.c_str()));
msgDialog->setNotes(QString::fromUtf8(msgInfo.description.c_str()));
msgDialog->setLink(QString::fromUtf8(msgInfo.link.c_str()));
msgDialog->show();
}
void FeedReaderMessageWidget::attachmentCopyLinkLocation()
{
QAction *action = dynamic_cast<QAction*>(sender()) ;
if (!action) {
return;
}
QVariant data = action->data();
if (!data.isValid()) {
return;
}
if (data.type() != QVariant::String) {
return;
}
QApplication::clipboard()->setText(data.toString());
}

View file

@ -73,6 +73,11 @@ private slots:
void openLinkMsg();
void copyLinkMsg();
void retransformMsg();
void fillForumMenu();
void fillPostedMenu();
void addToForum();
void addToPosted();
void attachmentCopyLinkLocation();
/* FeedReaderNotify */
void feedChanged(uint32_t feedId, int type);
@ -88,6 +93,7 @@ private:
void filterItem(QTreeWidgetItem *item, const QString &text, int filterColumn);
void filterItem(QTreeWidgetItem *item);
void toggleMsgText_internal();
void clearMessage();
bool mProcessSettings;
RSTreeWidgetItemCompareRole *mMsgCompareRole;

View file

@ -182,7 +182,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../../retroshare-gui/src/gui/images.qrc">
<iconset>
<normaloff>:/images/message-state-header.png</normaloff>:/images/message-state-header.png</iconset>
</property>
</column>
@ -245,7 +245,7 @@
<string/>
</property>
<property name="icon">
<iconset resource="../../../retroshare-gui/src/gui/images.qrc">
<iconset>
<normaloff>:/images/edit_remove24.png</normaloff>:/images/edit_remove24.png</iconset>
</property>
<property name="checkable">
@ -260,6 +260,9 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget">
<layout class="QHBoxLayout" name="pictureHorizontalLayout">
<item>
<widget class="RSTextBrowser" name="msgText">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -271,10 +274,50 @@
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QSplitter" name="pictureSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="AspectRatioPixmapLabel" name="attachmentLabel">
<property name="text">
<string notr="true">pictureLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="RSTextBrowser" name="msgTextSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
<action name="actionAttachmentCopyLinkLocation">
<property name="text">
<string>Copy Link Location</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>RSTextBrowser</class>
@ -297,10 +340,14 @@
<header location="global">gui/common/ElidedLabel.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AspectRatioPixmapLabel</class>
<extends>QLabel</extends>
<header location="global">util/AspectRatioPixmapLabel.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="FeedReader_images.qrc"/>
<include location="../../../retroshare-gui/src/gui/images.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -33,3 +33,8 @@ void FeedReaderNotify::notifyMsgChanged(uint32_t feedId, const std::string &msgI
{
emit msgChanged(feedId, QString::fromStdString(msgId), type);
}
void FeedReaderNotify::notifyOptimizeImage()
{
emit optimizeImage();
}

View file

@ -34,10 +34,12 @@ public:
/* RsFeedReaderNotify */
virtual void notifyFeedChanged(uint32_t feedId, int type);
virtual void notifyMsgChanged(uint32_t feedId, const std::string &msgId, int type);
virtual void notifyOptimizeImage();
signals:
void feedChanged(uint32_t feedId, int type);
void msgChanged(uint32_t feedId, const QString &msgId, int type);
void optimizeImage();
};
#endif

View file

@ -23,27 +23,27 @@
#include "FeedReaderStringDefs.h"
bool FeedReaderStringDefs::showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text)
bool FeedReaderStringDefs::showError(QWidget *parent, RsFeedResult result, const QString &title, const QString &text)
{
QString error;
switch (result) {
case RS_FEED_ADD_RESULT_SUCCESS:
case RS_FEED_RESULT_SUCCESS:
/* no error */
return false;
case RS_FEED_ADD_RESULT_FEED_NOT_FOUND:
case RS_FEED_RESULT_FEED_NOT_FOUND:
error = QApplication::translate("FeedReaderStringDefs", "Feed not found.");
break;
case RS_FEED_ADD_RESULT_PARENT_NOT_FOUND:
case RS_FEED_RESULT_PARENT_NOT_FOUND:
error = QApplication::translate("FeedReaderStringDefs", "Parent not found.");
break;
case RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER:
case RS_FEED_RESULT_PARENT_IS_NO_FOLDER:
error = QApplication::translate("FeedReaderStringDefs", "Parent is no folder.");
break;
case RS_FEED_ADD_RESULT_FEED_IS_FOLDER:
case RS_FEED_RESULT_FEED_IS_FOLDER:
error = QApplication::translate("FeedReaderStringDefs", "Feed is a folder.");
break;
case RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER:
case RS_FEED_RESULT_FEED_IS_NO_FOLDER:
error = QApplication::translate("FeedReaderStringDefs", "Feed is no folder.");
break;
default:
@ -101,6 +101,9 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
case RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE:
errorText = QApplication::translate("FeedReaderStringDefs", "Unknown response code");
break;
case RS_FEED_ERRORSTATE_DOWNLOAD_BLOCKED:
errorText = QApplication::translate("FeedReaderStringDefs", "Download blocked");
break;
/* process */
case RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR:
@ -109,9 +112,9 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
case RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT:
errorText = QApplication::translate("FeedReaderStringDefs", "Unknown XML format");
break;
case RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE:
errorText = QApplication::translate("FeedReaderStringDefs", "Can't create forum");
break;
// case RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE:
// errorText = QApplication::translate("FeedReaderStringDefs", "Can't create forum");
// break;
case RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND:
errorText = QApplication::translate("FeedReaderStringDefs", "Forum not found");
break;
@ -121,6 +124,21 @@ QString FeedReaderStringDefs::errorString(RsFeedReaderErrorState errorState, con
case RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_AUTHOR:
errorText = QApplication::translate("FeedReaderStringDefs", "Forum has no author");
break;
// case RS_FEED_ERRORSTATE_PROCESS_POSTED_CREATE:
// errorText = QApplication::translate("FeedReaderStringDefs", "Can't create board");
// break;
case RS_FEED_ERRORSTATE_PROCESS_POSTED_NOT_FOUND:
errorText = QApplication::translate("FeedReaderStringDefs", "Board not found");
break;
case RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_ADMIN:
errorText = QApplication::translate("FeedReaderStringDefs", "You are not admin of the board");
break;
case RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_AUTHOR:
errorText = QApplication::translate("FeedReaderStringDefs", "Board has no author");
break;
case RS_FEED_ERRORSTATE_PROCESS_POST:
errorText = QApplication::translate("FeedReaderStringDefs", "Some posts could not be created");
break;
case RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR:
errorText = QApplication::translate("FeedReaderStringDefs", "Can't read html");

View file

@ -30,7 +30,7 @@ class QWidget;
class FeedReaderStringDefs
{
public:
static bool showError(QWidget *parent, RsFeedAddResult result, const QString &title, const QString &text);
static bool showError(QWidget *parent, RsFeedResult result, const QString &title, const QString &text);
static QString workState(FeedInfo::WorkState state);
static QString errorString(const FeedInfo &feedInfo);
static QString errorString(RsFeedReaderErrorState errorState, const std::string &errorString);

View file

@ -0,0 +1,119 @@
/*******************************************************************************
* plugins/FeedReader/gui/FeedTreeWidget.cpp *
* *
* Copyright (C) 2012 RetroShare Team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <QDropEvent>
#include "FeedTreeWidget.h"
FeedTreeWidget::FeedTreeWidget(QWidget *parent) : RSTreeWidget(parent)
{
mDraggedItem = NULL;
}
void FeedTreeWidget::dragEnterEvent(QDragEnterEvent *event)
{
mDraggedItem = currentItem();
RSTreeWidget::dragEnterEvent(event);
}
void FeedTreeWidget::dragLeaveEvent(QDragLeaveEvent *event)
{
RSTreeWidget::dragLeaveEvent(event);
mDraggedItem = NULL;
}
bool FeedTreeWidget::canDrop(QDropEvent *event, QTreeWidgetItem **dropItem)
{
if (dropItem) {
*dropItem = NULL;
}
if (!mDraggedItem) {
/* no drag item */
return false;
}
QModelIndex droppedIndex = indexAt(event->pos());
if (!droppedIndex.isValid()) {
/* no drop target */
return false;
}
QTreeWidgetItem *dropItemIntern = itemFromIndex(droppedIndex);
if (!dropItemIntern) {
/* no drop item */
return false;
}
if ((dropItemIntern->flags() & Qt::ItemIsDropEnabled) == 0) {
/* drop is disabled */
return false;
}
if (dropItemIntern == mDraggedItem->parent()) {
/* drag item parent */
return false;
}
if (dropItem) {
*dropItem = dropItemIntern;
}
return true;
}
void FeedTreeWidget::dragMoveEvent(QDragMoveEvent *event)
{
if (!canDrop(event)) {
event->ignore();
return;
}
RSTreeWidget::dragMoveEvent(event);
}
void FeedTreeWidget::dropEvent(QDropEvent *event)
{
QTreeWidgetItem *dropItem;
if (!canDrop(event, &dropItem)) {
event->ignore();
return;
}
if (!mDraggedItem) {
/* no drag item */
event->ignore();
return;
}
QTreeWidgetItem *draggedParent = mDraggedItem->parent();
if (!draggedParent) {
/* no drag item parent */
event->ignore();
return;
}
if (!dropItem) {
/* no drop item */
event->ignore();
return;
}
emit feedReparent(mDraggedItem, dropItem);
}

View file

@ -0,0 +1,50 @@
/*******************************************************************************
* plugins/FeedReader/gui/FeedTreeWidget.h *
* *
* Copyright (C) 2012 by Retroshare Team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#ifndef _FEEDTREEWIDGET_H
#define _FEEDTREEWIDGET_H
#include "gui/common/RSTreeWidget.h"
/* Subclassing RSTreeWidget */
class FeedTreeWidget : public RSTreeWidget
{
Q_OBJECT
public:
FeedTreeWidget(QWidget *parent = 0);
Q_SIGNALS:
void feedReparent(QTreeWidgetItem *item, QTreeWidgetItem *newParent);
protected:
void dragEnterEvent(QDragEnterEvent *event);
void dragLeaveEvent(QDragLeaveEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent *event);
private:
bool canDrop(QDropEvent *event, QTreeWidgetItem **dropItem = NULL);
private:
QTreeWidgetItem *mDraggedItem;
};
#endif

View file

@ -25,8 +25,13 @@
#include <inttypes.h>
#include <string>
#include <list>
#include <vector>
class RsFeedReader;
class RsGxsForums;
class RsPosted;
class RsGxsForumGroup;
class RsPostedGroup;
extern RsFeedReader *rsFeedReader;
enum RsFeedReaderErrorState {
@ -38,14 +43,20 @@ enum RsFeedReaderErrorState {
RS_FEED_ERRORSTATE_DOWNLOAD_UNKNOWN_CONTENT_TYPE = 3,
RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND = 4,
RS_FEED_ERRORSTATE_DOWNLOAD_UNKOWN_RESPONSE_CODE = 5,
RS_FEED_ERRORSTATE_DOWNLOAD_BLOCKED = 6,
/* process */
RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR = 50,
RS_FEED_ERRORSTATE_PROCESS_UNKNOWN_FORMAT = 51,
RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE = 100,
// RS_FEED_ERRORSTATE_PROCESS_FORUM_CREATE = 100,
RS_FEED_ERRORSTATE_PROCESS_FORUM_NOT_FOUND = 101,
RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_ADMIN = 102,
RS_FEED_ERRORSTATE_PROCESS_FORUM_NO_AUTHOR = 103,
// RS_FEED_ERRORSTATE_PROCESS_POSTED_CREATE = 104,
RS_FEED_ERRORSTATE_PROCESS_POSTED_NOT_FOUND = 105,
RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_ADMIN = 106,
RS_FEED_ERRORSTATE_PROCESS_POSTED_NO_AUTHOR = 107,
RS_FEED_ERRORSTATE_PROCESS_POST = 108,
RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR = 150,
RS_FEED_ERRORSTATE_PROCESS_XPATH_INTERNAL_ERROR = 151,
@ -56,14 +67,14 @@ enum RsFeedReaderErrorState {
RS_FEED_ERRORSTATE_PROCESS_XSLT_NO_RESULT = 156
};
enum RsFeedAddResult
enum RsFeedResult
{
RS_FEED_ADD_RESULT_SUCCESS,
RS_FEED_ADD_RESULT_FEED_NOT_FOUND,
RS_FEED_ADD_RESULT_PARENT_NOT_FOUND,
RS_FEED_ADD_RESULT_PARENT_IS_NO_FOLDER,
RS_FEED_ADD_RESULT_FEED_IS_FOLDER,
RS_FEED_ADD_RESULT_FEED_IS_NO_FOLDER
RS_FEED_RESULT_SUCCESS,
RS_FEED_RESULT_FEED_NOT_FOUND,
RS_FEED_RESULT_PARENT_NOT_FOUND,
RS_FEED_RESULT_PARENT_IS_NO_FOLDER,
RS_FEED_RESULT_FEED_IS_FOLDER,
RS_FEED_RESULT_FEED_IS_NO_FOLDER
};
enum RsFeedTransformationType
@ -102,6 +113,8 @@ public:
flag.deactivated = false;
flag.forum = false;
flag.updateForumInfo = false;
flag.posted = false;
flag.updatePostedInfo = false;
flag.embedImages = false;
flag.saveCompletePage = false;
flag.preview = false;
@ -122,6 +135,7 @@ public:
time_t lastUpdate;
uint32_t storageTime;
std::string forumId;
std::string postedId;
WorkState workstate;
RsFeedReaderErrorState errorState;
std::string errorString;
@ -141,6 +155,11 @@ public:
bool deactivated : 1;
bool forum : 1;
bool updateForumInfo : 1;
bool posted : 1;
bool updatePostedInfo : 1;
bool postedFirstImage : 1;
bool postedOnlyImage : 1;
bool postedShrinkImage : 1;
bool embedImages : 1;
bool saveCompletePage : 1;
bool preview : 1;
@ -166,6 +185,9 @@ public:
std::string description;
std::string descriptionTransformed;
time_t pubDate;
std::string attachmentLink;
std::vector<unsigned char> attachment;
std::string attachmentMimeType;
struct {
bool isnew : 1;
@ -174,6 +196,32 @@ public:
} flag;
};
class FeedReaderOptimizeImageTask
{
public:
enum Type {
POSTED,
SIZE
};
public:
Type mType;
std::vector<unsigned char> mImage;
std::string mMimeType;
std::vector<unsigned char> mImageResult;
std::string mMimeTypeResult;
bool mResult;
public:
FeedReaderOptimizeImageTask(Type type, const std::vector<unsigned char> &image, const std::string &mimeType)
{
mType = type;
mImage = image;
mMimeType = mimeType;
mResult = false;
}
};
class RsFeedReaderNotify
{
public:
@ -181,6 +229,7 @@ public:
virtual void notifyFeedChanged(uint32_t /*feedId*/, int /*type*/) {}
virtual void notifyMsgChanged(uint32_t /*feedId*/, const std::string &/*msgId*/, int /*type*/) {}
virtual void notifyOptimizeImage() {}
};
class RsFeedReader
@ -201,10 +250,11 @@ public:
virtual bool getSaveInBackground() = 0;
virtual void setSaveInBackground(bool saveInBackground) = 0;
virtual RsFeedAddResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId) = 0;
virtual RsFeedAddResult setFolder(uint32_t feedId, const std::string &name) = 0;
virtual RsFeedAddResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId) = 0;
virtual RsFeedAddResult setFeed(uint32_t feedId, const FeedInfo &feedInfo) = 0;
virtual RsFeedResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId) = 0;
virtual RsFeedResult setFolder(uint32_t feedId, const std::string &name) = 0;
virtual RsFeedResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId) = 0;
virtual RsFeedResult setFeed(uint32_t feedId, const FeedInfo &feedInfo) = 0;
virtual RsFeedResult setParent(uint32_t feedId, uint32_t parentId) = 0;
virtual bool removeFeed(uint32_t feedId) = 0;
virtual bool addPreviewFeed(const FeedInfo &feedInfo, uint32_t &feedId) = 0;
virtual void getFeedList(uint32_t parentId, std::list<FeedInfo> &feedInfos) = 0;
@ -220,6 +270,14 @@ public:
virtual bool retransformMsg(uint32_t feedId, const std::string &msgId) = 0;
virtual bool clearMessageCache(uint32_t feedId) = 0;
virtual RsGxsForums* forums() = 0;
virtual RsPosted* posted() = 0;
virtual bool getForumGroups(std::vector<RsGxsForumGroup> &groups, bool onlyOwn) = 0;
virtual bool getPostedGroups(std::vector<RsPostedGroup> &groups, bool onlyOwn) = 0;
virtual FeedReaderOptimizeImageTask *getOptimizeImageTask() = 0;
virtual void setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask) = 0;
virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString) = 0;
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString) = 0;
};

View file

@ -9,7 +9,32 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+79"/>
<location line="+398"/>
<source>Board</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+9"/>
<source>Update board information</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<source>Only image</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<source>Use first image as board image</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<source>Shrink image</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="-391"/>
<source>Authentication (not yet supported)</source>
<translation type="unfinished"></translation>
</message>
@ -59,12 +84,12 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+121"/>
<location line="+88"/>
<source>Embed images</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="-249"/>
<location line="-216"/>
<source>Storage time</source>
<translation type="unfinished"></translation>
</message>
@ -99,22 +124,22 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="-148"/>
<location line="+200"/>
<source>Type</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+20"/>
<location line="+24"/>
<source>Forum</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+12"/>
<location line="+10"/>
<source>Local Feed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+239"/>
<location line="-67"/>
<source>Transformation</source>
<translation type="unfinished"></translation>
</message>
@ -129,7 +154,7 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+12"/>
<location line="-58"/>
<source>Misc</source>
<translation type="unfinished"></translation>
</message>
@ -144,17 +169,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<location line="+100"/>
<source>Update forum information</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+14"/>
<location line="-86"/>
<source>Save complete web page (experimental for local feeds)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="-103"/>
<location line="-70"/>
<source>Description:</source>
<translation type="unfinished"></translation>
</message>
@ -164,18 +189,18 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+10"/>
<location line="+17"/>
<source>Name:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/AddFeedDialog.cpp" line="+77"/>
<location filename="../gui/AddFeedDialog.cpp" line="+87"/>
<source>Feed Details</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+142"/>
<location line="+115"/>
<location line="+211"/>
<location line="+152"/>
<location line="+15"/>
<source>Edit feed</source>
<translation type="unfinished"></translation>
@ -277,7 +302,7 @@
<context>
<name>FeedReaderDialog</name>
<message>
<location filename="../gui/FeedReaderDialog.ui" line="+91"/>
<location filename="../gui/FeedReaderDialog.ui" line="+98"/>
<source>Feeds</source>
<translation type="unfinished"></translation>
</message>
@ -292,7 +317,7 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../gui/FeedReaderDialog.cpp" line="+92"/>
<location filename="../gui/FeedReaderDialog.cpp" line="+101"/>
<source>Message Folders</source>
<translation type="unfinished"></translation>
</message>
@ -342,12 +367,12 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+195"/>
<location line="+198"/>
<source>No name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+210"/>
<location line="+241"/>
<source>Add new folder</source>
<translation type="unfinished"></translation>
</message>
@ -398,11 +423,21 @@
<source>Please enter a new name for the folder</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+61"/>
<source>Move feed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>Cannot move feed.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FeedReaderFeedItem</name>
<message>
<location filename="../gui/FeedReaderFeedItem.ui" line="+195"/>
<location filename="../gui/FeedReaderFeedItem.ui" line="+197"/>
<location filename="../gui/FeedReaderFeedItem.cpp" line="+116"/>
<source>Expand</source>
<translation type="unfinished"></translation>
@ -500,7 +535,7 @@
</message>
<message>
<location line="+26"/>
<location filename="../gui/FeedReaderMessageWidget.cpp" line="+113"/>
<location filename="../gui/FeedReaderMessageWidget.cpp" line="+117"/>
<source>Title</source>
<translation type="unfinished"></translation>
</message>
@ -542,12 +577,22 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+122"/>
<location line="+127"/>
<source>The messages will be added to the forum</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+15"/>
<location line="-3"/>
<source>The messages will be added to the forum and the board</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+6"/>
<source>The messages will be added to the board</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+17"/>
<source>No name</source>
<translation type="unfinished"></translation>
</message>
@ -576,8 +621,18 @@
<source>Remove</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<source>Add to forum</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+5"/>
<source>Add to board</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<source>Retransform</source>
<translation type="unfinished"></translation>
</message>
@ -659,12 +714,12 @@
</message>
<message>
<location line="+3"/>
<location line="+96"/>
<location line="+114"/>
<source>Unknown</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="-79"/>
<location line="-97"/>
<source>Internal download error</source>
<translation type="unfinished"></translation>
</message>
@ -688,6 +743,11 @@
<source>Unknown response code</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
<source>Download blocked</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+5"/>
<source>Internal process error</source>
@ -699,12 +759,7 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
<source>Can&apos;t create forum</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
<location line="+6"/>
<source>Forum not found</source>
<translation type="unfinished"></translation>
</message>
@ -718,6 +773,26 @@
<source>Forum has no author</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+6"/>
<source>Board not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
<source>You are not admin of the board</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
<source>Board has no author</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+3"/>
<source>Some posts could not be created</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+4"/>
<source>Can&apos;t read html</source>
@ -790,7 +865,7 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+47"/>
<location line="+55"/>
<source>Name:</source>
<translation type="unfinished"></translation>
</message>
@ -825,7 +900,7 @@
<translation type="unfinished"></translation>
</message>
<message>
<location line="+20"/>
<location line="+28"/>
<source>Title:</source>
<translation type="unfinished"></translation>
</message>

File diff suppressed because it is too large Load diff

View file

@ -33,11 +33,14 @@ class p3FeedReaderThread;
class RsGxsForums;
struct RsGxsForumGroup;
class RsPosted;
struct RsPostedGroup;
class RsGxsIfaceHelper;
class p3FeedReader : public RsPQIService, public RsFeedReader
{
public:
p3FeedReader(RsPluginHandler *pgHandler, RsGxsForums *forums);
p3FeedReader(RsPluginHandler *pgHandler, RsGxsForums *forums, RsPosted *posted);
/****************** FeedReader Interface *************/
virtual void stop();
@ -52,10 +55,11 @@ public:
virtual bool getSaveInBackground();
virtual void setSaveInBackground(bool saveInBackground);
virtual RsFeedAddResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId);
virtual RsFeedAddResult setFolder(uint32_t feedId, const std::string &name);
virtual RsFeedAddResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId);
virtual RsFeedAddResult setFeed(uint32_t feedId, const FeedInfo &feedInfo);
virtual RsFeedResult addFolder(uint32_t parentId, const std::string &name, uint32_t &feedId);
virtual RsFeedResult setFolder(uint32_t feedId, const std::string &name);
virtual RsFeedResult addFeed(const FeedInfo &feedInfo, uint32_t &feedId);
virtual RsFeedResult setFeed(uint32_t feedId, const FeedInfo &feedInfo);
virtual RsFeedResult setParent(uint32_t feedId, uint32_t parentId);
virtual bool removeFeed(uint32_t feedId);
virtual bool addPreviewFeed(const FeedInfo &feedInfo, uint32_t &feedId);
virtual void getFeedList(uint32_t parentId, std::list<FeedInfo> &feedInfos);
@ -71,6 +75,14 @@ public:
virtual bool retransformMsg(uint32_t feedId, const std::string &msgId);
virtual bool clearMessageCache(uint32_t feedId);
virtual RsGxsForums* forums() { return mForums; }
virtual RsPosted* posted() { return mPosted; }
virtual bool getForumGroups(std::vector<RsGxsForumGroup> &groups, bool onlyOwn);
virtual bool getPostedGroups(std::vector<RsPostedGroup> &groups, bool onlyOwn);
virtual FeedReaderOptimizeImageTask *getOptimizeImageTask();
virtual void setOptimizeImageTaskResult(FeedReaderOptimizeImageTask *optimizeImageTask);
virtual RsFeedReaderErrorState processXPath(const std::list<std::string> &xpathsToUse, const std::list<std::string> &xpathsToRemove, std::string &description, std::string &errorString);
virtual RsFeedReaderErrorState processXslt(const std::string &xslt, std::string &description, std::string &errorString);
@ -83,7 +95,7 @@ public:
void onDownloadSuccess(uint32_t feedId, const std::string &content, std::string &icon);
void onDownloadError(uint32_t feedId, RsFeedReaderErrorState result, const std::string &errorString);
void onProcessSuccess_filterMsg(uint32_t feedId, std::list<RsFeedReaderMsg*> &msgs);
void onProcessSuccess_addMsgs(uint32_t feedId, std::list<RsFeedReaderMsg*> &msgs, bool single);
void onProcessSuccess_addMsgs(uint32_t feedId, std::list<RsFeedReaderMsg*> &msgs);
void onProcessError(uint32_t feedId, RsFeedReaderErrorState result, const std::string &errorString);
bool getFeedToProcess(RsFeedReaderFeed &feed, uint32_t neededFeedId);
@ -92,7 +104,10 @@ public:
bool getForumGroup(const RsGxsGroupId &groupId, RsGxsForumGroup &forumGroup);
bool updateForumGroup(const RsGxsForumGroup &forumGroup, const std::string &groupName, const std::string &groupDescription);
bool waitForToken(uint32_t token);
bool getPostedGroup(const RsGxsGroupId &groupId, RsPostedGroup &postedGroup);
bool updatePostedGroup(const RsPostedGroup &postedGroup, const std::string &groupName, const std::string &groupDescription);
bool waitForToken(RsGxsIfaceHelper *interface, uint32_t token);
bool optimizeImage(FeedReaderOptimizeImageTask::Type type, const std::vector<unsigned char> &image, const std::string &mimeType, std::vector<unsigned char> &resultImage, std::string &resultMimeType);
protected:
/****************** p3Config STUFF *******************/
@ -109,6 +124,7 @@ private:
private:
time_t mLastClean;
RsGxsForums *mForums;
RsPosted *mPosted;
RsFeedReaderNotify *mNotify;
volatile bool mStopped;
@ -133,6 +149,10 @@ private:
RsMutex mProcessMutex;
std::list<uint32_t> mProcessFeeds;
RsMutex mImageMutex;
std::list<FeedReaderOptimizeImageTask*> mImages;
std::list<FeedReaderOptimizeImageTask*> mResultImages;
RsMutex mPreviewMutex;
p3FeedReaderThread *mPreviewDownloadThread;
p3FeedReaderThread *mPreviewProcessThread;

View file

@ -89,7 +89,7 @@ void p3FeedReaderThread::threadTick()
/* first, filter the messages */
mFeedReader->onProcessSuccess_filterMsg(feed.feedId, msgs);
if (isRunning()) {
/* second, process the descriptions */
/* second, process the descriptions and attachment */
for (it = msgs.begin(); it != msgs.end(); ) {
if (!isRunning()) {
break;
@ -107,7 +107,7 @@ void p3FeedReaderThread::threadTick()
std::list<RsFeedReaderMsg*> msgSingle;
msgSingle.push_back(mi);
mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgSingle, true);
mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgSingle);
/* delete not accepted message */
std::list<RsFeedReaderMsg*>::iterator it1;
@ -122,15 +122,18 @@ void p3FeedReaderThread::threadTick()
++it;
}
}
if (!feed.preview) {
if (isRunning()) {
if (result == RS_FEED_ERRORSTATE_OK) {
/* third, add messages */
mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgs, false);
mFeedReader->onProcessSuccess_addMsgs(feed.feedId, msgs);
} else {
mFeedReader->onProcessError(feed.feedId, result, errorString);
}
}
}
}
} else {
mFeedReader->onProcessError(feed.feedId, result, errorString);
}
@ -150,12 +153,12 @@ void p3FeedReaderThread::threadTick()
/****************************** Download ***********************************/
/***************************************************************************/
static bool isContentType(const std::string &contentType, const char *type)
bool p3FeedReaderThread::isContentType(const std::string &contentType, const char *type)
{
return (strncasecmp(contentType.c_str(), type, strlen(type)) == 0);
}
static bool toBase64(const std::vector<unsigned char> &data, std::string &base64)
bool p3FeedReaderThread::toBase64(const std::vector<unsigned char> &data, std::string &base64)
{
bool result = false;
@ -184,6 +187,28 @@ static bool toBase64(const std::vector<unsigned char> &data, std::string &base64
return result;
}
bool p3FeedReaderThread::fromBase64(const std::string &base64, std::vector<unsigned char> &data)
{
bool result = false;
BIO *b64 = BIO_new(BIO_f_base64());
if (b64) {
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO *source = BIO_new_mem_buf(base64.c_str(), -1); // read-only source
if (source) {
BIO_push(b64, source);
const int maxlen = base64.length() / 4 * 3 + 1;
data.resize(maxlen);
const int len = BIO_read(b64, data.data(), maxlen);
data.resize(len);
result = true;
}
BIO_free_all(b64);
}
return result;
}
static std::string getBaseLink(std::string link)
{
size_t found = link.rfind('/');
@ -196,7 +221,7 @@ static std::string getBaseLink(std::string link)
static std::string calculateLink(const std::string &baseLink, const std::string &link)
{
if (link.substr(0, 7) == "http://") {
if (link.substr(0, 7) == "http://" || link.substr(0, 8) == "https://") {
/* absolute link */
return link;
}
@ -204,8 +229,14 @@ static std::string calculateLink(const std::string &baseLink, const std::string
/* calculate link of base link */
std::string resultLink = baseLink;
/* link should begin with "http://" */
if (resultLink.substr(0, 7) != "http://") {
int hostStart = 0;
/* link should begin with "http://" or "https://" */
if (resultLink.substr(0, 7) == "http://") {
hostStart = 7;
} else if (resultLink.substr(0, 8) == "https://") {
hostStart = 8;
} else {
hostStart = 7;
resultLink.insert(0, "http://");
}
@ -216,7 +247,7 @@ static std::string calculateLink(const std::string &baseLink, const std::string
if (*link.begin() == '/') {
/* link begins with "/" */
size_t found = resultLink.find('/', 7);
size_t found = resultLink.find('/', hostStart);
if (found != std::string::npos) {
resultLink.erase(found);
}
@ -245,12 +276,12 @@ static bool getFavicon(CURLWrapper &CURL, const std::string &url, std::string &i
if (code == CURLE_OK) {
if (CURL.responseCode() == 200) {
std::string contentType = CURL.contentType();
if (isContentType(contentType, "image/x-icon") ||
isContentType(contentType, "application/octet-stream") ||
isContentType(contentType, "text/plain")) {
if (p3FeedReaderThread::isContentType(contentType, "image/") ||
p3FeedReaderThread::isContentType(contentType, "application/octet-stream") ||
p3FeedReaderThread::isContentType(contentType, "text/plain")) {
if (!vicon.empty()) {
#warning p3FeedReaderThread.cc TODO thunder2: check it
result = toBase64(vicon, icon);
result = p3FeedReaderThread::toBase64(vicon, icon);
}
}
}
@ -296,6 +327,9 @@ RsFeedReaderErrorState p3FeedReaderThread::download(const RsFeedReaderFeed &feed
}
}
break;
case 403:
result = RS_FEED_ERRORSTATE_DOWNLOAD_BLOCKED;
break;
case 404:
result = RS_FEED_ERRORSTATE_DOWNLOAD_NOT_FOUND;
break;
@ -814,6 +848,22 @@ RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed,
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
time_t minimumPubDate = 0;
if (feed.lastUpdate == 0) {
// Get all items on first scan
} else {
// Get storage time
uint32_t storageTime = 0;
if (feed.flag & RS_FEED_FLAG_STANDARD_STORAGE_TIME) {
storageTime = mFeedReader->getStandardStorageTime();
} else {
storageTime = feed.storageTime;
}
if (storageTime > 0) {
minimumPubDate = time(NULL) - storageTime;
}
}
XMLWrapper xml;
if (xml.readXML(feed.content.c_str())) {
xmlNodePtr root = xml.getRootElement();
@ -959,6 +1009,26 @@ RsFeedReaderErrorState p3FeedReaderThread::process(const RsFeedReaderFeed &feed,
item->pubDate = time(NULL);
}
if (feedFormat == FORMAT_RSS) {
/* <enclosure url="" type=""></enclosure> */
xmlNodePtr enclosure = xml.findNode(node->children, "enclosure", false);
if (enclosure) {
std::string enclosureMimeType = xml.getAttr(enclosure, "type");
std::string enclosureUrl = xml.getAttr(enclosure, "url");
if (!enclosureUrl.empty()) {
item->attachmentLink = enclosureUrl;
item->attachmentMimeType = enclosureMimeType;
}
}
}
if (minimumPubDate) {
if (item->pubDate < minimumPubDate) {
// pubDate is less than storage time, don't add as new item
continue;
}
}
entries.push_back(item);
}
} else {
@ -1013,10 +1083,44 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
RsFeedReaderErrorState result = RS_FEED_ERRORSTATE_OK;
std::string proxy = getProxyForFeed(feed);
/* attachment */
if (!msg->attachmentLink.empty()) {
if (isContentType(msg->attachmentMimeType, "image/")) {
CURLWrapper CURL(proxy);
CURLcode code = CURL.downloadBinary(msg->attachmentLink, msg->attachmentBinary);
if (code == CURLE_OK && CURL.responseCode() == 200) {
std::string contentType = CURL.contentType();
if (isContentType(contentType, "image/")) {
msg->attachmentBinaryMimeType = contentType;
bool forum = (feed.flag & RS_FEED_FLAG_FORUM) && !feed.preview;
bool posted = (feed.flag & RS_FEED_FLAG_POSTED) && !feed.preview;
if (!forum && ! posted) {
/* no need to optimize image */
std::vector<unsigned char> optimizedBinary;
std::string optimizedMimeType;
if (mFeedReader->optimizeImage(FeedReaderOptimizeImageTask::SIZE, msg->attachmentBinary, msg->attachmentBinaryMimeType, optimizedBinary, optimizedMimeType)) {
if (toBase64(optimizedBinary, msg->attachment)) {
msg->attachmentMimeType = optimizedMimeType;
} else {
msg->attachment.clear();
}
}
}
} else {
msg->attachmentBinary.clear();
}
} else {
msg->attachmentBinary.clear();
}
}
}
std::string url;
if (feed.flag & RS_FEED_FLAG_SAVE_COMPLETE_PAGE) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") download page " << msg->link << std::endl;
std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") download page " << msg->link << std::endl;
#endif
std::string content;
CURLWrapper CURL(proxy);
@ -1053,7 +1157,7 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (result != RS_FEED_ERRORSTATE_OK) {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot download page, CURLCode = " << code << ", error = " << errorString << std::endl;
std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot download page, CURLCode = " << code << ", error = " << errorString << std::endl;
#endif
return result;
}
@ -1070,12 +1174,19 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (isRunning()) {
/* process description */
bool processPostedFirstImage = (feed.flag & RS_FEED_FLAG_POSTED_FIRST_IMAGE) ? true : false;
if (!msg->attachmentBinary.empty()) {
/* use attachment as image */
processPostedFirstImage = false;
}
//long todo; // encoding
HTMLWrapper html;
if (html.readHTML(msg->description.c_str(), url.c_str())) {
xmlNodePtr root = html.getRootElement();
if (root) {
std::list<xmlNodePtr> nodesToDelete;
xmlNodePtr postedFirstImageNode = NULL;
/* process all children */
std::list<xmlNodePtr> nodes;
@ -1192,7 +1303,7 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (!src.empty()) {
/* download image */
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") download image " << src << std::endl;
std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") download image " << src << std::endl;
#endif
std::vector<unsigned char> data;
CURLWrapper CURL(proxy);
@ -1200,15 +1311,25 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (code == CURLE_OK && CURL.responseCode() == 200) {
std::string contentType = CURL.contentType();
if (isContentType(contentType, "image/")) {
std::vector<unsigned char> optimizedData;
std::string optimizedMimeType;
if (mFeedReader->optimizeImage(FeedReaderOptimizeImageTask::SIZE, data, contentType, optimizedData, optimizedMimeType)) {
std::string base64;
if (toBase64(data, base64)) {
if (toBase64(optimizedData, base64)) {
std::string imageBase64;
rs_sprintf(imageBase64, "data:%s;base64,%s", contentType.c_str(), base64.c_str());
rs_sprintf(imageBase64, "data:%s;base64,%s", optimizedMimeType.c_str(), base64.c_str());
if (html.setAttr(node, "src", imageBase64.c_str())) {
removeImage = false;
}
}
}
if (processPostedFirstImage && postedFirstImageNode == NULL) {
/* set first image */
msg->postedFirstImage = data;
msg->postedFirstImageMimeType = contentType;
postedFirstImageNode = node;
}
}
}
}
}
@ -1241,10 +1362,25 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
if (result == RS_FEED_ERRORSTATE_OK) {
if (isRunning()) {
if (!html.saveHTML(msg->description)) {
if (html.saveHTML(msg->description)) {
if (postedFirstImageNode) {
/* Remove first image and create description without the image */
xmlUnlinkNode(postedFirstImageNode);
xmlFreeNode(postedFirstImageNode);
if (!html.saveHTML(msg->postedDescriptionWithoutFirstImage)) {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl;
std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
}
}
} else {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot dump html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_INTERNAL_ERROR;
@ -1253,14 +1389,14 @@ RsFeedReaderErrorState p3FeedReaderThread::processMsg(const RsFeedReaderFeed &fe
}
} else {
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl;
std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") no root element" << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;
}
} else {
errorString = html.lastError();
#ifdef FEEDREADER_DEBUG
std::cerr << "p3FeedReaderThread::processHTML - feed " << feed.feedId << " (" << feed.name << ") cannot read html" << std::endl;
std::cerr << "p3FeedReaderThread::processMsg - feed " << feed.feedId << " (" << feed.name << ") cannot read html" << std::endl;
std::cerr << " Error: " << errorString << std::endl;
#endif
result = RS_FEED_ERRORSTATE_PROCESS_HTML_ERROR;

View file

@ -54,6 +54,11 @@ public:
static RsFeedReaderErrorState processXslt(const std::string &xslt, HTMLWrapper &html, std::string &errorString);
static RsFeedReaderErrorState processTransformation(const RsFeedReaderFeed &feed, RsFeedReaderMsg *msg, std::string &errorString);
static bool isContentType(const std::string &contentType, const char *type);
static bool toBase64(const std::vector<unsigned char> &data, std::string &base64);
static bool fromBase64(const std::string &base64, std::vector<unsigned char> &data);
private:
virtual void threadTick() override; /// @see RsTickingThread

View file

@ -46,6 +46,7 @@ void RsFeedReaderFeed::clear()
storageTime = 0;
flag = 0;
forumId.clear();
postedId.clear();
description.clear();
icon.clear();
errorState = RS_FEED_ERRORSTATE_OK;
@ -85,6 +86,7 @@ uint32_t RsFeedReaderSerialiser::sizeFeed(RsFeedReaderFeed *item)
s += item->xpathsToUse.TlvSize();
s += item->xpathsToRemove.TlvSize();
s += GetTlvStringSize(item->xslt);
s += GetTlvStringSize(item->postedId);
return s;
}
@ -108,7 +110,7 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u
offset += 8;
/* add values */
ok &= setRawUInt16(data, tlvsize, &offset, 2); /* version */
ok &= setRawUInt16(data, tlvsize, &offset, 3); /* version */
ok &= setRawUInt32(data, tlvsize, &offset, item->feedId);
ok &= setRawUInt32(data, tlvsize, &offset, item->parentId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LINK, item->url);
@ -124,6 +126,7 @@ bool RsFeedReaderSerialiser::serialiseFeed(RsFeedReaderFeed *item, void *data, u
ok &= setRawUInt32(data, tlvsize, &offset, item->storageTime);
ok &= setRawUInt32(data, tlvsize, &offset, item->flag);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->forumId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->postedId);
ok &= setRawUInt32(data, tlvsize, &offset, item->errorState);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_VALUE, item->errorString);
ok &= setRawUInt32(data, tlvsize, &offset, item->transformationType);
@ -208,6 +211,9 @@ RsFeedReaderFeed *RsFeedReaderSerialiser::deserialiseFeed(void *data, uint32_t *
ok &= getRawUInt32(data, rssize, &offset, &(item->storageTime));
ok &= getRawUInt32(data, rssize, &offset, &(item->flag));
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->forumId);
if (version >= 3) {
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_VALUE, item->postedId);
}
uint32_t errorState = 0;
ok &= getRawUInt32(data, rssize, &offset, &errorState);
item->errorState = (RsFeedReaderErrorState) errorState;
@ -268,6 +274,9 @@ void RsFeedReaderMsg::clear()
descriptionTransformed.clear();
pubDate = 0;
flag = 0;
attachmentLink.clear();
attachment.clear();
attachmentMimeType.clear();
}
std::ostream &RsFeedReaderMsg::print(std::ostream &out, uint16_t /*indent*/)
@ -288,6 +297,9 @@ uint32_t RsFeedReaderSerialiser::sizeMsg(RsFeedReaderMsg *item)
s += GetTlvStringSize(item->descriptionTransformed);
s += sizeof(uint32_t); /* pubDate */
s += sizeof(uint32_t); /* flag */
s += GetTlvStringSize(item->attachmentLink);
s += GetTlvStringSize(item->attachment);
s += GetTlvStringSize(item->attachmentMimeType);
return s;
}
@ -311,7 +323,7 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin
offset += 8;
/* add values */
ok &= setRawUInt16(data, tlvsize, &offset, 2); /* version */
ok &= setRawUInt16(data, tlvsize, &offset, 3); /* version */
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_GENID, item->msgId);
ok &= setRawUInt32(data, tlvsize, &offset, item->feedId);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_NAME, item->title);
@ -321,6 +333,9 @@ bool RsFeedReaderSerialiser::serialiseMsg(RsFeedReaderMsg *item, void *data, uin
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_COMMENT, item->descriptionTransformed);
ok &= setRawUInt32(data, tlvsize, &offset, item->pubDate);
ok &= setRawUInt32(data, tlvsize, &offset, item->flag);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_LOCATION, item->attachmentLink);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_PIC_AUTH, item->attachment);
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_PIC_TYPE, item->attachmentMimeType);
if (offset != tlvsize)
{
@ -384,6 +399,11 @@ RsFeedReaderMsg *RsFeedReaderSerialiser::deserialiseMsg(void *data, uint32_t *pk
}
ok &= getRawUInt32(data, rssize, &offset, (uint32_t*) &(item->pubDate));
ok &= getRawUInt32(data, rssize, &offset, &(item->flag));
if (version >= 3) {
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_LOCATION, item->attachmentLink);
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_PIC_AUTH, item->attachment);
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_PIC_TYPE, item->attachmentMimeType);
}
if (offset != rssize)
{

View file

@ -34,17 +34,22 @@ const uint8_t RS_PKT_SUBTYPE_FEEDREADER_MSG = 0x03;
/**************************************************************************/
#define RS_FEED_FLAG_FOLDER 0x001
#define RS_FEED_FLAG_INFO_FROM_FEED 0x002
#define RS_FEED_FLAG_STANDARD_STORAGE_TIME 0x004
#define RS_FEED_FLAG_STANDARD_UPDATE_INTERVAL 0x008
#define RS_FEED_FLAG_STANDARD_PROXY 0x010
#define RS_FEED_FLAG_AUTHENTICATION 0x020
#define RS_FEED_FLAG_DEACTIVATED 0x040
#define RS_FEED_FLAG_FORUM 0x080
#define RS_FEED_FLAG_UPDATE_FORUM_INFO 0x100
#define RS_FEED_FLAG_EMBED_IMAGES 0x200
#define RS_FEED_FLAG_SAVE_COMPLETE_PAGE 0x400
#define RS_FEED_FLAG_FOLDER 0x0001
#define RS_FEED_FLAG_INFO_FROM_FEED 0x0002
#define RS_FEED_FLAG_STANDARD_STORAGE_TIME 0x0004
#define RS_FEED_FLAG_STANDARD_UPDATE_INTERVAL 0x0008
#define RS_FEED_FLAG_STANDARD_PROXY 0x0010
#define RS_FEED_FLAG_AUTHENTICATION 0x0020
#define RS_FEED_FLAG_DEACTIVATED 0x0040
#define RS_FEED_FLAG_FORUM 0x0080
#define RS_FEED_FLAG_UPDATE_FORUM_INFO 0x0100
#define RS_FEED_FLAG_EMBED_IMAGES 0x0200
#define RS_FEED_FLAG_SAVE_COMPLETE_PAGE 0x0400
#define RS_FEED_FLAG_POSTED 0x0800
#define RS_FEED_FLAG_UPDATE_POSTED_INFO 0x1000
#define RS_FEED_FLAG_POSTED_FIRST_IMAGE 0x2000
#define RS_FEED_FLAG_POSTED_ONLY_IMAGE 0x4000
#define RS_FEED_FLAG_POSTED_SHRINK_IMAGE 0x8000
class RsFeedReaderFeed : public RsItem
{
@ -62,6 +67,7 @@ public:
virtual ~RsFeedReaderFeed() {}
virtual void clear();
virtual void serial_process(RsGenericSerializer::SerializeJob, RsGenericSerializer::SerializeContext&) {}
uint32_t feedId;
uint32_t parentId;
@ -75,6 +81,7 @@ public:
time_t lastUpdate;
uint32_t flag; // RS_FEED_FLAG_...
std::string forumId;
std::string postedId;
uint32_t storageTime;
std::string description;
std::string icon;
@ -105,6 +112,7 @@ public:
virtual ~RsFeedReaderMsg() {}
virtual void clear();
virtual void serial_process(RsGenericSerializer::SerializeJob, RsGenericSerializer::SerializeContext&) {}
virtual std::ostream& print(std::ostream &out, uint16_t indent = 0);
std::string msgId;
@ -116,6 +124,16 @@ public:
std::string descriptionTransformed;
time_t pubDate;
uint32_t flag; // RS_FEEDMSG_FLAG_...
std::string attachmentLink;
std::string attachment; // binary as base64
std::string attachmentMimeType;
// Only in memory when receiving messages
std::vector<unsigned char> attachmentBinary;
std::string attachmentBinaryMimeType;
std::vector<unsigned char> postedFirstImage;
std::string postedFirstImageMimeType;
std::string postedDescriptionWithoutFirstImage;
};
class RsFeedReaderSerialiser: public RsSerialType

View file

@ -21,6 +21,8 @@
#include "CURLWrapper.h"
#include <string.h>
#define USERAGENT "Mozilla/5.0 (rv:109.0) Gecko/20100101 Firefox/112.0"
CURLWrapper::CURLWrapper(const std::string &proxy)
{
mCurl = curl_easy_init();
@ -65,6 +67,7 @@ CURLcode CURLWrapper::downloadText(const std::string &link, std::string &data)
curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, writeFunctionString);
curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(mCurl, CURLOPT_USERAGENT, USERAGENT);
return curl_easy_perform(mCurl);
}
@ -94,6 +97,8 @@ CURLcode CURLWrapper::downloadBinary(const std::string &link, std::vector<unsign
curl_easy_setopt(mCurl, CURLOPT_URL, link.c_str());
curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, writeFunctionBinary);
curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(mCurl, CURLOPT_USERAGENT, USERAGENT);
return curl_easy_perform(mCurl);
}

View file

@ -55,6 +55,11 @@ linux-* {
PKGCONFIG += speex speexdsp
} else {
LIBS += -lspeex -lspeexdsp -lavcodec -lavutil
win32:isEmpty(QMAKE_SH) {
# MinGW
LIBS += -lbcrypt
}
}
#################################### Windows #####################################

View file

@ -4,6 +4,8 @@
#include "util/rsbase64.h"
#include "util/radix64.h"
#include "crypto/hashstream.h"
#include "pgp/pgpkeyutil.h"
#include "pgp/rscertificate.h"
#include "pgp/openpgpsdkhandler.h"
@ -60,7 +62,7 @@ void FriendServer::threadTick()
if(last_debugprint_TS + DELAY_BETWEEN_TWO_DEBUG_PRINT < now)
{
last_debugprint_TS = now;
debugPrint();
debugPrint(false);
}
}
@ -75,39 +77,80 @@ void FriendServer::handleClientPublish(const RsFriendServerClientPublishItem *it
// First of all, read PGP key and short invites, parse them, and check that they contain the same information
std::map<RsPeerId,PeerInfo>::iterator pi = handleIncomingClientData(item->pgp_public_key_b64,item->short_invite);
RsPeerId pid;
// No need to test for it==mCurrentClients.end() because it will be directly caught by the exception handling below even before.
// Respond with a list of potential friends
if(!handleIncomingClientData(item->pgp_public_key_b64,item->short_invite,pid))
{
RsErr() << "Client data is dropped because of error." ;
return ;
}
// All good.
auto pi(mCurrentClientPeers.find(pid));
// Update the list of closest peers for other peers, based on which known friends it reports, and of current peer depending
// on friendship levels of other peers.
updateClosestPeers(pi->first,pi->second.pgp_fingerprint,item->already_received_peers);
RsDbg() << "Sending response item to " << item->PeerId() ;
RsFriendServerServerResponseItem *sr_item = new RsFriendServerServerResponseItem;
RsFriendServerServerResponseItem sr_item;
std::map<RsPeerId,RsPgpFingerprint> friends;
sr_item->nonce = pi->second.last_nonce;
sr_item->friend_invites = computeListOfFriendInvites(item->n_requested_friends,pi->first,friends);
sr_item->PeerId(item->PeerId());
std::set<RsPeerId> friends;
sr_item.unique_identifier = pi->second.last_identifier;
sr_item.friend_invites = computeListOfFriendInvites(pi->first,item->n_requested_friends,item->already_received_peers,friends);
sr_item.PeerId(item->PeerId());
// Update the have_added_as_friend for the list of each peer. We do that before sending because sending destroys
// the item.
RsDbg() << " Got " << sr_item.friend_invites.size() << " closest peers not in the list." ;
RsDbg() << " Updating local information for destination peer." ;
for(const auto& pid:friends)
// Update friendship levels of the peer that will receive the new list
for(auto fr:friends)
{
auto& p(mCurrentClientPeers[pid.first]);
p.have_added_this_peer[computePeerDistance(p.pgp_fingerprint, pi->second.pgp_fingerprint)] = pi->first;
auto& p(pi->second.friendship_levels[fr]);
RsDbg() << " Already a friend: " << fr << ", with local status " << static_cast<int>(p) ;
if(static_cast<int>(p) < static_cast<int>(RsFriendServer::PeerFriendshipLevel::HAS_KEY))
{
p = RsFriendServer::PeerFriendshipLevel::HAS_KEY;
RsDbg() << " --> updating status to HAS_KEY" ;
}
}
// Now encrypt the item with the public PGP key of the destination. This prevents the wrong person to request for
// someone else's data.
#warning TODO
RsDbg() << " Encrypting item..." ;
RsFriendServerEncryptedServerResponseItem *encrypted_response_item = new RsFriendServerEncryptedServerResponseItem;
uint32_t serialized_clear_size = FsSerializer().size(&sr_item);
RsTemporaryMemory serialized_clear_mem(serialized_clear_size);
FsSerializer().serialise(&sr_item,serialized_clear_mem,&serialized_clear_size);
uint32_t encrypted_mem_size = serialized_clear_size+1000; // leave some extra space
RsTemporaryMemory encrypted_mem(encrypted_mem_size);
if(!mPgpHandler->encryptDataBin(PGPHandler::pgpIdFromFingerprint(pi->second.pgp_fingerprint),
serialized_clear_mem,serialized_clear_size,
encrypted_mem,&encrypted_mem_size))
{
RsErr() << "Cannot encrypt item for PGP Id/FPR " << pi->second.pgp_fingerprint << ". Something went wrong." ;
return;
}
encrypted_response_item->PeerId(item->PeerId());
encrypted_response_item->bin_len = encrypted_mem_size;
encrypted_response_item->bin_data = malloc(encrypted_mem_size);
memcpy(encrypted_response_item->bin_data,encrypted_mem,encrypted_mem_size);
// Send the item.
mni->SendItem(sr_item);
// Update the list of closest peers for all peers currently in the database.
updateClosestPeers(pi->first,pi->second.pgp_fingerprint);
RsDbg() << " Sending item..." ;
mni->SendItem(encrypted_response_item);
}
catch(std::exception& e)
{
@ -121,11 +164,14 @@ void FriendServer::handleClientPublish(const RsFriendServerClientPublishItem *it
}
}
std::map<std::string, bool> FriendServer::computeListOfFriendInvites(uint32_t nb_reqs_invites, const RsPeerId &pid, std::map<RsPeerId,RsPgpFingerprint>& friends)
std::map<std::string,RsFriendServer::PeerFriendshipLevel> FriendServer::computeListOfFriendInvites(const RsPeerId &pid, uint32_t nb_reqs_invites,
const std::map<RsPeerId,RsFriendServer::PeerFriendshipLevel>& already_known_peers,
std::set<RsPeerId>& chosen_peers) const
{
// Strategy: we want to return the same set of friends for a given PGP profile key.
// Still, using some closest distance strategy, the n-closest peers for profile A is not the
// same set than the n-closest peers for profile B. We have multiple options:
// same set than the n-closest peers for profile B, so some peers will not be in both sets.
// We have multiple options:
//
// Option 1:
//
@ -145,38 +191,54 @@ std::map<std::string, bool> FriendServer::computeListOfFriendInvites(uint32_t nb
//
// So we choose Option 2.
std::map<std::string,bool> res;
std::map<std::string,RsFriendServer::PeerFriendshipLevel> res;
chosen_peers.clear();
auto pinfo_it(mCurrentClientPeers.find(pid));
auto add_from = [&res,&friends,nb_reqs_invites,this](bool added,const std::map<PeerInfo::PeerDistance,RsPeerId>& lst) -> bool
if(pinfo_it == mCurrentClientPeers.end())
{
for(const auto& pid:lst)
{
const auto p = mCurrentClientPeers.find(pid.second);
res.insert(std::make_pair(p->second.short_certificate,added));
friends.insert(std::make_pair(p->first,p->second.pgp_fingerprint));
if(res.size() >= nb_reqs_invites)
return true;
}
return false;
};
const auto& pinfo(mCurrentClientPeers[pid]);
// First add from peers who already added the current peer as friend, and leave if we already have enough
if(add_from(true,pinfo.have_added_this_peer))
RsErr() << "inconsistency in computeListOfFriendInvites. Something's wrong in the code." ;
return res;
}
auto pinfo(pinfo_it->second);
add_from(false,pinfo.closest_peers);
for(const auto& pit:pinfo.closest_peers)
{
if(already_known_peers.find(pit.second) == already_known_peers.end())
{
RsDbg() << " peer " << pit.second << ": not in supplied list => adding it.";
const auto p = mCurrentClientPeers.find(pit.second);
if(p == mCurrentClientPeers.end()) // should not happen, but just an extra security.
continue;
auto pp = p->second.friendship_levels.find(pid);
auto peer_friendship_level = (pp==p->second.friendship_levels.end())?(RsFriendServer::PeerFriendshipLevel::UNKNOWN):(pp->second);
res[p->second.short_certificate] = peer_friendship_level;
chosen_peers.insert(p->first);
if(res.size() + already_known_peers.size() >= nb_reqs_invites)
break;
}
else
{
auto p = already_known_peers.find(pit.second);
RsDbg() << " peer " << pit.second << ": already in supplied list, with status " << static_cast<int>(p->second) << ". Not adding it.";
}
}
return res;
}
std::map<RsPeerId,PeerInfo>::iterator FriendServer::handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64)
bool FriendServer::handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64,RsPeerId& pid)
{
// 1 - Check that the incoming data is sound.
try
{
RsDbg() << " Checking item data...";
std::string error_string;
@ -227,9 +289,6 @@ std::map<RsPeerId,PeerInfo>::iterator FriendServer::handleIncomingClientData(con
mPgpHandler->syncDatabase();
}
}
// All good.
// Store/update the peer info
auto& pi(mCurrentClientPeers[shortInviteDetails.id]);
@ -238,12 +297,18 @@ std::map<RsPeerId,PeerInfo>::iterator FriendServer::handleIncomingClientData(con
pi.last_connection_TS = time(nullptr);
pi.pgp_fingerprint = shortInviteDetails.fpr;
while(pi.last_nonce == 0) // reuse the same identifier (so it's not really a nonce, but it's kept secret whatsoever).
pi.last_nonce = RsRandom::random_u64();
while(pi.last_identifier == 0) // reuse the same identifier (so it's not really a nonce, but it's kept secret whatsoever).
pi.last_identifier = RsRandom::random_u64();
return mCurrentClientPeers.find(shortInviteDetails.id);
pid = shortInviteDetails.id;
return true;
}
catch (std::exception& e)
{
RsErr() << "Exception while adding client data: " << e.what() ;
return false;
}
}
void FriendServer::handleClientRemove(const RsFriendServerClientRemoveItem *item)
{
@ -257,14 +322,14 @@ void FriendServer::handleClientRemove(const RsFriendServerClientRemoveItem *item
return;
}
if(it->second.last_nonce != item->nonce)
if(it->second.last_identifier != item->unique_identifier)
{
RsErr() << " ERROR: Client supplied a nonce " << std::hex << item->nonce << std::dec << " that is not correct (expected "
<< std::hex << it->second.last_nonce << std::dec << ")";
RsErr() << " ERROR: Client supplied a nonce " << std::hex << item->unique_identifier << std::dec << " that is not correct (expected "
<< std::hex << it->second.last_identifier << std::dec << ")";
return;
}
RsDbg() << " Nonce is correct: " << std::hex << item->nonce << std::dec << ". Removing peer " << item->peer_id ;
RsDbg() << " Nonce is correct: " << std::hex << item->unique_identifier << std::dec << ". Removing peer " << item->peer_id ;
removePeer(item->peer_id);
}
@ -283,7 +348,7 @@ void FriendServer::removePeer(const RsPeerId& peer_id)
for(auto pit(it.second.closest_peers.begin());pit!=it.second.closest_peers.end();)
if(pit->second == peer_id)
{
RsDbg() << " Removing from n-closest peers of peer " << pit->first ;
RsDbg() << " Removing from n-closest peers of peer " << it.first ;
auto tmp(pit);
++tmp;
@ -293,27 +358,26 @@ void FriendServer::removePeer(const RsPeerId& peer_id)
else
++pit;
// Also remove that peer from peers that have accepted each peer
// Also remove that peer from friendship levels of that particular peer.
for(auto fit(it.second.have_added_this_peer.begin());fit!=it.second.have_added_this_peer.end();)
if(fit->second == peer_id)
auto fit = it.second.friendship_levels.find(peer_id);
if(fit != it.second.friendship_levels.end())
{
RsDbg() << " Removing from have_added_as_friend peers of peer " << fit->first ;
auto tmp(fit);
++tmp;
it.second.closest_peers.erase(fit);
fit=tmp;
RsDbg() << " Removing from have_added_as_friend peers of peer " << it.first ;
it.second.friendship_levels.erase(fit);
}
else
++fit;
}
}
PeerInfo::PeerDistance FriendServer::computePeerDistance(const RsPgpFingerprint& p1,const RsPgpFingerprint& p2)
{
std::cerr << "Computing peer distance: p1=" << p1 << " p2=" << p2 << " p1^p2=" << (p1^p2) << " distance=" << ((p1^p2)^mRandomPeerBias) << std::endl;
return (p1 ^ p2)^mRandomPeerBias;
auto res = (p1 ^ p2)^mRandomPeerBias;
auto res2 = RsDirUtil::sha1sum(res.toByteArray(),res.SIZE_IN_BYTES); // sha1sum prevents reverse finding the random bias
std::cerr << "Computing peer distance: p1=" << p1 << " p2=" << p2 << " p1^p2=" << (p1^p2) << " distance=" << res2 << std::endl;
return res2;
}
FriendServer::FriendServer(const std::string& base_dir,const std::string& listening_address,uint16_t listening_port)
: mListeningAddress(listening_address),mListeningPort(listening_port)
@ -349,8 +413,6 @@ void FriendServer::run()
void FriendServer::autoWash()
{
rstime_t now = time(nullptr);
RsDbg() << "autoWash..." ;
std::list<RsPeerId> to_remove;
for(std::map<RsPeerId,PeerInfo>::iterator it(mCurrentClientPeers.begin());it!=mCurrentClientPeers.end();++it)
@ -362,29 +424,116 @@ void FriendServer::autoWash()
for(auto peer_id:to_remove)
removePeer(peer_id);
RsDbg() << "done." ;
}
void FriendServer::updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr)
void FriendServer::updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr,const std::map<RsPeerId,RsFriendServer::PeerFriendshipLevel>& friended_peers)
{
auto find_multi = [](PeerInfo::PeerDistance dist,std::map< std::pair<RsFriendServer::PeerFriendshipLevel,PeerInfo::PeerDistance>,RsPeerId >& mp)
-> std::map< std::pair<RsFriendServer::PeerFriendshipLevel,PeerInfo::PeerDistance>,RsPeerId >::iterator
{
auto it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::UNKNOWN,dist)) ;
if(it == mp.end()) it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::NO_KEY ,dist));
if(it == mp.end()) it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::HAS_KEY ,dist));
if(it == mp.end()) it = mp.find(std::make_pair(RsFriendServer::PeerFriendshipLevel::HAS_ACCEPTED_KEY,dist));
return it;
};
auto remove_from_map = [find_multi](PeerInfo::PeerDistance dist,
std::map< std::pair<RsFriendServer::PeerFriendshipLevel,
PeerInfo::PeerDistance>,RsPeerId>& mp) -> bool
{
auto mpit = find_multi(dist,mp);
if(mpit != mp.end())
{
mp.erase(mpit);
return true;
}
else
return false;
};
auto& pit(mCurrentClientPeers[pid]);
for(auto& it:mCurrentClientPeers)
if(it.first != pid)
{
// 1 - for all existing peers, update the level at which the given peer has added the peer as friend.
auto peer_iterator = friended_peers.find(it.first);
auto peer_friendship_level = (peer_iterator==friended_peers.end())? (RsFriendServer::PeerFriendshipLevel::UNKNOWN):(peer_iterator->second);
PeerInfo::PeerDistance d = computePeerDistance(fpr,it.second.pgp_fingerprint);
it.second.closest_peers.insert(std::make_pair(d,pid));
// Remove the peer from the map. This is costly. I need to find something better. If the peer is already
// in the list, it has a map key with the same distance.
if(it.second.closest_peers.size() > MAXIMUM_PEERS_TO_REQUEST)
remove_from_map(d,it.second.closest_peers);
it.second.closest_peers[std::make_pair(peer_friendship_level,d)] = pid;
while(it.second.closest_peers.size() > MAXIMUM_PEERS_TO_REQUEST)
it.second.closest_peers.erase(std::prev(it.second.closest_peers.end()));
}
// 2 - for the current peer, update the list of closest peers
auto pit2 = it.second.friendship_levels.find(pid);
peer_friendship_level = (pit2==it.second.friendship_levels.end())? (RsFriendServer::PeerFriendshipLevel::UNKNOWN):(pit2->second);
remove_from_map(d,pit.closest_peers);
pit.closest_peers[std::make_pair(peer_friendship_level,d)] = it.first;
while(pit.closest_peers.size() > MAXIMUM_PEERS_TO_REQUEST)
pit.closest_peers.erase(std::prev(pit.closest_peers.end()));
}
void FriendServer::debugPrint()
// Also update the friendship levels for the current peer, of all friends from the list.
for(auto it:friended_peers)
pit.friendship_levels[it.first] = it.second;
}
Sha1CheckSum FriendServer::computeDataHash()
{
librs::crypto::HashStream s(librs::crypto::HashStream::SHA1);
for(auto p(mCurrentClientPeers.begin());p!=mCurrentClientPeers.end();++p)
{
s << p->first;
const auto& inf(p->second);
s << inf.pgp_fingerprint;
s << inf.short_certificate;
s << (uint64_t)inf.last_connection_TS;
s << inf.last_identifier;
for(auto d(inf.closest_peers.begin());d!=inf.closest_peers.end();++d)
{
s << static_cast<uint32_t>(d->first.first) ;
s << d->first.second ;
s << d->second;
}
for(auto d:inf.friendship_levels)
{
s << d.first ;
s << static_cast<uint32_t>(d.second);
}
}
return s.hash();
}
void FriendServer::debugPrint(bool force)
{
auto h = computeDataHash();
if((h != mCurrentDataHash) || force)
{
RsDbg() << "========== FriendServer statistics ============";
RsDbg() << " Base directory: "<< mBaseDirectory;
RsDbg() << " Random peer bias: "<< mRandomPeerBias;
RsDbg() << " Current hash: "<< h;
RsDbg() << " Network interface: ";
RsDbg() << " Max peers in n-closest list: " << MAXIMUM_PEERS_TO_REQUEST;
RsDbg() << " Current active peers: " << mCurrentClientPeers.size() ;
@ -393,20 +542,18 @@ void FriendServer::debugPrint()
for(const auto& it:mCurrentClientPeers)
{
RsDbg() << " " << it.first << ": nonce=" << std::hex << it.second.last_nonce << std::dec << " fpr: " << it.second.pgp_fingerprint << ", last contact: " << now - it.second.last_connection_TS << " secs ago.";
RsDbg() << " " << it.first << ": identifier=" << std::hex << it.second.last_identifier << std::dec << " fpr: " << it.second.pgp_fingerprint << ", last contact: " << now - it.second.last_connection_TS << " secs ago.";
RsDbg() << " Closest peers:" ;
for(const auto& pit:it.second.closest_peers)
RsDbg() << " " << pit.second << " distance=" << pit.first ;
RsDbg() << " Have added this peer:" ;
for(const auto& pit:it.second.have_added_this_peer)
RsDbg() << " " << pit.second << " distance=" << pit.first ;
for(auto pit:it.second.closest_peers)
RsDbg() << " " << pit.second << " distance=" << pit.first.second << " Peer reciprocal status:" << static_cast<int>(pit.first.first);
}
RsDbg() << "===============================================";
mCurrentDataHash = h;
}
}

View file

@ -24,23 +24,108 @@
#include "util/rsthreads.h"
#include "pqi/pqistreamer.h"
#include "pgp/pgphandler.h"
#include "retroshare/rsfriendserver.h"
#include "network.h"
class RsFriendServerClientRemoveItem;
class RsFriendServerClientPublishItem;
// +================================================================+
// | o---o o |
// | \ / - Retroshare Friend Server - / \ |
// | o o---o |
// +================================================================+
//
// The friend server facilitates a group of RS Tor-nodes to make friends. It maintains a pool of
// participants (RS nodes currently susbscribing to the friend server) and advertise them to each other
// as possible friends. Its goal is to allow new RS users to quickly experiment with the software without
// compromising their anonymity.
//
// Implementation
// ==============
//
// The implementation is entirely client-based: clients make a request, and get a response. No connection is maintained
// beyond this interaction. Consequently, the friend server returns a random ID to each client that the client can use to
// e.g. signal its departure from the friend server and the release of its data.
//
// Both client and server use a binary interface linked to a proxy-connected socket to stream RS items, everything
// happenning on top of Tor connections.
//
// Algorithms
// ==========
//
// * Protocol
//
// Retroshare Client Server (Friend Server)
//
// ------------ Tor connection --------> no action
// Server online MSG <-------------- Tor ACK ------------
//
//
// Friend Req. loop ------------ Friend Request --------> Friend list calculation / update
// <---------- Friend list + ID --------
//
//
// FS disabled ------------ FS Close + ID ---------> Data cleaning, peer removal.
//
//
// * Friend selection
//
// In order to reduce the ease to retrieve the list of all participants to a friend server, the
// friend server always returns the same list of friends to a given peer. To do so, participants are sorted
// for each peer, using a XOR distance such as:
//
// d(P1,P2) = P1 (XOR) P2 (XOR) R
//
// ...where R is a random bias.
//
// Since being in the n closest peers is not a reflexive relationship (P1 may be within the n closest peers
// to P2 but P2 may not be in the n closest peers to P1), selected friends for peer A are picked from both
// the closest peers of A, and the peers that received the RS certificate of A.
//
// Another important effect of the stability of retrieved friends is to maintain a network that is not
// fully connected and stable over time, which corresponds to the mesh model of the RS network.
//
// * Peer friendship level
//
// For display purposes, the friend server also stores the "friendship level" for each pair of peers,
// that means whether the peer has added the other peer as friend, or only reveived his key, etc.
//
// Peers send to the friend server the list of peers they already have, with their own friendship
// level with that peer. The FS needs to send back a list of peers, with the friendship level to the current peer.
// In the list of closest peers, the reverse friendship levels are stored: for a peer A the reverse friendship
// level to peer B is whether B has added A as friend or not.In the list of friends for a peer, the forward FL
// is stored. The forward FL of a peer A to a peer B is whether A has added B as friend or not.
//
// * Security
//
// Obviously the friend server knows who is possibly connected to whom. Since the connections to the
// friend server are anonymous, this information is difficult to protect, although the implementation
// currently makes it difficult to retrieve.
//
// The friend server is only available to Tor nodes, since it allows RS nodes to connect to random peers.
// This allows trying the software without compromizing one's privacy.
struct PeerInfo
{
typedef RsPgpFingerprint PeerDistance;
typedef Sha1CheckSum PeerDistance;
RsPgpFingerprint pgp_fingerprint;
std::string short_certificate;
rstime_t last_connection_TS;
uint64_t last_nonce;
uint64_t last_identifier;
std::map<PeerDistance,RsPeerId> closest_peers;
std::map<PeerDistance,RsPeerId> have_added_this_peer;
// The following map contains the list of closest peers. The sorting is based
// on a combination of the peer XOR distance and the friendship level, so that
// peers which already have added a peer are considered first as potential receivers of his key.
// The friendship level here is a reverse FL, e.g. whether each closest peer has added the current peer as friend.
std::map<std::pair<RsFriendServer::PeerFriendshipLevel,PeerDistance>,RsPeerId > closest_peers; // limited in size.
// Which peers have received the key for that particular peer, along with the direct friendship level: whether current peer has added each peer.
std::map<RsPeerId,RsFriendServer::PeerFriendshipLevel> friendship_levels; // unlimited in size, but no distance sorting.
};
class FriendServer : public RsTickingThread
@ -60,22 +145,25 @@ private:
void handleClientPublish(const RsFriendServerClientPublishItem *item);
// Updates for each peer in the database, the list of closest peers w.r.t. some arbitrary distance.
void updateClosestPeers(const RsPeerId& pid,const RsPgpFingerprint& fpr);
void updateClosestPeers(const RsPeerId& pid, const RsPgpFingerprint& fpr, const std::map<RsPeerId, RsFriendServer::PeerFriendshipLevel> &friended_peers);
// removes a single peer from all lists.
void removePeer(const RsPeerId& peer_id);
// Adds the incoming peer data to the list of current clients and returns the
std::map<RsPeerId,PeerInfo>::iterator handleIncomingClientData(const std::string& pgp_public_key_b64,const std::string& short_invite_b64);
bool handleIncomingClientData(const std::string& pgp_public_key_b64, const std::string& short_invite_b64, RsPeerId &pid);
// Computes the appropriate list of short invites to send to a given peer.
std::map<std::string, bool> computeListOfFriendInvites(uint32_t nb_reqs_invites, const RsPeerId &pid, std::map<RsPeerId,RsPgpFingerprint>& friends);
std::map<std::string,RsFriendServer::PeerFriendshipLevel> computeListOfFriendInvites(const RsPeerId &pid, uint32_t nb_reqs_invites,
const std::map<RsPeerId,RsFriendServer::PeerFriendshipLevel>& already_known_peers,
std::set<RsPeerId>& chosen_peers) const;
// Compute the distance between peers using the random bias (It's not really a distance though. I'm not sure about the triangular inequality).
PeerInfo::PeerDistance computePeerDistance(const RsPgpFingerprint &p1, const RsPgpFingerprint &p2);
void autoWash();
void debugPrint();
void debugPrint(bool force);
Sha1CheckSum computeDataHash();
// Local members
@ -88,4 +176,6 @@ private:
std::map<RsPeerId, PeerInfo> mCurrentClientPeers;
std::string mListeningAddress;
uint16_t mListeningPort;
Sha1CheckSum mCurrentDataHash;
};

View file

@ -27,6 +27,14 @@
#include <unistd.h>
#include <sys/types.h>
#ifdef WINDOWS_SYS
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netinet/tcp.h>
#endif
#include "util/rsnet.h"
#include "util/rsprint.h"
#include "util/rsdebug.h"
@ -45,6 +53,9 @@ FsNetworkInterface::FsNetworkInterface(const std::string& listening_address,uint
mClintListn = 0;
mClintListn = socket(AF_INET, SOCK_STREAM, 0); // creating socket
int flags=1;
setsockopt(mClintListn,SOL_SOCKET,TCP_NODELAY,(char*)&flags,sizeof(flags));
unix_fcntl_nonblock(mClintListn);
struct sockaddr_in ipOfServer;
@ -102,15 +113,19 @@ void FsNetworkInterface::threadTick()
std::list<RsPeerId> to_close;
{
RS_STACK_MUTEX(mFsNiMtx);
for(auto& it:mConnections)
if(it.second.bio->isactive())
{
it.second.pqi_thread->tick();
else
if(!it.second.bio->isactive() && !it.second.bio->moretoread(0))
to_close.push_back(it.first);
}
for(const auto& pid:to_close)
locked_closeConnection(pid);
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
@ -134,7 +149,9 @@ bool FsNetworkInterface::checkForNewConnections()
if(clintConnt < 0)
{
if(errno == EWOULDBLOCK)
int err = rs_socket_error();
if(err == EWOULDBLOCK || err == EAGAIN)
;//RsErr()<< "Incoming connection with nothing to read!" << std::endl;
else
RsErr()<< "Error when accepting connection." << std::endl;
@ -145,16 +162,24 @@ bool FsNetworkInterface::checkForNewConnections()
// Make the socket non blocking so that we can read from it and return if nothing comes
int flags=1;
setsockopt(clintConnt,SOL_SOCKET,TCP_NODELAY,(char*)&flags,sizeof(flags));
unix_fcntl_nonblock(clintConnt);
// Create connection info
RsDbg() << " Creating connection data." ;
ConnectionData c;
c.socket = clintConnt;
c.client_address = addr;
RsPeerId pid = makePeerId(clintConnt);
RsDbg() << " socket: " << clintConnt;
RsDbg() << " client address: " << sockaddr_storage_tostring(*(sockaddr_storage*)&addr);
RsDbg() << " peer id: " << pid ;
// Setup a pqistreamer to deserialize whatever comes from this connection
RsSerialiser *rss = new RsSerialiser ;
@ -167,11 +192,14 @@ bool FsNetworkInterface::checkForNewConnections()
c.pqi_thread = pqi;
c.bio = bio;
pqi->start();
{
RS_STACK_MUTEX(mFsNiMtx);
mConnections[pid] = c;
pqi->start();
}
RsDbg() << " streamer has properly started." ;
return true;
}
@ -179,6 +207,8 @@ bool FsNetworkInterface::RecvItem(RsItem *item)
{
RS_STACK_MUTEX(mFsNiMtx);
RsDbg() << "FsNetworkInterface: received item " << (void*)item;
auto it = mConnections.find(item->PeerId());
if(it == mConnections.end())
@ -203,6 +233,7 @@ RsItem *FsNetworkInterface::GetItem()
RsItem *item = it.second.incoming_items.front();
it.second.incoming_items.pop_front();
RsDbg() << "FsNetworkInterface: returning item " << (void*)item << " to caller.";
return item;
}
}

View file

@ -27,6 +27,7 @@
#include "retroshare/rstor.h"
#include "retroshare/rsinit.h"
#include "pqi/authgpg.h"
#include "friendserver.h"
@ -47,20 +48,32 @@ int main(int argc, char* argv[])
//RsControl::earlyInitNotificationSystem();
std::string base_directory = "FSData";
std::string tor_executable_path ;
bool verbose = false;
argstream as(argc,argv);
as >> parameter( 'c',"base-dir", base_directory, "set base directory to store data files (keys, etc)", false )
>> parameter( 't',"tor-executable", tor_executable_path, "set absolute path for tor executable", false )
>> option( 'v',"verbose", verbose, "display additional debug information")
>> help( 'h', "help", "Display this Help" );
as.defaultErrorHandling(true, true);
RsConfigOptions conf;
conf.optBaseDir = base_directory;
conf.main_executable_path = argv[0];
if(!tor_executable_path.empty())
RsTor::setTorExecutablePath(tor_executable_path);
RsTor::setVerbose(verbose);
RsInit::InitRsConfig();
RsInit::InitRetroShare(conf);
AuthPGP::exit(); // This allows to release the keyring created by libretroshare, since it's not useful, as TorManager has its own.
// Create the base directory if needed
if(!RsDirUtil::checkCreateDirectory(base_directory))
@ -68,6 +81,19 @@ int main(int argc, char* argv[])
RsErr() << "Cannot create base directory \"" << base_directory << "\". Check permissions, paths, etc." ;
return 1;
}
// Check the existance of the Tor executable path
auto tor_path = RsTor::torExecutablePath();
if (!RsDirUtil::fileExists(tor_path))
{
RsErr() << "Tor executable \"" << tor_path << "\" not found. Try supplying the correct full path for a tor executable with the -t option.";
return 1;
}
else
RsDbg() << "Using Tor executable: \"" << tor_path << "\"";
// Create/start TorManager
RsTor::setTorDataDirectory(RsDirUtil::makePath(base_directory,"tor"));
@ -89,22 +115,22 @@ int main(int argc, char* argv[])
RsTor::getHiddenServiceInfo(service_id,onion_address,service_port,service_target_address,target_port) ;
RsDbg() << "Tor properly started: " ;
RsDbg() << " Hidden service address: " << onion_address << ":" << service_port;
RsDbg() << " Target address : " << service_target_address << ":" << target_port;
RsInfo() << "Tor properly started: " ;
RsInfo() << " Hidden service address: " << onion_address << ":" << service_port;
RsInfo() << " Target address : " << service_target_address << ":" << target_port;
// Now start the real thing.
FriendServer fs(base_directory,service_target_address,target_port);
fs.start();
RsDbg() << "";
RsDbg() << "================== Retroshare Friend Server has properly started =====================" ;
RsDbg() << "= =";
RsDbg() << "= Address:Port " << onion_address << ":" << service_port << ((service_port<10000)?" ":"") << " =";
RsDbg() << "= =";
RsDbg() << "======================================================================================" ;
RsDbg() << "";
RsInfo() << "";
RsInfo() << "================== Retroshare Friend Server has properly started =====================" ;
RsInfo() << "= =";
RsInfo() << "= Address:Port " << onion_address << ":" << service_port << ((service_port<10000)?" ":"") << " =";
RsInfo() << "= =";
RsInfo() << "======================================================================================" ;
RsInfo() << "";
while(fs.isRunning())
std::this_thread::sleep_for(std::chrono::seconds(2));

View file

@ -29,8 +29,7 @@ SOURCES += retroshare-friendserver.cc \
network.cc
HEADERS += friendserver.h \
network.h \
fsitem.h
network.h
################################# Linux ##########################################
@ -44,6 +43,7 @@ unix {
win32-g++|win32-clang-g++ {
dLib = ws2_32 iphlpapi crypt32
LIBS *= $$linkDynamicLibs(dLib)
CONFIG += console
}
################################### COMMON stuff ##################################

View file

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

File diff suppressed because it is too large Load diff

View file

@ -2,17 +2,14 @@
README for RetroShare
=======================================================================================
RetroShare web site . . . . http://retroshare.net/index.html
RetroShare web site . . . . https://retroshare.cc/
Developer's blog . . . . . https://retroshareteam.wordpress.com
Documentation . . . . . . . https://retroshare.readthedocs.io/en/latest/
Support . . . . . . . . . . http://retroshare.net/support.html
Forums . . . . . . . . . . http://retroshare.sourceforge.net/forum/
Documentation . . . . . . . https://retrosharedocs.readthedocs.io/en/latest/
Wiki . . . . . . . . . . . https://github.com/RetroShare/documentation/wiki
Old developers site . . . . http://retroshare.sourceforge.net/wiki/index.php/Developers_Corner
Project site . . . . . . . https://github.com/RetroShare/RetroShare
Relted projects/plugins . . https://github.com/RetroShare
Related projects/plugins . .https://github.com/RetroShare
Contact: . . . . . . . . . retroshare@lunamutt.com ,defnax@users.sourceforge.net
Contact: . . . . . . . . . retroshare.project@gmail.com
=========================================================================================
Compiling RetroShare
@ -22,9 +19,9 @@ Build Scripts are avaible on GIT:
https://github.com/RetroShare/RetroShare/tree/master/build_scripts
You can find here instructions howto compile RetroShare:
https://retroshare.readthedocs.io/en/latest/developer/compilation/
https://retrosharedocs.readthedocs.io/en/latest/developer/compilation/
You can go on over to our forum or chat lobby when you have trouble with compiling:
You can go on over to our forum or chat room when you have trouble with compiling:
retroshare://forum?name=Developers%20Discussions&id=8fd22bd8f99754461e7ba1ca8a727995
retroshare://chat_room?name=Retroshare%20Devel%20%28signed%29&id=L68DB0A1E09BDA3A5
http://retroshare.sourceforge.net/forum/

View file

@ -21,7 +21,7 @@
#include "AboutDialog.h"
#include "HelpDialog.h"
#include "qmake_info.h"
#include "include/qmake_info.h"
#include "rshare.h"
#ifdef RS_JSONAPI
@ -142,7 +142,7 @@ void AboutWidget::updateTitle()
{
if (tWidget == NULL)
{
setWindowTitle(QString("%1 %2").arg(tr("About RetroShare"), Rshare::retroshareVersion(true)));
setWindowTitle(QString("%1 %2").arg(tr("About RetroShare"), RsApplication::retroshareVersion(true)));
}
else
{
@ -228,7 +228,7 @@ void AWidget::initImages()
#ifdef RS_ONLYHIDDENNODE
p.drawText(QPointF(10, 50), QString("%1 : %2 (With embedded Tor)").arg(tr("Retroshare version"), Rshare::retroshareVersion(true)));
#else
p.drawText(QPointF(10, 50), QString("%1 : %2").arg(tr("Retroshare version"), Rshare::retroshareVersion(true)));
p.drawText(QPointF(10, 50), QString("%1 : %2").arg(tr("Retroshare version"), RsApplication::retroshareVersion(true)));
#endif
/* Draw Qt's version number */
@ -936,7 +936,7 @@ void AboutWidget::on_copy_button_clicked()
{
QString verInfo;
QString rsVerString = "RetroShare Version: ";
rsVerString+=Rshare::retroshareVersion(true);
rsVerString+=RsApplication::retroshareVersion(true);
verInfo+=rsVerString;
#ifdef RS_ONLYHIDDENNODE
verInfo+=" " + tr("Only Hidden Node");

View file

@ -22,19 +22,20 @@
#include "ChatLobbyWidget.h"
#include "notifyqt.h"
#include "RetroShareLink.h"
#include "chat/ChatLobbyDialog.h"
#include "chat/ChatLobbyUserNotify.h"
#include "chat/ChatTabWidget.h"
#include "chat/CreateLobbyDialog.h"
#include "common/FilesDefs.h"
#include "common/RSTreeWidgetItem.h"
#include "common/RSElidedItemDelegate.h"
#include "gui/RetroShareLink.h"
#include "gui/gxs/GxsIdDetails.h"
#include "gui/Identity/IdEditDialog.h"
#include "gui/settings/rsharesettings.h"
#include "gxs/GxsIdDetails.h"
#include "Identity/IdEditDialog.h"
#include "settings/rsharesettings.h"
#include "util/HandleRichText.h"
#include "util/misc.h"
#include "util/QtVersion.h"
#include "gui/common/FilesDefs.h"
#include "retroshare/rsmsgs.h"
#include "retroshare/rspeers.h"
@ -142,28 +143,28 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
QHeaderView_setSectionResizeModeColumn(header, COLUMN_TOPIC, QHeaderView::Interactive);
privateSubLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
privateSubLobbyItem->setText(COLUMN_NAME, tr("Private Subscribed chat rooms"));
privateSubLobbyItem->setText(COLUMN_NAME, tr("Private Subscribed"));
privateSubLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "1");
// privateLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PRIVATE));
privateSubLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PRIVATE);
ui.lobbyTreeWidget->insertTopLevelItem(0, privateSubLobbyItem);
publicSubLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
publicSubLobbyItem->setText(COLUMN_NAME, tr("Public Subscribed chat rooms"));
publicSubLobbyItem->setText(COLUMN_NAME, tr("Public Subscribed"));
publicSubLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "2");
// publicLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PUBLIC));
publicSubLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PUBLIC);
ui.lobbyTreeWidget->insertTopLevelItem(1, publicSubLobbyItem);
privateLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
privateLobbyItem->setText(COLUMN_NAME, tr("Private chat rooms"));
privateLobbyItem->setText(COLUMN_NAME, tr("Private"));
privateLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "3");
// privateLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PRIVATE));
privateLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PRIVATE);
ui.lobbyTreeWidget->insertTopLevelItem(2, privateLobbyItem);
publicLobbyItem = new RSTreeWidgetItem(compareRole, TYPE_FOLDER);
publicLobbyItem->setText(COLUMN_NAME, tr("Public chat rooms"));
publicLobbyItem->setText(COLUMN_NAME, tr("Public"));
publicLobbyItem->setData(COLUMN_NAME, ROLE_SORT, "4");
// publicLobbyItem->setIcon(COLUMN_NAME, QIcon(IMAGE_PUBLIC));
publicLobbyItem->setData(COLUMN_DATA, ROLE_PRIVACYLEVEL, CHAT_LOBBY_PRIVACY_LEVEL_PUBLIC);
@ -206,28 +207,29 @@ ChatLobbyWidget::ChatLobbyWidget(QWidget *parent, Qt::WindowFlags flags)
// load settings
processSettings(true);
QString help_str = tr("\
<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Chat Rooms</h1> \
<p>Chat rooms work pretty much like IRC. \
They allow you to talk anonymously with tons of people without the need to make friends.</p> \
<p>A chat room can be public (your friends see it) or private (your friends can't see it, unless you \
invite them with <img src=\":/icons/png/add.png\" width=%2/>). \
Once you have been invited to a private room, you will be able to see it when your friends \
are using it.</p> \
<p>The list at left shows \
chat lobbies your friends are participating in. You can either \
<ul> \
<li>Right click to create a new chat room</li> \
<li>Double click a chat room to enter, chat, and show it to your friends</li> \
</ul> \
Note: For the chat rooms to work properly, your computer needs be on time. So check your system clock!\
</p> \
"
).arg(QString::number(4*W), QString::number(2*W)) ;
int hbH = misc::getFontSizeFactor("HelpButton").height();
QString help_str = tr(
"<h1><img width=\"%1\" src=\":/icons/help_64.png\">&nbsp;&nbsp;Chat Rooms</h1>"
"<p>Chat rooms work pretty much like IRC."
" They allow you to talk anonymously with tons of people without the need to make friends.</p>"
"<p>A chat room can be public (your friends see it) or private (your friends can't see it, unless you"
" invite them with <img src=\":/icons/png/add.png\" width=%2/>)."
" Once you have been invited to a private room, you will be able to see it when your friends"
" are using it.</p>"
"<p>The list at left shows"
" chat lobbies your friends are participating in. You can either"
" <ul>"
" <li>Right click to create a new chat room</li>"
" <li>Double click a chat room to enter, chat, and show it to your friends</li>"
" </ul>"
" Note: For the chat rooms to work properly, your computer needs be on time. So check your system clock!"
"</p>"
).arg(QString::number(2*hbH), QString::number(hbH)) ;
registerHelpButton(ui.helpButton,help_str,"ChatLobbyDialog") ;
ui.lobbyTreeWidget->setIconSize(QSize(H*1.5,H*1.5));
int ltwH = misc::getFontSizeFactor("LobbyTreeWidget", 1.5).height();
ui.lobbyTreeWidget->setIconSize(QSize(ltwH,ltwH));
}
ChatLobbyWidget::~ChatLobbyWidget()
@ -306,9 +308,6 @@ void ChatLobbyWidget::lobbyTreeWidgetCustomPopupMenu(QPoint)
contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_UNSUBSCRIBE), tr("Leave this room"), this, SLOT(unsubscribeItem()));
else
{
QTreeWidgetItem *item = ui.lobbyTreeWidget->currentItem();
//ChatLobbyId id = item->data(COLUMN_DATA, ROLE_ID).toULongLong();
ChatLobbyFlags flags(item->data(COLUMN_DATA, ROLE_FLAGS).toUInt());
bool removed = false ;
@ -373,7 +372,7 @@ void ChatLobbyWidget::lobbyTreeWidgetCustomPopupMenu(QPoint)
void ChatLobbyWidget::lobbyChanged()
{
updateDisplay();
ChatLobbyWidget::updateDisplay();
}
static void updateItem(QTreeWidget *treeWidget, QTreeWidgetItem *item, ChatLobbyId id, const std::string &name, const std::string &topic, int count, bool subscribed, bool autoSubscribe,ChatLobbyFlags lobby_flags)
@ -607,7 +606,7 @@ void ChatLobbyWidget::updateDisplay()
QTreeWidgetItem *itemLoop = lobby_other_item->child(childIndex);
if (itemLoop->type() == TYPE_LOBBY && itemLoop->data(COLUMN_DATA, ROLE_ID).toULongLong() == lobby.lobby_id) {
delete(lobby_other_item->takeChild(lobby_other_item->indexOfChild(itemLoop)));
childCnt = lobby_other_item->childCount();
//childCnt = lobby_other_item->childCount();
break;
}
}
@ -722,9 +721,9 @@ void ChatLobbyWidget::updateDisplay()
}
}
publicSubLobbyItem->setHidden(publicSubLobbyItem->childCount()==0);
publicSubLobbyItem->setText(COLUMN_NAME, tr("Public Subscribed chat rooms")+ QString(" (") + QString::number(publicSubLobbyItem->childCount())+QString(")"));
publicSubLobbyItem->setText(COLUMN_NAME, tr("Public Subscribed")+ QString(" (") + QString::number(publicSubLobbyItem->childCount())+QString(")"));
privateSubLobbyItem->setHidden(privateSubLobbyItem->childCount()==0);
publicLobbyItem->setText(COLUMN_NAME, tr("Public chat rooms")+ " (" + QString::number(publicLobbyItem->childCount())+QString(")"));
publicLobbyItem->setText(COLUMN_NAME, tr("Public")+ " (" + QString::number(publicLobbyItem->childCount())+QString(")"));
}
void ChatLobbyWidget::createChatLobby()

View file

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

View file

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

View file

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

View file

@ -358,7 +358,7 @@ void CreateCircleDialog::addMember(const QString& keyId, const QString& idtype,
tree->addTopLevelItem(member);
ui.members_groupBox->setTitle( tr("Invited Members") + " (" + QString::number(ui.treeWidget_membership->topLevelItemCount()) + ")" );
updateMembership();
}
/** Maybe we can use RsGxsCircleGroup instead of RsGxsCircleDetails ??? (TODO)**/
@ -413,6 +413,8 @@ void CreateCircleDialog::removeMember()
// does this just work? (TODO)
delete(item);
updateMembership();
}
void CreateCircleDialog::createCircle()
@ -584,9 +586,13 @@ void CreateCircleDialog::createCircle()
circle.mMeta.mCircleType = GXS_CIRCLE_TYPE_LOCAL;
}
uint32_t token;
bool is_existing_circle(mIsExistingCircle);
if(mIsExistingCircle)
RsThread::async([circle,is_existing_circle]()
{
RsGxsCircleId circleId;
if(is_existing_circle)
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::updateCircle() : mCircleType: " << circle.mMeta.mCircleType << std::endl;
@ -595,8 +601,9 @@ void CreateCircleDialog::createCircle()
std::cerr << "CreateCircleDialog::updateCircle() Checks and Balances Okay - calling service proper.."<< std::endl;
#endif
rsGxsCircles->editCircle(*const_cast<RsGxsCircleGroup*>(&circle)); // const_cast: Not nice, but simpler.
rsGxsCircles->updateGroup(token, circle);
circleId = RsGxsCircleId(circle.mMeta.mGroupId);
}
else
{
@ -606,10 +613,25 @@ void CreateCircleDialog::createCircle()
std::cerr << "CreateCircleDialog::createCircle() Checks and Balances Okay - calling service proper.."<< std::endl;
#endif
rsGxsCircles->createGroup(token, circle);
rsGxsCircles->createCircle(circle.mMeta.mGroupName,
static_cast<RsGxsCircleType>(circle.mMeta.mCircleType),
circleId,
circle.mMeta.mCircleId,
circle.mMeta.mAuthorId,
circle.mInvitedMembers,
circle.mLocalFriends);
}
if(!is_existing_circle)
RsQThreadUtils::postToObject( [circle,circleId]()
{
QMessageBox::information(nullptr,tr("Circle created"),
tr("Your new circle has been created:\n Name: %1\n Id: %2.")
.arg(QString::fromUtf8(circle.mMeta.mGroupName.c_str()))
.arg(QString::fromStdString(circleId.toStdString())));
});
});
close();
}
@ -810,11 +832,6 @@ void CreateCircleDialog::loadIdentities()
void CreateCircleDialog::fillIdentitiesList(const std::vector<RsGxsIdGroup>& id_groups)
{
#ifdef DEBUG_CREATE_CIRCLE_DIALOG
std::cerr << "CreateCircleDialog::loadIdentities(" << token << ")";
std::cerr << std::endl;
#endif
QTreeWidget *tree = ui.treeWidget_IdList;
tree->clear();
@ -937,3 +954,8 @@ void CreateCircleDialog::MembershipListCustomPopupMenu( QPoint )
contextMnu.exec(QCursor::pos());
}
void CreateCircleDialog::updateMembership()
{
ui.members_groupBox->setTitle( tr("Invited Members") + " (" + QString::number(ui.treeWidget_membership->topLevelItemCount()) + ")" );
}

View file

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

18
retroshare-gui/src/gui/FileTransfer/DLListDelegate.cpp Normal file → Executable file
View file

@ -32,6 +32,8 @@
Q_DECLARE_METATYPE(FileProgressInfo)
#define MAX_CHAR_TMP 128
DLListDelegate::DLListDelegate(QObject *parent) : QAbstractItemDelegate(parent)
{
}
@ -100,7 +102,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
multi *= 1024.0;
}
}
painter->drawText(option.rect, Qt::AlignRight, temp);
painter->drawText(option.rect, Qt::AlignRight | Qt::AlignVCenter, temp);
break;
case COLUMN_REMAINING:
remaining = index.data().toLongLong();
@ -119,7 +121,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
multi *= 1024.0;
}
}
painter->drawText(option.rect, Qt::AlignRight, temp);
painter->drawText(option.rect, Qt::AlignRight | Qt::AlignVCenter, temp);
break;
case COLUMN_COMPLETED:
completed = index.data().toLongLong();
@ -138,7 +140,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
multi *= 1024.0;
}
}
painter->drawText(option.rect, Qt::AlignRight, temp);
painter->drawText(option.rect, Qt::AlignRight | Qt::AlignVCenter, temp);
break;
case COLUMN_DLSPEED:
dlspeed = index.data().toDouble();
@ -149,7 +151,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
temp.sprintf("%.2f", dlspeed/1024.);
temp += " KB/s";
}
painter->drawText(option.rect, Qt::AlignRight, temp);
painter->drawText(option.rect, Qt::AlignRight | Qt::AlignVCenter, temp);
break;
case COLUMN_PROGRESS:
{
@ -234,7 +236,7 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
pixmap = qvariant_cast<QIcon>(value).pixmap(option.decorationSize, option.state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled, option.state & QStyle::State_Open ? QIcon::On : QIcon::Off);
pixmapRect = (pixmap.isNull() ? QRect(0, 0, 0, 0): QRect(QPoint(0, 0), option.decorationSize));
if (pixmapRect.isValid()){
QPoint p = QStyle::alignedRect(option.direction, Qt::AlignLeft, pixmap.size(), option.rect).topLeft();
QPoint p = QStyle::alignedRect(option.direction, Qt::AlignLeft | Qt::AlignVCenter, pixmap.size(), option.rect).topLeft();
p.setX( p.x() + pixOffset);
painter->drawPixmap(p, pixmap);
temp = " " + temp;
@ -245,13 +247,13 @@ void DLListDelegate::paint(QPainter * painter, const QStyleOptionViewItem & opti
pixmap = qvariant_cast<QIcon>(value).pixmap(option.decorationSize, option.state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled, option.state & QStyle::State_Open ? QIcon::On : QIcon::Off);
pixmapRect = (pixmap.isNull() ? QRect(0, 0, 0, 0): QRect(QPoint(0, 0), option.decorationSize));
if (pixmapRect.isValid()){
QPoint p = QStyle::alignedRect(option.direction, Qt::AlignLeft, pixmap.size(), option.rect).topLeft();
QPoint p = QStyle::alignedRect(option.direction, Qt::AlignLeft | Qt::AlignVCenter, pixmap.size(), option.rect).topLeft();
p.setX( p.x() + pixOffset);
painter->drawPixmap(p, pixmap);
temp = " " + temp;
pixOffset += pixmap.size().width();
}
painter->drawText(option.rect.translated(pixOffset, 0), Qt::AlignLeft, temp);
painter->drawText(option.rect.translated(pixOffset, 0), Qt::AlignLeft | Qt::AlignVCenter, temp);
}
break;
case COLUMN_LASTDL:
@ -277,7 +279,7 @@ QSize DLListDelegate::sizeHint(const QStyleOptionViewItem & option, const QModel
{
float w = QFontMetricsF(option.font).width(index.data(Qt::DisplayRole).toString());
int S = QFontMetricsF(option.font).height() ;
int S = QFontMetricsF(option.font).height()*1.5 ;
return QSize(w,S);
}

View file

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

View file

@ -30,13 +30,14 @@
#include "gui/RetroShareLink.h"
#include "retroshare-gui/RsAutoUpdatePage.h"
#include "gui/msgs/MessageComposer.h"
#include "gui/common/RsCollection.h"
#include "gui/common/RsCollectionDialog.h"
#include "gui/common/FilesDefs.h"
#include "gui/common/RsUrlHandler.h"
#include "gui/settings/rsharesettings.h"
#include "gui/advsearch/advancedsearchdialog.h"
#include "gui/common/RSTreeWidgetItem.h"
#include "util/QtVersion.h"
#include "util/qtthreadsutils.h"
#include <retroshare/rsfiles.h>
#include <retroshare/rsturtle.h>
@ -85,6 +86,18 @@ const int SearchDialog::FILETYPE_IDX_DIRECTORY = 8;
QMap<int, QString> * SearchDialog::FileTypeExtensionMap = new QMap<int, QString>();
bool SearchDialog::initialised = false;
struct SearchDialog::FileDetail
{
public:
RsPeerId id;
std::string name;
RsFileHash hash;
std::string path;
uint64_t size;
uint32_t mtime;
uint32_t rank;
};
/** Constructor */
SearchDialog::SearchDialog(QWidget *parent)
: MainPage(parent),
@ -188,6 +201,10 @@ SearchDialog::SearchDialog(QWidget *parent)
ui.searchResultWidget->sortItems(SR_NAME_COL, Qt::AscendingOrder);
QFontMetricsF fontMetrics(ui.searchResultWidget->font());
int iconHeight = fontMetrics.height() * 1.4;
ui.searchResultWidget->setIconSize(QSize(iconHeight, iconHeight));
/* Set initial size the splitter */
QList<int> sizes;
sizes << 250 << width(); // Qt calculates the right sizes
@ -213,6 +230,15 @@ SearchDialog::SearchDialog(QWidget *parent)
checkText(ui.lineEdit->text());
// add an event handler to get search results (previously available through notifyQt)
mEventHandlerId = 0;
rsEvents->registerEventsHandler( [this](std::shared_ptr<const RsEvent> event)
{
RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this );
}, mEventHandlerId, RsEventType::FILE_TRANSFER );
}
SearchDialog::~SearchDialog()
@ -231,6 +257,31 @@ SearchDialog::~SearchDialog()
ui.searchResultWidget->setItemDelegateForColumn(SR_SIZE_COL, nullptr);
ui.searchResultWidget->setItemDelegateForColumn(SR_AGE_COL, nullptr);
rsEvents->unregisterEventsHandler(mEventHandlerId);
}
void SearchDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> event)
{
if(event->mType != RsEventType::FILE_TRANSFER)
return;
auto fe = dynamic_cast<const RsFileTransferEvent*>(event.get());
if(!fe || fe->mFileTransferEventCode!=RsFileTransferEventCode::NEW_DISTANT_SEARCH_RESULTS)
return;
for(uint32_t i=0;i<fe->mResults.size();++i)
{
FileDetail f;
f.hash = fe->mResults[i].fHash;
f.name = fe->mResults[i].fName;
f.size = fe->mResults[i].fSize;
f.mtime = 0; // zero what's not available, otherwise we'll get some random values displayed.
f.rank = 0;
updateFiles(fe->mRequestId,f);
}
}
void SearchDialog::processSettings(bool bLoad)
@ -274,7 +325,7 @@ void SearchDialog::checkText(const QString& txt)
ui.searchButton->setDisabled(txt.length() < 3);
ui.searchLineFrame->setProperty("valid", (txt.length() >= 3));
ui.searchLineFrame->style()->unpolish(ui.searchLineFrame);
Rshare::refreshStyleSheet(ui.searchLineFrame, false);
RsApplication::refreshStyleSheet(ui.searchLineFrame, false);
}
void SearchDialog::initialiseFileTypeMappings()
@ -282,7 +333,7 @@ void SearchDialog::initialiseFileTypeMappings()
/* edit these strings to change the range of extensions recognised by the search */
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_ANY, "");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_AUDIO,
"aac aif flac iff m3u m4a mid midi mp3 mpa ogg ra ram wav wma");
"aac aif flac iff m3u m4a mid midi mp3 mpa ogg ra ram wav wma weba");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_ARCHIVE,
"7z bz2 gz pkg rar sea sit sitx tar zip tgz");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_CDIMAGE,
@ -291,11 +342,11 @@ void SearchDialog::initialiseFileTypeMappings()
"doc odt ott rtf pdf ps txt log msg wpd wps ods xls epub" );
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_PICTURE,
"3dm 3dmf ai bmp drw dxf eps gif ico indd jpe jpeg jpg mng pcx pcc pct pgm "
"pix png psd psp qxd qxprgb sgi svg tga tif tiff xbm xcf");
"pix png psd psp qxd qxprgb sgi svg tga tif tiff xbm xcf webp");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_PROGRAM,
"app bat cgi com bin exe js pif py pl sh vb ws bash");
SearchDialog::FileTypeExtensionMap->insert(FILETYPE_IDX_VIDEO,
"3gp asf asx avi mov mp4 mkv flv mpeg mpg qt rm swf vob wmv");
"3gp asf asx avi mov mp4 mkv flv mpeg mpg qt rm swf vob wmv webm");
SearchDialog::initialised = true;
}
@ -446,25 +497,23 @@ void SearchDialog::collCreate()
int selectedCount = selectedItems.size() ;
QTreeWidgetItem * item ;
for (int i = 0; i < selectedCount; ++i) {
RsFileTree tree;
for (int i = 0; i < selectedCount; ++i)
{
item = selectedItems.at(i) ;
if (!item->text(SR_HASH_COL).isEmpty()) {
if (!item->text(SR_HASH_COL).isEmpty())
{
std::string name = item->text(SR_NAME_COL).toUtf8().constData();
RsFileHash hash( item->text(SR_HASH_COL).toStdString() );
uint64_t count = item->text(SR_SIZE_COL).toULongLong();
DirDetails details;
details.name = name;
details.hash = hash;
details.size = count;
details.type = DIR_TYPE_FILE;
dirVec.push_back(details);
tree.addFile(tree.root(),name,hash,count);
}
}
RsCollection(dirVec,RS_FILE_HINTS_LOCAL).openNewColl(this);
RsCollectionDialog::openNewCollection(tree);
}
void SearchDialog::collModif()
@ -491,12 +540,8 @@ void SearchDialog::collModif()
/* open file with a suitable application */
QFileInfo qinfo;
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists()) {
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
RsCollection collection;
collection.openColl(qinfo.absoluteFilePath());
}//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString))
}//if (qinfo.exists())
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath());
}
void SearchDialog::collView()
@ -523,12 +568,8 @@ void SearchDialog::collView()
/* open file with a suitable application */
QFileInfo qinfo;
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists()) {
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
RsCollection collection;
collection.openColl(qinfo.absoluteFilePath(), true);
}//if (qinfo.absoluteFilePath().endsWith(RsCollectionFile::ExtensionString))
}//if (qinfo.exists())
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath(), true);
}
void SearchDialog::collOpen()
@ -546,32 +587,35 @@ void SearchDialog::collOpen()
if (rsFiles->FileDetails(hash, RS_FILE_HINTS_EXTRA | RS_FILE_HINTS_LOCAL
| RS_FILE_HINTS_BROWSABLE | RS_FILE_HINTS_NETWORK_WIDE
| RS_FILE_HINTS_SPEC_ONLY, info)) {
| RS_FILE_HINTS_SPEC_ONLY, info))
{
/* make path for downloaded files */
std::string path;
path = info.path;
/* open file with a suitable application */
QFileInfo qinfo;
RsCollection::RsCollectionErrorCode err;
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists()) {
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
RsCollection collection;
if (collection.load(qinfo.absoluteFilePath())) {
collection.downloadFiles();
return;
}
}
}
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
RsCollectionDialog::downloadFiles(RsCollection(qinfo.absoluteFilePath(),err));
}
}
}
RsCollection collection;
if (collection.load(this)) {
collection.downloadFiles();
}//if (collection.load(this))
QString fileName;
if (!misc::getOpenFileName(nullptr, RshareSettings::LASTDIR_EXTRAFILE, QApplication::translate("RsCollectionFile", "Open collection file"), QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")", fileName))
return ;
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
RsCollection::RsCollectionErrorCode err;
RsCollection collection(fileName, err);
if(err == RsCollection::RsCollectionErrorCode::COLLECTION_NO_ERROR)
RsCollectionDialog::downloadFiles(collection);
else
QMessageBox::information(nullptr,tr("Error open RsCollection file"),RsCollection::errorString(err));
}
void SearchDialog::downloadDirectory(const QTreeWidgetItem *item, const QString &base)
@ -801,28 +845,29 @@ void SearchDialog::advancedSearch(RsRegularExpression::Expression* expression)
{
advSearchDialog->hide();
/* call to core */
std::list<DirDetails> results;
// send a turtle search request
RsRegularExpression::LinearizedExpression e ;
expression->linearize(e) ;
TurtleRequestId req_id = rsFiles->turtleSearch(e) ;
TurtleRequestId req_id ;
if(ui._anonF2Fsearch_CB->isChecked())
req_id = rsFiles->turtleSearch(e) ;
else
req_id = RSRandom::random_u32() ; // generate a random 32 bits request id
// This will act before turtle results come to the interface, thanks to the signals scheduling policy.
initSearchResult(QString::fromStdString(e.GetStrings()),req_id, ui.FileTypeComboBox->currentIndex(), true) ;
rsFiles -> SearchBoolExp(expression, results, RS_FILE_HINTS_REMOTE);// | DIR_FLAGS_NETWORK_WIDE | DIR_FLAGS_BROWSABLE);
std::list<DirDetails> results;
FileSearchFlags flags(0);
if(ui._ownFiles_CB->isChecked()) flags |= RS_FILE_HINTS_LOCAL;
if(ui._friendListsearch_SB->isChecked()) flags |= RS_FILE_HINTS_REMOTE;
rsFiles -> SearchBoolExp(expression, results, flags);
/* abstraction to allow reusee of tree rendering code */
resultsToTree(advSearchDialog->getSearchAsString(),req_id, results);
// // debug stuff
// Expression *expression2 = LinearizedExpression::toExpr(e) ;
// results.clear() ;
// rsFiles -> SearchBoolExp(expression2, results, DIR_FLAGS_REMOTE | DIR_FLAGS_NETWORK_WIDE | DIR_FLAGS_BROWSABLE);
// resultsToTree((advSearchDialog->getSearchAsString()).toStdString(),req_id+1, results);
}
void SearchDialog::searchKeywords()
@ -928,7 +973,7 @@ void SearchDialog::searchKeywords(const QString& keywords)
}
}
void SearchDialog::updateFiles(qulonglong search_id,FileDetail file)
void SearchDialog::updateFiles(qulonglong search_id,const FileDetail& file)
{
searchResultsQueue.push_back(std::pair<qulonglong,FileDetail>(search_id,file)) ;
@ -956,7 +1001,7 @@ void SearchDialog::processResultQueue()
while(!searchResultsQueue.empty() && nb_treated_elements++ < 250)
{
qulonglong search_id = searchResultsQueue.back().first ;
FileDetail& file = searchResultsQueue.back().second ;
const FileDetail& file = searchResultsQueue.back().second ;
#ifdef DEBUG
std::cout << "Updating file detail:" << std::endl ;
@ -1282,8 +1327,8 @@ void SearchDialog::insertFile(qulonglong searchId, const FileDetail& file, int s
item->setText(SR_SIZE_COL, QString::number(file.size));
item->setData(SR_SIZE_COL, ROLE_SORT, (qulonglong) file.size);
item->setText(SR_AGE_COL, QString::number(file.age));
item->setData(SR_AGE_COL, ROLE_SORT, file.age);
item->setText(SR_AGE_COL, QString::number(file.mtime));
item->setData(SR_AGE_COL, ROLE_SORT, file.mtime);
item->setTextAlignment( SR_SIZE_COL, Qt::AlignRight );
int friendSource = 0;
int anonymousSource = 0;
@ -1358,21 +1403,21 @@ void SearchDialog::resultsToTree(const QString& txt,qulonglong searchId, const s
std::list<DirDetails>::const_iterator it;
for(it = results.begin(); it != results.end(); ++it)
if (it->type == DIR_TYPE_FILE) {
if (it->type == DIR_TYPE_FILE)
{
FileDetail fd;
fd.id = it->id;
fd.name = it->name;
fd.hash = it->hash;
fd.path = it->path;
fd.size = it->size;
fd.age = it->mtime;
fd.mtime= it->mtime;
fd.rank = 0;
insertFile(searchId,fd, FRIEND_SEARCH);
} else if (it->type == DIR_TYPE_DIR) {
// insertDirectory(txt, searchId, *it, NULL);
insertDirectory(txt, searchId, *it);
}
else if (it->type == DIR_TYPE_DIR)
insertDirectory(txt, searchId, *it);
ui.searchResultWidget->setSortingEnabled(true);
}
@ -1451,32 +1496,37 @@ void SearchDialog::copyResultLink()
{
/* should also be able to handle multi-selection */
QList<QTreeWidgetItem*> itemsForCopy = ui.searchResultWidget->selectedItems();
int numdls = itemsForCopy.size();
QTreeWidgetItem * item;
std::set<RsFileHash> already_seen_hashes;
QList<RetroShareLink> urls ;
for (int i = 0; i < numdls; ++i)
for (auto item:itemsForCopy)
{
item = itemsForCopy.at(i);
// call copy
if (!item->childCount())
QString fhash = item->text(SR_HASH_COL);
RsFileHash hash(fhash.toStdString());
if(!hash.isNull() && (already_seen_hashes.end() == already_seen_hashes.find(hash)))
{
std::cerr << "SearchDialog::copyResultLink() Calling set retroshare link";
std::cerr << std::endl;
QString fhash = item->text(SR_HASH_COL);
qulonglong fsize = item->text(SR_SIZE_COL).toULongLong();
QString fname = item->text(SR_NAME_COL);
RetroShareLink link = RetroShareLink::createFile(fname, fsize, fhash);
if (link.valid()) {
if (link.valid())
{
std::cerr << "new link added to clipboard: " << link.toString().toStdString() << std::endl ;
urls.push_back(link);
already_seen_hashes.insert(hash);
}
}
}
if(!urls.empty())
RSLinkClipboard::copyLinks(urls) ;
}

View file

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

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>783</width>
<width>793</width>
<height>511</height>
</rect>
</property>
@ -64,10 +64,7 @@
<property name="bottomMargin">
<number>2</number>
</property>
<property name="horizontalSpacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
@ -343,8 +340,8 @@
<string>Any</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeAny.png</normaloff>:/images/FileTypeAny.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/any.svg</normaloff>:/icons/filetype/any.svg</iconset>
</property>
</item>
<item>
@ -352,8 +349,8 @@
<string>Archive</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeArchive.png</normaloff>:/images/FileTypeArchive.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/archive.svg</normaloff>:/icons/filetype/archive.svg</iconset>
</property>
</item>
<item>
@ -361,8 +358,8 @@
<string>Audio</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeAudio.png</normaloff>:/images/FileTypeAudio.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/audio.svg</normaloff>:/icons/filetype/audio.svg</iconset>
</property>
</item>
<item>
@ -370,8 +367,8 @@
<string>CD-Image</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeCDImage.png</normaloff>:/images/FileTypeCDImage.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/img.svg</normaloff>:/icons/filetype/img.svg</iconset>
</property>
</item>
<item>
@ -379,8 +376,8 @@
<string>Document</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeDocument.png</normaloff>:/images/FileTypeDocument.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/document.svg</normaloff>:/icons/filetype/document.svg</iconset>
</property>
</item>
<item>
@ -388,8 +385,8 @@
<string>Picture</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypePicture.png</normaloff>:/images/FileTypePicture.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/picture.svg</normaloff>:/icons/filetype/picture.svg</iconset>
</property>
</item>
<item>
@ -397,8 +394,8 @@
<string>Program</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeProgram.png</normaloff>:/images/FileTypeProgram.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/program.svg</normaloff>:/icons/filetype/program.svg</iconset>
</property>
</item>
<item>
@ -406,8 +403,8 @@
<string>Video</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/FileTypeVideo.png</normaloff>:/images/FileTypeVideo.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/filetype/video.svg</normaloff>:/icons/filetype/video.svg</iconset>
</property>
</item>
<item>
@ -415,8 +412,8 @@
<string>Directory</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/foldermail.png</normaloff>:/images/foldermail.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/folder.png</normaloff>:/icons/folder.png</iconset>
</property>
</item>
</widget>

View file

@ -27,7 +27,7 @@
#include "gui/RetroShareLink.h"
#include "gui/ShareManager.h"
#include "gui/common/PeerDefs.h"
#include "gui/common/RsCollection.h"
#include "gui/common/RsCollectionDialog.h"
#include "gui/msgs/MessageComposer.h"
#include "gui/gxschannels/GxsChannelDialog.h"
#include "gui/gxsforums/GxsForumsDialog.h"
@ -55,6 +55,16 @@
#include <set>
#define SHARED_FILES_DIALOG_COLUMN_NAME 0
#define SHARED_FILES_DIALOG_COLUMN_FILENB 1
#define SHARED_FILES_DIALOG_COLUMN_SIZE 2
#define SHARED_FILES_DIALOG_COLUMN_AGE 3
#define SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS 4
#define SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR 5
#define SHARED_FILES_DIALOG_COLUMN_COUNT 6
#define SHARED_FILES_DIALOG_FILTER_STRING "filtered"
/* Images for context menu icons */
#define IMAGE_DOWNLOAD ":/icons/png/download.png"
#define IMAGE_PLAY ":/images/start.png"
@ -75,7 +85,7 @@
#define IMAGE_COLLOPEN ":/icons/collections.png"
#define IMAGE_EDITSHARE ":/icons/png/pencil-edit-button.png"
#define IMAGE_MYFILES ":/icons/svg/folders1.svg"
#define IMAGE_UNSHAREEXTRA ":/images/button_cancel.png"
#define IMAGE_UNSHAREEXTRA ":/icons/cancel.svg"
/*define viewType_CB value */
#define VIEW_TYPE_TREE 0
@ -91,7 +101,7 @@
//#define DEBUG_SHARED_FILES_DIALOG 1
const QString Image_AddNewAssotiationForFile = ":/images/kcmsystem24.png";
const QString Image_AddNewAssotiationForFile = ":/icons/svg/options.svg";
class SFDSortFilterProxyModel : public QSortFilterProxyModel
{
@ -187,17 +197,17 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent)
tree_proxyModel->setSourceModel(tree_model);
tree_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
tree_proxyModel->setSortRole(RetroshareDirModel::SortRole);
tree_proxyModel->sort(COLUMN_NAME);
tree_proxyModel->sort(SHARED_FILES_DIALOG_COLUMN_NAME);
tree_proxyModel->setFilterRole(RetroshareDirModel::FilterRole);
tree_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ;
tree_proxyModel->setFilterRegExp(QRegExp(QString(SHARED_FILES_DIALOG_FILTER_STRING))) ;
flat_proxyModel = new SFDSortFilterProxyModel(flat_model, this);
flat_proxyModel->setSourceModel(flat_model);
flat_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
flat_proxyModel->setSortRole(RetroshareDirModel::SortRole);
flat_proxyModel->sort(COLUMN_NAME);
flat_proxyModel->sort(SHARED_FILES_DIALOG_COLUMN_NAME);
flat_proxyModel->setFilterRole(RetroshareDirModel::FilterRole);
flat_proxyModel->setFilterRegExp(QRegExp(QString(RETROSHARE_DIR_MODEL_FILTER_STRING))) ;
flat_proxyModel->setFilterRegExp(QRegExp(QString(SHARED_FILES_DIALOG_FILTER_STRING))) ;
connect(ui.filterClearButton, SIGNAL(clicked()), this, SLOT(clearFilter()));
connect(ui.filterStartButton, SIGNAL(clicked()), this, SLOT(startFilter()));
@ -219,12 +229,12 @@ SharedFilesDialog::SharedFilesDialog(bool remote_mode, QWidget *parent)
int charWidth = ui.dirTreeView->fontMetrics().horizontalAdvance("_");
#endif
header->resizeSection ( COLUMN_NAME , charWidth*100 );
header->resizeSection ( COLUMN_FILENB , charWidth*15 );
header->resizeSection ( COLUMN_SIZE , charWidth*10 );
header->resizeSection ( COLUMN_AGE , charWidth*6 );
header->resizeSection ( COLUMN_FRIEND_ACCESS, charWidth*10 );
header->resizeSection ( COLUMN_WN_VISU_DIR , charWidth*20 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_NAME , charWidth*100 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FILENB , charWidth*15 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_SIZE , charWidth*10 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_AGE , charWidth*6 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, charWidth*10 );
header->resizeSection ( SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR , charWidth*20 );
header->setStretchLastSection(true);
@ -256,7 +266,7 @@ LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent)
: SharedFilesDialog(false,parent)
{
// Hide columns after loading the settings
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
ui.downloadButton->hide() ;
// load settings
@ -275,14 +285,14 @@ LocalSharedFilesDialog::LocalSharedFilesDialog(QWidget *parent)
ui.titleBarPixmap->setPixmap(FilesDefs::getPixmapFromQtResourcePath(IMAGE_MYFILES)) ;
ui.dirTreeView->setItemDelegateForColumn(COLUMN_FRIEND_ACCESS,new ShareFlagsItemDelegate()) ;
ui.dirTreeView->setItemDelegateForColumn(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS,new ShareFlagsItemDelegate()) ;
}
RemoteSharedFilesDialog::RemoteSharedFilesDialog(QWidget *parent)
: SharedFilesDialog(true,parent)
{
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, true) ;
ui.checkButton->hide() ;
connect(ui.downloadButton, SIGNAL(clicked()), this, SLOT(downloadRemoteSelected()));
@ -309,7 +319,7 @@ void SharedFilesDialog::hideEvent(QHideEvent *)
model->setVisible(false) ;
}
void SharedFilesDialog::showEvent(QShowEvent *)
void SharedFilesDialog::showEvent(QShowEvent *event)
{
if(model!=NULL)
{
@ -322,6 +332,10 @@ void SharedFilesDialog::showEvent(QShowEvent *)
restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes);
}
if (!event->spontaneous()) {
updateFontSize();
}
}
RemoteSharedFilesDialog::~RemoteSharedFilesDialog()
{
@ -436,9 +450,9 @@ void SharedFilesDialog::changeCurrentViewModel(int viewTypeIndex)
restoreExpandedPathsAndSelection(expanded_indexes,hidden_indexes,selected_indexes);
QHeaderView * header = ui.dirTreeView->header () ;
QHeaderView_setSectionResizeModeColumn(header, COLUMN_NAME, QHeaderView::Interactive);
QHeaderView_setSectionResizeModeColumn(header, SHARED_FILES_DIALOG_COLUMN_NAME, QHeaderView::Interactive);
ui.dirTreeView->header()->headerDataChanged(Qt::Horizontal, COLUMN_NAME, COLUMN_WN_VISU_DIR) ;
ui.dirTreeView->header()->headerDataChanged(Qt::Horizontal, SHARED_FILES_DIALOG_COLUMN_NAME, SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR) ;
// recursRestoreExpandedItems(ui.dirTreeView->rootIndex(),expanded_indexes);
FilterItems();
@ -448,9 +462,9 @@ void LocalSharedFilesDialog::showProperColumns()
{
if(model == tree_model)
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->hide();
ui.filterPatternLineEdit->hide();
@ -460,9 +474,9 @@ void LocalSharedFilesDialog::showProperColumns()
}
else
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->show();
ui.filterPatternLineEdit->show();
@ -473,9 +487,9 @@ void RemoteSharedFilesDialog::showProperColumns()
{
if(model == tree_model)
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, true) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->hide();
ui.filterPatternLineEdit->hide();
@ -485,9 +499,9 @@ void RemoteSharedFilesDialog::showProperColumns()
}
else
{
ui.dirTreeView->setColumnHidden(COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(COLUMN_WN_VISU_DIR, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FILENB, true) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_FRIEND_ACCESS, false) ;
ui.dirTreeView->setColumnHidden(SHARED_FILES_DIALOG_COLUMN_WN_VISU_DIR, false) ;
#ifdef DONT_USE_SEARCH_IN_TREE_VIEW
ui.filterLabel->show();
ui.filterPatternLineEdit->show();
@ -515,7 +529,6 @@ void LocalSharedFilesDialog::checkUpdate()
void LocalSharedFilesDialog::forceCheck()
{
rsFiles->ForceDirectoryCheck();
return;
}
void RemoteSharedFilesDialog::spawnCustomPopupMenu( QPoint point )
@ -639,7 +652,7 @@ void SharedFilesDialog::copyLinks(const QModelIndexList& lst, bool remote,QList<
QString dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName();
RetroShareLink link = RetroShareLink::createFileTree(dir_name,ft->mTotalSize,ft->mTotalFiles,QString::fromStdString(ft->toRadix64())) ;
RetroShareLink link = RetroShareLink::createFileTree(dir_name,ft->totalFileSize(),ft->numFiles(),QString::fromStdString(ft->toRadix64())) ;
if(link.valid())
urls.push_back(link) ;
@ -721,7 +734,32 @@ void SharedFilesDialog::sendLinkTo()
void SharedFilesDialog::collCreate()
{
QModelIndexList lst = getSelected();
model->createCollectionFile(this, lst);
std::vector <DirDetails> dirVec;
model->getDirDetailsFromSelect(lst, dirVec);
auto RemoteMode = isRemote();
FileSearchFlags f = RemoteMode?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL ;
QString dir_name;
if(!RemoteMode)
{
if(!dirVec.empty())
{
const DirDetails& details = dirVec[0];
dir_name = QDir(QString::fromUtf8(details.name.c_str())).dirName();
}
}
RsFileTree tree;
for(uint32_t i=0;i<dirVec.size();++i)
tree.addFileTree(tree.root(),*RsFileTree::fromDirDetails(dirVec[i],RemoteMode,true));
RsCollectionDialog::openNewCollection(tree);
//auto ft = RsFileTree::fromDirDetails(details,remote);
}
void SharedFilesDialog::collModif()
@ -746,12 +784,8 @@ void SharedFilesDialog::collModif()
/* open file with a suitable application */
QFileInfo qinfo;
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists()) {
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
RsCollection collection;
collection.openColl(qinfo.absoluteFilePath());
}
}
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
RsCollectionDialog::editExistingCollection(qinfo.absoluteFilePath());
}
void SharedFilesDialog::collView()
@ -776,12 +810,8 @@ void SharedFilesDialog::collView()
/* open file with a suitable application */
QFileInfo qinfo;
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists()) {
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
RsCollection collection;
collection.openColl(qinfo.absoluteFilePath(), true);
}
}
if (qinfo.exists() && qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString))
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath(), true);
}
void SharedFilesDialog::collOpen()
@ -808,20 +838,24 @@ void SharedFilesDialog::collOpen()
qinfo.setFile(QString::fromUtf8(path.c_str()));
if (qinfo.exists()) {
if (qinfo.absoluteFilePath().endsWith(RsCollection::ExtensionString)) {
RsCollection collection;
if (collection.load(qinfo.absoluteFilePath())) {
collection.downloadFiles();
return;
}
RsCollectionDialog::openExistingCollection(qinfo.absoluteFilePath(),true);
}
}
}
}
RsCollection collection;
if (collection.load(this)) {
collection.downloadFiles();
}
QString fileName;
if (!misc::getOpenFileName(nullptr, RshareSettings::LASTDIR_EXTRAFILE, QApplication::translate("RsCollectionFile", "Open collection file"), QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")", fileName))
return ;
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
RsCollection::RsCollectionErrorCode err;
RsCollection collection(fileName,err);
if(err == RsCollection::RsCollectionErrorCode::COLLECTION_NO_ERROR)
RsCollectionDialog::downloadFiles(collection);
}
void LocalSharedFilesDialog::playselectedfiles()
@ -1132,12 +1166,14 @@ void LocalSharedFilesDialog::spawnCustomPopupMenu( QPoint point )
collViewAct->setEnabled(bIsRsColl);
collOpenAct->setEnabled(true);
QMenu collectionMenu(tr("Collection"), this);
QMenu collectionMenu(tr("Retroshare Collection"), this);
collectionMenu.setIcon(QIcon(IMAGE_LIBRARY));
collectionMenu.addAction(collCreateAct);
if(bIsRsColl)
collectionMenu.addAction(collModifAct);
collectionMenu.addAction(collViewAct);
collectionMenu.addAction(collOpenAct);
//collectionMenu.addAction(collViewAct);
//collectionMenu.addAction(collOpenAct);
switch (type) {
case DIR_TYPE_DIR :
@ -1333,9 +1369,9 @@ void SharedFilesDialog::indicatorChanged(int index)
ui.dirTreeView->update(ui.dirTreeView->rootIndex());
if (correct_indicator[index] != IND_ALWAYS)
ui.dirTreeView->sortByColumn(COLUMN_AGE, Qt::AscendingOrder);
ui.dirTreeView->sortByColumn(SHARED_FILES_DIALOG_COLUMN_AGE, Qt::AscendingOrder);
else
ui.dirTreeView->sortByColumn(COLUMN_NAME, Qt::AscendingOrder);
ui.dirTreeView->sortByColumn(SHARED_FILES_DIALOG_COLUMN_NAME, Qt::AscendingOrder);
updateDisplay() ;
}
@ -1672,3 +1708,20 @@ bool SharedFilesDialog::tree_FilterItem(const QModelIndex &index, const QString
return (visible || visibleChildCount);
}
#endif
void SharedFilesDialog::updateFontSize()
{
#if defined(Q_OS_DARWIN)
int customFontSize = Settings->valueFromGroup("File", "MinimumFontSize", 13).toInt();
#else
int customFontSize = Settings->valueFromGroup("File", "MinimumFontSize", 11).toInt();
#endif
QFont newFont = ui.dirTreeView->font();
if (newFont.pointSize() != customFontSize) {
newFont.setPointSize(customFontSize);
QFontMetricsF fontMetrics(newFont);
int iconHeight = fontMetrics.height()*1.5;
ui.dirTreeView->setFont(newFont);
ui.dirTreeView->setIconSize(QSize(iconHeight, iconHeight));
}
}

View file

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

View file

@ -243,8 +243,8 @@ border-image: url(:/images/closepressed.png)
<string>All</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_list.png</normaloff>:/images/view_calendar_list.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-blank.svg</normaloff>:/icons/svg/calendar-blank.svg</iconset>
</property>
</item>
<item>
@ -252,8 +252,8 @@ border-image: url(:/images/closepressed.png)
<string>One day old</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_day.png</normaloff>:/images/view_calendar_day.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-today.svg</normaloff>:/icons/svg/calendar-today.svg</iconset>
</property>
</item>
<item>
@ -261,8 +261,8 @@ border-image: url(:/images/closepressed.png)
<string>One Week old</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_week.png</normaloff>:/images/view_calendar_week.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-week.svg</normaloff>:/icons/svg/calendar-week.svg</iconset>
</property>
</item>
<item>
@ -270,8 +270,8 @@ border-image: url(:/images/closepressed.png)
<string>One month old</string>
</property>
<property name="icon">
<iconset resource="../images.qrc">
<normaloff>:/images/view_calendar_month.png</normaloff>:/images/view_calendar_month.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/calendar-month.svg</normaloff>:/icons/svg/calendar-month.svg</iconset>
</property>
</item>
</widget>
@ -285,9 +285,16 @@ border-image: url(:/images/closepressed.png)
</item>
<item>
<widget class="QPushButton" name="checkButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Forces the re-check of all shared directories. While automatic file checking only cares for new/removed files for efficiency reasons, this button will force the re-scan of all files, possibly re-hashing existing files that may have changed. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>check files</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/svg/refresh.svg</normaloff>:/icons/svg/refresh.svg</iconset>
</property>
</widget>
</item>
<item>
@ -311,8 +318,8 @@ border-image: url(:/images/closepressed.png)
<string>Download</string>
</property>
<property name="icon">
<iconset>
<normaloff>:/images/download16.png</normaloff>:/images/download16.png</iconset>
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/download.png</normaloff>:/icons/png/download.png</iconset>
</property>
</widget>
</item>
@ -392,8 +399,8 @@ border-image: url(:/images/closepressed.png)
</customwidget>
</customwidgets>
<resources>
<include location="../icons.qrc"/>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View file

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

File diff suppressed because it is too large Load diff

View file

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

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