Drop libresapi and modules that depends on it

A copy of the repository freezed before this deletion to ease old code
  viewing is available at
  https://github.com/RetroShare/zz_archive_RetroShare_libresapi
Removed some more cruft
Temporarly disabled JSON API generation of RsWebUi methods due to
  compilation breakage when rs_webui is disabled, RsWebUi keeps working
  but cannot be restarted from JSON API
This commit is contained in:
Gioacchino Mazzurco 2019-11-27 23:32:38 +01:00
parent 887e3ad394
commit c73bdb83cd
No known key found for this signature in database
GPG Key ID: A1FBCA3872E87051
963 changed files with 73 additions and 48608 deletions

3
.gitmodules vendored
View File

@ -11,3 +11,6 @@
path = supportlibs/udp-discovery-cpp
url = https://github.com/truvorskameikin/udp-discovery-cpp.git
branch = develop
[submodule "supportlibs/rapidjson"]
path = supportlibs/rapidjson
url = https://github.com/Tencent/rapidjson.git

49
README.asciidoc Normal file
View File

@ -0,0 +1,49 @@
= RetroShare
RetroShare is a decentralized, private, secure, cross-platform, communication
toolkit.
RetroShare provides filesharing, chat, messages, forums, channels and
more.
.Build Status
|===============================================================================
|GNU/Linux, MacOS, (via travis-ci) | image:https://travis-ci.org/RetroShare/RetroShare.svg?branch=master[link="https://travis-ci.org/RetroShare/RetroShare"]
|Windows (via appveyor) | image:https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true[link="https://ci.appveyor.com/project/RetroShare58622/retroshare"]
|===============================================================================
== Compilation
It is very difficult to keep a comprehensive (we support many platforms) and
updated documentation on how to build retroshare, instead we provide scripts and
receipts that are used to create the packages for the supported platforms and
more in the `build_scripts` directory of this repository.
== Using RetroShare on a headless computer with WebUI
WARNING: This section is outdated need to be adapted to new WebUI
The webUI needs to be enabled as a parameter option in retroshare-service:
[source,bash]
--------
./retroshare-nogui --webinterface 9090 --docroot /usr/share/retroshare/webui/
--------
The webUI is only accessible on localhost:9090. It is advised to keep it that way so that your RS
cannot be controlled using an untrusted connection.
To access your web UI from a distance, just open a SSH tunnel on it:
[source,bash]
--------
distant_machine:~/ > ssh rs_host -L 9090:localhost:9090 -N
--------
"rs_host" is the machine running retroshare-nogui. Then on the distant machine, access your webUI on
http://localhost:9090
That also works with a retroshare GUI of course.

151
README.md
View File

@ -1,151 +0,0 @@
RetroShare
==============================
RetroShare is a decentralized, private and secure commmunication and sharing platform. RetroShare provides filesharing, chat, messages, forums and channels.
Build Status
------------
| Platform | Build Status |
| :------------- | :------------- |
| GNU/Linux, MacOS, (via travis-ci) | [![Build Status](https://travis-ci.org/RetroShare/RetroShare.svg?branch=master)](https://travis-ci.org/RetroShare/RetroShare) |
| Windows, `MSys2` (via appveyor) | [![Build status](https://ci.appveyor.com/api/projects/status/github/RetroShare/RetroShare?svg=true)](https://ci.appveyor.com/project/RetroShare58622/retroshare) |
Compilation on Windows
----------------------------
Follow this file : [WindowsMSys2_InstallGuide.md](https://github.com/RetroShare/RetroShare/blob/master/WindowsMSys2_InstallGuide.md)
Compilation on MacOSX
----------------------------
Follow this file : [MacOS_X_InstallGuide](https://github.com/RetroShare/RetroShare/blob/master/MacOS_X_InstallGuide.md)
Compilation for Android
---------------------------
Follow this file : [README-Android](https://github.com/RetroShare/RetroShare/blob/master/README-Android.asciidoc)
Compilation on Linux
----------------------------
1. Install package dependencies:
* Debian/Ubuntu
```bash
sudo apt-get install libglib2.0-dev libupnp-dev qt4-dev-tools \
libqt4-dev libssl-dev libxss-dev libgnome-keyring-dev libbz2-dev \
libqt4-opengl-dev libqtmultimediakit1 qtmobility-dev libsqlcipher-dev \
libspeex-dev libspeexdsp-dev libxslt1-dev libcurl4-openssl-dev \
libopencv-dev tcl8.5 libmicrohttpd-dev rapidjson-dev
```
* openSUSE
```bash
sudo zypper install gcc-c++ libqt4-devel libgnome-keyring-devel \
glib2-devel speex-devel libssh-devel protobuf-devel libcurl-devel \
libxml2-devel libxslt-devel sqlcipher-devel libmicrohttpd-devel \
opencv-devel speexdsp-devel libupnp-devel libavcodec-devel rapidjson
```
* Arch Linux
```bash
pacman -S base-devel libgnome-keyring libmicrohttpd libupnp libxslt \
libxss opencv qt4 speex speexdsp sqlcipher rapidjson
```
2. Checkout the source code
```bash
mkdir ~/retroshare
cd ~/retroshare
git clone https://github.com/RetroShare/RetroShare.git trunk
```
3. Compile
```bash
cd trunk
qmake CONFIG+=debug
make
```
4. Install
```bash
sudo make install
```
The executables produced will be:
/usr/bin/retroshare
/usr/bin/retroshare-nogui
5. Uninstall:
```bash
sudo make uninstall
```
Compile only retroshare-nogui
-----------------------------
If you want to run RetroShare on a server and dont need the gui and plugins,
you can run the following commands to only compile/install the nogui version:
```bash
qmake
make retroshare-nogui
sudo make retroshare-nogui-install_subtargets
```
For packagers
-------------
Packagers can use PREFIX and LIB\_DIR to customize the installation paths:
```bash
qmake PREFIX=/usr LIB_DIR=/usr/lib64 "CONFIG-=debug" "CONFIG+=release"
make
make INSTALL_ROOT=${PKGDIR} install
```
If libsqlcipher is not available as a package
---------------------------------------------
You need to place sqlcipher so that the hierarchy is:
retroshare
|
+--- trunk
|
+--- lib
|
+---- sqlcipher
```bash
mkdir lib
cd lib
git clone git://github.com/sqlcipher/sqlcipher.git
cd sqlcipher
./configure --enable-tempstore=yes CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="-lcrypto"
make
cd ..
```
Using retroshare-nogui & webUI
------------------------------
The webUI needs to be enabled as a parameter option in retroshare-nogui:
```bash
./retroshare-nogui --webinterface 9090 --docroot /usr/share/retroshare/webui/
```
The webUI is only accessible on localhost:9090. It is advised to keep it that way so that your RS
cannot be controlled using an untrusted connection.
To access your web UI from a distance, just open a SSH tunnel on it:
```bash
distant_machine:~/ > ssh rs_host -L 9090:localhost:9090 -N
```
"rs_host" is the machine running retroshare-nogui. Then on the distant machine, access your webUI on
http://localhost:9090
That also works with a retroshare GUI of course.
Compile and run tests
---------------------
qmake CONFIG+=tests
make
tests/unittests/unittests

View File

@ -1,47 +0,0 @@
Legend:
[X]: done, [ ]: pending, [-]: Cancelled, [/]: ongoing
GUI
[X] disable edit circles or make it work
[X] make GUI update when creating a circle
[ ] handle the display of encrypted forums/channels/etc when we do not have the keys
[ ] disable posting to circles for which we do not have the rights
[ ] warn about creating a circle without one of your ids in it
[X] group "show details" should be made up to date for restrinction options
[ ] allow to share circles publish rights
[X] allow non admin to see circles details but not edit
[ ] update TS of groups linked to a modified circle, so as to cause updates when a new member is addded to the group
[X] forums GUI should show correct restriction options for restricted forums
[X] changing a circle's name should update the GUI
[ ] make sure we cannot post in forums using IDs that are not in the circle
[ ] make sure unauthorised ids are rejected when validating posts from a circle restricted group
[ ] add GUI for local circles in circle GUI
[ ] share publish rights between circles
[ ] clean GUI (hide IDs)
Tests
[X] create a safe testing environment: 2 peers with no other contacts, 2 IDs each, and some forums
[X] test #001: transactions should encrypt and decrypt ok on both sides
[X] test #002: peers inside a circles should see the data
[X] test #003: peers outside of a circles shouldn't see the data
[-] test #004: peers subscribed to encrypted circles shouldn't keep asking for the data if they cannot decrypt
[X] test #005: peers can be added/removed from circles, and the circles update between friends
[ ] test #006: peers subscribed to encrypted circles who recently got added to the circle should get the data
[ ] test #007: peers outside a circle shouldn't be able to post (or posts shouldn't propagate)
Backend
[X] add debug output later RS_NXS_DEBUG_7 in rsgxsnetservice.cc for the crypto layer
Bugs
[X] Peers cannot be removed from a circle
[X] Context menu should not allow to edit non admin circles
[X] Editing circles make them subscribed, even non admin ones.
[X] Disable autosync for circles (in rsinit.cc)
[-] Add explicit circle sync request for all grps restricted to a circle (same as IDs)
[X] when a peer receives a circle, there is no way to view its details, only the id is available
[X] if a peer has circles available in "Circles (others)"... if you click on
"Create Circle" on the gui, then all circles are automatically subscribed

135
TODO.txt
View File

@ -1,135 +0,0 @@
Legend:
E: easy. Just GUI stuff, no algorithmics involved. That does not mean it's easy to come up with.
M: medium, either because it's a big task, or because it needs to understand a lot of code.
H: hard. Needs digging deep into libretroshare, possibly involving some crypto.
[ ] pending
[/] half-done, someone's on it
[-] cancelled
[?] needs discussion. Not sure we need this.
[X] done
PS: no tabs in this file,thx ;-)
---------------------------------------- TODO for RC ---------------------------------------------
E [ ] Implement creation of a default (signed) gxs id in startup wizard. Needs to show to the user a proper separation between nodes (network layer) and GXS ids (service layer).
E [ ] add tooltips in in permission matrix when hovering over the top most button/service name explain what the service does and the dependencies
E [ ] finish pass of GXS network service
----------------------------------------- TODO list ----------------------------------------------
GUI
E [X] add a "Contact" list to help selecting peers in People/FriendChooser/messages/etc.
E [ ] fix posted GUI. Needs to be more handy and more appealing.
E [ ] add RS links for GXS identities, so that they can be transferred easily between nodes in forums, etc
E [ ] enable people dialog
M [ ] Personal Page (Profile Page for GXS ID Users,view latest Posts, public forum/channels,posted posts, comments in all areas, Reputation Infos, Popularity and more)
E [ ] fix RSButtonOnText::eventFilter, and fix all places where RSButtonOnText gets deleted
E [ ] find all places where the deprecated(in Qt5) Q_WS_WIN and other Q_WS* macros are used, and replace with something else
E [ ] Make RS fully compatible with High DPI screens.
M [ ] improve comments in channels. Comments should be more available, more visible, and easier to handle.
E [ ] make GRouter statistics less CPU greedy
M [ ] Merge the various help systems. there's 3 of them now. Lots of duplicate code, etc.
M [ ] New and consistent icon set (Purplehaze420 said he would provide a consistent set of icons. Is that still alive?)
M [ ] add in options->Network a way to select which network interface is used to talk (choice between "auto" or selected from a list)
M [?] add a RS link for encrypted messages (encrypt for multiple GXS ids at once). Messages will appear as a link when encrypted and
be automatically decrypted when a suitable GXS id is available for it. Could be a way to securely post something in a public place.
File transfer (all 3 tasks are related)
E [ ] Add a limit of the total number of files simultaneously asked to the same peer using direct transfer.
H [ ] implement a new file list management, with new info such as access time,total upload, popularity, etc
H [ ] implement a new file list sharing service based on auto-sync system
M [ ] get rid of the old cache system (remove CacheStrapper, CacheSource,etc)
M [ ] implement end-to-end encryption using chacha20 and H(F), requesting file on H(H(F)). Make this optional and backward compatible.
DHT
H [-] improve DHT lookups to find masquerading peers using fake peers. First experiments (by cyril) do not prove very efficient.
GXS
M [X] create a tunnel service from distant chat code to make it (1) more generic (2) resistant to packet loss.
M [X] optimise GXS sync BW. For the moment too much is sent between nodes.
H [/] add the ability to use anonymous identities into circles. Needs new distribution model using items encrypted for multiple GXS keys.
H [ ] use different and incompatible classes for public and private keys in GxsSecurity
VOIP
H [ ] use proper video encoding. What we have now is decent for video, but sound should be prioritized. Experiments
with QtAV seem to work nicely. Finish and integrate!
M [ ] Deactivate Voip Buttons, when Friend has no Voip plugin enabled.
M [ ] Implement Voice for Video Chat
M [ ] Improve Voice and Video Quality
M [ ] Video Quality/Resolution Settings (High, Medium, Low) HD, HQ, SD )
M [ ] Video Device: WebCam(s) or Desktop Selection
M [ ] Audio Input Device Selection (Microphone)
M [ ] Audio Output Device Selection (Speaker)
M [ ] Video Snapshots https://support.skype.com/en/faq/FA1222/what-is-video-snapshot
M [ ] Voice Messaging (record and send a voice message)
M [ ] Video Messages (send a video greeting to multiple friends/coworkers/developers at once.)
M [ ] Add Call status (when im on the phone changes my status with phone icon)
H [ ] Audio Conference
H [ ] Video Conference
Messages
H [X] make the mail system re-send failed emails notified by the global router. This is hard because it needs a proper management of duplicate messages
E [X] add flags to allow distant messaging from contact list only / everyone / noone / only signed ids.
M [ ] add Signature feature to messages
M [ ] add vacation responser for messages (automatic replies)
Chat
E [X] add flags to allow distant chat from contact list only / everyone / noone / only signed ids.
Libretroshare
E [X] groups small packets in pqistreamer::handleoutgoing_locked(), and see if that removes some padding overhead
E [ ] make sure at least one location is kept when removing old locations as it avoids lots of connection problems.
M [ ] improve serialisation system. Lots of serialisation tasks (header, verifications, serialiser=>template, can be factored)
M [ ] separate chat stuff from rsmsgs.h into rschat.h
M [ ] crash at shutdown due to memory management already cleared while new objects keep being requested.
#0 0x0000000000da52eb in RsMemoryManagement:¿mallObject:¿mallObject() ()
#1 0x0000000000da3694 in RsItem::RsItem(unsigned int) ()
#2 0x0000000000dcb365 in RsRawItem::RsRawItem (this=0x0, t=33559555, size=38) at ./serialiser/rsserial.h
#3 0x0000000000fc1643 in RsServiceSerialiser::deserialise(void*, unsigned int*) ()
#4 0x0000000000da4451 in RsSerialiser::deserialise(void*, unsigned int*) ()
#5 0x0000000000fb5b4b in pqistreamer::handleincoming_locked() ()
#6 0x0000000000fb4b3e in pqistreamer::tick_recv(unsigned int) ()
#7 0x0000000000fb744a in pqithreadstreamer::data_tick() ()
#8 0x0000000000e19b27 in RsTickingThread::runloop (this=0x7fffb004e7e8) at util/rsthreads.cc:196
#9 0x0000000000e19748 in RsThread::rsthread_init (p=0x7fffb004e7e8) at util/rsthreads.cc:63
#10 0x00007ffff4006f6b in start_thread () from /lib64/libpthread.so.0
#11 0x00007ffff351c7ed in clone () from /lib64/libc.so.6
Channels
E [ ] allow to set a download directory per channel
M [ ] Thumbnail View for Channels
M [ ] Count commments on Comments Button
M [ ] Notify Channel comments/replies
Posted
E [ ] Show an info page for unsubscribed posted threads (same as forums)
E [ ] Make the GUI of Posted more sexy: more compact items, remove unecessary text, use consistent icons,...
Forums
E [ ] do we keep "Getting Started" ? the look needs to be improved. Any help doing this is welcome!
E [ ] some widgets in the GUI do not follow the system style => GUI looks bad on these systems
Bugs
E [ ] find the bug that shows some peers online in Network although they are not.
M [X] Selecting different options in messages composer -> contact list then come back,
shows disappearing / re-appearing people. What causes this???
Sounds
E [ ] add default sounds for Friend go online, New Chat Message, Message arrived, Download complete
M [ ] make sure sound is working for users on linux. We also need a "test sound" button in config->sound.
Packaging
E [ ] Sound files should be part of the install package at least on ubuntu (put them in /usr/share)
List of pending non backward compatible changes
===============================================
* RsGxsGrpMetaData::deserialis should use a proper TLV_STR_TYPE_NAME instead of 0 for
mGroupName and mMsgName, in rsgxsdata.cc
* p3IdService::service_CreateGroup() should convert the mPgpSign member to radix64!! For now
it is hard-coded in binary in a string.

View File

@ -1,333 +0,0 @@
#!/bin/bash
## Define default value for variable, take two arguments, $1 variable name,
## $2 default variable value, if the variable is not already define define it
## with default value.
function define_default_value()
{
VAR_NAME="${1}"
DEFAULT_VALUE="${2}"
[ -z "${!VAR_NAME}" ] && export ${VAR_NAME}="${DEFAULT_VALUE}"
}
## You are supposed to provide the following variables according to your system setup
define_default_value ANDROID_NDK_PATH "/opt/android-ndk/"
define_default_value ANDROID_NDK_ARCH "arm"
define_default_value ANDROID_NDK_ABI_VER "4.9"
define_default_value ANDROID_PLATFORM_VER "18"
define_default_value NATIVE_LIBS_TOOLCHAIN_PATH "${HOME}/Builds/android-toolchains/retroshare-android-${ANDROID_PLATFORM_VER}-${ANDROID_NDK_ARCH}-abi${ANDROID_NDK_ABI_VER}/"
define_default_value HOST_NUM_CPU $(nproc)
define_default_value BZIP2_SOURCE_VERSION "1.0.6"
define_default_value BZIP2_SOURCE_SHA256 a2848f34fcd5d6cf47def00461fcb528a0484d8edef8208d6d2e2909dc61d9cd
define_default_value OPENSSL_SOURCE_VERSION "1.0.2n"
define_default_value OPENSSL_SOURCE_SHA256 370babb75f278c39e0c50e8c4e7493bc0f18db6867478341a832a982fd15a8fe
define_default_value SQLITE_SOURCE_YEAR "2018"
define_default_value SQLITE_SOURCE_VERSION "3220000"
define_default_value SQLITE_SOURCE_SHA256 2824ab1238b706bc66127320afbdffb096361130e23291f26928a027b885c612
define_default_value SQLCIPHER_SOURCE_VERSION "3.4.2"
define_default_value SQLCIPHER_SOURCE_SHA256 69897a5167f34e8a84c7069f1b283aba88cdfa8ec183165c4a5da2c816cfaadb
define_default_value LIBUPNP_SOURCE_VERSION "1.6.24"
define_default_value LIBUPNP_SOURCE_SHA256 7d83d79af3bb4062e5c3a58bf2e90d2da5b8b99e2b2d57c23b5b6f766288cf96
define_default_value INSTALL_QT_ANDROID "false"
define_default_value QT_VERSION "5.9.4"
define_default_value QT_ANDROID_INSTALLER_SHA256 a214084e2295c9a9f8727e8a0131c37255bf724bfc69e80f7012ba3abeb1f763
## $1 filename, $2 sha256 hash
function check_sha256()
{
echo ${2} "${1}" | sha256sum -c &> /dev/null
}
## $1 filename, $2 sha256 hash, $3 url
function verified_download()
{
FILENAME="$1"
SHA256="$2"
URL="$3"
check_sha256 "${FILENAME}" "${SHA256}" ||
{
rm -rf "${FILENAME}"
wget -O "${FILENAME}" "$URL" ||
{
echo "Failed downloading ${FILENAME} from $URL"
exit 1
}
check_sha256 "${FILENAME}" "${SHA256}" ||
{
echo "SHA256 mismatch for ${FILENAME} from ${URL} expected sha256 ${SHA256} got $(sha256sum ${FILENAME} | awk '{print $1}')"
exit 1
}
}
}
if [ "${ANDROID_NDK_ARCH}" == "x86" ]; then
cArch="i686"
eABI=""
else
cArch="${ANDROID_NDK_ARCH}"
eABI="eabi"
fi
export SYSROOT="${NATIVE_LIBS_TOOLCHAIN_PATH}/sysroot"
export PREFIX="${SYSROOT}"
export CC="${NATIVE_LIBS_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-gcc"
export CXX="${NATIVE_LIBS_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-g++"
export AR="${NATIVE_LIBS_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-ar"
export RANLIB="${NATIVE_LIBS_TOOLCHAIN_PATH}/bin/${cArch}-linux-android${eABI}-ranlib"
export ANDROID_DEV="${ANDROID_NDK_PATH}/platforms/android-${ANDROID_PLATFORM_VER}/arch-${ANDROID_NDK_ARCH}/usr"
## More information available at https://android.googlesource.com/platform/ndk/+/ics-mr0/docs/STANDALONE-TOOLCHAIN.html
build_toolchain()
{
rm -rf ${NATIVE_LIBS_TOOLCHAIN_PATH}
[ "${ANDROID_NDK_ARCH}" == "x86" ] && toolchainName="${ANDROID_NDK_ARCH}-${ANDROID_NDK_ABI_VER}" || toolchainName="${ANDROID_NDK_ARCH}-linux-androideabi-${ANDROID_NDK_ABI_VER}"
${ANDROID_NDK_PATH}/build/tools/make-standalone-toolchain.sh --ndk-dir=${ANDROID_NDK_PATH} --arch=${ANDROID_NDK_ARCH} --install-dir=${NATIVE_LIBS_TOOLCHAIN_PATH} --platform=android-${ANDROID_PLATFORM_VER} --toolchain=${toolchainName} --verbose
}
## More information available at https://gitlab.com/relan/provisioners/merge_requests/1 and http://stackoverflow.com/a/34032216
install_qt_android()
{
QT_VERSION_CODE=$(echo $QT_VERSION | tr -d .)
QT_INSTALL_PATH=${NATIVE_LIBS_TOOLCHAIN_PATH}/Qt
QT_INSTALLER="qt-unified-linux-x64-3.0.2-online.run"
verified_download $QT_INSTALLER $QT_ANDROID_INSTALLER_SHA256 \
http://master.qt.io/archive/online_installers/3.0/${QT_INSTALLER}
chmod a+x ${QT_INSTALLER}
QT_INSTALLER_SCRIPT="qt_installer_script.js"
cat << EOF > "${QT_INSTALLER_SCRIPT}"
function Controller() {
installer.autoRejectMessageBoxes();
installer.installationFinished.connect(function() {
gui.clickButton(buttons.NextButton);
});
var welcomePage = gui.pageWidgetByObjectName("WelcomePage");
welcomePage.completeChanged.connect(function() {
if (gui.currentPageWidget().objectName == welcomePage.objectName)
gui.clickButton(buttons.NextButton);
});
}
Controller.prototype.WelcomePageCallback = function() {
gui.clickButton(buttons.NextButton);
}
Controller.prototype.CredentialsPageCallback = function() {
gui.clickButton(buttons.NextButton);
}
Controller.prototype.IntroductionPageCallback = function() {
gui.clickButton(buttons.NextButton);
}
Controller.prototype.TargetDirectoryPageCallback = function() {
gui.currentPageWidget().TargetDirectoryLineEdit.setText("$QT_INSTALL_PATH");
gui.clickButton(buttons.NextButton);
}
Controller.prototype.ComponentSelectionPageCallback = function() {
var widget = gui.currentPageWidget();
// You can get these component names by running the installer with the
// --verbose flag. It will then print out a resource tree.
widget.deselectComponent("qt.tools.qtcreator");
widget.deselectComponent("qt.tools.doc");
widget.deselectComponent("qt.tools.examples");
widget.selectComponent("qt.$QT_VERSION_CODE.android_armv7");
gui.clickButton(buttons.NextButton);
}
Controller.prototype.LicenseAgreementPageCallback = function() {
gui.currentPageWidget().AcceptLicenseRadioButton.setChecked(true);
gui.clickButton(buttons.NextButton);
}
Controller.prototype.StartMenuDirectoryPageCallback = function() {
gui.clickButton(buttons.NextButton);
}
Controller.prototype.ReadyForInstallationPageCallback = function() {
gui.clickButton(buttons.NextButton);
}
Controller.prototype.FinishedPageCallback = function() {
var checkBoxForm = gui.currentPageWidget().LaunchQtCreatorCheckBoxForm;
if (checkBoxForm && checkBoxForm.launchQtCreatorCheckBox)
checkBoxForm.launchQtCreatorCheckBox.checked = false;
gui.clickButton(buttons.FinishButton);
}
EOF
QT_QPA_PLATFORM=minimal ./${QT_INSTALLER} --script ${QT_INSTALLER_SCRIPT}
}
## More information available at retroshare://file?name=Android%20Native%20Development%20Kit%20Cookbook.pdf&size=29214468&hash=0123361c1b14366ce36118e82b90faf7c7b1b136
build_bzlib()
{
B_dir="bzip2-${BZIP2_SOURCE_VERSION}"
rm -rf $B_dir
verified_download $B_dir.tar.gz $BZIP2_SOURCE_SHA256 \
http://www.bzip.org/${BZIP2_SOURCE_VERSION}/bzip2-${BZIP2_SOURCE_VERSION}.tar.gz
tar -xf $B_dir.tar.gz
cd $B_dir
sed -i "/^CC=.*/d" Makefile
sed -i "/^AR=.*/d" Makefile
sed -i "/^RANLIB=.*/d" Makefile
sed -i "/^LDFLAGS=.*/d" Makefile
sed -i "s/^all: libbz2.a bzip2 bzip2recover test/all: libbz2.a bzip2 bzip2recover/" Makefile
make -j${HOST_NUM_CPU}
make install PREFIX=${SYSROOT}/usr
# sed -i "/^CC=.*/d" Makefile-libbz2_so
# make -f Makefile-libbz2_so -j${HOST_NUM_CPU}
# cp libbz2.so.1.0.6 ${SYSROOT}/usr/lib/libbz2.so
cd ..
}
## More information available at http://doc.qt.io/qt-5/opensslsupport.html
build_openssl()
{
B_dir="openssl-${OPENSSL_SOURCE_VERSION}"
rm -rf $B_dir
verified_download $B_dir.tar.gz $OPENSSL_SOURCE_SHA256 \
https://www.openssl.org/source/$B_dir.tar.gz
tar -xf $B_dir.tar.gz
cd $B_dir
if [ "${ANDROID_NDK_ARCH}" == "arm" ]; then
oArch="armv7"
else
oArch="${ANDROID_NDK_ARCH}"
fi
# ANDROID_NDK_ROOT="${ANDROID_NDK_PATH}" ./Configure android-${oArch} shared --prefix="${SYSROOT}/usr" --openssldir="${SYSROOT}/etc/ssl"
## We link openssl statically to avoid android silently sneaking in his own
## version of libssl.so (we noticed this because it had some missing symbol
## that made RS crash), the crash in some android version is only one of the
## possible problems the fact that android insert his own binary libssl.so pose
## non neglegible security concerns.
ANDROID_NDK_ROOT="${ANDROID_NDK_PATH}" ./Configure android-${oArch} --prefix="${SYSROOT}/usr" --openssldir="${SYSROOT}/etc/ssl"
sed -i 's/LIBNAME=$$i LIBVERSION=$(SHLIB_MAJOR).$(SHLIB_MINOR) \\/LIBNAME=$$i \\/g' Makefile
sed -i '/LIBCOMPATVERSIONS=";$(SHLIB_VERSION_HISTORY)" \\/d' Makefile
make -j${HOST_NUM_CPU}
make install
# cp *.so "${SYSROOT}/usr/lib"
cd ..
}
build_sqlite()
{
B_dir="sqlite-autoconf-${SQLITE_SOURCE_VERSION}"
verified_download $B_dir.tar.gz $SQLITE_SOURCE_SHA256 \
https://www.sqlite.org/${SQLITE_SOURCE_YEAR}/$B_dir.tar.gz
tar -xf $B_dir.tar.gz
cd $B_dir
./configure --prefix="${SYSROOT}/usr" --host=${ANDROID_NDK_ARCH}-linux
make -j${HOST_NUM_CPU}
make install
rm -f ${SYSROOT}/usr/lib/libsqlite3.so*
# ${CC} -shared -o libsqlite3.so -fPIC sqlite3.o -ldl
# cp libsqlite3.so "${SYSROOT}/usr/lib"
cd ..
}
build_sqlcipher()
{
B_dir="sqlcipher-${SQLCIPHER_SOURCE_VERSION}"
rm -rf $B_dir
T_file="${B_dir}.tar.gz"
verified_download $T_file $SQLCIPHER_SOURCE_SHA256 \
https://github.com/sqlcipher/sqlcipher/archive/v${SQLCIPHER_SOURCE_VERSION}.tar.gz
tar -xf $T_file
cd $B_dir
./configure --build=$(sh ./config.guess) \
--host=${ANDROID_NDK_ARCH}-linux \
--prefix="${SYSROOT}/usr" --with-sysroot="${SYSROOT}" \
--enable-tempstore=yes \
--disable-tcl --disable-shared \
CFLAGS="-DSQLITE_HAS_CODEC" LDFLAGS="${SYSROOT}/usr/lib/libcrypto.a"
make -j${HOST_NUM_CPU}
make install
cd ..
}
build_libupnp()
{
B_dir="libupnp-${LIBUPNP_SOURCE_VERSION}"
rm -rf $B_dir
verified_download $B_dir.tar.bz2 $LIBUPNP_SOURCE_SHA256 \
https://sourceforge.net/projects/pupnp/files/pupnp/libUPnP%20${LIBUPNP_SOURCE_VERSION}/$B_dir.tar.bz2
tar -xf $B_dir.tar.bz2
cd $B_dir
## liupnp must be configured as static library because if not the linker will
## look for libthreadutils.so.6 at runtime that cannot be packaged on android
## as it supports only libname.so format for libraries, thus resulting in a
## crash at startup.
./configure --enable-static --disable-shared --disable-samples --prefix="${SYSROOT}/usr" --host=${ANDROID_NDK_ARCH}-linux
make -j${HOST_NUM_CPU}
make install
cd ..
}
build_libmicrohttpd()
{
echo "libmicrohttpd not supported yet on android"
return 0
B_dir="libmicrohttpd-0.9.50"
rm -rf $B_dir
[ -f $B_dir.tar.gz ] || wget ftp://ftp.gnu.org/gnu/libmicrohttpd/$B_dir.tar.gz
tar -xf $B_dir.tar.gz
cd $B_dir
./configure --prefix="${SYSROOT}/usr" --host=${ANDROID_NDK_ARCH}-linux
#make -e ?
make -j${HOST_NUM_CPU}
make install
cd ..
}
build_rapidjson()
{
B_dir="rapidjson-1.1.0"
[ -f $B_dir.tar.gz ] || wget -O $B_dir.tar.gz https://github.com/Tencent/rapidjson/archive/v1.1.0.tar.gz
tar -xf $B_dir.tar.gz
cp -r rapidjson-1.1.0/include/rapidjson/ "${SYSROOT}/usr/include/rapidjson"
}
build_toolchain
[ "${INSTALL_QT_ANDROID}X" == "trueX" ] && install_qt_android
build_bzlib
build_openssl
build_sqlite
build_sqlcipher
build_libupnp
build_rapidjson
echo NATIVE_LIBS_TOOLCHAIN_PATH=${NATIVE_LIBS_TOOLCHAIN_PATH}

View File

@ -23,10 +23,7 @@ Files: src/retroshare-gui/src/TorControl/
Copyright: 2014, John Brooks <john.brooks@dereferenced.net>
License: BSD-New
Files: src/libresapi/src/api/json.h \
libresapi/src/webui-src/app/mithril.min.js \
src/rapidjson-1.1.0/* \
retroshare-gui/src/gui/common/PictureFlow.* \
Files: retroshare-gui/src/gui/common/PictureFlow.* \
src/retroshare-gui/src/qss/qdarkstyle.qss
Copyright: 2013 Jeff Weinstein <jeff.weinstein@gmail.com>
License: MIT

View File

@ -1 +0,0 @@
webui/*

View File

@ -1,14 +0,0 @@
libresapi: resource_api and new webinterface
============================================
* ./api contains a C++ backend to control retroshare from webinterfaces or scripting
* ./webui contains compiled files for the webinterface (after build)
* ./webui-src contains HTML/CSS/JavaScript source files for the webinterface (NEW, webinterface made with mithril.js)
Quickinfo for builders and packagers
====================================
* copy the files in ./webui to
* ./webui (Windows)
* /usr/share/retroshare/webui (Linux)
* other OS: see RsAccountsDetail::PathDataDirectory()

View File

@ -1,62 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiPluginHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "ApiPluginHandler.h"
namespace resource_api
{
ApiPluginHandler::ApiPluginHandler(StateTokenServer* statetokenserver, const RsPlugInInterfaces& ifaces)
{
for(int i = 0; i < ifaces.mPluginHandler->nbPlugins(); i++)
{
RsPlugin* plugin = ifaces.mPluginHandler->plugin(i);
// if plugin is not loaded, pointer is null
if(plugin == 0)
continue;
std::string entrypoint;
ResourceRouter* child = plugin->new_resource_api_handler(ifaces, statetokenserver, entrypoint);
if(child != 0)
{
mChildren.push_back(child);
if(isNameUsed(entrypoint))
{
std::cerr << "Cannot add plugin api entry point with name=" << entrypoint << ", becaus ethis name is already in use!" << std::endl;
}
else
{
std::cerr << "Added libresapi plugin with entrypoint " << entrypoint << std::endl;
addResourceHandler(entrypoint, child, &ResourceRouter::handleRequest);
}
}
}
}
ApiPluginHandler::~ApiPluginHandler()
{
for(std::vector<ResourceRouter*>::iterator vit = mChildren.begin(); vit != mChildren.end(); ++vit)
{
delete *vit;
}
mChildren.clear();
}
} // namespace resource_api

View File

@ -1,41 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiPluginHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
#include <retroshare/rsplugin.h>
namespace resource_api
{
// forwards all incoming requests to retroshare plugins
class ApiPluginHandler: public ResourceRouter
{
public:
ApiPluginHandler(StateTokenServer* statetokenserver, const RsPlugInInterfaces& ifaces);
virtual ~ApiPluginHandler();
private:
std::vector<ResourceRouter*> mChildren;
};
} // namespace resource_api

View File

@ -1,464 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiServer.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "ApiServer.h"
#include <retroshare/rspeers.h>
#include <retroshare/rsmsgs.h>
#include <time.h>
#include <unistd.h>
#include <sstream>
#include <algorithm>
#include "json.h"
#include <retroshare/rsservicecontrol.h>
#include "JsonStream.h"
#include "StateTokenServer.h" // for the state token serialisers
#include "ApiPluginHandler.h"
#include "ChannelsHandler.h"
#include "StatsHandler.h"
#ifdef LIBRESAPI_SETTINGS
#include "SettingsHandler.h"
#endif
/*
data types in json http://json.org/
string (utf-8 unicode)
number (int and float)
object (key value pairs, key must be a string)
true
false
null
data types in lua http://www.lua.org/pil/2.html
nil
boolean
number (double)
string (8-bit)
table (key value pairs, keys can be anything except nil)
data types in QML http://qt-project.org/doc/qt-5/qtqml-typesystem-basictypes.html
bool
string
real/double
int
list
object types?
QML has many more types with special meaning like date
C++ delivers
std::string
bool
int
(double? i don't know)
enum
bitflags
raw binary data
objects
std::vector
std::list
different types of ids/hashes
-> convert to/from string with a generic operator
-> the operator signals ok/fail to the stream
*/
/*
data types to handle:
- bool
- string
- bitflags
- enums
containers:
- arrays: collection of objects or values without name, usually of the same type
- objects: objects and values with names
careful: the json lib has many asserts, so retroshare will stop if the smalles thing goes wrong
-> check type of json before usage
there are two possible implementations:
- with a virtual base class for the serialisation targets
- better documentation of the interface
- with templates
*/
/*
the general idea is:
want output in many different formats, while the retrival of the source data is always the same
get, put
ressource adress like
.org.retroshare.api.peers
generic router from adress to the ressource handler object
data formats for input and output:
- json
- lua
- QObject
- maybe just a typed c++ object
rest inspired / resource based interface
- have resources with adresses
- requests:
- put
- get
- request has parameters
- response:
- returncode:
- ok
- not modified
- error
- data = object or list of objects
want to have a typesafe interface at the top?
probably not, a system with generic return values is enough
this interface is for scripting languages which don't have types
the interface at the top should look like this:
template <class RequestDataFormatT, class ResponseDataFormatT>
processRequest(const RequestMeta& req, const RequestDataFormatT& reqData,
ResponseMeta& respMeta, ResponseDataFormatT& respData);
idea: pass all the interfaces to the retroshare core to this function,
or have this function as part of an object
the processor then applies all members of the request and response data to the data format like this:
reqData << "member1" << member1
<< "member2" << member2 ... ;
these operators have to be implemented for common things like boolean, int, std::string, std::vector, std::list ...
request data gets only deserialised
response data gets only serialised
response and request meta contains things like resource address, method and additional parameters
want generic resource caching mechanism
- on first request a request handler is created
- request handler is stored with its input data
- if a request handler for a given resource adress and parameters exists
then the request handler is asked if the result is still valid
if yes the result from the existing handler is used
- request handler gets deleted after timeout
- can have two types of resource handlers: static handlers and dynamic handlers
- static handlers don't get deleted, because they don't contain result data
- dynamic handlers contain result data, and thus get deleted after a while
it is even possible to implement a resource-changed check at the highest level
this allows to compute everything on the server side and only send changes to the client
the different resource providers don't have to implement a resource changed check then
a top level change detector will poll them
of course this does not work with a deep resource tree with millions of nodes
for this we have the dynamic handlers,
they are created on demand and know how to listen for changes which affect them
*/
namespace resource_api{
// old code, only to copy and paste from
// to be removed
/*
class ChatlobbiesHandler
{
public:
ChatlobbiesHandler(RsMsgs* msgs): mMsgs(msgs) {}
template <class InputT, class OutputT>
void handleRequest(Request& req, InputT& reqData, Response& resp, OutputT& respData)
{
if(req.mMethod == "GET")
{
typename OutputT::Array result;
// subscribed lobbies
std::list<ChatLobbyInfo> slobbies;
mMsgs->getChatLobbyList(slobbies);
for(std::list<ChatLobbyInfo>::iterator lit = slobbies.begin(); lit != slobbies.end(); lit++)
{
typename OutputT::Object lobby;
ChatLobbyInfo& lobbyRecord = *lit;
lobby["name"] = lobbyRecord.lobby_name;
RsPeerId pid;
mMsgs->getVirtualPeerId(lobbyRecord.lobby_id, pid);
lobby["id"] = pid.toStdString();
lobby["subscribed"] = true;
result.push_back(lobby);
}
// unsubscirbed lobbies
std::vector<VisibleChatLobbyRecord> ulobbies;
mMsgs->getListOfNearbyChatLobbies(ulobbies);
for(std::vector<VisibleChatLobbyRecord>::iterator vit = ulobbies.begin(); vit != ulobbies.end(); vit++)
{
typename OutputT::Object lobby;
VisibleChatLobbyRecord& lobbyRecord = *vit;
lobby["name"] = lobbyRecord.lobby_name;
RsPeerId pid;
mMsgs->getVirtualPeerId(lobbyRecord.lobby_id, pid);
lobby["id"] = pid.toStdString();
lobby["subscribed"] = false;
result.push_back(lobby);
}
respData = result;
}
else if(req.mMethod == "PUT")
{
RsPeerId id = RsPeerId(req.mAdress.substr(1));
if(!id.isNull() && reqData.HasKey("msg"))
{
// for now can send only id as message
mMsgs->sendPrivateChat(id, reqData["msg"]);
}
}
}
RsMsgs* mMsgs;
};
*/
class ApiServerMainModules
{
public:
ApiServerMainModules(ResourceRouter& router, StateTokenServer* sts, const RsPlugInInterfaces &ifaces):
mPeersHandler(sts, ifaces.mNotify, ifaces.mPeers, ifaces.mMsgs),
mIdentityHandler(sts, ifaces.mNotify, ifaces.mIdentity),
mForumHandler(ifaces.mGxsForums),
mServiceControlHandler(ifaces.mServiceControl),
mFileSearchHandler(sts, ifaces.mNotify, ifaces.mTurtle, ifaces.mFiles),
mFileSharingHandler(sts, ifaces.mFiles, *ifaces.mNotify),
mTransfersHandler(sts, ifaces.mFiles, ifaces.mPeers, *ifaces.mNotify),
mChatHandler(sts, ifaces.mNotify, ifaces.mMsgs, ifaces.mPeers, ifaces.mIdentity, &mPeersHandler),
mApiPluginHandler(sts, ifaces),
mChannelsHandler(*ifaces.mGxsChannels),
mStatsHandler()
#ifdef LIBRESAPI_SETTINGS
,mSettingsHandler(sts)
#endif
{
// the dynamic cast is to not confuse the addResourceHandler template like this:
// addResourceHandler(derived class, parent class)
// the template would then be instantiated using derived class as parameter
// and then parent class would not match the type
router.addResourceHandler("peers",dynamic_cast<ResourceRouter*>(&mPeersHandler),
&PeersHandler::handleRequest);
router.addResourceHandler("identity", dynamic_cast<ResourceRouter*>(&mIdentityHandler),
&IdentityHandler::handleRequest);
router.addResourceHandler("forums", dynamic_cast<ResourceRouter*>(&mForumHandler),
&ForumHandler::handleRequest);
router.addResourceHandler("servicecontrol", dynamic_cast<ResourceRouter*>(&mServiceControlHandler),
&ServiceControlHandler::handleRequest);
router.addResourceHandler("filesearch", dynamic_cast<ResourceRouter*>(&mFileSearchHandler),
&FileSearchHandler::handleRequest);
router.addResourceHandler("filesharing", dynamic_cast<ResourceRouter*>(&mFileSharingHandler),
&FileSharingHandler::handleRequest);
router.addResourceHandler("transfers", dynamic_cast<ResourceRouter*>(&mTransfersHandler),
&TransfersHandler::handleRequest);
router.addResourceHandler("chat", dynamic_cast<ResourceRouter*>(&mChatHandler),
&ChatHandler::handleRequest);
router.addResourceHandler("apiplugin", dynamic_cast<ResourceRouter*>(&mApiPluginHandler),
&ChatHandler::handleRequest);
router.addResourceHandler("channels", dynamic_cast<ResourceRouter*>(&mChannelsHandler),
&ChannelsHandler::handleRequest);
router.addResourceHandler("stats", dynamic_cast<ResourceRouter*>(&mStatsHandler),
&StatsHandler::handleRequest);
#ifdef LIBRESAPI_SETTINGS
router.addResourceHandler("settings", dynamic_cast<ResourceRouter*>(&mSettingsHandler),
&SettingsHandler::handleRequest);
#endif
}
PeersHandler mPeersHandler;
IdentityHandler mIdentityHandler;
ForumHandler mForumHandler;
ServiceControlHandler mServiceControlHandler;
FileSearchHandler mFileSearchHandler;
FileSharingHandler mFileSharingHandler;
TransfersHandler mTransfersHandler;
ChatHandler mChatHandler;
ApiPluginHandler mApiPluginHandler;
ChannelsHandler mChannelsHandler;
StatsHandler mStatsHandler;
#ifdef LIBRESAPI_SETTINGS
SettingsHandler mSettingsHandler;
#endif
};
ApiServer::ApiServer():
mMtx("ApiServer mMtx"),
mStateTokenServer(),
mLivereloadhandler(&mStateTokenServer),
mTmpBlobStore(&mStateTokenServer),
mMainModules(0)
{
mRouter.addResourceHandler("statetokenservice", dynamic_cast<ResourceRouter*>(&mStateTokenServer),
&StateTokenServer::handleRequest);
mRouter.addResourceHandler("livereload", dynamic_cast<ResourceRouter*>(&mLivereloadhandler),
&LivereloadHandler::handleRequest);
}
ApiServer::~ApiServer()
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
for(std::vector<RequestId>::iterator vit = mRequests.begin(); vit != mRequests.end(); ++vit)
delete vit->task;
mRequests.clear();
if(mMainModules)
delete mMainModules;
}
void ApiServer::loadMainModules(const RsPlugInInterfaces &ifaces)
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
if(mMainModules == 0)
mMainModules = new ApiServerMainModules(mRouter, &mStateTokenServer, ifaces);
}
std::string ApiServer::handleRequest(Request &request)
{
resource_api::JsonStream outstream;
std::stringstream debugString;
StreamBase& data = outstream.getStreamToMember("data");
resource_api::Response resp(data, debugString);
ResponseTask* task = 0;
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
task = mRouter.handleRequest(request, resp);
}
//time_t start = time(NULL);
bool morework = true;
while(task && morework)
{
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
morework = task->doWork(request, resp);
}
if(morework)
usleep(10*1000);
/*if(time(NULL) > (start+5))
{
std::cerr << "ApiServer::handleRequest() Error: task timed out" << std::endl;
resp.mDebug << "Error: task timed out." << std::endl;
resp.mReturnCode = resource_api::Response::FAIL;
break;
}*/
}
if(task)
delete task;
std::string returncode;
switch(resp.mReturnCode){
case resource_api::Response::NOT_SET:
returncode = "not_set";
break;
case resource_api::Response::OK:
returncode = "ok";
break;
case resource_api::Response::WARNING:
returncode = "warning";
break;
case resource_api::Response::FAIL:
returncode = "fail";
break;
}
// evil HACK, remove this
if(data.isRawData())
return data.getRawData();
if(!resp.mCallbackName.empty())
outstream << resource_api::makeKeyValueReference("callback_name", resp.mCallbackName);
outstream << resource_api::makeKeyValue("debug_msg", debugString.str());
outstream << resource_api::makeKeyValueReference("returncode", returncode);
if(!resp.mStateToken.isNull())
outstream << resource_api::makeKeyValueReference("statetoken", resp.mStateToken);
return outstream.getJsonString();
}
ApiServer::RequestId ApiServer::handleRequest(Request &request, Response &response)
{
RequestId id;
ResponseTask* task = 0;
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
task = mRouter.handleRequest(request, response);
}
if(task == 0)
{
id.done = true;
return id;
}
id.done = false,
id.task = task;
id.request = &request;
id.response = &response;
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
mRequests.push_back(id);
}
return id;
}
bool ApiServer::isRequestDone(RequestId id)
{
if(id.done)
return true;
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
std::vector<RequestId>::iterator vit = std::find(mRequests.begin(), mRequests.end(), id);
// Request id not found, maybe the id is old and was removed from the list
if(vit == mRequests.end())
return true;
if(id.task->doWork(*id.request, *id.response))
return false;
// if we reach this point, the request is in the list and done
// remove the id from the list of valid ids
// delete the ResponseTask object
*vit = mRequests.back();
mRequests.pop_back();
delete id.task;
return true;
}
} // namespace resource_api

View File

@ -1,132 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiServer.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include <retroshare/rsplugin.h>
#include "ApiTypes.h"
#include "PeersHandler.h"
#include "IdentityHandler.h"
#include "ForumHandler.h"
#include "ServiceControlHandler.h"
#include "StateTokenServer.h"
#include "FileSearchHandler.h"
#include "FileSharingHandler.h"
#include "TransfersHandler.h"
#include "LivereloadHandler.h"
#include "TmpBlobStore.h"
#include "ChatHandler.h"
namespace resource_api{
class ApiServerMainModules;
// main entry point for all resource_api calls
// general part of the api server
// should work with any http library or a different transport protocol (e.g. SSH)
// call chain is like this:
// HTTP server -> ApiServer -> different handlers
// or
// GUI -> ApiServer -> different handlers
// multiple clients can use the same ApiServer instance at the same time
// ALL public methods in this class are thread safe
// this allows differen threads to send requests
class ApiServer
{
public:
ApiServer();
~ApiServer();
class RequestId{
public:
RequestId(): done(false), task(0), request(0), response(0){}
bool operator ==(const RequestId& r){
const RequestId& l = *this;
return (l.done==r.done)&&(l.task==r.task)&&(l.request==r.request)&&(l.response&&r.response);
}
private:
friend class ApiServer;
bool done; // this flag will be set to true, to signal the task id is valid and the task is done
// (in case there was no ResponseTask and task was zero)
ResponseTask* task; // null when the task id is invalid or when there was no task
Request* request;
Response* response;
};
// process the requestgiven by request and return the response as json string
// blocks until the request was processed
std::string handleRequest(Request& request);
// request and response must stay valid until isRequestDone returns true
// this method may do some work but it does not block
RequestId handleRequest(Request& request, Response& response);
// ticks the request
// returns true if the request is done or the id is invalid
// this method may do some work but it does not block
bool isRequestDone(RequestId id);
// load the main api modules
void loadMainModules(const RsPlugInInterfaces& ifaces);
// allows to add more handlers
// make sure the livetime of the handlers is longer than the api server
template <class T>
void addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp));
template <class T>
void addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp));
StateTokenServer* getStateTokenServer(){ return &mStateTokenServer; }
TmpBlobStore* getTmpBlobStore(){ return &mTmpBlobStore; }
private:
RsMutex mMtx;
StateTokenServer mStateTokenServer; // goes first, as others may depend on it
// is always loaded, because it has no dependencies
LivereloadHandler mLivereloadhandler;
TmpBlobStore mTmpBlobStore;
// only pointers here, to load/unload modules at runtime
ApiServerMainModules* mMainModules; // loaded when RS is started
ResourceRouter mRouter;
std::vector<RequestId> mRequests;
};
// implementations
template <class T>
void ApiServer::addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp))
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
mRouter.addResourceHandler(name, instance, callback);
}
template <class T>
void ApiServer::addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp))
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
mRouter.addResourceHandler(name, instance, callback);
}
}

View File

@ -1,180 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiServer.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2016 by Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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 <QStringList>
#include <QFileInfo>
#include <QDir>
#include "ApiServerLocal.h"
#include "JsonStream.h"
namespace resource_api{
ApiServerLocal::ApiServerLocal(ApiServer* server,
const QString &listenPath, QObject *parent) :
QObject(parent), serverThread(this),
// Must have no parent to be movable to other thread
localListener(server, listenPath)
{
qRegisterMetaType<QAbstractSocket::SocketState>();
localListener.moveToThread(&serverThread);
serverThread.start();
}
ApiServerLocal::~ApiServerLocal()
{
serverThread.quit();
serverThread.wait();
}
ApiLocalListener::ApiLocalListener(ApiServer *server,
const QString &listenPath,
QObject *parent) :
QObject(parent), mApiServer(server), mLocalServer(this)
{
QFileInfo fileInfo(listenPath);
if(fileInfo.exists())
{
std::cerr << __PRETTY_FUNCTION__ << listenPath.toLatin1().data()
<< " already exists. "
<< "Removing it assuming it's a past crash leftover! "
<< "Are you sure another instance is not listening there?"
<< std::endl;
mLocalServer.removeServer(listenPath);
}
#if QT_VERSION >= 0x050000
mLocalServer.setSocketOptions(QLocalServer::UserAccessOption);
#endif
connect( &mLocalServer, &QLocalServer::newConnection,
this, &ApiLocalListener::handleConnection );
QDir&& lDir(fileInfo.absoluteDir());
if(!lDir.exists())
{
std::cerr << __PRETTY_FUNCTION__ << " Directory for listening socket "
<< listenPath.toLatin1().data() << " doesn't exists. "
<< " Creating it!" << std::endl;
lDir.mkpath(lDir.absolutePath());
}
if(!mLocalServer.listen(listenPath))
{
std::cerr << __PRETTY_FUNCTION__ << " mLocalServer.listen("
<< listenPath.toLatin1().data() << ") failed with: "
<< mLocalServer.errorString().toLatin1().data() << std::endl;
}
}
void ApiLocalListener::handleConnection()
{
new ApiLocalConnectionHandler(mApiServer,
mLocalServer.nextPendingConnection(), this);
}
ApiLocalConnectionHandler::ApiLocalConnectionHandler(
ApiServer* apiServer, QLocalSocket* sock, QObject *parent) :
QObject(parent), mApiServer(apiServer), mLocalSocket(sock),
mState(WAITING_PATH)
{
connect(mLocalSocket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
connect(sock, SIGNAL(readyRead()), this, SLOT(handlePendingRequests()));
}
ApiLocalConnectionHandler::~ApiLocalConnectionHandler()
{
/* Any attempt of closing the socket here also deferred method call, causes
* crash when the core is asked to stop, at least from the JSON API call.
* QMetaObject::invokeMethod(&app, "close", Qt::QueuedConnection)
* mLocalSocket->disconnectFromServer()
* mLocalSocket->close() */
mLocalSocket->deleteLater();
}
void ApiLocalConnectionHandler::handlePendingRequests()
{
switch(mState)
{
case WAITING_PATH:
{
if(mLocalSocket->canReadLine())
{
readPath:
QString rString(mLocalSocket->readLine());
rString = rString.simplified();
if (!rString.isEmpty())
{
if(rString.startsWith("PUT", Qt::CaseInsensitive)) reqMeth = resource_api::Request::PUT;
else if (rString.startsWith("DELETE", Qt::CaseInsensitive)) reqMeth = resource_api::Request::DELETE_AA;
else reqMeth = resource_api::Request::GET;
if(rString.contains(' ')) rString = rString.split(' ')[1];
reqPath = rString.toStdString();
mState = WAITING_DATA;
/* Because QLocalSocket is SOCK_STREAM some clients implementations
* like the one based on QLocalSocket feel free to send the whole
* request (PATH + DATA) in a single write(), causing readyRead()
* signal being emitted only once, in that case we should continue
* processing without waiting for readyRead() being fired again, so
* we don't break here as there may be more lines to read */
}
else break;
}
}
case WAITING_DATA:
{
if(mLocalSocket->canReadLine())
{
resource_api::JsonStream reqJson;
reqJson.setJsonString(std::string(mLocalSocket->readLine().constData()));
resource_api::Request req(reqJson);
req.mMethod = reqMeth;
req.setPath(reqPath);
// Need this idiom because binary result may contains \0
std::string&& resultString = mApiServer->handleRequest(req);
QByteArray rB(resultString.data(), resultString.length());
// Dirty trick to support avatars answers
if(rB.contains("\n") || !rB.startsWith("{") || !rB.endsWith("}"))
mLocalSocket->write(rB.toBase64());
else mLocalSocket->write(rB);
mLocalSocket->write("\n\0");
mState = WAITING_PATH;
/* Because QLocalSocket is SOCK_STREAM some clients implementations
* like the one based on QLocalSocket feel free to coalesce multiple
* upper level write() into a single socket write(), causing
* readyRead() signal being emitted only once, in that case we should
* keep processing without waiting for readyRead() being fired again */
if(mLocalSocket->canReadLine()) goto readPath;
// Now there are no more requests to process we can break
break;
}
}
}
}
} // namespace resource_api

View File

@ -1,100 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiServer.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2016 by Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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/>. *
* *
*******************************************************************************/
#pragma once
#include <QLocalServer>
#include <QString>
#include <QThread>
#include <QLocalSocket>
#include <retroshare/rsinit.h>
#include <string>
#include "ApiTypes.h"
#include "ApiServer.h"
namespace resource_api
{
class ApiLocalListener : public QObject
{
Q_OBJECT
public:
ApiLocalListener(ApiServer* server, const QString &listenPath, QObject *parent=0);
~ApiLocalListener() { mLocalServer.close(); }
public slots:
void handleConnection();
private:
ApiServer* mApiServer;
QLocalServer mLocalServer;
};
class ApiServerLocal : public QObject
{
Q_OBJECT
public:
ApiServerLocal(ApiServer* server, const QString& listenPath, QObject *parent=0);
~ApiServerLocal();
const static QString& loginServerPath()
{
const static QString sockPath(RsAccounts::ConfigDirectory()
.append("/libresapi.sock").c_str());
return sockPath;
}
const static QString& serverPath()
{
const static QString sockPath(RsAccounts::AccountDirectory()
.append("/libresapi.sock").c_str());
return sockPath;
}
private:
QThread serverThread;
ApiLocalListener localListener;
};
class ApiLocalConnectionHandler : public QObject
{
Q_OBJECT
public:
ApiLocalConnectionHandler(ApiServer* apiServer, QLocalSocket* sock, QObject *parent = 0);
~ApiLocalConnectionHandler();
enum State {WAITING_PATH, WAITING_DATA};
public slots:
void handlePendingRequests();
private:
ApiServer* mApiServer;
QLocalSocket* mLocalSocket;
State mState;
std::string reqPath;
resource_api::Request::Method reqMeth;
};
} // namespace resource_api

View File

@ -1,693 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiServerMHD.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "ApiServerMHD.h"
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdio>
#include <algorithm>
#include <util/rsdir.h>
#include "util/ContentTypes.h"
// for filestreamer
#include <retroshare/rsfiles.h>
// to determine default docroot
#include <retroshare/rsinit.h>
#include "JsonStream.h"
#include "ApiServer.h"
#if MHD_VERSION < 0x00090000
// very old version, probably v0.4.x on old debian/ubuntu
#define OLD_04_MHD_FIX
#endif
// filestreamer only works if a content reader callback is allowed to return 0
// this is allowed since libmicrohttpd revision 30402
#if MHD_VERSION >= 0x00093101 // 0.9.31-1
#define ENABLE_FILESTREAMER
#else
#warning libmicrohttpd is too old to support file streaming. upgrade to a newer version.
#endif
#ifdef OLD_04_MHD_FIX
#define MHD_CONTENT_READER_END_OF_STREAM ((size_t) -1LL)
/**
* Create a response object. The response object can be extended with
* header information and then be used any number of times.
*
* @param size size of the data portion of the response
* @param fd file descriptor referring to a file on disk with the
* data; will be closed when response is destroyed;
* fd should be in 'blocking' mode
* @return NULL on error (i.e. invalid arguments, out of memory)
* @ingroup response
*/
struct MHD_Response * MHD_create_response_from_fd(size_t size, int fd)
{
unsigned char *buf = (unsigned char *)malloc(size) ;
if(buf == 0)
{
std::cerr << "replacement MHD_create_response_from_fd: malloc failed, size was " << size << std::endl;
close(fd);
return NULL ;
}
if(read(fd,buf,size) != size)
{
std::cerr << "replacement MHD_create_response_from_fd: READ error in file descriptor " << fd << " requested read size was " << size << std::endl;
close(fd);
free(buf) ;
return NULL ;
}
else
{
close(fd);
return MHD_create_response_from_data(size, buf,1,0) ;
}
}
#endif
namespace resource_api{
std::string getDefaultDocroot()
{
return RsAccounts::systemDataDirectory(false) + "/webui";
}
const char* API_ENTRY_PATH = "/api/v2";
const char* FILESTREAMER_ENTRY_PATH = "/fstream/";
const char* STATIC_FILES_ENTRY_PATH = "/static/";
const char* UPLOAD_ENTRY_PATH = "/upload/";
static void secure_queue_response(MHD_Connection *connection, unsigned int status_code, struct MHD_Response* response);
static void sendMessage(MHD_Connection *connection, unsigned int status, std::string message);
// interface for request handler classes
class MHDHandlerBase
{
public:
virtual ~MHDHandlerBase(){}
// return MHD_NO to terminate connection
// return MHD_YES otherwise
// this function will get called by MHD until a response was queued
virtual int handleRequest( struct MHD_Connection *connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size) = 0;
};
// handles calls to the resource_api
class MHDUploadHandler: public MHDHandlerBase
{
public:
MHDUploadHandler(ApiServer* s): mState(BEGIN), mApiServer(s){}
virtual ~MHDUploadHandler(){}
// return MHD_NO or MHD_YES
virtual int handleRequest( struct MHD_Connection *connection,
const char */*url*/, const char *method, const char */*version*/,
const char *upload_data, size_t *upload_data_size)
{
// new request
if(mState == BEGIN)
{
if(strcmp(method, "POST") == 0)
{
mState = WAITING_DATA;
// first time there is no data, do nothing and return
return MHD_YES;
}
}
if(mState == WAITING_DATA)
{
if(upload_data && *upload_data_size)
{
mRequesString += std::string(upload_data, *upload_data_size);
*upload_data_size = 0;
return MHD_YES;
}
}
std::vector<uint8_t> bytes(mRequesString.begin(), mRequesString.end());
int id = mApiServer->getTmpBlobStore()->storeBlob(bytes);
resource_api::JsonStream responseStream;
if(id)
responseStream << makeKeyValue("ok", true);
else
responseStream << makeKeyValue("ok", false);
responseStream << makeKeyValueReference("id", id);
std::string result = responseStream.getJsonString();
struct MHD_Response* resp = MHD_create_response_from_data(result.size(), (void*)result.data(), 0, 1);
MHD_add_response_header(resp, "Content-Type", "application/json");
secure_queue_response(connection, MHD_HTTP_OK, resp);
MHD_destroy_response(resp);
return MHD_YES;
}
enum State {BEGIN, WAITING_DATA};
State mState;
std::string mRequesString;
ApiServer* mApiServer;
};
// handles calls to the resource_api
class MHDApiHandler: public MHDHandlerBase
{
public:
MHDApiHandler(ApiServer* s): mState(BEGIN), mApiServer(s){}
virtual ~MHDApiHandler(){}
// return MHD_NO or MHD_YES
virtual int handleRequest( struct MHD_Connection *connection,
const char *url, const char *method, const char */*version*/,
const char *upload_data, size_t *upload_data_size)
{
// new request
if(mState == BEGIN)
{
if(strcmp(method, "POST") == 0)
{
mState = WAITING_DATA;
// first time there is no data, do nothing and return
return MHD_YES;
}
}
if(mState == WAITING_DATA)
{
if(upload_data && *upload_data_size)
{
mRequesString += std::string(upload_data, *upload_data_size);
*upload_data_size = 0;
return MHD_YES;
}
}
if(strstr(url, API_ENTRY_PATH) != url)
{
std::cerr << "FATAL ERROR in MHDApiHandler::handleRequest(): url does not start with api entry path, which is \"" << API_ENTRY_PATH << "\"" << std::endl;
return MHD_NO;
}
std::string path2 = (url + strlen(API_ENTRY_PATH));
resource_api::JsonStream instream;
instream.setJsonString(mRequesString);
resource_api::Request req(instream);
if(strcmp(method, "GET") == 0)
{
req.mMethod = resource_api::Request::GET;
}
else if(strcmp(method, "POST") == 0)
{
req.mMethod = resource_api::Request::PUT;
}
else if(strcmp(method, "DELETE") == 0)
{
req.mMethod = resource_api::Request::DELETE_AA;
}
req.setPath(path2);
std::string result = mApiServer->handleRequest(req);
struct MHD_Response* resp = MHD_create_response_from_data(result.size(), (void*)result.data(), 0, 1);
// EVIL HACK remove
if(result[0] != '{')
MHD_add_response_header(resp, "Content-Type", "image/png");
else
MHD_add_response_header(resp, "Content-Type", "application/json");
secure_queue_response(connection, MHD_HTTP_OK, resp);
MHD_destroy_response(resp);
return MHD_YES;
}
enum State {BEGIN, WAITING_DATA};
State mState;
std::string mRequesString;
ApiServer* mApiServer;
};
#ifdef ENABLE_FILESTREAMER
class MHDFilestreamerHandler: public MHDHandlerBase
{
public:
MHDFilestreamerHandler(): mSize(0){}
virtual ~MHDFilestreamerHandler(){}
RsFileHash mHash;
uint64_t mSize;
// return MHD_NO or MHD_YES
virtual int handleRequest( struct MHD_Connection *connection,
const char *url, const char */*method*/, const char */*version*/,
const char */*upload_data*/, size_t */*upload_data_size*/)
{
if(rsFiles == 0)
{
sendMessage(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, "Error: rsFiles is null. Retroshare is probably not yet started.");
return MHD_YES;
}
std::string urls(url);
urls = urls.substr(strlen(FILESTREAMER_ENTRY_PATH));
size_t perpos = urls.find('/');
if(perpos == std::string::npos){
mHash = RsFileHash(urls);
}else{
mHash = RsFileHash(urls.substr(0, perpos));
}
if(urls.empty() || mHash.isNull())
{
sendMessage(connection, MHD_HTTP_NOT_FOUND, "Error: URL is not a valid file hash");
return MHD_YES;
}
FileInfo info;
std::list<RsFileHash> dls;
rsFiles->FileDownloads(dls);
if(!(rsFiles->alreadyHaveFile(mHash, info) || std::find(dls.begin(), dls.end(), mHash) != dls.end()))
{
sendMessage(connection, MHD_HTTP_NOT_FOUND, "Error: file not existing on local peer and not downloading. Start the download before streaming it.");
return MHD_YES;
}
mSize = info.size;
struct MHD_Response* resp = MHD_create_response_from_callback(
mSize, 1024*1024, &contentReadercallback, this, NULL);
// get content-type from extension
std::string ext = "";
std::string::size_type i = info.fname.rfind('.');
if(i != std::string::npos)
ext = info.fname.substr(i+1);
MHD_add_response_header(resp, "Content-Type", ContentTypes::cTypeFromExt(ext).c_str());
secure_queue_response(connection, MHD_HTTP_OK, resp);
MHD_destroy_response(resp);
return MHD_YES;
}
static ssize_t contentReadercallback(void *cls, uint64_t pos, char *buf, size_t max)
{
MHDFilestreamerHandler* handler = (MHDFilestreamerHandler*)cls;
if(pos >= handler->mSize)
return MHD_CONTENT_READER_END_OF_STREAM;
uint32_t size_to_send = max;
if(!rsFiles->getFileData(handler->mHash, pos, size_to_send, (uint8_t*)buf))
return 0;
return size_to_send;
}
};
#endif // ENABLE_FILESTREAMER
// MHD will call this for each element of the http header
static int _extract_host_header_it_cb(void *cls,
enum MHD_ValueKind kind,
const char *key,
const char *value)
{
if(kind == MHD_HEADER_KIND)
{
// check if key is host
const char* h = "host";
while(*key && *h)
{
if(tolower(*key) != *h)
return MHD_YES;
key++;
h++;
}
// strings have same length and content
if(*key == 0 && *h == 0)
{
*((std::string*)cls) = value;
}
}
return MHD_YES;
}
// add security related headers and send the response on the given connection
// the reference counter is not touched
// this function is a wrapper around MHD_queue_response
// MHD_queue_response should be replaced with this function
static void secure_queue_response(MHD_Connection *connection, unsigned int status_code, struct MHD_Response* response)
{
// TODO: protect againts handling untrusted content to the browser
// see:
// http://www.dotnetnoob.com/2012/09/security-through-http-response-headers.html
// http://www.w3.org/TR/CSP2/
// https://code.google.com/p/doctype-mirror/wiki/ArticleContentSniffing
// check content type
// don't server when no type or no whitelisted type is given
// TODO sending invalid mime types is as bad as not sending them TODO
/*
std::vector<std::string> allowed_types;
allowed_types.push_back("text/html");
allowed_types.push_back("application/json");
allowed_types.push_back("image/png");
*/
const char* type = MHD_get_response_header(response, "Content-Type");
if(type == 0 /*|| std::find(allowed_types.begin(), allowed_types.end(), std::string(type)) == allowed_types.end()*/)
{
std::string page;
if(type == 0)
page = "<html><body><p>Fatal Error: no content type was set on this response. This is a bug.</p></body></html>";
else
page = "<html><body><p>Fatal Error: this content type is not allowed. This is a bug.<br/> Content-Type: "+std::string(type)+"</p></body></html>";
struct MHD_Response* resp = MHD_create_response_from_data(page.size(), (void*)page.data(), 0, 1);
MHD_add_response_header(resp, "Content-Type", "text/html");
MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, resp);
MHD_destroy_response(resp);
}
// tell Internet Explorer to not do content sniffing
MHD_add_response_header(response, "X-Content-Type-Options", "nosniff");
// Prevent clickjacking attacks (also prevented by CSP, but not in all browsers, including FireFox)
MHD_add_response_header(response, "X-Frame-Options", "SAMEORIGIN");
// Content security policy header, its a new technology and not implemented everywhere
// get own host name as the browser sees it
std::string host;
MHD_get_connection_values(connection, MHD_HEADER_KIND, _extract_host_header_it_cb, (void*)&host);
std::string csp;
csp += "default-src 'none';";
csp += "script-src '"+host+STATIC_FILES_ENTRY_PATH+"';";
csp += "font-src '"+host+STATIC_FILES_ENTRY_PATH+"';";
csp += "img-src 'self';"; // allow images from all paths on this server
csp += "media-src 'self';"; // allow media files from all paths on this server
MHD_add_response_header(response, "X-Content-Security-Policy", csp.c_str());
MHD_queue_response(connection, status_code, response);
}
// wraps the given string in a html page and sends it as response with the given status code
static void sendMessage(MHD_Connection *connection, unsigned int status, std::string message)
{
std::string page = "<html><body><p>"+message+"</p></body></html>";
struct MHD_Response* resp = MHD_create_response_from_data(page.size(), (void*)page.data(), 0, 1);
MHD_add_response_header(resp, "Content-Type", "text/html");
secure_queue_response(connection, status, resp);
MHD_destroy_response(resp);
}
// convert all character to hex html entities
static std::string escape_html(std::string in)
{
std::string out;
for(uint32_t i = 0; i < in.size(); i++)
{
char a = (in[i]&0xF0)>>4;
a = a < 10? a+'0': a-10+'A';
char b = (in[i]&0x0F);
b = b < 10? b+'0': b-10+'A';
out += std::string("&#x")+a+b+";";
}
return out;
}
ApiServerMHD::ApiServerMHD(ApiServer *server):
mConfigOk(false), mDaemon(0), mApiServer(server)
{
memset(&mListenAddr, 0, sizeof(mListenAddr));
}
ApiServerMHD::~ApiServerMHD()
{
stop();
}
bool ApiServerMHD::configure(std::string docroot, uint16_t port, std::string /*bind_address*/, bool allow_from_all)
{
mRootDir = docroot;
// make sure the docroot dir ends with a slash
if(mRootDir.empty())
mRootDir = "./";
else if (mRootDir[mRootDir.size()-1] != '/' && mRootDir[mRootDir.size()-1] != '\\')
mRootDir += "/";
mListenAddr.sin_family = AF_INET;
mListenAddr.sin_port = htons(port);
// untested
/*
if(!bind_address.empty())
{
if(!inet_pton(AF_INET6, bind_address.c_str(), &mListenAddr.sin6_addr))
{
std::cerr << "ApiServerMHD::configure() invalid bind address: \"" << bind_address << "\"" << std::endl;
return false;
}
}
else*/ if(allow_from_all)
{
mListenAddr.sin_addr.s_addr = htonl(INADDR_ANY);
std::cerr << "ApiServerMHD::configure(): will serve the webinterface to ALL IP adresses." << std::endl;
}
else
{
mListenAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
std::cerr << "ApiServerMHD::configure(): will serve the webinterface to LOCALHOST only." << std::endl;
}
mConfigOk = true;
return true;
}
bool ApiServerMHD::start()
{
if(!mConfigOk)
{
std::cerr << "ApiServerMHD::start() ERROR: server not configured. You have to call configure() first." << std::endl;
return false;
}
if(mDaemon)
{
std::cerr << "ApiServerMHD::start() ERROR: server already started. You have to call stop() first." << std::endl;
return false;
}
mDaemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, 9999, // port will be overwritten by MHD_OPTION_SOCK_ADDR
&static_acceptPolicyCallback, this,
&static_accessHandlerCallback, this,
MHD_OPTION_NOTIFY_COMPLETED, &static_requestCompletedCallback, this,
MHD_OPTION_SOCK_ADDR, &mListenAddr,
MHD_OPTION_END);
if(mDaemon)
{
std::cerr << "ApiServerMHD::start() SUCCESS. Started server on port " << ntohs(mListenAddr.sin_port) << ". Serving files from \"" << mRootDir << "\" at " << STATIC_FILES_ENTRY_PATH << std::endl;
return true;
}
else
{
std::cerr << "ApiServerMHD::start() ERROR: starting the server failed. Maybe port " << ntohs(mListenAddr.sin_port) << " is already in use?" << std::endl;
return false;
}
}
void ApiServerMHD::stop()
{
if(mDaemon == 0)
return;
MHD_stop_daemon(mDaemon);
mDaemon = 0;
}
int ApiServerMHD::static_acceptPolicyCallback(void *cls, const sockaddr *addr, socklen_t addrlen)
{
return ((ApiServerMHD*)cls)->acceptPolicyCallback(addr, addrlen);
}
int ApiServerMHD::static_accessHandlerCallback(void* cls, struct MHD_Connection * connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size,
void **con_cls)
{
return ((ApiServerMHD*)cls)->accessHandlerCallback(connection, url, method, version,
upload_data, upload_data_size, con_cls);
}
void ApiServerMHD::static_requestCompletedCallback(void *cls, MHD_Connection* connection,
void **con_cls, MHD_RequestTerminationCode toe)
{
((ApiServerMHD*)cls)->requestCompletedCallback(connection, con_cls, toe);
}
int ApiServerMHD::acceptPolicyCallback(const sockaddr* /*addr*/, socklen_t /*addrlen*/)
{
// accept all connetions
return MHD_YES;
}
int ApiServerMHD::accessHandlerCallback(MHD_Connection *connection,
const char *url, const char *method, const char *version,
const char *upload_data, size_t *upload_data_size,
void **con_cls)
{
// is this call a continuation for an existing request?
if(*con_cls)
{
return ((MHDHandlerBase*)(*con_cls))->handleRequest(connection, url, method, version, upload_data, upload_data_size);
}
// these characters are not allowed in the url, raise an error if they occur
// reason: don't want to serve files outside the current document root
const char *double_dots = "..";
if(strstr(url, double_dots))
{
const char *error = "<html><body><p>Fatal error: found double dots (\"..\") in the url. This is not allowed</p></body></html>";
struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1);
MHD_add_response_header(resp, "Content-Type", "text/html");
secure_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, resp);
MHD_destroy_response(resp);
return MHD_YES;
}
// if no path is given, redirect to index.html in static files directory
if(strlen(url) == 1 && url[0] == '/')
{
std::string location = std::string(STATIC_FILES_ENTRY_PATH) + "index.html";
std::string errstr = "<html><body><p>Webinterface is at <a href=\""+location+"\">"+location+"</a></p></body></html>";
const char *error = errstr.c_str();
struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1);
MHD_add_response_header(resp, "Content-Type", "text/html");
MHD_add_response_header(resp, "Location", location.c_str());
secure_queue_response(connection, MHD_HTTP_FOUND, resp);
MHD_destroy_response(resp);
return MHD_YES;
}
// is it a call to the resource api?
if(strstr(url, API_ENTRY_PATH) == url)
{
// create a new handler and store it in con_cls
MHDHandlerBase* handler = new MHDApiHandler(mApiServer);
*con_cls = (void*) handler;
return handler->handleRequest(connection, url, method, version, upload_data, upload_data_size);
}
// is it a call to the filestreamer?
if(strstr(url, FILESTREAMER_ENTRY_PATH) == url)
{
#ifdef ENABLE_FILESTREAMER
// create a new handler and store it in con_cls
MHDHandlerBase* handler = new MHDFilestreamerHandler();
*con_cls = (void*) handler;
return handler->handleRequest(connection, url, method, version, upload_data, upload_data_size);
#else
sendMessage(connection, MHD_HTTP_NOT_FOUND, "The filestreamer is not available, because this executable was compiled with a too old version of libmicrohttpd.");
return MHD_YES;
#endif
}
// is it a path to the static files?
if(strstr(url, STATIC_FILES_ENTRY_PATH) == url)
{
url = url + strlen(STATIC_FILES_ENTRY_PATH);
// else server static files
std::string filename = mRootDir + url;
// important: binary open mode (windows)
// else libmicrohttpd will replace crlf with lf and add garbage at the end of the file
#ifdef O_BINARY
int fd = open(filename.c_str(), O_RDONLY | O_BINARY);
#else
int fd = open(filename.c_str(), O_RDONLY);
#endif
if(fd == -1)
{
std::string direxists;
if(RsDirUtil::checkDirectory(mRootDir))
direxists = "directory &quot;"+mRootDir+"&quot; exists";
else
direxists = "directory &quot;"+mRootDir+"&quot; does not exist!";
std::string msg = "<html><body><p>Error: can't open the requested file. path=&quot;"+escape_html(filename)+"&quot;</p><p>"+direxists+"</p></body></html>";
sendMessage(connection, MHD_HTTP_NOT_FOUND, msg);
return MHD_YES;
}
struct stat s;
if(fstat(fd, &s) == -1)
{
close(fd);
const char *error = "<html><body><p>Error: file was opened but stat failed.</p></body></html>";
struct MHD_Response* resp = MHD_create_response_from_data(strlen(error), (void*)error, 0, 1);
MHD_add_response_header(resp, "Content-Type", "text/html");
secure_queue_response(connection, MHD_HTTP_NOT_FOUND, resp);
MHD_destroy_response(resp);
return MHD_YES;
}
// find the file extension and the content type
std::string extension;
int i = filename.size()-1;
while(i >= 0 && filename[i] != '.')
{
extension = filename[i] + extension;
i--;
};
struct MHD_Response* resp = MHD_create_response_from_fd(s.st_size, fd);
MHD_add_response_header(resp, "Content-Type", ContentTypes::cTypeFromExt(extension).c_str());
secure_queue_response(connection, MHD_HTTP_OK, resp);
MHD_destroy_response(resp);
return MHD_YES;
}
if(strstr(url, UPLOAD_ENTRY_PATH) == url)
{
// create a new handler and store it in con_cls
MHDHandlerBase* handler = new MHDUploadHandler(mApiServer);
*con_cls = (void*) handler;
return handler->handleRequest(connection, url, method, version, upload_data, upload_data_size);
}
// if url is not a valid path, then serve a help page
sendMessage(connection, MHD_HTTP_NOT_FOUND,
"This address is invalid. Try one of the adresses below:<br/>"
"<ul>"
"<li>/ <br/>Retroshare webinterface</li>"
"<li>"+std::string(API_ENTRY_PATH)+" <br/>JSON over http api</li>"
"<li>"+std::string(FILESTREAMER_ENTRY_PATH)+" <br/>file streamer</li>"
"<li>"+std::string(STATIC_FILES_ENTRY_PATH)+" <br/>static files</li>"
"</ul>"
);
return MHD_YES;
}
void ApiServerMHD::requestCompletedCallback(struct MHD_Connection */*connection*/,
void **con_cls, MHD_RequestTerminationCode /*toe*/)
{
if(*con_cls)
{
delete (MHDHandlerBase*)(*con_cls);
}
}
} // namespace resource_api

View File

@ -1,74 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiServerMHD.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <microhttpd.h>
#include <string>
#ifndef WINDOWS_SYS
#include <netinet/in.h>
#endif
namespace resource_api{
class ApiServer;
// returns the default docroot path
// (it is differen on different operating systems)
std::string getDefaultDocroot();
class ApiServerMHD
{
public:
ApiServerMHD(ApiServer* server);
~ApiServerMHD();
/**
* @brief configure the http server
* @param docroot sets the directory from which static files should be served. default = ./
* @param port the port to listen on. The server will listen on ipv4 and ipv6.
* @param bind_address NOT IMPLEMENTED optional, specifies an ipv6 adress to listen on.
* @param allow_from_all when true, listen on all ips. (only when bind_adress is empty)
* @return true on success
*/
bool configure(std::string docroot, uint16_t port, std::string bind_address, bool allow_from_all);
bool start();
void stop();
private:
// static callbacks for libmicrohttpd, they call the members below
static int static_acceptPolicyCallback(void* cls, const struct sockaddr * addr, socklen_t addrlen);
static int static_accessHandlerCallback(void* cls, struct MHD_Connection * connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls);
static void static_requestCompletedCallback(void *cls, struct MHD_Connection* connection, void **con_cls, enum MHD_RequestTerminationCode toe);
int acceptPolicyCallback(const struct sockaddr * addr, socklen_t addrlen);
int accessHandlerCallback(struct MHD_Connection * connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls);
void requestCompletedCallback(struct MHD_Connection *connection, void **con_cls, MHD_RequestTerminationCode toe);
bool mConfigOk;
std::string mRootDir;
struct sockaddr_in mListenAddr;
MHD_Daemon* mDaemon;
ApiServer* mApiServer;
};
} // namespace resource_api

View File

@ -1,335 +0,0 @@
/*******************************************************************************
* libresapi/api/ApiTypes.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include <string>
#include <vector>
#include <stack>
#include <stdint.h>
#include <ostream>
#include "util/rsdeprecate.h"
namespace resource_api
{
// things to clean up:
// - temporary variables when serialising rs-ids
// - always ensure proper return values
// - add help functions
// - remove unused functions or implement them
// - add operators or functions for std::set, std::list, std::vector, std::map
// idea:
// make a second parameter like
// ValueReference(this->member, OPTIONAL); // optional signals that it is not an error if this member is missing
// make a third parameter with a type hint: time, length, kilobytes
// to make arrays
template<class T>
class ValueReference
{
public:
ValueReference(T& value): value(value){}
T& value;
};
template<class T>
ValueReference<T> makeValueReference(T& value);
template<class T>
class Value
{
public:
Value(T value): value(value){}
operator ValueReference<T>(){ return ValueReference<T>(value);}
T value;
};
template<class T>
Value<T> makeValue(T value);
// to make objects
template<class T>
class KeyValueReference
{
public:
KeyValueReference(std::string key, T& value): key(key), value(value){}
//KeyValueReference(const char* key, T& value): key(key), value(value){}
std::string key;
T& value;
};
template<class T>
KeyValueReference<T> makeKeyValueReference(std::string key, T& value);
// for serialisation
// copies the supplied value
// automatically converts itself to a KeyValueReference
template<class T>
class KeyValue
{
public:
KeyValue(std::string key, T value): key(key), value(value){}
operator KeyValueReference<T>(){ return KeyValueReference<T>(key, value);}
std::string key;
T value;
};
template<class T>
KeyValue<T> makeKeyValue(std::string key, T value);
// interface for streams
class StreamBase
{
public:
// the stream provides operators for basic data types
// everything else should be broken down by others
// the same stream can either become an object or an array stream, or a binary data object
// a binary data object is just raw binary data without any decoration
// binary data is good to pass things like images and small files
// this depends on how this stream is used
// but once the stream is used as array, then only array operations are allowed
// same with an stream used as object
// idea: can have filter streams which forward the calls to another stream
// to make debug protocols of other steam implementations
// idea: make a stream shich creates a hash from the input to detect changes in the data
// make an array
virtual StreamBase& operator<<(ValueReference<bool> value) = 0;
virtual StreamBase& operator<<(ValueReference<int> value) = 0;
virtual StreamBase& operator<<(ValueReference<double> value) = 0;
virtual StreamBase& operator<<(ValueReference<std::string> value) = 0;
// usefull if the new array member should be an array or object
// the reference should be at least valid until another method of this class gets called
virtual StreamBase& getStreamToMember() = 0;
// make an object
virtual StreamBase& operator<<(KeyValueReference<bool> keyValue) = 0;
virtual StreamBase& operator<<(KeyValueReference<int> keyValue) = 0;
virtual StreamBase& operator<<(KeyValueReference<double> keyValue) = 0;
virtual StreamBase& operator<<(KeyValueReference<std::string> keyValue) = 0;
// usefull if the new object member should be an array or object
// the reference should be at least valid until another method of this class gets called
virtual StreamBase& getStreamToMember(std::string name) = 0;
// make a binay data object (not a real object, just binary data)
// idea: can use vector.swap() to allow passing larger data items without copying
virtual StreamBase& operator<<(std::vector<uint8_t>& data) = 0;
// return true if there are more members in this object/array
// useful for array reading
virtual bool hasMore() = 0;
virtual bool serialise() = 0; // let external operators find out they should serialise or deserialise
// return true if no serialisation/deserialisation error occoured
virtual bool isOK() = 0;
virtual void setError() = 0; // let external operators set the failed bit
//virtual void addLogMsg(std::string msg) = 0;
virtual void addErrorMsg(std::string msg) = 0;
virtual std::string getLog() = 0;
virtual std::string getErrorLog() = 0;
virtual bool isRawData() = 0;
virtual std::string getRawData() = 0;// HACK, remove this
};
// todo:
// define clear rules how a response to a request should look like
// the clients should be able to know when something failed
// then it is desired to have a minimum of debug output to track the errors down
// currently no check for successful serialisation/deserialisation is performed
//
// response metadata:
// - etag, when will this result expire
// - maybe a hint how often this etag should be checked for updates
//
// outcome of a request:
// - full ok
// - partial ok
// - resource not found, invalid address or resource not available
// - not ok, internal error
// - wrong usage, parameters or POST data is wrong, like deserialisation error
// is is hard to find the cause of the error
// maybe include a comment with additional info
//
// want to include a mime type of the resulting data?
// because some data is json, othe plain text, other unknown binary stuff
// live-stream resources
// some resources like typing notifications are only valid for a short time
// resource types:
// - list, either with objects or adresses of objects.
// lists need a navigation mechanism like get objects before and get objects after
// - object
// - stream
// - binary data, for example files
// TODO: record a timestamp for each token, to allow garbage collection of very old tokens
class StateToken{
public:
StateToken(): value(0){}
StateToken(uint32_t value): value(value){}
std::string toString();
uint32_t getValue() const {return value;}
bool isNull() const {return value == 0;}
private:
uint32_t value; // 0 is reserved for invalid token
};
class Request
{
public:
Request(StreamBase& stream) : mStream(stream), mMethod(GET){}
RS_DEPRECATED bool isGet(){ return mMethod == GET;}
RS_DEPRECATED bool isPut(){ return mMethod == PUT;}
RS_DEPRECATED bool isDelete(){ return mMethod == DELETE_AA;}
RS_DEPRECATED bool isExec(){ return mMethod == EXEC;}
/**
* Path is the adress to the resource if the path has multiple parts which
* get handled by different handlers, then each handler should pop the top
* element
*/
std::stack<std::string> mPath;
std::string mFullPath;
bool setPath(const std::string &reqPath)
{
std::string str;
std::string::const_reverse_iterator sit;
for( sit = reqPath.rbegin(); sit != reqPath.rend(); ++sit )
{
// add to front because we are traveling in reverse order
if((*sit) != '/') str = *sit + str;
else if(!str.empty())
{
mPath.push(str);
str.clear();
}
}
if(!str.empty()) mPath.push(str);
mFullPath = reqPath;
return true;
}
/// Contains data for new resources
StreamBase& mStream;
/**
* @deprecated
* Method and derivated stuff usage is deprecated as it make implementation
* more complex and less readable without advantage
*/
enum Method { GET, PUT, DELETE_AA, EXEC};
RS_DEPRECATED Method mMethod;
};
// new notes on responses
// later we want to send multiple requests over the same link
// and we want to be able to send the responses in a different order than the requests
// for this we need a unique token in every request which gets returned in the response
// response:
// message token
// status (ok, warning, fail)
// data (different for different resources)
// debugstring (a human readable error message in case something went wrong)
class Response
{
public:
Response(StreamBase& stream, std::ostream& debug): mReturnCode(NOT_SET), mDataStream(stream), mDebug(debug){}
// WARNING means: a valid result is available, but an error occoured
// FAIL means: the result is not valid
enum ReturnCode{ NOT_SET, OK, WARNING, FAIL};
ReturnCode mReturnCode;
StateToken mStateToken;
//Just for GUI benefit
std::string mCallbackName;
// the result
StreamBase& mDataStream;
// humand readable string for debug messages/logging
std::ostream& mDebug;
inline void setOk(){mReturnCode = OK;}
inline void setWarning(std::string msg = ""){
mReturnCode = WARNING;
if(msg != "")
mDebug << msg << std::endl;}
inline void setFail(std::string msg = ""){
mReturnCode = FAIL;
if(msg != "")
mDebug << msg << std::endl;
}
};
// if a response can not be handled immediately,
// then the handler should return a ResponseTask object
// the api server will then call the doWork() method periodically
class ResponseTask
{
public:
virtual ~ResponseTask(){}
// return true if function should get called again
// return false when finished
virtual bool doWork(Request& req, Response& resp) = 0;
};
// implementations
template<class T>
ValueReference<T> makeValueReference(T& value)
{
return ValueReference<T>(value);
}
template<class T>
Value<T> makeValue(T value)
{
return Value<T>(value);
}
template<class T>
KeyValueReference<T> makeKeyValueReference(std::string key, T& value)
{
return KeyValueReference<T>(key, value);
}
template<class T>
KeyValue<T> makeKeyValue(std::string key, T value)
{
return KeyValue<T>(key, value);
}
} // namespace resource_api

View File

@ -1,540 +0,0 @@
/*******************************************************************************
* libresapi/api/ChannelsHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 by Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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 "ChannelsHandler.h"
#include <retroshare/rsgxschannels.h>
#include <util/radix64.h>
#include <util/rstime.h>
#include <algorithm>
#include <time.h>
#include "Operators.h"
namespace resource_api
{
StreamBase& operator << (StreamBase& left, RsGxsFile& file)
{
left << makeKeyValueReference("name", file.mName)
<< makeKeyValueReference("hash", file.mHash);
if(left.serialise())
{
double size = file.mSize;
left << makeKeyValueReference("size", size);
}
else
{
double size = 0;
left << makeKeyValueReference("size", size);
file.mSize = size;
}
return left;
}
ChannelsHandler::ChannelsHandler(RsGxsChannels& channels): mChannels(channels)
{
addResourceHandler("list_channels", this,
&ChannelsHandler::handleListChannels);
addResourceHandler("get_channel_info", this, &ChannelsHandler::handleGetChannelInfo);
addResourceHandler("get_channel_content", this, &ChannelsHandler::handleGetChannelContent);
addResourceHandler("toggle_subscribe", this, &ChannelsHandler::handleToggleSubscription);
addResourceHandler("toggle_auto_download", this, &ChannelsHandler::handleToggleAutoDownload);
addResourceHandler("toggle_read", this, &ChannelsHandler::handleTogglePostRead);
addResourceHandler("create_channel", this, &ChannelsHandler::handleCreateChannel);
addResourceHandler("create_post", this, &ChannelsHandler::handleCreatePost);
}
void ChannelsHandler::handleListChannels(Request& /*req*/, Response& resp)
{
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_META;
uint32_t token;
RsTokenService& tChannels = *mChannels.getTokenService();
tChannels.requestGroupInfo(token, RS_DEPRECATED_TOKREQ_ANSTYPE, opts);
time_t start = time(NULL);
while((tChannels.requestStatus(token) != RsTokenService::COMPLETE)
&&(tChannels.requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))) rstime::rs_usleep(500*1000);
std::list<RsGroupMetaData> grps;
if( tChannels.requestStatus(token) == RsTokenService::COMPLETE
&& mChannels.getGroupSummary(token, grps) )
{
for( RsGroupMetaData& grp : grps )
{
KeyValueReference<RsGxsGroupId> id("channel_id", grp.mGroupId);
KeyValueReference<uint32_t> vis_msg("visible_msg_count", grp.mVisibleMsgCount);
bool own = (grp.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
bool subscribed = IS_GROUP_SUBSCRIBED(grp.mSubscribeFlags);
std::string lastPostTsStr = std::to_string(grp.mLastPost);
std::string publishTsStr = std::to_string(grp.mPublishTs);
resp.mDataStream.getStreamToMember()
<< id
<< makeKeyValueReference("name", grp.mGroupName)
<< makeKeyValueReference("last_post_ts", lastPostTsStr)
<< makeKeyValueReference("popularity", grp.mPop)
<< makeKeyValueReference("publish_ts", publishTsStr)
<< vis_msg
<< makeKeyValueReference("group_status", grp.mGroupStatus)
<< makeKeyValueReference("author_id", grp.mAuthorId)
<< makeKeyValueReference("parent_grp_id", grp.mParentGrpId)
<< makeKeyValueReference("own", own)
<< makeKeyValueReference("subscribed", subscribed);
}
resp.setOk();
}
else resp.setFail("Cant get data from GXS!");
}
void ChannelsHandler::handleGetChannelInfo(Request& req, Response& resp)
{
std::string chanIdStr;
req.mStream << makeKeyValueReference("channel_id", chanIdStr);
if(chanIdStr.empty())
{
resp.setFail("channel_id required!");
return;
}
RsGxsGroupId chanId(chanIdStr);
if(chanId.isNull())
{
resp.setFail("Invalid channel_id:" + chanIdStr);
return;
}
bool wantThumbnail = true;
req.mStream << makeKeyValueReference("want_thumbnail", wantThumbnail);
std::list<RsGxsGroupId> groupIds; groupIds.push_back(chanId);
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
uint32_t token;
RsTokenService& tChannels = *mChannels.getTokenService();
tChannels.requestGroupInfo( token, RS_DEPRECATED_TOKREQ_ANSTYPE,
opts, groupIds );
time_t start = time(NULL);
while((tChannels.requestStatus(token) != RsTokenService::COMPLETE)
&&(tChannels.requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))) rstime::rs_usleep(500*1000);
std::vector<RsGxsChannelGroup> grps;
if( tChannels.requestStatus(token) == RsTokenService::COMPLETE
&& mChannels.getGroupData(token, grps) )
{
for( RsGxsChannelGroup& grp : grps )
{
KeyValueReference<RsGxsGroupId> id("channel_id", grp.mMeta.mGroupId);
KeyValueReference<uint32_t> vis_msg("visible_msg_count", grp.mMeta.mVisibleMsgCount);
bool own = (grp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
bool subscribed = IS_GROUP_SUBSCRIBED(grp.mMeta.mSubscribeFlags);
std::string lastPostTsStr = std::to_string(grp.mMeta.mLastPost);
std::string publishTsStr = std::to_string(grp.mMeta.mPublishTs);
StreamBase& rgrp(resp.mDataStream.getStreamToMember());
rgrp << id
<< makeKeyValueReference("name", grp.mMeta.mGroupName)
<< makeKeyValueReference("last_post_ts", lastPostTsStr)
<< makeKeyValueReference("popularity", grp.mMeta.mPop)
<< makeKeyValueReference("publish_ts", publishTsStr)
<< vis_msg
<< makeKeyValueReference("group_status", grp.mMeta.mGroupStatus)
<< makeKeyValueReference("author_id", grp.mMeta.mAuthorId)
<< makeKeyValueReference("parent_grp_id", grp.mMeta.mParentGrpId)
<< makeKeyValueReference("description", grp.mDescription)
<< makeKeyValueReference("own", own)
<< makeKeyValueReference("subscribed", subscribed)
<< makeKeyValueReference("auto_download", grp.mAutoDownload);
if(wantThumbnail)
{
std::string thumbnail_base64;
Radix64::encode(grp.mImage.mData, grp.mImage.mSize, thumbnail_base64);
rgrp << makeKeyValueReference("thumbnail_base64_png", thumbnail_base64);
}
}
resp.setOk();
}
else resp.setFail("Cant get data from GXS!");
}
void ChannelsHandler::handleGetChannelContent(Request& req, Response& resp)
{
std::string chanIdStr;
req.mStream << makeKeyValueReference("channel_id", chanIdStr);
if(chanIdStr.empty())
{
resp.setFail("channel_id required!");
return;
}
RsGxsGroupId chanId(chanIdStr);
if(chanId.isNull())
{
resp.setFail("Invalid channel_id:" + chanIdStr);
return;
}
std::list<RsGxsGroupId> groupIds; groupIds.push_back(chanId);
uint32_t token;
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA;
if(! mChannels.getTokenService()->
requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds) )
{
resp.setFail("Unknown GXS error!");
return;
}
time_t start = time(NULL);
while((mChannels.getTokenService()->requestStatus(token) != RsTokenService::COMPLETE)
&&(mChannels.getTokenService()->requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))) rstime::rs_usleep(500*1000);
std::vector<RsGxsChannelPost> posts;
std::vector<RsGxsComment> comments;
if( mChannels.getTokenService()->requestStatus(token) ==
RsTokenService::COMPLETE &&
mChannels.getPostData(token, posts, comments) )
{
for( std::vector<RsGxsChannelPost>::iterator vit = posts.begin();
vit != posts.end(); ++vit )
{
RsGxsChannelPost& post = *vit;
RsMsgMetaData& pMeta = post.mMeta;
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("channel_id", pMeta.mGroupId)
<< makeKeyValueReference("name", pMeta.mMsgName)
<< makeKeyValueReference("post_id", pMeta.mMsgId)
<< makeKeyValueReference("parent_id", pMeta.mParentId)
<< makeKeyValueReference("author_id", pMeta.mAuthorId)
<< makeKeyValueReference("orig_msg_id", pMeta.mOrigMsgId)
<< makeKeyValueReference("thread_id", pMeta.mThreadId)
<< makeKeyValueReference("message", post.mMsg);
}
for( std::vector<RsGxsComment>::iterator vit = comments.begin();
vit != comments.end(); ++vit )
{
RsGxsComment& comment = *vit;
RsMsgMetaData& cMeta = comment.mMeta;
std::string scoreStr = std::to_string(comment.mScore);
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("channel_id", cMeta.mGroupId)
<< makeKeyValueReference("name", cMeta.mMsgName)
<< makeKeyValueReference("comment_id", cMeta.mMsgId)
<< makeKeyValueReference("parent_id", cMeta.mParentId)
<< makeKeyValueReference("author_id", cMeta.mAuthorId)
<< makeKeyValueReference("orig_msg_id", cMeta.mOrigMsgId)
<< makeKeyValueReference("thread_id", cMeta.mThreadId)
<< makeKeyValueReference("score", scoreStr)
<< makeKeyValueReference("message", comment.mComment);
}
resp.setOk();
}
else resp.setFail("Cant get data from GXS!");
}
void ChannelsHandler::handleToggleSubscription(Request& req, Response& resp)
{
std::string chanIdStr;
bool subscribe = true;
req.mStream << makeKeyValueReference("channel_id", chanIdStr)
<< makeKeyValueReference("subscribe", subscribe);
if(chanIdStr.empty())
{
resp.setFail("channel_id required!");
return;
}
RsGxsGroupId chanId(chanIdStr);
if(chanId.isNull())
{
resp.setFail("Invalid channel_id:" + chanIdStr);
return;
}
uint32_t token;
if(mChannels.subscribeToGroup(token, chanId, subscribe))
{
RsTokenService& tChannels = *mChannels.getTokenService();
time_t start = time(NULL);
while((tChannels.requestStatus(token) != RsTokenService::COMPLETE)
&&(tChannels.requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))) rstime::rs_usleep(500*1000);
if(tChannels.requestStatus(token) == RsTokenService::COMPLETE)
resp.setOk();
else resp.setFail("Unknown GXS error!");
}
else resp.setFail("Unknown GXS error!");
}
void ChannelsHandler::handleCreateChannel(Request& req, Response& resp)
{
RsGxsChannelGroup chan;
RsGroupMetaData& cMeta = chan.mMeta;
std::string authorIdStr;
std::string thumbnail_base64;
req.mStream << makeKeyValueReference("author_id", authorIdStr)
<< makeKeyValueReference("name", cMeta.mGroupName)
<< makeKeyValueReference("description", chan.mDescription)
<< makeKeyValueReference("thumbnail_base64_png", thumbnail_base64);
if(cMeta.mGroupName.empty())
{
resp.setFail("Channel name required!");
return;
}
if(thumbnail_base64.empty()) chan.mImage.clear();
else
{
std::vector<uint8_t> png_data = Radix64::decode(thumbnail_base64);
if(!png_data.empty())
{
if(png_data.size() < 8)
{
resp.setFail("Decoded thumbnail_base64_png is smaller than 8 byte. This can't be a valid png file!");
return;
}
uint8_t png_magic_number[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
if(!std::equal(&png_magic_number[0],&png_magic_number[8],png_data.begin()))
{
resp.setFail("Decoded thumbnail_base64_png does not seem to be a png file. (Header is missing magic number)");
return;
}
chan.mImage.copy(png_data.data(), png_data.size());
}
}
if(!authorIdStr.empty()) cMeta.mAuthorId = RsGxsId(authorIdStr);
// ATM supports creating only public channels
cMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC;
// I am not sure about those flags I have reversed them with the debugger
// that gives 520 as value of this member when a channel with default
// options is created from Qt Gui
cMeta.mSignFlags = GXS_SERV::MSG_AUTHEN_CHILD_AUTHOR_SIGN |
GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_REQUIRED;
uint32_t token;
if(mChannels.createGroup(token, chan))
{
RsTokenService& tChannels = *mChannels.getTokenService();
time_t start = time(NULL);
while((tChannels.requestStatus(token) != RsTokenService::COMPLETE)
&&(tChannels.requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))) rstime::rs_usleep(500*1000);
if(tChannels.requestStatus(token) == RsTokenService::COMPLETE)
resp.setOk();
else resp.setFail("Unknown GXS error!");
}
else resp.setFail("Unkown GXS error!");
}
void ChannelsHandler::handleToggleAutoDownload(Request& req, Response& resp)
{
std::string chanIdStr;
bool autoDownload = true;
req.mStream << makeKeyValueReference("channel_id", chanIdStr)
<< makeKeyValueReference("auto_download", autoDownload);
if(chanIdStr.empty())
{
resp.setFail("channel_id required!");
return;
}
RsGxsGroupId chanId(chanIdStr);
if(chanId.isNull())
{
resp.setFail("Invalid channel_id:" + chanIdStr);
return;
}
if(mChannels.setChannelAutoDownload(chanId, autoDownload))
resp.setOk();
else resp.setFail();
}
void ChannelsHandler::handleTogglePostRead(Request& req, Response& resp)
{
std::string chanIdStr;
std::string postIdStr;
bool read = true;
req.mStream << makeKeyValueReference("channel_id", chanIdStr)
<< makeKeyValueReference("post_id", postIdStr)
<< makeKeyValueReference("read", read);
if(chanIdStr.empty())
{
resp.setFail("channel_id required!");
return;
}
RsGxsGroupId chanId(chanIdStr);
if(chanId.isNull())
{
resp.setFail("Invalid channel_id:" + chanIdStr);
return;
}
if(postIdStr.empty())
{
resp.setFail("post_id required!");
return;
}
RsGxsMessageId postId(postIdStr);
if(postId.isNull())
{
resp.setFail("Invalid post_id:" + postIdStr);
return;
}
std::cerr << __PRETTY_FUNCTION__ << " " << chanIdStr << " " << postIdStr
<< " " << read << std::endl;
uint32_t token;
mChannels.setMessageReadStatus(token, std::make_pair(chanId,postId), read);
RsTokenService& tChannels = *mChannels.getTokenService();
time_t start = time(NULL);
while((tChannels.requestStatus(token) != RsTokenService::COMPLETE)
&&(tChannels.requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))) rstime::rs_usleep(500*1000);
if(tChannels.requestStatus(token) == RsTokenService::COMPLETE)
resp.setOk();
else resp.setFail("Unknown GXS error!");
}
void ChannelsHandler::handleCreatePost(Request &req, Response &resp)
{
RsGxsChannelPost post;
req.mStream << makeKeyValueReference("channel_id", post.mMeta.mGroupId);
req.mStream << makeKeyValueReference("subject", post.mMeta.mMsgName);
req.mStream << makeKeyValueReference("message", post.mMsg);
StreamBase& file_array = req.mStream.getStreamToMember("files");
while(file_array.hasMore())
{
RsGxsFile file;
file_array.getStreamToMember() << file;
post.mFiles.push_back(file);
}
std::string thumbnail_base64;
req.mStream << makeKeyValueReference("thumbnail_base64_png", thumbnail_base64);
if(post.mMeta.mGroupId.isNull())
{
resp.setFail("groupd_id is null");
return;
}
if(post.mMeta.mMsgName.empty())
{
resp.setFail("subject is empty");
return;
}
if(post.mMsg.empty())
{
resp.setFail("msg text is empty");
return;
}
// empty file list is ok, but files have to be valid
for(std::list<RsGxsFile>::iterator lit = post.mFiles.begin(); lit != post.mFiles.end(); ++lit)
{
if(lit->mHash.isNull())
{
resp.setFail("at least one file hash is empty");
return;
}
if(lit->mName.empty())
{
resp.setFail("at leats one file name is empty");
return;
}
if(lit->mSize == 0)
{
resp.setFail("at least one file size is empty");
return;
}
}
std::vector<uint8_t> png_data = Radix64::decode(thumbnail_base64);
if(!png_data.empty())
{
if(png_data.size() < 8)
{
resp.setFail("Decoded thumbnail_base64_png is smaller than 8 byte. This can't be a valid png file!");
return;
}
uint8_t png_magic_number[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
if(!std::equal(&png_magic_number[0],&png_magic_number[8],png_data.begin()))
{
resp.setFail("Decoded thumbnail_base64_png does not seem to be a png file. (Header is missing magic number)");
return;
}
post.mThumbnail.copy(png_data.data(), png_data.size());
}
uint32_t token;
if(mChannels.createPost(token, post))
{
RsTokenService& tChannels = *mChannels.getTokenService();
time_t start = time(NULL);
while((tChannels.requestStatus(token) != RsTokenService::COMPLETE)
&&(tChannels.requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))) rstime::rs_usleep(500*1000);
if(tChannels.requestStatus(token) == RsTokenService::COMPLETE)
resp.setOk();
else resp.setFail("Unknown GXS error!");
}
else resp.setFail("Unknown GXS error!");
}
} // namespace resource_api

View File

@ -1,48 +0,0 @@
/*******************************************************************************
* libresapi/api/ChannelsHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 by Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
class RsGxsChannels;
namespace resource_api
{
struct ChannelsHandler : ResourceRouter
{
ChannelsHandler(RsGxsChannels& channels);
private:
void handleListChannels(Request& req, Response& resp);
void handleGetChannelInfo(Request& req, Response& resp);
void handleGetChannelContent(Request& req, Response& resp);
void handleToggleSubscription(Request& req, Response& resp);
void handleCreateChannel(Request& req, Response& resp);
void handleToggleAutoDownload(Request& req, Response& resp);
void handleTogglePostRead(Request& req, Response& resp);
void handleCreatePost(Request& req, Response& resp);
RsGxsChannels& mChannels;
};
} // namespace resource_api

File diff suppressed because it is too large Load Diff

View File

@ -1,203 +0,0 @@
/*******************************************************************************
* libresapi/api/ChatHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2015 by electron128 <electron128@yahoo.com> *
* Copyright 2017 by Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
#include "StateTokenServer.h"
#include <retroshare/rsnotify.h>
#include <retroshare/rsmsgs.h>
#include "util/rstime.h"
class RsPeers;
struct RsIdentity;
namespace resource_api
{
class UnreadMsgNotify
{
public:
virtual void notifyUnreadMsgCountChanged(const RsPeerId& peer, uint32_t count) = 0;
};
class ChatHandler: public ResourceRouter, NotifyClient, Tickable
{
public:
ChatHandler(StateTokenServer* sts, RsNotify* notify, RsMsgs* msgs, RsPeers* peers, RsIdentity *identity, UnreadMsgNotify* unread);
virtual ~ChatHandler();
// from NotifyClient
// note: this may get called from the own and from foreign threads
virtual void notifyChatMessage(const ChatMessage& msg);
virtual void notifyChatCleared(const ChatId& chat_id);
// typing label for peer, broadcast and distant chat
virtual void notifyChatStatus (const ChatId& /* chat_id */, const std::string& /* status_string */);
//typing label for lobby chat, peer join and leave messages
virtual void notifyChatLobbyEvent (uint64_t /* lobby id */, uint32_t /* event type */ ,
const RsGxsId& /* nickname */,const std::string& /* any string */);
virtual void notifyListChange(int list, int type);
// from tickable
virtual void tick();
public:
class Triple
{
public:
Triple(): first(-1), second(-1), third(-1){}
double first;
double second;
double third;
};
class Msg{
public:
bool read;
bool incoming;
bool was_send;
//std::string chat_type;
std::string author_id; // peer or gxs id or "system" for system messages
std::string author_name;
std::string msg; // plain text only!
std::vector<Triple> links;
uint32_t recv_time;
uint32_t send_time;
uint32_t id;
};
class Lobby{
public:
Lobby(): id(0), subscribed(false), auto_subscribe(false), is_private(false), is_broadcast(false){}
ChatLobbyId id;
std::string name;
std::string topic;
bool subscribed;
bool auto_subscribe;
bool is_private;
bool is_broadcast;
RsGxsId gxs_id;// for subscribed lobbies: the id we use to write messages
bool operator==(const Lobby& l) const
{
return id == l.id
&& name == l.name
&& topic == l.topic
&& subscribed == l.subscribed
&& auto_subscribe == l.auto_subscribe
&& is_private == l.is_private
&& is_broadcast == l.is_broadcast
&& gxs_id == l.gxs_id;
}
};
class LobbyParticipantsInfo{
public:
StateToken state_token;
std::map<RsGxsId, rstime_t> participants;
};
class ChatInfo{
public:
bool is_broadcast;
bool is_distant_chat_id;
bool is_lobby;
bool is_peer;
std::string own_author_id;
std::string own_author_name;
std::string remote_author_id;
std::string remote_author_name;
};
class TypingLabelInfo{
public:
time_t timestamp;
std::string status;
StateToken state_token;
// only for lobbies
RsGxsId author_id;
};
private:
void handleWildcard(Request& req, Response& resp);
void handleLobbies(Request& req, Response& resp);
void handleCreateLobby(Request& req, Response& resp);
void handleSubscribeLobby(Request& req, Response& resp);
void handleUnsubscribeLobby(Request& req, Response& resp);
void handleAutoSubsribeLobby(Request& req, Response& resp);
void handleInviteToLobby(Request& req, Response& resp);
void handleGetInvitationsToLobby(Request& req, Response& resp);
void handleAnswerToInvitation(Request& req, Response& resp);
void handleClearLobby(Request& req, Response& resp);
ResponseTask* handleLobbyParticipants(Request& req, Response& resp);
void handleGetDefaultIdentityForChatLobby(Request& req, Response& resp);
void handleSetDefaultIdentityForChatLobby(Request& req, Response& resp);
void handleGetIdentityForChatLobby(Request& req, Response& resp);
void handleSetIdentityForChatLobby(Request& req, Response& resp);
void handleMessages(Request& req, Response& resp);
void handleSendMessage(Request& req, Response& resp);
void handleMarkMessageAsRead(Request& req, Response& resp);
void handleMarkChatAsRead(Request& req, Response& resp);
void handleInfo(Request& req, Response& resp);
ResponseTask *handleReceiveStatus(Request& req, Response& resp);
void handleSendStatus(Request& req, Response& resp);
void handleUnreadMsgs(Request& req, Response& resp);
void handleInitiateDistantChatConnexion(Request& req, Response& resp);
void handleDistantChatStatus(Request& req, Response& resp);
void handleCloseDistantChatConnexion(Request& req, Response& resp);
void getPlainText(const std::string& in, std::string &out, std::vector<Triple> &links);
// last parameter is only used for lobbies!
void locked_storeTypingInfo(const ChatId& chat_id, std::string status, RsGxsId lobby_gxs_id = RsGxsId());
StateTokenServer* mStateTokenServer;
RsNotify* mNotify;
RsMsgs* mRsMsgs;
RsPeers* mRsPeers;
RsIdentity* mRsIdentity;
UnreadMsgNotify* mUnreadMsgNotify;
RsMutex mMtx;
StateToken mMsgStateToken;
std::list<ChatMessage> mRawMsgs;
std::map<ChatId, std::list<Msg> > mMsgs;
std::map<ChatId, ChatInfo> mChatInfo;
std::map<ChatId, TypingLabelInfo> mTypingLabelInfo;
StateToken mLobbiesStateToken;
std::vector<Lobby> mLobbies;
std::map<ChatLobbyId, LobbyParticipantsInfo> mLobbyParticipantsInfos;
StateToken mUnreadMsgsStateToken;
StateToken mInvitationsStateToken;
};
} // namespace resource_api

View File

@ -1,324 +0,0 @@
/*******************************************************************************
* libresapi/api/FileSearchHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include <time.h>
#include "FileSearchHandler.h"
#include <retroshare/rspeers.h>
#include <retroshare/rsexpr.h>
#include <sstream>
#include "Operators.h"
namespace resource_api
{
FileSearchHandler::FileSearchHandler(StateTokenServer *sts, RsNotify *notify, RsTurtle */*turtle*/, RsFiles *files):
mStateTokenServer(sts), mNotify(notify)/*, mTurtle(turtle)*/, mRsFiles(files),
mMtx("FileSearchHandler")
{
mNotify->registerNotifyClient(this);
addResourceHandler("*", this, &FileSearchHandler::handleWildcard);
addResourceHandler("create_search", this, &FileSearchHandler::handleCreateSearch);
addResourceHandler("get_search_result", this, &FileSearchHandler::handleGetSearchResult);
mSearchesStateToken = mStateTokenServer->getNewToken();
}
FileSearchHandler::~FileSearchHandler()
{
mNotify->unregisterNotifyClient(this);
mStateTokenServer->discardToken(mSearchesStateToken);
}
void FileSearchHandler::notifyTurtleSearchResult(const RsPeerId& /*pid*/,uint32_t search_id, const std::list<TurtleFileInfo>& files)
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
std::map<uint32_t, Search>::iterator mit = mSearches.find(search_id);
if(mit == mSearches.end())
return;
Search& search = mit->second;
// set to a limit of 100 for now, can have more when we have pagination
std::list<TurtleFileInfo>::const_iterator lit = files.begin();
bool changed = false;
while(search.mResults.size() < 100 && lit != files.end())
{
if(search.mHashes.find(lit->hash) == search.mHashes.end())
{
changed = true;
FileDetail det ;
det.rank = 0 ;
det.age = 0 ;
det.name = (*lit).name;
det.hash = (*lit).hash;
det.size = (*lit).size;
det.id.clear();
search.mResults.push_back(det);
search.mHashes.insert(lit->hash);
}
lit++;
}
if(changed)
{
mStateTokenServer->discardToken(search.mStateToken);
search.mStateToken = mStateTokenServer->getNewToken();
}
}
// TODO: delete searches
void FileSearchHandler::handleWildcard(Request &req, Response &resp)
{
if(!req.mPath.empty())
{
std::string str = req.mPath.top();
req.mPath.pop();
if(str.size() != 8)
{
resp.setFail("Error: id has wrong size, should be 8 characters");
return;
}
uint32_t id = 0;
// TODO fix this
for(uint8_t i = 0; i < 8; i++)
{
id += (uint32_t(str[i]-'A')) << (i*4);
}
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
std::map<uint32_t, Search>::iterator mit = mSearches.find(id);
if(mit == mSearches.end())
{
resp.setFail("Error: search id invalid");
return;
}
Search& search = mit->second;
resp.mStateToken = search.mStateToken;
resp.mDataStream.getStreamToMember();
for(std::list<FileDetail>::iterator lit = search.mResults.begin(); lit != search.mResults.end(); ++lit)
{
FileDetail& fd = *lit;
double size = fd.size;
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("id", fd.hash)
<< makeKeyValueReference("name", fd.name)
<< makeKeyValueReference("hash", fd.hash)
<< makeKeyValueReference("path", fd.path)
<< makeKeyValue("peer_id", fd.id.toStdString())
<< makeKeyValueReference("size", size)
<< makeKeyValueReference("rank", fd.rank)
<< makeKeyValueReference("age", fd.age);
}
}
}
else
{
// list searches
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
resp.mDataStream.getStreamToMember();
for(std::map<uint32_t, Search>::iterator mit = mSearches.begin(); mit != mSearches.end(); ++mit)
{
uint32_t id = mit->first;
std::string idstr;
// how many times do i have to write int to string conversation?
// a library should do this
for(uint8_t i = 0; i < 8; i++)
{
char c = ((id>>(i*4))&0xF)+'A';
idstr += c;
}
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("id", idstr)
<< makeKeyValueReference("search_string", mit->second.mSearchString);
}
resp.mStateToken = mSearchesStateToken;
resp.setOk();
}
}
static bool dirDetailToFileDetail(const DirDetails& dir, FileDetail& fd)
{
if (dir.type == DIR_TYPE_FILE)
{
fd.id = dir.id;
fd.name = dir.name;
fd.hash = dir.hash;
fd.path = dir.path;
fd.size = dir.count;
fd.age = time(NULL) - dir.mtime;
fd.rank = 0;
return true;
}
else
return false;
}
// see retroshare-gui/src/gui/Searchdialog.cpp
void FileSearchHandler::handleCreateSearch(Request &req, Response &resp)
{
bool distant = false;// distant involves sending data, so have it off by default for privacy
bool local = true;
bool remote = true;
std::string search_string;
req.mStream << makeKeyValueReference("distant", distant)
<< makeKeyValueReference("local", local)
<< makeKeyValueReference("remote", remote)
<< makeKeyValueReference("search_string", search_string);
std::istringstream iss(search_string);
std::list<std::string> words;
std::string s;
while (std::getline(iss, s, ' ')) {
std::cout << s << std::endl;
words.push_back(s);
}
if(words.empty())
{
resp.setFail("Error: no search string given");
return;
}
RsRegularExpression::NameExpression exprs(RsRegularExpression::ContainsAllStrings,words,true) ;
RsRegularExpression::LinearizedExpression lin_exp ;
exprs.linearize(lin_exp) ;
uint32_t search_id = RSRandom::random_u32();
if(distant)
{
// i have no idea what the reasons for two different search modes are
// rs-gui does it, so do we
if(words.size() == 1)
search_id = rsFiles->turtleSearch(words.front());
else
search_id = rsFiles->turtleSearch(lin_exp);
}
std::list<FileDetail> results;
if(local)
{
std::list<DirDetails> local_results;
mRsFiles->SearchBoolExp(&exprs, local_results, RS_FILE_HINTS_LOCAL);
for(std::list<DirDetails>::iterator lit = local_results.begin(); lit != local_results.end(); ++lit)
{
FileDetail fd;
if(dirDetailToFileDetail(*lit, fd))
results.push_back(fd);
}
}
if(remote)
{
std::list<DirDetails> remote_results;
mRsFiles->SearchBoolExp(&exprs, remote_results, RS_FILE_HINTS_REMOTE);
for(std::list<DirDetails>::iterator lit = remote_results.begin(); lit != remote_results.end(); ++lit)
{
FileDetail fd;
if(dirDetailToFileDetail(*lit, fd))
results.push_back(fd);
}
}
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
Search& search = mSearches[search_id];
search.mStateToken = mStateTokenServer->getNewToken();
search.mSearchString = search_string;
search.mResults.swap(results);
mStateTokenServer->discardToken(mSearchesStateToken);
mSearchesStateToken = mStateTokenServer->getNewToken();
}
std::string idstr;
// how many times do i have to write int to string conversation?
// a library should do this
for(uint8_t i = 0; i < 8; i++)
{
char c = ((search_id>>(i*4))&0xF)+'A';
idstr += c;
}
resp.mDataStream << makeKeyValueReference("search_id", idstr);
resp.setOk();
}
void FileSearchHandler::handleGetSearchResult(Request& req, Response& resp)
{
std::string search_id;
req.mStream << makeKeyValueReference("search_id", search_id);
if(search_id.size() != 8)
{
resp.setFail("Error: id has wrong size, should be 8 characters");
return;
}
uint32_t id = 0;
for(uint8_t i = 0; i < 8; i++)
{
id += (uint32_t(search_id[i]-'A')) << (i*4);
}
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
std::map<uint32_t, Search>::iterator mit = mSearches.find(id);
if(mit == mSearches.end())
{
resp.setFail("Error: search id invalid");
return;
}
Search& search = mit->second;
resp.mStateToken = search.mStateToken;
resp.mDataStream.getStreamToMember();
RsPgpId ownId = rsPeers->getGPGOwnId();
for(std::list<FileDetail>::iterator lit = search.mResults.begin(); lit != search.mResults.end(); ++lit)
{
FileDetail& fd = *lit;
bool isFriend = rsPeers->isFriend(fd.id);
bool isOwn = false;
if(ownId == rsPeers->getGPGId(fd.id))
isOwn = true;
double size = fd.size;
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("id", fd.hash)
<< makeKeyValueReference("name", fd.name)
<< makeKeyValueReference("hash", fd.hash)
<< makeKeyValueReference("path", fd.path)
<< makeKeyValue("peer_id", fd.id.toStdString())
<< makeKeyValueReference("is_friends", isFriend)
<< makeKeyValueReference("is_own", isOwn)
<< makeKeyValueReference("size", size)
<< makeKeyValueReference("rank", fd.rank)
<< makeKeyValueReference("age", fd.age);
}
}
resp.setOk();
}
} // namespace resource_api

View File

@ -1,65 +0,0 @@
/*******************************************************************************
* libresapi/api/FileSearchHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
#include "StateTokenServer.h"
#include <retroshare/rsnotify.h>
#include <retroshare/rsturtle.h>
#include <retroshare/rsfiles.h>
namespace resource_api
{
class FileSearchHandler: public ResourceRouter, NotifyClient
{
public:
FileSearchHandler(StateTokenServer* sts, RsNotify* notify, RsTurtle* turtle, RsFiles* files);
virtual ~FileSearchHandler();
// from NotifyClient
virtual void notifyTurtleSearchResult(const RsPeerId &pid, uint32_t search_id, const std::list<TurtleFileInfo>& files);
private:
void handleWildcard(Request& req, Response& resp);
void handleCreateSearch(Request& req, Response& resp);
void handleGetSearchResult(Request& req, Response& resp);
StateTokenServer* mStateTokenServer;
RsNotify* mNotify;
//RsTurtle* mTurtle;
RsFiles* mRsFiles;
class Search{
public:
StateToken mStateToken;
std::string mSearchString; // extra service: store the search string
std::list<FileDetail> mResults;
// a set for fast deduplication lookup
std::set<RsFileHash> mHashes;
};
RsMutex mMtx;
StateToken mSearchesStateToken;
std::map<uint32_t, Search> mSearches; // mutex protected
};
} // namespace resource_api

View File

@ -1,573 +0,0 @@
/*******************************************************************************
* libresapi/api/FileSharingHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright (C) 2017, Konrad Dębiec <konradd@tutanota.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 "FileSharingHandler.h"
namespace resource_api
{
FileSharingHandler::FileSharingHandler(StateTokenServer *sts, RsFiles *files,
RsNotify& notify):
mStateTokenServer(sts), mMtx("FileSharingHandler Mtx"), mRsFiles(files),
mNotify(notify)
{
addResourceHandler("*", this, &FileSharingHandler::handleWildcard);
addResourceHandler("force_check", this, &FileSharingHandler::handleForceCheck);
addResourceHandler("get_shared_dirs", this, &FileSharingHandler::handleGetSharedDir);
addResourceHandler("set_shared_dir", this, &FileSharingHandler::handleSetSharedDir);
addResourceHandler("update_shared_dir", this, &FileSharingHandler::handleUpdateSharedDir);
addResourceHandler("remove_shared_dir", this, &FileSharingHandler::handleRemoveSharedDir);
addResourceHandler("get_dir_parent", this, &FileSharingHandler::handleGetDirectoryParent);
addResourceHandler("get_dir_childs", this, &FileSharingHandler::handleGetDirectoryChilds);
addResourceHandler( "get_download_directory", this,
&FileSharingHandler::handleGetDownloadDirectory );
addResourceHandler( "set_download_directory", this,
&FileSharingHandler::handleSetDownloadDirectory );
addResourceHandler( "get_partials_directory", this,
&FileSharingHandler::handleGetPartialsDirectory );
addResourceHandler( "set_partials_directory", this,
&FileSharingHandler::handleSetPartialsDirectory );
addResourceHandler("is_dl_dir_shared", this, &FileSharingHandler::handleIsDownloadDirShared);
addResourceHandler("share_dl_dir", this, &FileSharingHandler::handleShareDownloadDirectory);
addResourceHandler("download", this, &FileSharingHandler::handleDownload);
mLocalDirStateToken = mStateTokenServer->getNewToken();
mRemoteDirStateToken = mStateTokenServer->getNewToken();
mNotify.registerNotifyClient(this);
}
FileSharingHandler::~FileSharingHandler()
{
mNotify.unregisterNotifyClient(this);
}
void FileSharingHandler::notifyListChange(int list, int /* type */)
{
if(list == NOTIFY_LIST_DIRLIST_LOCAL)
{
RS_STACK_MUTEX(mMtx);
mStateTokenServer->discardToken(mLocalDirStateToken);
mLocalDirStateToken = mStateTokenServer->getNewToken();
}
else if(list == NOTIFY_LIST_DIRLIST_FRIENDS)
{
RS_STACK_MUTEX(mMtx);
mStateTokenServer->discardToken(mRemoteDirStateToken);
mRemoteDirStateToken = mStateTokenServer->getNewToken();
}
}
void FileSharingHandler::handleWildcard(Request & /*req*/, Response & /*resp*/)
{
}
void FileSharingHandler::handleForceCheck(Request&, Response& resp)
{
mRsFiles->ForceDirectoryCheck();
resp.setOk();
}
void FileSharingHandler::handleGetSharedDir(Request& /*req*/, Response& resp)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails(NULL, dirDetails, RS_FILE_HINTS_LOCAL);
resp.mDataStream << makeKeyValue("parent_reference", *reinterpret_cast<int*>(&dirDetails.ref));
resp.mDataStream << makeKeyValue("path", dirDetails.path);
StreamBase &childsStream = resp.mDataStream.getStreamToMember("childs");
for(std::vector<DirStub>::iterator it = dirDetails.children.begin(); it != dirDetails.children.end(); ++it)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails((*it).ref, dirDetails, RS_FILE_HINTS_LOCAL);
for(std::vector<DirStub>::iterator vit = dirDetails.children.begin(); vit != dirDetails.children.end(); ++vit)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails((*vit).ref, dirDetails, RS_FILE_HINTS_LOCAL);
std::string type;
switch(dirDetails.type)
{
case DIR_TYPE_PERSON:
type = "person";
break;
case DIR_TYPE_DIR:
type = "folder";
break;
case DIR_TYPE_FILE:
type = "file";
break;
}
bool browsable = false;
bool anon_dl = false;
bool anon_search = false;
if(dirDetails.flags & DIR_FLAGS_BROWSABLE)
browsable = true;
if(dirDetails.flags & DIR_FLAGS_ANONYMOUS_DOWNLOAD)
anon_dl = true;
if(dirDetails.flags & DIR_FLAGS_ANONYMOUS_SEARCH)
anon_search = true;
StreamBase &streamBase = childsStream.getStreamToMember()
<< makeKeyValue("name", dirDetails.name)
<< makeKeyValue("path", dirDetails.path)
<< makeKeyValue("hash", dirDetails.hash.toStdString())
<< makeKeyValue("peer_id", dirDetails.id.toStdString())
<< makeKeyValue("parent_reference", *reinterpret_cast<int*>(&dirDetails.parent))
<< makeKeyValue("reference", *reinterpret_cast<int*>(&dirDetails.ref))
<< makeKeyValue("count", static_cast<int>(dirDetails.count))
<< makeKeyValueReference("type", type)
<< makeKeyValueReference("browsable", browsable)
<< makeKeyValueReference("anon_dl", anon_dl)
<< makeKeyValueReference("anon_search", anon_search);
int contain_files = 0;
int contain_folders = 0;
if(dirDetails.count != 0)
{
for(std::vector<DirStub>::iterator vit = dirDetails.children.begin(); vit != dirDetails.children.end(); ++vit)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails((*vit).ref, dirDetails, RS_FILE_HINTS_LOCAL);
switch(dirDetails.type)
{
case DIR_TYPE_DIR:
contain_folders++;
break;
case DIR_TYPE_FILE:
contain_files++;
break;
}
}
}
streamBase
<< makeKeyValueReference("contain_files", contain_files)
<< makeKeyValueReference("contain_folders", contain_folders);
}
}
resp.mStateToken = mLocalDirStateToken;
}
void FileSharingHandler::handleSetSharedDir(Request& req, Response& resp)
{
std::string dir;
bool browsable = false;
bool anon_dl = false;
bool anon_search = false;
req.mStream << makeKeyValueReference("directory", dir);
req.mStream << makeKeyValueReference("browsable", browsable);
req.mStream << makeKeyValueReference("anon_dl", anon_dl);
req.mStream << makeKeyValueReference("anon_search", anon_search);
SharedDirInfo sDI;
sDI.filename = dir;
sDI.virtualname.clear();
sDI.shareflags.clear();
if(browsable)
sDI.shareflags |= DIR_FLAGS_BROWSABLE;
if(anon_dl)
sDI.shareflags |= DIR_FLAGS_ANONYMOUS_DOWNLOAD;
if(anon_search)
sDI.shareflags |= DIR_FLAGS_ANONYMOUS_SEARCH;
if(mRsFiles->addSharedDirectory(sDI))
resp.setOk();
else
resp.setFail("Couldn't share folder");
}
void FileSharingHandler::handleUpdateSharedDir(Request& req, Response& resp)
{
std::string dir;
std::string virtualname;
bool browsable = false;
bool anon_dl = false;
bool anon_search = false;
req.mStream << makeKeyValueReference("directory", dir);
req.mStream << makeKeyValueReference("virtualname", virtualname);
req.mStream << makeKeyValueReference("browsable", browsable);
req.mStream << makeKeyValueReference("anon_dl", anon_dl);
req.mStream << makeKeyValueReference("anon_search", anon_search);
SharedDirInfo sDI;
sDI.filename = dir;
sDI.virtualname = virtualname;
sDI.shareflags.clear();
if(browsable)
sDI.shareflags |= DIR_FLAGS_BROWSABLE;
if(anon_dl)
sDI.shareflags |= DIR_FLAGS_ANONYMOUS_DOWNLOAD;
if(anon_search)
sDI.shareflags |= DIR_FLAGS_ANONYMOUS_SEARCH;
if(mRsFiles->updateShareFlags(sDI))
resp.setOk();
else
resp.setFail("Couldn't update shared folder's flags");
}
void FileSharingHandler::handleRemoveSharedDir(Request& req, Response& resp)
{
std::string dir;
req.mStream << makeKeyValueReference("directory", dir);
if(mRsFiles->removeSharedDirectory(dir))
resp.setOk();
else
resp.setFail("Couldn't remove shared directory.");
}
void FileSharingHandler::handleGetDirectoryParent(Request& req, Response& resp)
{
int reference;
bool remote = false;
bool local = true;
req.mStream << makeKeyValueReference("reference", reference);
req.mStream << makeKeyValueReference("remote", remote);
req.mStream << makeKeyValueReference("local", local);
void *ref = reinterpret_cast<void*>(reference);
FileSearchFlags flags;
if(remote)
{
flags |= RS_FILE_HINTS_REMOTE;
resp.mStateToken = mRemoteDirStateToken;
}
if(local)
{
flags |= RS_FILE_HINTS_LOCAL;
resp.mStateToken = mLocalDirStateToken;
}
DirDetails dirDetails;
mRsFiles->RequestDirDetails(ref, dirDetails, flags);
mRsFiles->RequestDirDetails(dirDetails.parent, dirDetails, flags);
resp.mDataStream << makeKeyValue("parent_reference", *reinterpret_cast<int*>(&dirDetails.ref));
resp.mDataStream << makeKeyValue("path", dirDetails.path);
StreamBase &childsStream = resp.mDataStream.getStreamToMember("childs");
if(dirDetails.count != 0)
{
for(std::vector<DirStub>::iterator vit = dirDetails.children.begin(); vit != dirDetails.children.end(); ++vit)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails((*vit).ref, dirDetails, flags);
std::string type;
switch(dirDetails.type)
{
case DIR_TYPE_PERSON:
type = "person";
break;
case DIR_TYPE_DIR:
type = "folder";
break;
case DIR_TYPE_FILE:
type = "file";
break;
}
bool browsable = false;
bool anon_dl = false;
bool anon_search = false;
if(dirDetails.flags & DIR_FLAGS_BROWSABLE)
browsable = true;
if(dirDetails.flags & DIR_FLAGS_ANONYMOUS_DOWNLOAD)
anon_dl = true;
if(dirDetails.flags & DIR_FLAGS_ANONYMOUS_SEARCH)
anon_search = true;
StreamBase &streamBase = childsStream.getStreamToMember()
<< makeKeyValue("name", dirDetails.name)
<< makeKeyValue("path", dirDetails.path)
<< makeKeyValue("hash", dirDetails.hash.toStdString())
<< makeKeyValue("peer_id", dirDetails.id.toStdString())
<< makeKeyValue("parent_reference", *reinterpret_cast<int*>(&dirDetails.parent))
<< makeKeyValue("reference", *reinterpret_cast<int*>(&dirDetails.ref))
<< makeKeyValue("count", static_cast<int>(dirDetails.count))
<< makeKeyValueReference("type", type)
<< makeKeyValueReference("browsable", browsable)
<< makeKeyValueReference("anon_dl", anon_dl)
<< makeKeyValueReference("anon_search", anon_search);
int contain_files = 0;
int contain_folders = 0;
if(dirDetails.count != 0)
{
for(std::vector<DirStub>::iterator vit = dirDetails.children.begin(); vit != dirDetails.children.end(); ++vit)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails((*vit).ref, dirDetails, flags);
switch(dirDetails.type)
{
case DIR_TYPE_DIR:
contain_folders++;
break;
case DIR_TYPE_FILE:
contain_files++;
break;
}
}
}
streamBase
<< makeKeyValueReference("contain_files", contain_files)
<< makeKeyValueReference("contain_folders", contain_folders);
}
}
}
void FileSharingHandler::handleGetDirectoryChilds(Request& req, Response& resp)
{
int reference = 0;
bool remote = false;
bool local = true;
req.mStream << makeKeyValueReference("reference", reference);
req.mStream << makeKeyValueReference("remote", remote);
req.mStream << makeKeyValueReference("local", local);
void *ref = reinterpret_cast<void*>(reference);
FileSearchFlags flags;
if(remote)
{
flags |= RS_FILE_HINTS_REMOTE;
resp.mStateToken = mRemoteDirStateToken;
}
if(local)
{
flags |= RS_FILE_HINTS_LOCAL;
resp.mStateToken = mLocalDirStateToken;
}
DirDetails dirDetails;
mRsFiles->RequestDirDetails(ref, dirDetails, flags);
resp.mDataStream << makeKeyValue("parent_reference", *reinterpret_cast<int*>(&dirDetails.ref));
resp.mDataStream << makeKeyValue("path", dirDetails.path);
resp.mDataStream << makeKeyValue("hash", dirDetails.hash.toStdString());
StreamBase &childsStream = resp.mDataStream.getStreamToMember("childs");
if(dirDetails.count != 0)
{
for(std::vector<DirStub>::iterator vit = dirDetails.children.begin(); vit != dirDetails.children.end(); ++vit)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails((*vit).ref, dirDetails, flags);
std::string type;
switch(dirDetails.type)
{
case DIR_TYPE_PERSON:
type = "person";
break;
case DIR_TYPE_DIR:
type = "folder";
break;
case DIR_TYPE_FILE:
type = "file";
break;
}
bool browsable = false;
bool anon_dl = false;
bool anon_search = false;
if(dirDetails.flags & DIR_FLAGS_BROWSABLE)
browsable = true;
if(dirDetails.flags & DIR_FLAGS_ANONYMOUS_DOWNLOAD)
anon_dl = true;
if(dirDetails.flags & DIR_FLAGS_ANONYMOUS_SEARCH)
anon_search = true;
StreamBase &streamBase = childsStream.getStreamToMember()
<< makeKeyValue("name", dirDetails.name)
<< makeKeyValue("path", dirDetails.path)
<< makeKeyValue("hash", dirDetails.hash.toStdString())
<< makeKeyValue("peer_id", dirDetails.id.toStdString())
<< makeKeyValue("parent_reference", *reinterpret_cast<int*>(&dirDetails.parent))
<< makeKeyValue("reference", *reinterpret_cast<int*>(&dirDetails.ref))
<< makeKeyValue("count", static_cast<int>(dirDetails.count))
<< makeKeyValueReference("type", type)
<< makeKeyValueReference("browsable", browsable)
<< makeKeyValueReference("anon_dl", anon_dl)
<< makeKeyValueReference("anon_search", anon_search);
int contain_files = 0;
int contain_folders = 0;
if(dirDetails.count != 0)
{
for(std::vector<DirStub>::iterator vit = dirDetails.children.begin(); vit != dirDetails.children.end(); ++vit)
{
DirDetails dirDetails;
mRsFiles->RequestDirDetails((*vit).ref, dirDetails, flags);
switch(dirDetails.type)
{
case DIR_TYPE_DIR:
contain_folders++;
break;
case DIR_TYPE_FILE:
contain_files++;
break;
}
}
}
streamBase
<< makeKeyValueReference("contain_files", contain_files)
<< makeKeyValueReference("contain_folders", contain_folders);
}
}
}
void FileSharingHandler::handleIsDownloadDirShared(Request&, Response& resp)
{
bool shared = mRsFiles->getShareDownloadDirectory();
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("shared", shared);
resp.setOk();
}
void FileSharingHandler::handleShareDownloadDirectory(Request& req, Response& resp)
{
bool share;
req.mStream << makeKeyValueReference("share", share);
if(mRsFiles->shareDownloadDirectory(share))
resp.setOk();
else
resp.setFail("Couldn't share/unshare download directory.");
}
void FileSharingHandler::handleDownload(Request& req, Response& resp)
{
int size;
std::string hashString;
std::string name;
req.mStream << makeKeyValueReference("hash", hashString);
req.mStream << makeKeyValueReference("name", name);
req.mStream << makeKeyValueReference("size", size);
std::list<RsPeerId> srcIds;
RsFileHash hash(hashString);
FileInfo finfo;
mRsFiles->FileDetails(hash, RS_FILE_HINTS_REMOTE, finfo);
for(std::vector<TransferInfo>::const_iterator it(finfo.peers.begin());it!=finfo.peers.end();++it)
srcIds.push_back((*it).peerId);
if(!mRsFiles->FileRequest(name, hash, static_cast<uint64_t>(size), "",
RS_FILE_REQ_ANONYMOUS_ROUTING, srcIds))
{
resp.setOk();
return;
}
resp.setFail("Couldn't download file");
}
void FileSharingHandler::handleGetDownloadDirectory( Request& /*req*/,
Response& resp )
{
std::string dlDir = mRsFiles->getDownloadDirectory();
resp.mDataStream << makeKeyValueReference("download_directory", dlDir);
resp.setOk();
}
void FileSharingHandler::handleSetDownloadDirectory( Request& req,
Response& resp )
{
std::string dlDir;
req.mStream << makeKeyValueReference("download_directory", dlDir);
if(dlDir.empty()) resp.setFail("missing download_directory");
else
{
mRsFiles->setDownloadDirectory(dlDir);
resp.setOk();
}
}
void FileSharingHandler::handleGetPartialsDirectory( Request& /*req*/,
Response& resp )
{
std::string partialsDir = mRsFiles->getPartialsDirectory();
resp.mDataStream << makeKeyValueReference("partials_directory", partialsDir);
resp.setOk();
}
void FileSharingHandler::handleSetPartialsDirectory( Request& req,
Response& resp )
{
std::string partialsDir;
req.mStream << makeKeyValueReference("partials_directory", partialsDir);
if(partialsDir.empty()) resp.setFail("missing partials_directory");
else
{
mRsFiles->setPartialsDirectory(partialsDir);
resp.setOk();
}
}
} // namespace resource_api

View File

@ -1,87 +0,0 @@
/*******************************************************************************
* libresapi/api/FileSharingHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright (C) 2017, Konrad Dębiec <konradd@tutanota.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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
#include "StateTokenServer.h"
#include <retroshare/rsfiles.h>
#include <retroshare/rsnotify.h>
#include <util/rsthreads.h>
namespace resource_api
{
class FileSharingHandler: public ResourceRouter, NotifyClient
{
public:
FileSharingHandler(StateTokenServer* sts, RsFiles* files, RsNotify& notify);
~FileSharingHandler();
/**
Derived from NotifyClient
This function may be called from foreign thread
*/
virtual void notifyListChange(int list, int type);
private:
void handleWildcard(Request& req, Response& resp);
void handleForceCheck(Request& req, Response& resp);
void handleGetSharedDir(Request& req, Response& resp);
void handleSetSharedDir(Request& req, Response& resp);
void handleUpdateSharedDir(Request& req, Response& resp);
void handleRemoveSharedDir(Request& req, Response& resp);
void handleGetDirectoryParent(Request& req, Response& resp);
void handleGetDirectoryChilds(Request& req, Response& resp);
void handleIsDownloadDirShared(Request& req, Response& resp);
void handleShareDownloadDirectory(Request& req, Response& resp);
void handleDownload(Request& req, Response& resp);
void handleGetDownloadDirectory(Request& req, Response& resp);
void handleSetDownloadDirectory(Request& req, Response& resp);
void handleGetPartialsDirectory(Request& req, Response& resp);
void handleSetPartialsDirectory(Request& req, Response& resp);
/// Token indicating change in local shared files
StateToken mLocalDirStateToken;
/// Token indicating change in remote (friends') shared files
StateToken mRemoteDirStateToken;
StateTokenServer* mStateTokenServer;
/**
Protects mLocalDirStateToken and mRemoteDirStateToken that may be changed in foreign thread
@see FileSharingHandler::notifyListChange(...)
*/
RsMutex mMtx;
RsFiles* mRsFiles;
RsNotify& mNotify;
};
} // namespace resource_api

View File

@ -1,169 +0,0 @@
/*******************************************************************************
* libresapi/api/ForumHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "ForumHandler.h"
#include <retroshare/rsgxsforums.h>
#include <time.h>
#include "Operators.h"
#include "ApiTypes.h"
#include "GxsResponseTask.h"
#ifndef WINDOWS_SYS
#include "unistd.h"
#endif
namespace resource_api
{
ForumHandler::ForumHandler(RsGxsForums* forums):
mRsGxsForums(forums)
{
addResourceHandler("*", this, &ForumHandler::handleWildcard);
}
void ForumHandler::handleWildcard(Request &req, Response &resp)
{
bool ok = true;
if(!req.mPath.empty())
{
std::string str = req.mPath.top();
req.mPath.pop();
if(str != "")
{
//assume we have a groupID
RsGxsGroupId grpId(str);
std::list<RsGxsGroupId> groupIds;
groupIds.push_back(grpId);
uint32_t token;
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA;
mRsGxsForums->getTokenService()->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds);
time_t start = time(NULL);
while((mRsGxsForums->getTokenService()->requestStatus(token) != RsTokenService::COMPLETE)
&&(mRsGxsForums->getTokenService()->requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))
)
{
#ifdef WINDOWS_SYS
Sleep(500);
#else
usleep(500*1000) ;
#endif
}
if(mRsGxsForums->getTokenService()->requestStatus(token) == RsTokenService::COMPLETE)
{
std::vector<RsGxsForumMsg> grps;
ok &= mRsGxsForums->getMsgData(token, grps);
for(std::vector<RsGxsForumMsg>::iterator vit = grps.begin(); vit != grps.end(); vit++)
{
RsGxsForumMsg& grp = *vit;
KeyValueReference<RsGxsGroupId> group_id("group_id", grp.mMeta.mGroupId);
resp.mDataStream.getStreamToMember()
<< group_id
<< makeKeyValueReference("name", grp.mMeta.mMsgName)
<< makeKeyValueReference("id", grp.mMeta.mMsgId)
<< makeKeyValueReference("parent_id", grp.mMeta.mParentId)
<< makeKeyValueReference("author_id", grp.mMeta.mAuthorId)
<< makeKeyValueReference("orig_msg_id", grp.mMeta.mOrigMsgId)
<< makeKeyValueReference("thread_id", grp.mMeta.mThreadId)
<< makeKeyValueReference("message", grp.mMsg);
}
}
else
{
ok = false;
}
}
}
else
{
// no more path element
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
uint32_t token;
mRsGxsForums->getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts);
time_t start = time(NULL);
while((mRsGxsForums->getTokenService()->requestStatus(token) != RsTokenService::COMPLETE)
&&(mRsGxsForums->getTokenService()->requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))
)
{
#ifdef WINDOWS_SYS
Sleep(500);
#else
usleep(500*1000) ;
#endif
}
if(mRsGxsForums->getTokenService()->requestStatus(token) == RsTokenService::COMPLETE)
{
std::vector<RsGxsForumGroup> grps;
ok &= mRsGxsForums->getGroupData(token, grps);
for(std::vector<RsGxsForumGroup>::iterator vit = grps.begin(); vit != grps.end(); vit++)
{
RsGxsForumGroup& grp = *vit;
KeyValueReference<RsGxsGroupId> id("id", grp.mMeta.mGroupId);
KeyValueReference<uint32_t> vis_msg("visible_msg_count", grp.mMeta.mVisibleMsgCount);
//KeyValueReference<RsPgpId> pgp_id("pgp_id",grp.mPgpId );
// not very happy about this, i think the flags should stay hidden in rsidentities
bool own = (grp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
bool pgp_linked = (grp.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility);
bool subscribed = IS_GROUP_SUBSCRIBED(grp.mMeta.mSubscribeFlags);
resp.mDataStream.getStreamToMember()
<< id
//<< pgp_id
<< makeKeyValueReference("name", grp.mMeta.mGroupName)
//<< makeKeyValueReference("last_post", grp.mMeta.mLastPost)
<< makeKeyValueReference("pop", grp.mMeta.mPop)
//<< makeKeyValueReference("publish_ts", grp.mMeta.mPublishTs)
<< vis_msg
<< makeKeyValueReference("group_status", grp.mMeta.mGroupStatus)
<< makeKeyValueReference("author_id", grp.mMeta.mAuthorId)
<< makeKeyValueReference("parent_grp_id", grp.mMeta.mParentGrpId)
<< makeKeyValueReference("description", grp.mDescription)
<< makeKeyValueReference("own", own)
<< makeKeyValueReference("subscribed", subscribed)
<< makeKeyValueReference("pgp_linked", pgp_linked);
}
}
else
{
ok = false;
}
}
if(ok)
{
resp.setOk();
}
else
{
resp.setFail();
}
}
} // namespace resource_api

View File

@ -1,41 +0,0 @@
/*******************************************************************************
* libresapi/api/ForumHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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 FORUMHANDLER_H
#define FORUMHANDLER_H
#include "ResourceRouter.h"
class RsGxsForums;
namespace resource_api
{
class ForumHandler : public ResourceRouter
{
public:
ForumHandler(RsGxsForums* forums);
private:
RsGxsForums* mRsGxsForums;
void handleWildcard(Request& req, Response& resp);
};
} // namespace resource_api
#endif // FORUMHANDLER_H

View File

@ -1,72 +0,0 @@
/*******************************************************************************
* libresapi/api/GetPluginInterfaces.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "GetPluginInterfaces.h"
#include <retroshare/rsplugin.h>
#include <retroshare/rsmsgs.h>
#include <retroshare/rsturtle.h>
#include <retroshare/rsdisc.h>
#include <retroshare/rsdht.h>
#include <retroshare/rsnotify.h>
#include <retroshare/rsservicecontrol.h>
#include <retroshare/rsidentity.h>
#include <retroshare/rsgxscircles.h>
#include <retroshare/rsgxsforums.h>
#include <retroshare/rsgxschannels.h>
namespace resource_api{
bool getPluginInterfaces(RsPlugInInterfaces& interfaces)
{
// when rsPlugins is null, then rs was not started
if(rsPlugins == 0)
return false;
interfaces.mFiles = rsFiles;
interfaces.mPeers = rsPeers;
interfaces.mMsgs = rsMsgs;
interfaces.mTurtle = rsTurtle;
interfaces.mDisc = rsDisc;
interfaces.mDht = rsDht;
interfaces.mNotify = rsNotify;
interfaces.mServiceControl = rsServiceControl;
interfaces.mPluginHandler = rsPlugins;
// gxs
interfaces.mGxsDir = "";
interfaces.mIdentity = rsIdentity;
// not exposed with global variable, can't get it
interfaces.mRsNxsNetMgr = 0;
// same as identity service, but different interface
interfaces.mGxsIdService = 0;
//
interfaces.mGxsCirlces = 0;
// not exposed with global variable
interfaces.mPgpAuxUtils = 0;
interfaces.mGxsForums = rsGxsForums;
interfaces.mGxsChannels = rsGxsChannels;
return true;
}
} // namespace resource_api

View File

@ -1,31 +0,0 @@
/*******************************************************************************
* libresapi/api/GetPluginInterfaces.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
class RsPlugInInterfaces;
namespace resource_api{
// populates the given RsPlugInInterfaces object with pointers from gloabl variables like rsPeers, rsMsgs, rsFiles...
bool getPluginInterfaces(RsPlugInInterfaces& interfaces);
} // namespace resource_api

View File

@ -1,6 +0,0 @@
#pragma once
#include <retroshare/rsgxsifacetypes.h>
// operators for rsgxsgrpmeta and rsgxsmsgmeta
//

View File

@ -1,148 +0,0 @@
/*******************************************************************************
* libresapi/api/GxsResponseTask.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "GxsResponseTask.h"
#include "Operators.h"
namespace resource_api
{
GxsResponseTask::GxsResponseTask(RsIdentity *id_service, RsTokenService *token_service):
mIdService(id_service), mTokenService(token_service),
mDone(false)
{
}
bool GxsResponseTask::doWork(Request &req, Response &resp)
{
bool ready = true;
// check if gxs requests are ready
if(mTokenService && !mWaitingTokens.empty())
{
for(std::vector<uint32_t>::iterator vit = mWaitingTokens.begin(); vit != mWaitingTokens.end(); ++vit)
{
uint8_t status = mTokenService->requestStatus(*vit);
if(status != RsTokenService::COMPLETE)
{
ready = false;
}
if(status == RsTokenService::FAILED)
{
std::cerr << "GxsResponseTask::doWork() Error: token failed. aborting." << std::endl;
resp.setFail("GxsResponseTask::doWork() Error: token failed.");
return false; // don't continue
}
}
}
if(mIdService == 0)
{
std::cerr << "GxsResponseTask::doWork() ERROR: constucted with idservice = 0. Fix your code or report this bug." << std::endl;
resp.setFail("GxsResponseTask::doWork() ERROR: constucted with idservice = 0. Fix your code or report this bug.");
return false; // don't continue
}
// check if we have identities to fetch
bool more = true;
while(!mIdentitiesToFetch.empty() && more)
{
// there are two methods to fetch identity data:
// - request gxs group, get token, get group
// - the direct way where we may have to wait, but identities will cache the result
// if we need to get many identuties, then we may flush the cache
// but if we reaquest the groups, no caching is done on the rs side (OS will cache the identities file)
// it has to be measured what is better
RsGxsId id = mIdentitiesToFetch.back();
RsIdentityDetails details;
if(mIdService->getIdDetails(id, details))
{
mIdentitiesToFetch.pop_back();
mIdentityDetails.push_back(details);
}
else
{
more = false; // pause when an id failed, to give the service time tim fetch the data
ready = false;
// TODO: remove identities which failed many times from list, to avoid blocking when ids fail
}
}
if(!ready)
return true; // want to continue later
mWaitingTokens.clear();
mIdentitiesToFetch.clear();
gxsDoWork(req, resp);
if(mDone) return false;
else return true;
}
void GxsResponseTask::addWaitingToken(uint32_t token)
{
if(mTokenService)
mWaitingTokens.push_back(token);
else
std::cerr << "GxsResponseTask::addWaitingToken() ERROR: constructed with tokenservice=0. Unable to handle token processing. Fix your code or report this bug." << std::endl;
}
void GxsResponseTask::done()
{
mDone = true;
}
void GxsResponseTask::requestGxsId(RsGxsId id)
{
mIdentitiesToFetch.push_back(id);
}
void GxsResponseTask::streamGxsId(RsGxsId id, StreamBase &stream)
{
// will see if this works or if we have to use an index
for(std::vector<RsIdentityDetails>::iterator vit = mIdentityDetails.begin();
vit != mIdentityDetails.end(); ++vit)
{
if(vit->mId == id)
{
stream << makeKeyValueReference("id", id)
<< makeKeyValueReference("gxs_id", id)
<< makeKeyValueReference("flags", vit->mFlags)
<< makeKeyValueReference("name", vit->mNickname);
return;
}
}
}
std::string GxsResponseTask::getName(RsGxsId id)
{
for(std::vector<RsIdentityDetails>::iterator vit = mIdentityDetails.begin();
vit != mIdentityDetails.end(); ++vit)
{
if(vit->mId == id)
return vit->mNickname;
}
std::cerr << "Warning: identity not found in GxsResponseTask::getName(). This is probably a bug. You must call GxsResponseTask::requestGxsId() before you can get the name." << std::endl;
return "";
}
} // namespace resource_api

View File

@ -1,72 +0,0 @@
/*******************************************************************************
* libresapi/api/GxsResponseTask.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ApiTypes.h"
#include <retroshare/rsidentity.h>
namespace resource_api
{
// parent class for all responses that use the gxs backend
// this class implements the polling for gxs-tokens
// child classes pass gxs tokens to this class
// question: should this class be able to handle tokens from different services?
// then we would have to store a pointer to the token service for ever token
class GxsResponseTask: public ResponseTask
{
public:
// token service is allowed to be null if no token functions are wanted
GxsResponseTask(RsIdentity* id_service, RsTokenService* token_service = 0);
virtual bool doWork(Request &req, Response& resp);
protected:
// this method gets called when all the pending tokens have either status ok or fail
// (= when the requests for these tokens are processed)
// how will the child class find out if a request failed?
// idea: don't call gxsDoWork() when a request failed, instead set the api response to fail
// con: then the child class has no way to tell the outside world which request failed
// pro: child class gets simpler, because no special error handling is required
// implement this in a child class
virtual void gxsDoWork(Request& req, Response& resp) = 0;
// call this to wait for tokens before the next call to gxsDoWork()
void addWaitingToken(uint32_t token);
// call this to end the task
void done();
// request name for gxs id
void requestGxsId(RsGxsId id);
// call stream function in the next cycle, then the names are available
void streamGxsId(RsGxsId id, StreamBase& stream);
std::string getName(RsGxsId id);
private:
RsIdentity* mIdService;
RsTokenService* mTokenService;
std::vector<uint32_t> mWaitingTokens;
bool mDone;
std::vector<RsGxsId> mIdentitiesToFetch;
std::vector<RsIdentityDetails> mIdentityDetails;
};
} // namespace resource_api

View File

@ -1,722 +0,0 @@
/*******************************************************************************
* libresapi/api/IdentityHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright (C) 2015 electron128 <electron128@yahoo.com> *
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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 "IdentityHandler.h"
#include <retroshare/rsidentity.h>
#include <retroshare/rspeers.h>
#include <util/radix64.h>
#include <time.h>
#include "Operators.h"
#include "ApiTypes.h"
#include "GxsResponseTask.h"
#ifndef WINDOWS_SYS
#include "unistd.h"
#endif
namespace resource_api
{
class SendIdentitiesListTask: public GxsResponseTask
{
public:
SendIdentitiesListTask(RsIdentity* idservice, std::list<RsGxsId> ids, StateToken state):
GxsResponseTask(idservice, 0), mStateToken(state)
{
for(std::list<RsGxsId>::iterator vit = ids.begin(); vit != ids.end(); ++vit)
{
requestGxsId(*vit);
mIds.push_back(*vit);// convert from list to vector
}
}
private:
std::vector<RsGxsId> mIds;
StateToken mStateToken;
protected:
virtual void gxsDoWork(Request& /*req*/, Response &resp)
{
resp.mDataStream.getStreamToMember();
for(std::vector<RsGxsId>::iterator vit = mIds.begin(); vit != mIds.end(); ++vit)
{
streamGxsId(*vit, resp.mDataStream.getStreamToMember());
}
resp.mStateToken = mStateToken;
resp.setOk();
done();
}
};
class CreateIdentityTask: public GxsResponseTask
{
public:
CreateIdentityTask(RsIdentity* idservice):
GxsResponseTask(idservice, idservice->getTokenService()), mState(BEGIN), mToken(0), mRsIdentity(idservice){}
private:
enum State {BEGIN, WAIT_ACKN, WAIT_ID};
State mState;
uint32_t mToken;
RsIdentity* mRsIdentity;
RsGxsId mId;
protected:
virtual void gxsDoWork(Request &req, Response &resp)
{
switch(mState)
{
case BEGIN:{
RsIdentityParameters params;
req.mStream
<< makeKeyValueReference("name", params.nickname)
<< makeKeyValueReference("pgp_linked", params.isPgpLinked);
std::string avatar;
req.mStream << makeKeyValueReference("avatar", avatar);
std::vector<uint8_t> avatar_data = Radix64::decode(avatar);
uint8_t *p_avatar_data = &avatar_data[0];
uint32_t size = avatar_data.size();
params.mImage.clear();
params.mImage.copy(p_avatar_data, size);
if(params.nickname == "")
{
resp.setFail("name can't be empty");
done();
return;
}
mRsIdentity->createIdentity(mToken, params);
addWaitingToken(mToken);
mState = WAIT_ACKN;
break;
}
case WAIT_ACKN:{
RsGxsGroupId grpId;
if(!mRsIdentity->acknowledgeGrp(mToken, grpId))
{
resp.setFail("acknowledge of group id failed");
done();
return;
}
mId = RsGxsId(grpId);
requestGxsId(mId);
mState = WAIT_ID;
break;
}
case WAIT_ID:
streamGxsId(mId, resp.mDataStream);
resp.setOk();
done();
}
}
};
class DeleteIdentityTask : public GxsResponseTask
{
public:
DeleteIdentityTask(RsIdentity* idservice) :
GxsResponseTask(idservice, idservice->getTokenService()),
mToken(0),
mRsIdentity(idservice)
{}
protected:
virtual void gxsDoWork(Request &req, Response & /* resp */)
{
RsGxsIdGroup group;
std::string gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
group.mMeta.mGroupId = RsGxsGroupId(gxs_id);
mRsIdentity->deleteIdentity(mToken, group);
addWaitingToken(mToken);
done();
return;
}
private:
uint32_t mToken;
RsIdentity* mRsIdentity;
RsGxsId mId;
};
IdentityHandler::IdentityHandler(StateTokenServer *sts, RsNotify *notify,
RsIdentity *identity) :
mStateTokenServer(sts), mNotify(notify), mRsIdentity(identity),
mMtx("IdentityHandler Mtx"), mStateToken(sts->getNewToken())
{
mNotify->registerNotifyClient(this);
addResourceHandler("*", this, &IdentityHandler::handleWildcard);
addResourceHandler("own", this, &IdentityHandler::handleOwn);
addResourceHandler("own_ids", this, &IdentityHandler::handleOwnIdsRequest);
addResourceHandler("notown_ids", this,
&IdentityHandler::handleNotOwnIdsRequest);
addResourceHandler("export_key", this, &IdentityHandler::handleExportKey);
addResourceHandler("import_key", this, &IdentityHandler::handleImportKey);
addResourceHandler("add_contact", this, &IdentityHandler::handleAddContact);
addResourceHandler("remove_contact", this, &IdentityHandler::handleRemoveContact);
addResourceHandler("create_identity", this, &IdentityHandler::handleCreateIdentity);
addResourceHandler("delete_identity", this, &IdentityHandler::handleDeleteIdentity);
addResourceHandler("get_identity_details", this, &IdentityHandler::handleGetIdentityDetails);
addResourceHandler("get_avatar", this, &IdentityHandler::handleGetAvatar);
addResourceHandler("set_avatar", this, &IdentityHandler::handleSetAvatar);
addResourceHandler("set_ban_node", this, &IdentityHandler::handleSetBanNode);
addResourceHandler("set_opinion", this, &IdentityHandler::handleSetOpinion);
}
IdentityHandler::~IdentityHandler()
{
mNotify->unregisterNotifyClient(this);
}
void IdentityHandler::notifyGxsChange(const RsGxsChanges &changes)
{
RS_STACK_MUTEX(mMtx);
// if changes come from identity service, invalidate own state token
if(changes.mService == mRsIdentity->getTokenService())
mStateTokenServer->replaceToken(mStateToken);
}
void IdentityHandler::handleWildcard(Request & /*req*/, Response &resp)
{
bool ok = true;
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
uint32_t token;
mRsIdentity->getTokenService()->requestGroupInfo(
token, RS_TOKREQ_ANSTYPE_DATA, opts);
time_t timeout = time(NULL)+10;
uint8_t rStatus = mRsIdentity->getTokenService()->requestStatus(token);
while( rStatus != RsTokenService::COMPLETE &&
rStatus != RsTokenService::FAILED &&
time(NULL) < timeout )
{
usleep(50*1000);
rStatus = mRsIdentity->getTokenService()->requestStatus(token);
}
if(rStatus == RsTokenService::COMPLETE)
{
std::vector<RsGxsIdGroup> grps;
ok &= mRsIdentity->getGroupData(token, grps);
for( std::vector<RsGxsIdGroup>::iterator vit = grps.begin();
vit != grps.end(); ++vit )
{
RsGxsIdGroup& grp = *vit;
/* electron: not very happy about this, i think the flags should
* stay hidden in rsidentities */
bool own = (grp.mMeta.mSubscribeFlags &
GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
bool pgp_linked = (grp.mMeta.mGroupFlags &
RSGXSID_GROUPFLAG_REALID_kept_for_compatibility);
resp.mDataStream.getStreamToMember()
#warning Gioacchino Mazzurco 2017-03-24: @deprecated using "id" as key can cause problems in some JS based \
languages like Qml @see gxs_id instead
<< makeKeyValueReference("id", grp.mMeta.mGroupId)
<< makeKeyValueReference("gxs_id", grp.mMeta.mGroupId)
<< makeKeyValueReference("pgp_id",grp.mPgpId )
<< makeKeyValueReference("name", grp.mMeta.mGroupName)
<< makeKeyValueReference("contact", grp.mIsAContact)
<< makeKeyValueReference("own", own)
<< makeKeyValueReference("pgp_linked", pgp_linked)
<< makeKeyValueReference("is_contact", grp.mIsAContact);
}
}
else ok = false;
if(ok) resp.setOk();
else resp.setFail();
}
void IdentityHandler::handleNotOwnIdsRequest(Request & /*req*/, Response &resp)
{
bool ok = true;
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
uint32_t token;
mRsIdentity->getTokenService()->requestGroupInfo(
token, RS_TOKREQ_ANSTYPE_DATA, opts);
time_t timeout = time(NULL)+10;
uint8_t rStatus = mRsIdentity->getTokenService()->requestStatus(token);
while( rStatus != RsTokenService::COMPLETE &&
rStatus != RsTokenService::FAILED &&
time(NULL) < timeout )
{
usleep(50*1000);
rStatus = mRsIdentity->getTokenService()->requestStatus(token);
}
if(rStatus == RsTokenService::COMPLETE)
{
std::vector<RsGxsIdGroup> grps;
ok &= mRsIdentity->getGroupData(token, grps);
for(std::vector<RsGxsIdGroup>::iterator vit = grps.begin();
vit != grps.end(); vit++)
{
RsGxsIdGroup& grp = *vit;
if(!(grp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN))
{
bool pgp_linked = (
grp.mMeta.mGroupFlags &
RSGXSID_GROUPFLAG_REALID_kept_for_compatibility );
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("gxs_id", grp.mMeta.mGroupId)
<< makeKeyValueReference("pgp_id",grp.mPgpId )
<< makeKeyValueReference("name", grp.mMeta.mGroupName)
<< makeKeyValueReference("pgp_linked", pgp_linked)
<< makeKeyValueReference("is_contact", grp.mIsAContact);
}
}
}
else ok = false;
if(ok) resp.setOk();
else resp.setFail();
}
void IdentityHandler::handleOwnIdsRequest(Request & /*req*/, Response &resp)
{
bool ok = true;
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
uint32_t token;
mRsIdentity->getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts);
time_t start = time(NULL);
while((mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::COMPLETE)
&&(mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))
)
{
#ifdef WINDOWS_SYS
Sleep(500);
#else
usleep(500*1000);
#endif
}
if(mRsIdentity->getTokenService()->requestStatus(token) == RsTokenService::COMPLETE)
{
std::vector<RsGxsIdGroup> grps;
ok &= mRsIdentity->getGroupData(token, grps);
for(std::vector<RsGxsIdGroup>::iterator vit = grps.begin(); vit != grps.end(); vit++)
{
RsGxsIdGroup& grp = *vit;
//electron: not very happy about this, i think the flags should stay hidden in rsidentities
if(vit->mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)
{
bool pgp_linked = (grp.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility ) ;
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("own_gxs_id", grp.mMeta.mGroupId)
<< makeKeyValueReference("pgp_id",grp.mPgpId )
<< makeKeyValueReference("name", grp.mMeta.mGroupName)
<< makeKeyValueReference("pgp_linked", pgp_linked);
}
}
}
else
ok = false;
if(ok) resp.setOk();
else resp.setFail();
}
void IdentityHandler::handleAddContact(Request& req, Response& resp)
{
std::string gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
mRsIdentity->setAsRegularContact(RsGxsId(gxs_id), true);
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
mStateTokenServer->replaceToken(mStateToken);
}
resp.setOk();
}
void IdentityHandler::handleRemoveContact(Request& req, Response& resp)
{
std::string gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
mRsIdentity->setAsRegularContact(RsGxsId(gxs_id), false);
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
mStateTokenServer->replaceToken(mStateToken);
}
resp.setOk();
}
void IdentityHandler::handleGetIdentityDetails(Request& req, Response& resp)
{
std::string gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
uint32_t token;
std::list<RsGxsGroupId> groupIds;
groupIds.push_back(RsGxsGroupId(gxs_id));
mRsIdentity->getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds);
time_t start = time(NULL);
while((mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::COMPLETE)
&&(mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))
)
{
#ifdef WINDOWS_SYS
Sleep(500);
#else
usleep(500*1000);
#endif
}
RsGxsIdGroup data;
std::vector<RsGxsIdGroup> datavector;
if (!mRsIdentity->getGroupData(token, datavector))
{
resp.setFail();
return;
}
if(datavector.empty())
{
resp.setFail();
return;
}
data = datavector[0];
resp.mDataStream << makeKeyValue("gxs_name", data.mMeta.mGroupName);
resp.mDataStream << makeKeyValue("gxs_id", data.mMeta.mGroupId.toStdString());
resp.mDataStream << makeKeyValue("pgp_id_known", data.mPgpKnown);
resp.mDataStream << makeKeyValue("pgp_id", data.mPgpId.toStdString());
std::string pgp_name;
if (data.mPgpKnown)
{
RsPeerDetails details;
rsPeers->getGPGDetails(data.mPgpId, details);
pgp_name = details.name;
}
else
{
if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)
pgp_name = "[Unknown node]";
else
pgp_name = "Anonymous Id";
}
resp.mDataStream << makeKeyValue("pgp_name", pgp_name);
resp.mDataStream << makeKeyValue("last_usage", (uint32_t)data.mLastUsageTS);
bool isAnonymous = false;
if(!data.mPgpKnown)
{
if (!(data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility))
isAnonymous = true;
}
resp.mDataStream << makeKeyValue("anonymous", isAnonymous);
bool isOwnId = (data.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
resp.mDataStream << makeKeyValue("own", isOwnId);
std::string type;
if(isOwnId)
{
if (data.mPgpKnown && !data.mPgpId.isNull())
type = "Identity owned by you, linked to your Retroshare node";
else
type = "Anonymous identity, owned by you";
}
else if (data.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)
{
if (data.mPgpKnown)
{
if (rsPeers->isGPGAccepted(data.mPgpId))
type = "Linked to a friend Retroshare node";
else
type = "Linked to a known Retroshare node";
}
else
type = "Linked to unknown Retroshare node";
}
else
type = "Anonymous identity";
resp.mDataStream << makeKeyValue("type", type);
resp.mDataStream << makeKeyValue("bannned_node", rsReputations->isNodeBanned(data.mPgpId));
RsReputationInfo info;
rsReputations->getReputationInfo(RsGxsId(data.mMeta.mGroupId), data.mPgpId, info);
resp.mDataStream << makeKeyValue("friends_positive_votes", info.mFriendsPositiveVotes);
resp.mDataStream << makeKeyValue("friends_negative_votes", info.mFriendsNegativeVotes);
resp.mDataStream << makeKeyValue("overall_reputation_level", (int)info.mOverallReputationLevel);
resp.mDataStream << makeKeyValue("own_opinion", (int)info.mOwnOpinion);
RsIdentityDetails details;
mRsIdentity->getIdDetails(RsGxsId(data.mMeta.mGroupId), details);
std::string base64Avatar;
Radix64::encode(details.mAvatar.mData, details.mAvatar.mSize, base64Avatar);
resp.mDataStream << makeKeyValue("avatar", base64Avatar);
StreamBase& usagesStream = resp.mDataStream.getStreamToMember("usages");
usagesStream.getStreamToMember();
for(auto it(details.mUseCases.begin()); it != details.mUseCases.end(); ++it)
{
usagesStream.getStreamToMember()
<< makeKeyValue("usage_time", (uint32_t)data.mLastUsageTS)
<< makeKeyValue("usage_service", (int)(it->first.mServiceId))
<< makeKeyValue("usage_case", (int)(it->first.mUsageCode));
}
resp.setOk();
}
void IdentityHandler::handleSetAvatar(Request& req, Response& resp)
{
std::string gxs_id;
std::string avatar;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
req.mStream << makeKeyValueReference("avatar", avatar);
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
uint32_t token;
std::list<RsGxsGroupId> groupIds;
groupIds.push_back(RsGxsGroupId(gxs_id));
mRsIdentity->getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds);
time_t start = time(NULL);
while((mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::COMPLETE)
&&(mRsIdentity->getTokenService()->requestStatus(token) != RsTokenService::FAILED)
&&((time(NULL) < (start+10)))
)
{
#ifdef WINDOWS_SYS
Sleep(500);
#else
usleep(500*1000);
#endif
}
RsGxsIdGroup data;
std::vector<RsGxsIdGroup> datavector;
if (!mRsIdentity->getGroupData(token, datavector))
{
resp.setFail();
return;
}
if(datavector.empty())
{
resp.setFail();
return;
}
data = datavector[0];
if(!avatar.empty())
{
std::vector<uint8_t> avatar_data = Radix64::decode(avatar);
uint8_t *p_avatar_data = &avatar_data[0];
uint32_t size = avatar_data.size();
data.mImage.clear();
data.mImage.copy(p_avatar_data, size);
std::string base64Avatar;
Radix64::encode(data.mImage.mData, data.mImage.mSize, base64Avatar);
resp.mDataStream << makeKeyValue("avatar", base64Avatar);
}
else
data.mImage.clear();
uint32_t dummyToken = 0;
mRsIdentity->updateIdentity(dummyToken, data);
resp.setOk();
}
void IdentityHandler::handleGetAvatar(Request& req, Response& resp)
{
std::string gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
RsIdentityDetails details;
bool got = mRsIdentity->getIdDetails(RsGxsId(gxs_id), details);
std::string base64Avatar;
Radix64::encode(details.mAvatar.mData, details.mAvatar.mSize, base64Avatar);
resp.mDataStream << makeKeyValue("avatar", base64Avatar);
if(got)
resp.setOk();
else
resp.setFail();
}
void IdentityHandler::handleSetBanNode(Request& req, Response& resp)
{
std::string pgp_id;
req.mStream << makeKeyValueReference("pgp_id", pgp_id);
RsPgpId pgpId(pgp_id);
bool banned_node;
req.mStream << makeKeyValueReference("banned_node", banned_node);
rsReputations->banNode(pgpId, banned_node);
resp.setOk();
}
void IdentityHandler::handleSetOpinion(Request& req, Response& resp)
{
std::string gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
RsGxsId gxsId(gxs_id);
int own_opinion;
req.mStream << makeKeyValueReference("own_opinion", own_opinion);
RsOpinion opinion;
switch(own_opinion)
{
case 0: opinion = RsOpinion::NEGATIVE; break;
case 1: opinion = RsOpinion::NEUTRAL; break;
case 2: opinion = RsOpinion::POSITIVE; break;
default:
resp.setFail();
return;
}
rsReputations->setOwnOpinion(gxsId, opinion);
resp.setOk();
}
ResponseTask* IdentityHandler::handleOwn(Request & /* req */, Response &resp)
{
StateToken state;
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
state = mStateToken;
}
std::list<RsGxsId> ids;
if(mRsIdentity->getOwnIds(ids))
return new SendIdentitiesListTask(mRsIdentity, ids, state);
resp.mDataStream.getStreamToMember();
resp.setWarning("identities not loaded yet");
return 0;
}
ResponseTask* IdentityHandler::handleCreateIdentity(Request & /* req */, Response & /* resp */)
{
return new CreateIdentityTask(mRsIdentity);
}
void IdentityHandler::handleExportKey(Request& req, Response& resp)
{
RsGxsId gxs_id;
req.mStream << makeKeyValueReference("gxs_id", gxs_id);
std::string radix;
time_t timeout = time(NULL)+2;
bool found = mRsIdentity->serialiseIdentityToMemory(gxs_id, radix);
while(!found && time(nullptr) < timeout)
{
usleep(5000);
found = mRsIdentity->serialiseIdentityToMemory(gxs_id, radix);
}
if(found)
{
resp.mDataStream << makeKeyValueReference("gxs_id", gxs_id)
<< makeKeyValueReference("radix", radix);
resp.setOk();
return;
}
resp.setFail();
}
void IdentityHandler::handleImportKey(Request& req, Response& resp)
{
std::string radix;
req.mStream << makeKeyValueReference("radix", radix);
RsGxsId gxs_id;
if(mRsIdentity->deserialiseIdentityFromMemory(radix, &gxs_id))
{
resp.mDataStream << makeKeyValueReference("gxs_id", gxs_id)
<< makeKeyValueReference("radix", radix);
resp.setOk();
return;
}
resp.setFail();
}
ResponseTask* IdentityHandler::handleDeleteIdentity(Request& /*req*/,
Response& /*resp*/)
{ return new DeleteIdentityTask(mRsIdentity); }
} // namespace resource_api

View File

@ -1,76 +0,0 @@
/*******************************************************************************
* libresapi/api/IdentityHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright (C) 2015 electron128 <electron128@yahoo.com> *
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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/>. *
* *
*******************************************************************************/
#pragma once
#include <retroshare/rsnotify.h>
#include <util/rsthreads.h>
#include "ResourceRouter.h"
#include "StateTokenServer.h"
struct RsIdentity;
namespace resource_api
{
class IdentityHandler: public ResourceRouter, NotifyClient
{
public:
IdentityHandler(StateTokenServer* sts, RsNotify* notify, RsIdentity* identity);
virtual ~IdentityHandler();
// from NotifyClient
// note: this may get called from foreign threads
virtual void notifyGxsChange(const RsGxsChanges &changes);
private:
void handleWildcard(Request& req, Response& resp);
void handleNotOwnIdsRequest(Request& req, Response& resp);
void handleOwnIdsRequest(Request& req, Response& resp);
void handleExportKey(Request& req, Response& resp);
void handleImportKey(Request& req, Response& resp);
void handleAddContact(Request& req, Response& resp);
void handleRemoveContact(Request& req, Response& resp);
void handleGetIdentityDetails(Request& req, Response& resp);
void handleGetAvatar(Request& req, Response& resp);
void handleSetAvatar(Request& req, Response& resp);
void handleSetBanNode(Request& req, Response& resp);
void handleSetOpinion(Request& req, Response& resp);
ResponseTask *handleOwn(Request& req, Response& resp);
ResponseTask *handleCreateIdentity(Request& req, Response& resp);
ResponseTask *handleDeleteIdentity(Request& req, Response& resp);
StateTokenServer* mStateTokenServer;
RsNotify* mNotify;
RsIdentity* mRsIdentity;
RsMutex mMtx;
StateToken mStateToken; // mutex protected
};
} // namespace resource_api

View File

@ -1,545 +0,0 @@
/*******************************************************************************
* libresapi/api/jsonStream.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "JsonStream.h"
#include <iostream>
namespace resource_api
{
JsonStream::JsonStream():
mSerialise(true), mDataType(TYPE_UNDEFINED), mArrayNextRead(0), mIsOk(true), mChild(NULL)
{
}
JsonStream::~JsonStream()
{
deleteCurrentChild();
}
void JsonStream::setJsonString(std::string jsonStr)
{
mRawString = jsonStr;
// have to delay the deserialisation, because this stream can also be a raw data stream without json
// can find this out when others atucally call the operators
mSerialise = false;
}
std::string JsonStream::getJsonString()
{
deleteCurrentChild();
if(mIsOk)
{
switch(mDataType)
{
case TYPE_UNDEFINED:
return "";
case TYPE_ARRAY:
return json::Serialize(mArray);
case TYPE_OBJECT:
return json::Serialize(mObject);
case TYPE_RAW:
return mRawString;
default:
return "";
}
}
std::cerr << "JsonStream::getJsonString() Warning: stream not ok, will return empty string." << std::endl;
return "";
}
void JsonStream::switchToDeserialisation()
{
deleteCurrentChild();
mSerialise = false;
}
//----------Stream Interface ---------------
//----------Array---------------
StreamBase& JsonStream::operator<<(ValueReference<bool> value)
{
if(serialise())
{
setType(TYPE_ARRAY);
mArray.push_back(value.value);
}
else
{
if(checkDeserialisation() && arrayBoundsOk())
{
valueToBool(mArray[mArrayNextRead], value.value);
mArrayNextRead++;
}
}
return *this;
}
StreamBase& JsonStream::operator<<(ValueReference<int> value)
{
if(serialise())
{
setType(TYPE_ARRAY);
mArray.push_back(value.value);
}
else
{
if(checkDeserialisation() && arrayBoundsOk())
{
valueToInt(mArray[mArrayNextRead], value.value);
mArrayNextRead++;
}
}
return *this;
}
StreamBase& JsonStream::operator<<(ValueReference<double> value)
{
if(serialise())
{
setType(TYPE_ARRAY);
mArray.push_back(value.value);
}
else
{
if(checkDeserialisation() && arrayBoundsOk())
{
valueToDouble(mArray[mArrayNextRead], value.value);
mArrayNextRead++;
}
}
return *this;
}
StreamBase& JsonStream::operator<<(ValueReference<std::string> value)
{
if(serialise())
{
setType(TYPE_ARRAY);
mArray.push_back(value.value);
}
else
{
if(checkDeserialisation() && arrayBoundsOk())
{
valueToString(mArray[mArrayNextRead], value.value);
mArrayNextRead++;
}
}
return *this;
}
StreamBase& JsonStream::getStreamToMember()
{
setType(TYPE_ARRAY);
deleteCurrentChild();
mChild = new JsonStream();
if(!serialise())
{
if(checkDeserialisation() && arrayBoundsOk())
{
mChild->mValue = mArray[mArrayNextRead];
mChild->mSerialise = false;
mArrayNextRead++;
}
}
return *mChild;
}
//----------Object---------------
StreamBase& JsonStream::operator<<(KeyValueReference<bool> keyValue)
{
if(serialise())
{
setType(TYPE_OBJECT);
mObject[keyValue.key] = keyValue.value;
}
else
{
if(checkDeserialisation() && checkObjectMember(keyValue.key))
{
valueToBool(mObject[keyValue.key], keyValue.value);
}
}
return *this;
}
StreamBase& JsonStream::operator<<(KeyValueReference<int> keyValue)
{
if(serialise())
{
setType(TYPE_OBJECT);
mObject[keyValue.key] = keyValue.value;
}
else
{
if(checkDeserialisation() && checkObjectMember(keyValue.key))
{
valueToInt(mObject[keyValue.key], keyValue.value);
}
}
return *this;
}
StreamBase& JsonStream::operator<<(KeyValueReference<double> keyValue)
{
if(serialise())
{
setType(TYPE_OBJECT);
mObject[keyValue.key] = keyValue.value;
}
else
{
if(checkDeserialisation() && checkObjectMember(keyValue.key))
{
valueToDouble(mObject[keyValue.key], keyValue.value);
}
}
return *this;
}
StreamBase& JsonStream::operator<<(KeyValueReference<std::string> keyValue)
{
if(serialise())
{
setType(TYPE_OBJECT);
mObject[keyValue.key] = keyValue.value;
}
else
{
if(checkDeserialisation() && checkObjectMember(keyValue.key))
{
valueToString(mObject[keyValue.key], keyValue.value);
}
}
return *this;
}
// usefull if the new object member should be an array or object
// the reference should be at least valid until another method of this class gets called
StreamBase& JsonStream::getStreamToMember(std::string name)
{
setType(TYPE_OBJECT);
deleteCurrentChild();
mChildKey = name;
mChild = new JsonStream();
if(!serialise())
{
mChild->mSerialise = false;
if(checkDeserialisation() && checkObjectMember(name))
{
mChild->mValue = mObject[name];
}
}
return *mChild;
}
// make a binay data object (not a real object, just binary data)
StreamBase& JsonStream::operator<<(std::vector<uint8_t>& data)
{
if(serialise())
{
if((mDataType == TYPE_UNDEFINED)||(mDataType == TYPE_RAW))
{
mDataType = TYPE_RAW;
mRawString = std::string(data.begin(), data.end());
}
else
{
mErrorLog += "Error: trying to set raw data while the type of this object is already another type\n";
mIsOk = false;
}
}
else
{
if((mDataType == TYPE_UNDEFINED)||(mDataType == TYPE_RAW))
{
mDataType = TYPE_RAW;
data = std::vector<uint8_t>(mRawString.begin(), mRawString.end());
}
else
{
mErrorLog += "Error: trying to read raw data while the type of this object is already another type\n";
mIsOk = false;
}
}
return *this;
}
// return true if there are more members in this object/array
// useful for array reading
bool JsonStream::hasMore()
{
return arrayBoundsOk();
}
bool JsonStream::serialise()
{
return mSerialise;
}
bool JsonStream::isOK()
{
return mIsOk;
}
void JsonStream::setError()
{
mIsOk = false;
}
/*
void JsonStream::addLogMsg(std::string msg)
{}
*/
void JsonStream::addErrorMsg(std::string msg)
{
mErrorLog += msg;
}
std::string JsonStream::getLog()
{
return "not implemented yet";
}
std::string JsonStream::getErrorLog()
{
return mErrorLog;
}
bool JsonStream::isRawData()
{
return mDataType == TYPE_RAW;
}
std::string JsonStream::getRawData()
{
return mRawString;
}
void JsonStream::setType(DataType type)
{
if((mDataType == TYPE_UNDEFINED)||(mDataType == type))
{
mDataType = type;
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::setType() Error: type alread set to another type\n";
}
}
bool JsonStream::checkObjectMember(std::string key)
{
if(mDataType == TYPE_OBJECT)
{
if(mObject.HasKey(key))
{
return true;
}
else
{
mErrorLog += "JsonStream::checkObjectMember() Warning: missing key \""+key+"\"\n";
return false;
}
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::checkObjectMember() Error: type is not TYPE_OBJECT\n";
return false;
}
}
bool JsonStream::arrayBoundsOk()
{
if(checkDeserialisation())
{
if(mDataType == TYPE_ARRAY)
{
if(mArrayNextRead < mArray.size())
{
return true;
}
else
{
return false;
}
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::arrayBoundsOk() Error: type is not TYPE_ARRAY\n";
return false;
}
}
return false;
}
bool JsonStream::checkDeserialisation()
{
if(mDataType != TYPE_RAW)
{
if(mDataType == TYPE_UNDEFINED)
{
if((mValue.GetType() == json::NULLVal) && mRawString != "")
{
mValue = json::Deserialize(mRawString);
}
if(mValue.GetType() == json::ObjectVal)
{
mDataType = TYPE_OBJECT;
mObject = mValue;
return true;
}
else if(mValue.GetType() == json::ArrayVal)
{
mDataType = TYPE_ARRAY;
mArray = mValue;
return true;
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::checkDeserialisation() Error: deserialisation did not end with an object or array\n";
return false;
}
}
else
{
// already deserialised
return true;
}
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::checkDeserialisation() Error: type is TYPE_RAW\n";
return false;
}
}
void JsonStream::valueToBool(json::Value &value, bool &boolean)
{
if(value.GetType() == json::BoolVal)
{
boolean = value;
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::valueToBool() Error: wrong type\n";
}
}
void JsonStream::valueToInt(json::Value &value, int &integer)
{
if(value.GetType() == json::IntVal)
{
integer = value;
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::valueToInt() Error: wrong type\n";
}
}
void JsonStream::valueToDouble(json::Value &value, double &doubleVal)
{
if(value.IsNumeric())
{
doubleVal = value;
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::valueToDouble() Error: wrong type\n";
}
}
void JsonStream::valueToString(json::Value &value, std::string& str)
{
if(value.GetType() == json::StringVal)
{
str = value.ToString();
}
else
{
mIsOk = false;
mErrorLog += "JsonStream::valueToString() Error: wrong type\n";
}
}
void JsonStream::deleteCurrentChild()
{
if(mChild)
{
if(serialise())
{
if(mDataType == TYPE_ARRAY)
{
// don't add empty value
if(mChild->getJsonValue().GetType() != json::NULLVal)
mArray.push_back(mChild->getJsonValue());
}
else if(mDataType == TYPE_OBJECT)
{
mObject[mChildKey] = mChild->getJsonValue();
}
else
{
mErrorLog += "JsonStream::deleteCurrentChild() Error: cannot add child because own type is wrong\n";
}
}
else
{
// don't have to do anything for deserialisation
}
delete mChild;
mChild = NULL;
}
}
json::Value JsonStream::getJsonValue()
{
// remove the child and add it to own data
deleteCurrentChild();
switch(mDataType)
{
case TYPE_UNDEFINED:
return json::Value();
case TYPE_ARRAY:
return mArray;
case TYPE_OBJECT:
return mObject;
case TYPE_RAW:
return mRawString;
default:
return json::Value();
}
}
} // namespace resource_api

View File

@ -1,127 +0,0 @@
/*******************************************************************************
* libresapi/api/jsonStream.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ApiTypes.h"
#include "json.h"
namespace resource_api
{
class JsonStream: public StreamBase
{
public:
JsonStream();
virtual ~JsonStream();
void setJsonString(std::string jsonStr);
std::string getJsonString();
// it is possible to use this class as buffer
// first use as serialiser and fill with values
// then call this method to deserialise the values
void switchToDeserialisation();
//----------Stream Interface ---------------
// make an array
virtual StreamBase& operator<<(ValueReference<bool> value);
virtual StreamBase& operator<<(ValueReference<int> value);
virtual StreamBase& operator<<(ValueReference<double> value);
virtual StreamBase& operator<<(ValueReference<std::string> value);
// usefull if the new array member should be an array or object
// the reference should be at least valid until another method of this class gets called
virtual StreamBase& getStreamToMember();
// make an object
virtual StreamBase& operator<<(KeyValueReference<bool> keyValue);
virtual StreamBase& operator<<(KeyValueReference<int> keyValue);
virtual StreamBase& operator<<(KeyValueReference<double> keyValue);
virtual StreamBase& operator<<(KeyValueReference<std::string> keyValue);
// usefull if the new object member should be an array or object
// the reference should be at least valid until another method of this class gets called
virtual StreamBase& getStreamToMember(std::string name);
// make a binay data object (not a real object, just binary data)
// idea: can use vector.swap() to allow passing larger data items without copying
virtual StreamBase& operator<<(std::vector<uint8_t>& data);
// return true if there are more members in this object/array
// useful for array reading
virtual bool hasMore();
virtual bool serialise(); // let external operators find out they should serialise or deserialise
// return true if no serialisation/deserialisation error occoured
virtual bool isOK();
virtual void setError(); // let external operators set the failed bit
//virtual void addLogMsg(std::string msg); // mayb remove? (put log messages to error log einstead)
virtual void addErrorMsg(std::string msg);
virtual std::string getLog();
virtual std::string getErrorLog();
virtual bool isRawData();
virtual std::string getRawData();
private:
bool mSerialise;
enum DataType{ TYPE_UNDEFINED, TYPE_ARRAY, TYPE_OBJECT, TYPE_RAW };
// check if the current type is undefined
// if not check if the new type matches the old type
// if not set the error bit
void setType(DataType type);
DataType mDataType;
json::Value mValue;
json::Object mObject;
// check if we are and object
// check if this key exists
bool checkObjectMember(std::string key);
json::Array mArray;
size_t mArrayNextRead;
// check if we are an array
// check if next read is valid
// if not set error bit
bool arrayBoundsOk();
std::string mRawString;
bool mIsOk;
std::string mErrorLog;
// try serialisation and set error bit on error
bool checkDeserialisation();
// check if value has correct type
// if yes return the extracted value
// if not then set the error bit
void valueToBool(json::Value& value, bool& boolean);
void valueToInt(json::Value& value, int& integer);
void valueToDouble(json::Value& value, double& doubleVal);
void valueToString(json::Value& value, std::string& str);
void deleteCurrentChild();
json::Value getJsonValue();
JsonStream* mChild;
std::string mChildKey;
};
} // namespace resource_api

View File

@ -1,46 +0,0 @@
/*******************************************************************************
* libresapi/api/LivereloadHarder.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "LivereloadHandler.h"
namespace resource_api
{
LivereloadHandler::LivereloadHandler(StateTokenServer *sts):
mStateTokenServer(sts), mStateToken(sts->getNewToken())
{
addResourceHandler("*", this, &LivereloadHandler::handleWildcard);
addResourceHandler("trigger", this, &LivereloadHandler::handleTrigger);
}
void LivereloadHandler::handleWildcard(Request &/*req*/, Response &resp)
{
resp.mStateToken = mStateToken;
resp.setOk();
}
void LivereloadHandler::handleTrigger(Request &/*req*/, Response &resp)
{
mStateTokenServer->replaceToken(mStateToken);
resp.mStateToken = mStateToken;
resp.setOk();
}
} // namespace resource_api

View File

@ -1,44 +0,0 @@
/*******************************************************************************
* libresapi/api/LivereloadHarder.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
#include "StateTokenServer.h"
namespace resource_api
{
// very simple livereload system, integrated into the existing state token system
// the response to / is only a statetoken
// if /trigger is called, then the state token is invalidaten and replaced wiht a new one
class LivereloadHandler: public ResourceRouter
{
public:
LivereloadHandler(StateTokenServer* sts);
private:
void handleWildcard(Request& req, Response& resp);
void handleTrigger(Request& req, Response& resp);
StateTokenServer* mStateTokenServer;
StateToken mStateToken;
};
} // namespace resource_api

View File

@ -1,154 +0,0 @@
/*******************************************************************************
* libresapi/api/Operators.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "Operators.h"
namespace resource_api
{
StreamBase& operator <<(StreamBase& left, KeyValueReference<uint32_t> ref)
{
if(left.serialise())
{
uint32_t num = ref.value;
uint8_t digit;
std::string str;
while(num >= 10)
{
digit = num % 10;
num = num / 10;
str = (char)(digit + '0') + str;
}
str = (char)(num + '0') + str;
left << makeKeyValueReference(ref.key, str);
}
else
{
std::string str;
left << makeKeyValueReference(ref.key, str);
uint32_t num = 0;
for(std::string::iterator sit = str.begin(); sit != str.end(); sit++)
{
uint32_t numbefore = num;
num = num * 10;
if(num < numbefore)
{
left.addErrorMsg("operator for uint32_t to std::string: oveflow");
left.setError();
}
else if((*sit)<'0' || (*sit)>'9')
{
left.addErrorMsg("operator for uint32_t to std::string: invalid characters");
left.setError();
}
else
{
// everything ok, can add value
num += (*sit) - '0';
}
}
}
return left;
}
/*
template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
StreamBase& operator <<(StreamBase& left, t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID>& id)
{
if(left.serialise())
{
left << id.toStdString();
}
else
{
std::string str;
left << str;
}
return left;
}
*/
template<class T_ID>
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID>& ref)
{
if(left.serialise())
{
left << makeValueReference(ref.value.toStdString());
}
else
{
std::string str;
left << makeValueReference(str);
T_ID id(str);
if(id.isNull)
{
left.setError();
left.addErrorMsg("operator for retroshare id value: id is null\n");
}
ref.value = id;
}
return left;
}
// idea: have external operators which do the translation form different containers to basic operations
// better idea: take input iterators as arguments, will then work with everything which has an iterator
// but what about deserilisation?
template<template <class> class ContainerT, class ValueT>
StreamBase& operator<<(StreamBase& left, ContainerT<ValueT>& right)
{
if(left.serialise())
{
typename ContainerT<ValueT>::iterator vit;
for(vit = right.begin(); vit != right.end(); vit++)
{
left << ValueReference<ValueT>(*vit);
}
}
else
{
while(left.hasMore())
{
ValueReference<ValueT> ref;
left << ref;
right.push_back(ref.value);
}
}
return left;
}
// maybe like this:
template<class ItertatorT>
class Array
{
public:
Array(ItertatorT begin, ItertatorT end): begin(begin), end(end) {}
ItertatorT begin;
ItertatorT end;
};
} // namespace resource_api

View File

@ -1,114 +0,0 @@
/*******************************************************************************
* libresapi/api/Operators.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include <retroshare/rstypes.h>
#include "ApiTypes.h"
namespace resource_api
{
// note: pass the KeyValueReference and ValueReference objects by value to enable such things:
// stream << somefunc(); // can't get a reference to the return value of somefunc
// uint32_t to std::string with decimal numbers
StreamBase& operator <<(StreamBase& left, KeyValueReference<uint32_t> ref);
// convert retroshare ids to strings and back
//template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
//StreamBase& operator <<(StreamBase& left, t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID>& id);
// operators for retroshare ids
/*
template<class T_ID>
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID>& ref);
*/
template<class T_ID>
StreamBase& operator <<(StreamBase& left, KeyValueReference<T_ID> ref);
template<class T_ID>
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID> ref);
//template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
//StreamBase& operator <<(StreamBase& left, KeyValueReference<t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID> >& ref);
// implementations
// idea: each rs generic id type has a number
// put this number in front of the id data to make the ids type safe, even across languages
template<class T_ID>
StreamBase& operator <<(StreamBase& left, KeyValueReference<T_ID> ref)
//template<uint32_t ID_SIZE, bool ID_UPPER, uint32_t ID_ID>
//StreamBase& operator <<(StreamBase& left, KeyValueReference<t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID> >& ref)
{
if(left.serialise())
{
std::string idStr = ref.value.toStdString();
left << makeKeyValueReference(ref.key, idStr);
}
else
{
std::string str;
left << makeKeyValueReference(ref.key, str);
//t_RsGenericIdType<ID_SIZE, ID_UPPER, ID_ID> id(str);
T_ID id(str);
if(id.isNull())
{
left.setError();
left.addErrorMsg("operator for retroshare id keyValue: id is null\n");
}
ref.value = id;
}
return left;
}
template<class T_ID>
StreamBase& operator <<(StreamBase& left, ValueReference<T_ID> ref)
{
if(left.serialise())
{
std::string idStr = ref.value.toStdString();
left << makeValueReference(idStr);
}
else
{
std::string str;
left << makeValueReference(str);
T_ID id(str);
if(id.isNull())
{
left.setError();
left.addErrorMsg("operator for retroshare id Value: id is null\n");
}
ref.value = id;
}
return left;
}
} // namespace resource_api

View File

@ -1,110 +0,0 @@
#pragma once
/*
* libresapi
* Copyright (C) 2015 electron128 <electron128@yahoo.com>
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "ApiTypes.h"
namespace resource_api
{
// C must be a type with STL like iterator, a begin() and an end() method
// additionally a function id() which gives a unique value for every container element
// the type of the id should be string
// the type returned by dereferencing the iterator should have a stream operator for StreamBase
// the stream operator must not add an element "id", this is done by the pagination handler
template<class C>
void handlePaginationRequest(Request& req, Response& resp, C& data)
{
if(data.begin() == data.end()){
// set result type to list
resp.mDataStream.getStreamToMember();
resp.mDebug << "note: list is empty" << std::endl;
resp.setOk();
return;
}
std::string begin_after;
std::string last;
req.mStream << makeKeyValueReference("begin_after", begin_after)
<< makeKeyValueReference("last", last);
typename C::iterator it_first = data.begin();
if(begin_after != "begin" && begin_after != "")
{
while(it_first != data.end() && id(*it_first) != begin_after)
it_first++;
it_first++; // get after the specified element
if(it_first == data.end())
{
resp.setFail("Error: first id did not match any element");
return;
}
}
typename C::iterator it_last = data.begin();
if(last == "end" || last == "")
{
it_last = data.end();
}
else
{
while(it_last != data.end() && id(*it_last) != last)
it_last++;
if(it_last == data.end())
{
resp.setFail("Error: last id did not match any element");
return;
}
++it_last; // increment to get iterator to element after the last wanted element
}
/* G10h4ck: Guarded message count limitation with
* JSON_API_LIMIT_CHAT_MSG_COUNT_BY_DEFAULT as ATM it seems that handling a
* big bunch of messages hasn't been a problem for client apps, and even in
* that case the client can specify +begin_after+ and +last+ in the request,
* this way we don't make more difficult the life of those who just want get
* the whole list of chat messages that seems to be a common usecase
*/
#ifdef JSON_API_LIMIT_CHAT_MSG_COUNT_BY_DEFAULT
int count = 0;
#endif
for(typename C::iterator it = it_first; it != it_last; ++it)
{
StreamBase& stream = resp.mDataStream.getStreamToMember();
stream << *it;
stream << makeKeyValue("id", id(*it));
// todo: also handle the case when the last element is specified and the first element is begin
// then want to return the elements near the specified element
// G10h4ck: @see first comment about JSON_API_LIMIT_CHAT_MSG_COUNT_BY_DEFAULT
#ifdef JSON_API_LIMIT_CHAT_MSG_COUNT_BY_DEFAULT
++count;
if(count > 20)
{
resp.mDebug << "limited the number of returned items to 20";
break;
}
#endif
}
resp.setOk();
}
} // namespace resource_api

File diff suppressed because it is too large Load Diff

View File

@ -1,116 +0,0 @@
/*******************************************************************************
* libresapi/api/PeersHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright (C) 2015 electron128 <electron128@yahoo.com> *
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org> *
* *
* 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
#include "StateTokenServer.h"
#include "ChatHandler.h"
#include <retroshare/rsnotify.h>
#include <util/rsthreads.h>
class RsPeers;
class RsMsgs;
namespace resource_api
{
class PeersHandler: public ResourceRouter, NotifyClient, Tickable, public UnreadMsgNotify
{
public:
PeersHandler(StateTokenServer* sts, RsNotify* notify, RsPeers* peers, RsMsgs* msgs);
virtual ~PeersHandler();
// from NotifyClient
// note: this may get called from foreign threads
virtual void notifyListChange(int list, int type); // friends list change
virtual void notifyPeerStatusChanged(const std::string& /*peer_id*/, uint32_t /*state*/);
virtual void notifyPeerHasNewAvatar(std::string /*peer_id*/);
// from Tickable
virtual void tick();
// from UnreadMsgNotify
// ChatHandler calls this to tell us about unreadmsgs
// this allows to merge unread msgs info with the peers list
virtual void notifyUnreadMsgCountChanged(const RsPeerId& peer, uint32_t count);
private:
void handleWildcard(Request& req, Response& resp);
void handleAttemptConnection(Request& req, Response& resp);
void handleExamineCert(Request& req, Response& resp);
void handleGetStateString(Request& req, Response& resp);
void handleSetStateString(Request& req, Response& resp);
void handleGetCustomStateString(Request& req, Response& resp);
void handleSetCustomStateString(Request& req, Response& resp);
void handleGetNetworkOptions(Request& req, Response& resp);
void handleSetNetworkOptions(Request& req, Response& resp);
void handleGetPGPOptions(Request& req, Response& resp);
void handleSetPGPOptions(Request& req, Response& resp);
void handleGetNodeName(Request& req, Response& resp);
void handleGetNodeOptions(Request& req, Response& resp);
void handleSetNodeOptions(Request& req, Response& resp);
/**
* \brief Remove specific location from trusted nodes
*
* \param [in] req request from user either peer_id is needed.
* \param [out] resp response to user
*/
void handleRemoveNode(Request &req, Response &resp);
/**
* \brief Retrieve inactive user list before specific UNIX time
*
* \param [in] req request from user, datatime in UNIX timestamp.
* \param [in] resp response to request
* \return a pgp_id list.
*/
void handleGetInactiveUsers(Request &req, Response &resp);
/// Helper which ensures proper mutex locking
StateToken getCurrentStateToken();
StateTokenServer* mStateTokenServer;
RsNotify* mNotify;
RsPeers* mRsPeers;
RsMsgs* mRsMsgs; // required for avatar data
std::list<RsPeerId> mOnlinePeers;
uint32_t status;
std::string custom_state_string;
RsMutex mMtx;
StateToken mStateToken; // mutex protected
StateToken mStringStateToken; // mutex protected
StateToken mCustomStateToken; // mutex protected
std::map<RsPeerId, uint32_t> mUnreadMsgsCounts;
};
} // namespace resource_api

View File

@ -1,67 +0,0 @@
New programming interface for Retroshare
========================================
* access to libretroshare for webinterfaces, QML and scripting
* client - server architecture.
* network friendly: transport messages over high latency and low bandwidth networks
* multiple clients: can use scripting and webinterface at the same time
* simple packet format: no special serialiser required
* simple protocol: one request creates one response. A requets does not depend on a previous request.
* automatic state change propagation: if a resource on the server changes, the clients will get notified
* no shared state: Client and server don't have to track what they send each other.
* works with all programming languages
How does it work?
-----------------
- Client sends a request: adress of a resource and optional parameters encoded as JSON
{
"method": "get",
"resource": ["peers"],
}
- Server sends a Response:
{
"returncode": "ok",
"statetoken": "ASDF",
"data": [...]
}
- Client asks if data is still valid
{
"method": "exec",
"resource": "statetokenservice"
"data": ["ASDF", "GHJK"]
}
- Server answers Client that statetoken "ASDF" expired
{
"returncode": "ok",
"data": ["ASDF"]
}
Transport
---------
A transport protocol transports requests and responses between client and server.
* tracks message boundaries, so messages don't get merged
* may be able to handle concurrent requests with out of order delivery of responses
* knows to which request a response belongs to
Transport could do encryption and authentication with a standardized protocol like SSH or TLS.
Ideas:
* request id + length + request data -> SSH -> TCP -> ...
* Websockets
* Retroshare Items
* Unix domain sockets
Currently only unencrypted http is implemented. libmicrohttpd (short MHD) is used as http server.
Can use a proxy to add TLS encryption.
Message encoding
----------------
Currently JSON, because it is already available in JavaScript and QML.
Other key-value encodings could be used as well.
Read more about basic data types of different languages (JavaScript, QML, Lua, C++) in ./ApiServer.cpp.

View File

@ -1,99 +0,0 @@
/*******************************************************************************
* libresapi/api/ResourceRouter.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "ResourceRouter.h"
namespace resource_api
{
class TestResource: public ResourceRouter
{
public:
TestResource()
{
addResourceHandler("eins", this, &TestResource::eins);
}
ResponseTask* eins(Request& /* req */, Response& /* resp */)
{
return 0;
}
};
ResourceRouter::~ResourceRouter()
{
std::vector<std::pair<std::string, HandlerBase*> >::iterator vit;
for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++)
{
delete vit->second;
}
}
ResponseTask* ResourceRouter::handleRequest(Request& req, Response& resp)
{
std::vector<std::pair<std::string, HandlerBase*> >::iterator vit;
if(!req.mPath.empty())
{
for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++)
{
if(vit->first == req.mPath.top())
{
req.mPath.pop();
//Just for GUI benefit
std::string callbackName;
req.mStream << makeKeyValueReference("callback_name", callbackName);
resp.mCallbackName = callbackName;
//
return vit->second->handleRequest(req, resp);
}
}
}
// not found, search for wildcard handler
for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++)
{
if(vit->first == "*")
{
// don't pop the path component, because it may contain usefull info for the wildcard handler
//req.mPath.pop();
return vit->second->handleRequest(req, resp);
}
}
resp.setFail("ResourceRouter::handleRequest() Error: no handler for this path.");
return 0;
}
bool ResourceRouter::isNameUsed(std::string name)
{
std::vector<std::pair<std::string, HandlerBase*> >::iterator vit;
for(vit = mHandlers.begin(); vit != mHandlers.end(); vit++)
{
if(vit->first == name)
{
return true;
}
}
return false;
}
} // namespace resource_api

View File

@ -1,110 +0,0 @@
/*******************************************************************************
* libresapi/api/ResourceRouter.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ApiTypes.h"
#include <iostream>
namespace resource_api
{
// a base class for routing requests to handler methods
// nothing is thread safe here
class ResourceRouter
{
public:
virtual ~ResourceRouter();
// can return NULL, if the request was processed
// if the Response can not be created immediately,
// then return a object which implements the ResponseTask interface
ResponseTask* handleRequest(Request& req, Response& resp);
template <class T>
void addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp));
template <class T>
void addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp));
bool isNameUsed(std::string name);
private:
class HandlerBase
{
public:
virtual ~HandlerBase(){}
virtual ResponseTask* handleRequest(Request& req, Response& resp) = 0;
};
template <class T>
class Handler: public HandlerBase
{
public:
virtual ResponseTask* handleRequest(Request &req, Response &resp)
{
return (instance->*method)(req, resp);
}
T* instance;
ResponseTask* (T::*method)(Request& req, Response& resp);
};
template <class T>
class InstantResponseHandler: public HandlerBase
{
public:
virtual ResponseTask* handleRequest(Request &req, Response &resp)
{
(instance->*method)(req, resp);
return 0;
}
T* instance;
void (T::*method)(Request& req, Response& resp);
};
std::vector<std::pair<std::string, HandlerBase*> > mHandlers;
};
// the advantage of this approach is:
// the method name is arbitrary, one class can have many different handler methods
// with raw objects the name of the handler method would be fixed, and we would need one class for every handler
// the downside is complicated template magic
template <class T>
void ResourceRouter::addResourceHandler(std::string name, T* instance, ResponseTask* (T::*callback)(Request& req, Response& resp))
{
if(isNameUsed(name))
{
std::cerr << "ResourceRouter::addResourceHandler ERROR: name=" << name << " alerady in use. Not adding new Handler!" << std::endl;
}
Handler<T>* handler = new Handler<T>();
handler->instance = instance;
handler->method = callback;
mHandlers.push_back(std::make_pair(name, handler));
}
template <class T>
void ResourceRouter::addResourceHandler(std::string name, T* instance, void (T::*callback)(Request& req, Response& resp))
{
if(isNameUsed(name))
{
std::cerr << "ResourceRouter::addResourceHandler ERROR: name=" << name << " alerady in use. Not adding new Handler!" << std::endl;
}
InstantResponseHandler<T>* handler = new InstantResponseHandler<T>();
handler->instance = instance;
handler->method = callback;
mHandlers.push_back(std::make_pair(name, handler));
}
} // namespace resource_api

View File

@ -1,600 +0,0 @@
/*******************************************************************************
* libresapi/api/RsControlModule.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "RsControlModule.h"
#include <sstream>
#include <unistd.h>
#include <cstdio>
#include <retroshare/rsinit.h>
#include <retroshare/rsiface.h>
#include <util/rsdir.h>
#include "api/ApiServer.h"
#include "api/Operators.h"
#include "api/StateTokenServer.h"
#include "GetPluginInterfaces.h"
//#define DEBUG_CONTROL_MODULE 1
namespace resource_api{
RsControlModule::RsControlModule(int argc, char **argv, StateTokenServer* sts, ApiServer *apiserver, bool full_control):
mStateTokenServer(sts),
mApiServer(apiserver),
mExitFlagMtx("RsControlModule::mExitFlagMtx"),
mProcessShouldExit(false),
mDataMtx("RsControlModule::mDataMtx"),
mRunState(WAITING_INIT),
mAutoLoginNextTime(false),
mWantPassword(false),
mPrevIsBad(false),
mCountAttempts(0),
mPassword("")
{
mStateToken = sts->getNewToken();
this->argc = argc;
this->argv = argv;
// start worker thread
if(full_control)
start("resapi ctrl mod");
else
mRunState = RUNNING_OK_NO_FULL_CONTROL;
addResourceHandler("runstate", this, &RsControlModule::handleRunState);
addResourceHandler("identities", this, &RsControlModule::handleIdentities);
addResourceHandler("locations", this, &RsControlModule::handleLocations);
addResourceHandler("password", this, &RsControlModule::handlePassword);
addResourceHandler("login", this, &RsControlModule::handleLogin);
addResourceHandler("shutdown", this, &RsControlModule::handleShutdown);
addResourceHandler("import_pgp", this, &RsControlModule::handleImportPgp);
addResourceHandler("create_location", this, &RsControlModule::handleCreateLocation);
}
RsControlModule::~RsControlModule()
{
// join();
}
bool RsControlModule::processShouldExit()
{
RS_STACK_MUTEX(mExitFlagMtx); // ********** LOCKED **********
return mProcessShouldExit;
}
bool RsControlModule::askForPassword(const std::string &title, const std::string &key_details, bool prev_is_bad, std::string &password, bool& cancelled)
{
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::askForPassword(): current passwd is \"" << mPassword << "\"" << std::endl;
#endif
cancelled = false ;
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
mCountAttempts++;
if(mCountAttempts == 3)
{
mPrevIsBad = prev_is_bad;
mCountAttempts = 0;
}
else
mPrevIsBad = false;
if(mFixedPassword != "")
{
password = mFixedPassword;
return true;
}
mWantPassword = true;
mTitle = title;
mKeyName = key_details;
if(mPassword != "")
{
password = mPassword;
mWantPassword = false;
return true;
}
mStateTokenServer->replaceToken(mStateToken);
}
int i = 0;
while(i<100)
{
usleep(5*1000);
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
if(mPassword != "")
{
password = mPassword;
mWantPassword = false;
mStateTokenServer->replaceToken(mStateToken);
return true;
}
i++;
}
return false;
}
void RsControlModule::run()
{
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule: initialising libretroshare..." << std::endl;
#endif
RsInit::InitRsConfig();
RsConfigOptions opt;
int initResult = RsInit::InitRetroShare(opt);
if (initResult < 0) {
std::cerr << "RsControlModule: FATAL ERROR, initialising libretroshare FAILED." << std::endl;
/* Error occured */
std::stringstream ss;
switch (initResult) {
case RS_INIT_AUTH_FAILED:
ss << "RsControlModule::run() AuthGPG::InitAuth failed" << std::endl;
break;
default:
/* Unexpected return code */
ss << "ControlModule::run() unexpected return code " << initResult << std::endl;
break;
}
// FATAL ERROR, we can't recover from this. Just send the message to the user.
setRunState(FATAL_ERROR, ss.str());
return;
}
// This is needed to allocate rsNotify, so that it can be used to ask for PGP passphrase
RsControl::earlyInitNotificationSystem();
rsNotify->registerNotifyClient(this);
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() Entering login wait loop." << std::endl;
#endif
bool login_ok = false;
while(!login_ok)
{
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() reseting passwd." << std::endl;
#endif
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
mPassword = "";
}
// skip account selection if autologin is available
bool wait_for_account_select = (initResult != RS_INIT_HAVE_ACCOUNT);
// wait for login request
bool auto_login = false;
if(wait_for_account_select)
{
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() wait_for_account_select=true => setting run state to WAITING_ACCOUNT_SELECT." << std::endl;
#endif
setRunState(WAITING_ACCOUNT_SELECT);
}
while(wait_for_account_select && !processShouldExit())
{
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() while(wait_for_account_select) mLoadPeerId=" << mLoadPeerId << std::endl;
#endif
usleep(500*1000);
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
if(!mLoadPeerId.isNull())
{
wait_for_account_select = wait_for_account_select && !RsAccounts::SelectAccount(mLoadPeerId);
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() mLoadPeerId != NULL, account selection result: " << !wait_for_account_select << std::endl;
#endif
}
auto_login = mAutoLoginNextTime;
//if(!wait_for_account_select)
//{
// if(wait_for_account_select)
// setRunState(WAITING_ACCOUNT_SELECT);
//}
}
if(processShouldExit())
return;
bool autoLogin = (initResult == RS_INIT_HAVE_ACCOUNT) | auto_login;
std::string lockFile;
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() trying to load certificate..." << std::endl;
#endif
int retVal = RsInit::LockAndLoadCertificates(autoLogin, lockFile);
std::string error_string;
switch (retVal) {
case 0:
login_ok = true;
break;
case 1:
error_string = "Another RetroShare using the same profile is "
"already running on your system. Please close "
"that instance first\n Lock file:\n" + lockFile;
break;
case 2:
error_string = "An unexpected error occurred when Retroshare "
"tried to acquire the single instance lock\n Lock file:\n"
+ lockFile;
break;
case 3:
error_string = "Login Failure: Maybe password is wrong";
break;
default:
std::cerr << "RsControlModule::run() LockAndLoadCertificates failed. Unexpected switch value: " << retVal << std::endl;
break;
}
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() Error string: \"" << error_string << "\"" << std::endl;
#endif
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
mLoadPeerId.clear();
}
}
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::run() login is ok. Starting up..." << std::endl;
#endif
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
mFixedPassword = mPassword;
std::cerr << "***Reseting mPasswd " << std::endl;
mPassword = "";
}
setRunState(WAITING_STARTUP);
std::cerr << "RsControlModule: login ok, starting Retroshare worker threads..." << std::endl;
RsControl::instance() -> StartupRetroShare();
std::cerr << "RsControlModule: loading main resource api modules..." << std::endl;
RsPlugInInterfaces ifaces;
getPluginInterfaces(ifaces);
mApiServer->loadMainModules(ifaces);
std::cerr << "RsControlModule: Retroshare is up and running. Enjoy!" << std::endl;
setRunState(RUNNING_OK);
while(!processShouldExit())
{
usleep(5*1000);
}
std::cerr << "RsControlModule: stopping Retroshare..." << std::endl;
RsControl::instance() -> rsGlobalShutDown();
std::cerr << "RsControlModule: Retroshare stopped. Bye!" << std::endl;
}
void RsControlModule::handleRunState(Request &, Response &resp)
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
std::string state;
switch(mRunState)
{
case WAITING_INIT:
state = "waiting_init";
break;
case FATAL_ERROR:
state = "fatal_error";
break;
case WAITING_ACCOUNT_SELECT:
state = "waiting_account_select";
break;
case WAITING_STARTUP:
state = "waiting_startup";
break;
case RUNNING_OK:
state = "running_ok";
break;
case RUNNING_OK_NO_FULL_CONTROL:
state = "running_ok_no_full_control";
break;
default:
state = "error_should_not_happen_this_is_a_bug";
}
resp.mDataStream << makeKeyValueReference("runstate", state);
resp.mStateToken = mStateToken;
resp.setOk();
}
void RsControlModule::handleIdentities(Request &, Response &resp)
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
if(mRunState == WAITING_INIT || mRunState == FATAL_ERROR)
{
resp.setFail("Retroshare is not initialised. Operation not possible.");
return;
}
std::list<RsPgpId> pgp_ids;
RsAccounts::GetPGPLogins(pgp_ids);
resp.mDataStream.getStreamToMember();
for(std::list<RsPgpId>::iterator lit = pgp_ids.begin(); lit != pgp_ids.end(); ++lit)
{
std::string name;
std::string email;
if(RsAccounts::GetPGPLoginDetails(*lit, name, email))
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("id", *lit)
<< makeKeyValueReference("pgp_id", *lit)
<< makeKeyValueReference("name", name);
}
resp.mStateToken = mStateToken;
resp.setOk();
}
void RsControlModule::handleLocations(Request &, Response &resp)
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
if(mRunState == WAITING_INIT || mRunState == FATAL_ERROR)
{
resp.setFail("Retroshare is not initialised. Operation not possible.");
return;
}
RsPeerId preferedId;
RsAccounts::GetPreferredAccountId(preferedId);
std::list<RsPeerId> peer_ids;
RsAccounts::GetAccountIds(peer_ids);
resp.mDataStream.getStreamToMember();
for(std::list<RsPeerId>::iterator lit = peer_ids.begin(); lit != peer_ids.end(); ++lit)
{
bool preferred = preferedId==*lit;
RsPgpId pgp_id;
std::string pgp_name, pgp_mail, location_name;
if(RsAccounts::GetAccountDetails(*lit, pgp_id, pgp_name, pgp_mail, location_name))
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("id", *lit)
<< makeKeyValueReference("pgp_id", pgp_id)
<< makeKeyValueReference("peer_id", *lit)
<< makeKeyValueReference("name", pgp_name)
<< makeKeyValueReference("location", location_name)
<< makeKeyValueReference("preferred", preferred);
}
resp.mStateToken = mStateToken;
resp.setOk();
}
void RsControlModule::handlePassword(Request &req, Response &resp)
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
std::string passwd;
req.mStream << makeKeyValueReference("password", passwd);
if(passwd != "")// && mWantPassword)
{
// client sends password
mPassword = passwd;
mWantPassword = false;
mStateTokenServer->replaceToken(mStateToken);
#ifdef DEBUG_CONTROL_MODULE
std::cerr << "RsControlModule::handlePassword(): setting mPasswd=\"" << mPassword << "\"" << std::endl;
#endif
}
#ifdef DEBUG_CONTROL_MODULE
else
std::cerr << "RsControlModule::handlePassword(): not setting mPasswd=\"" << mPassword << "\"!!!" << std::endl;
#endif
resp.mDataStream
<< makeKeyValueReference("want_password", mWantPassword)
<< makeKeyValueReference("key_name", mKeyName)
<< makeKeyValueReference("prev_is_bad", mPrevIsBad);
resp.mStateToken = mStateToken;
resp.setOk();
}
void RsControlModule::handleLogin(Request &req, Response &resp)
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
if(mRunState != WAITING_ACCOUNT_SELECT)
{
resp.setFail("Operation not allowed in this runstate. Login is only allowed rigth after initialisation.");
return;
}
req.mStream << makeKeyValueReference("id", mLoadPeerId)
<< makeKeyValueReference("autologin", mAutoLoginNextTime);
resp.setOk();
}
void RsControlModule::handleShutdown(Request &, Response &resp)
{
requestShutdown();
resp.setOk();
}
void RsControlModule::handleImportPgp(Request &req, Response &resp)
{
std::string key_string;
req.mStream << makeKeyValueReference("key_string", key_string);
if(key_string.empty())
{
resp.setFail("required field key_string is empty");
return;
}
RsPgpId pgp_id;
std::string error_string;
if(RsAccounts::importIdentityFromString(key_string, pgp_id, error_string))
{
resp.mDataStream << makeKeyValueReference("pgp_id", pgp_id);
resp.setOk();
return;
}
resp.setFail("Failed to import key: " + error_string);
}
void RsControlModule::handleCreateLocation(Request &req, Response &resp)
{
std::string hidden_address;
std::string hidden_port_str;
req.mStream << makeKeyValueReference("hidden_adress", hidden_address)
<< makeKeyValueReference("hidden_port", hidden_port_str);
uint16_t hidden_port = 0;
bool auto_tor = false ; // to be set by API, so disabled until then.
if(hidden_address.empty() != hidden_port_str.empty())
{
resp.setFail("you must both specify string hidden_adress and string hidden_port to create a hidden node.");
return;
}
if(!hidden_address.empty())
{
int p;
if(sscanf(hidden_port_str.c_str(), "%i", &p) != 1)
{
resp.setFail("failed to parse hidden_port, not a number. Must be a dec or hex number.");
return;
}
if(p < 0 || p > 0xFFFF)
{
resp.setFail("hidden_port out of range. It must fit into uint16!");
return;
}
hidden_port = static_cast<uint16_t>(p);
}
RsPgpId pgp_id;
std::string pgp_name;
std::string pgp_password;
std::string ssl_name;
req.mStream << makeKeyValueReference("pgp_id", pgp_id)
<< makeKeyValueReference("pgp_name", pgp_name)
<< makeKeyValueReference("pgp_password", pgp_password)
<< makeKeyValueReference("ssl_name", ssl_name);
if(pgp_password.empty())
{
resp.setFail("Error: pgp_password is empty.");
return;
}
// pgp_id is set: use existing pgp key
// pgp_name is set: attempt to create a new key
if(pgp_id.isNull() && (pgp_name.empty()||pgp_password.empty()))
{
resp.setFail("You have to set pgp_id to use an existing key, or pgp_name and pgp_password to create a new pgp key.");
return;
}
if(pgp_id.isNull())
{
std::string err_string;
if(!RsAccounts::GeneratePGPCertificate(pgp_name, "", pgp_password, pgp_id, 2048, err_string))
{
resp.setFail("could not create pgp key: "+err_string);
return;
}
}
if(hidden_port) {
/// TODO add bob to webui
RsInit::SetHiddenLocation(hidden_address, hidden_port, false);
}
std::string ssl_password = RSRandom::random_alphaNumericString(static_cast<uint32_t>(RsInit::getSslPwdLen())) ;
/* GenerateSSLCertificate - selects the PGP Account */
//RsInit::SelectGPGAccount(PGPId);
RsPeerId ssl_id;
std::string err_string;
// give the password to the password callback
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
mPassword = pgp_password;
mFixedPassword = pgp_password;
}
bool ssl_ok = RsAccounts::createNewAccount(pgp_id, "", ssl_name, "", hidden_port!=0, auto_tor!=0, ssl_password, ssl_id, err_string);
// clear fixed password to restore normal password operation
// {
// RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
// mFixedPassword = "";
// }
if (ssl_ok)
{
// load ssl password and load account
RsInit::LoadPassword(ssl_password);
// trigger login in init thread
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
mLoadPeerId = ssl_id;
}
resp.mDataStream << makeKeyValueReference("pgp_id", pgp_id)
<< makeKeyValueReference("pgp_name", pgp_name)
<< makeKeyValueReference("ssl_name", ssl_name)
<< makeKeyValueReference("ssl_id", ssl_id);
resp.setOk();
return;
}
resp.setFail("could not create a new location. Error: "+err_string);
}
bool RsControlModule::askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,int& signature_result, std::string reason /*=""*/)
{
if(rsPeers->gpgSignData(data,len,sign,signlen,reason))
{
signature_result = SELF_SIGNATURE_RESULT_SUCCESS;
return true;
}
else
{
signature_result = SELF_SIGNATURE_RESULT_FAILED;
return false;
}
}
void RsControlModule::requestShutdown()
{
RS_STACK_MUTEX(mExitFlagMtx);
mProcessShouldExit = true;
}
void RsControlModule::setRunState(RunState s, std::string errstr)
{
RS_STACK_MUTEX(mDataMtx); // ********** LOCKED **********
mRunState = s;
mLastErrorString = errstr;
mStateTokenServer->replaceToken(mStateToken);
}
} // namespace resource_api

View File

@ -1,120 +0,0 @@
/*******************************************************************************
* libresapi/api/RsControlModule.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include <util/rsthreads.h>
#include <util/cxx11retrocompat.h>
#include <retroshare/rsnotify.h>
#include "api/ResourceRouter.h"
namespace resource_api{
class StateTokenServer;
class ApiServer;
// resource api module to control accounts, startup and shutdown of retroshare
// - this module handles everything, no things are required from outside
// - exception: users of this module have to create an api server and register this module
// tasks:
// - show, import, export and create private pgp keys
// - show existing and create new locations
// - load certificate, startup retroshare
// - handle password callback
// - confirm plugin loading
// - shutdown retroshare
class RsControlModule: public ResourceRouter, NotifyClient, private RsSingleJobThread
{
public:
enum RunState { WAITING_INIT, FATAL_ERROR, WAITING_ACCOUNT_SELECT, WAITING_STARTUP, RUNNING_OK, RUNNING_OK_NO_FULL_CONTROL};
// ApiServer will be called once RS is started, to load additional api modules
// full_control: set to true if this module should handle rsinit and login
// set to false if rsinit is handled by the Qt gui
RsControlModule(int argc, char **argv, StateTokenServer* sts, ApiServer* apiserver, bool full_control);
~RsControlModule() override;
// returns true if the process should terminate
bool processShouldExit();
// returns the current state of the software booting process
RunState runState() const { return mRunState ; }
// from NotifyClient
virtual bool askForPassword(const std::string &title, const std::string& key_details, bool prev_is_bad , std::string& password,bool& canceled) override;
virtual bool askForDeferredSelfSignature(const void *data, const uint32_t len, unsigned char *sign, unsigned int *signlen,int& signature_result, std::string reason = "") override;
virtual void requestShutdown();
protected:
// from RsThread
// wee need a thread to call into things which block like askForPassword()
virtual void run() override;
private:
void handleRunState(Request& req, Response& resp);
void handleIdentities(Request& req, Response& resp);
void handleLocations(Request& req, Response& resp);
void handlePassword(Request& req, Response& resp);
void handleLogin(Request& req, Response& resp);
void handleShutdown(Request& req, Response& resp);
void handleImportPgp(Request& req, Response& resp);
void handleCreateLocation(Request& req, Response& resp);
void setRunState(RunState s, std::string errstr = "");
// for startup
int argc;
char **argv;
StateTokenServer* const mStateTokenServer;
ApiServer* const mApiServer;
RsMutex mExitFlagMtx;
bool mProcessShouldExit;
RsMutex mDataMtx;
StateToken mStateToken; // one state token for everything, to make life easier
RunState mRunState;
std::string mLastErrorString;
// id of the account to load
// null when no account was selected
RsPeerId mLoadPeerId;
bool mAutoLoginNextTime;
// to notify that a password callback is waiting
// to answer the request, clear the flag and set the password
bool mWantPassword;
bool mPrevIsBad;
int mCountAttempts;
std::string mTitle;
std::string mKeyName;
std::string mPassword;
// for ssl cert generation:
// we know the password already, so we want to avoid to rpompt the user
// we store the password in this variable, it has higher priority than the normal password variable
// it is also to avoid a lock, when we make a synchronous call into librs, like in ssl cert generation
std::string mFixedPassword;
};
} // namespace resource_api

View File

@ -1,206 +0,0 @@
/*******************************************************************************
* libresapi/api/ServiceControlHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "ServiceControlHandler.h"
#include "retroshare/rsservicecontrol.h"
#include "Operators.h"
#include <stdlib.h>
namespace resource_api
{
// maybe move to another place later
// need more generic operators for list, vector, map
template<class T>
void setToStream(StreamBase& stream, std::set<T>& set)
{
if(stream.serialise())
{
for(typename std::set<T>::iterator sit = set.begin(); sit != set.end(); sit++)
{
T item = *sit;
stream << makeValueReference(item);
}
}
else
{
while(stream.hasMore())
{
T item;
stream << makeValueReference(item);
set.insert(item);
}
}
}
void servicePermissionToStream(StreamBase& stream, RsServicePermissions& perm)
{
stream << makeKeyValueReference("service_id", perm.mServiceId)
<< makeKeyValueReference("service_name", perm.mServiceName)
<< makeKeyValueReference("default_allowed", perm.mDefaultAllowed)
;
setToStream(stream.getStreamToMember("peers_allowed"), perm.mPeersAllowed);
setToStream(stream.getStreamToMember("peers_denied"), perm.mPeersDenied);
}
ServiceControlHandler::ServiceControlHandler(RsServiceControl* control):
mRsServiceControl(control)
{
addResourceHandler("*", this, &ServiceControlHandler::handleWildcard);
addResourceHandler("user", this, &ServiceControlHandler::handleUser);
}
void ServiceControlHandler::handleWildcard(Request &req, Response &resp)
{
bool ok = false;
if(!req.mPath.empty())
{
}
else
{
// no more path element
if(req.isGet())
{
// list all peers
ok = true;
RsPeerServiceInfo psi;
ok &= mRsServiceControl->getOwnServices(psi);
for(std::map<uint32_t, RsServiceInfo>::iterator mit = psi.mServiceList.begin(); mit != psi.mServiceList.end(); mit++)
{
RsServicePermissions perms;
ok &= mRsServiceControl->getServicePermissions(mit->first, perms);
if(ok)
{
servicePermissionToStream(resp.mDataStream.getStreamToMember(), perms);
}
}
}
else if(req.isPut())
{
// change service default
std::string serviceidtext;
bool enabled;
req.mStream << makeKeyValueReference("service_id", serviceidtext)
<< makeKeyValueReference("default_allowed", enabled);
RsServicePermissions serv_perms ;
//uint32_t serviceid = fromString<uint32_t>(serviceidtext);
uint32_t serviceid = atoi(serviceidtext.c_str());
if (serviceid == 0) {
resp.setFail("service_id missed");
return;
}
if(!rsServiceControl->getServicePermissions(serviceid, serv_perms)){
resp.setFail("service_id " + serviceidtext + " is invalid");
return;
}
serv_perms.mDefaultAllowed = enabled;
if(serv_perms.mDefaultAllowed)
{
serv_perms.mPeersDenied.clear() ;
}
else
{
serv_perms.mPeersAllowed.clear() ;
}
ok = rsServiceControl->updateServicePermissions(serviceid,serv_perms);
if (!ok) {
resp.setFail("updateServicePermissions failed");
return;
}
}
}
if(ok)
{
resp.setOk();
}
else
{
resp.setFail();
}
}
void ServiceControlHandler::handleUser(Request& req, Response& resp){
// no get, only put (post) to allow user or delete to remove user
std::string serviceidtext;
std::string peeridtext;
bool enabled;
bool ok;
req.mStream << makeKeyValueReference("service_id", serviceidtext)
<< makeKeyValueReference("peer_id", peeridtext)
<< makeKeyValueReference("enabled", enabled);
RsPeerId peer_id(peeridtext);
if (peer_id.isNull()) {
resp.setFail("peer_id missing or not found");
return;
}
RsServicePermissions serv_perms ;
uint32_t serviceid = atoi(serviceidtext.c_str());
if (serviceid == 0) {
resp.setFail("service_id missed");
return;
}
if(!rsServiceControl->getServicePermissions(serviceid, serv_perms)){
resp.setFail("service_id " + serviceidtext + " is invalid");
return;
}
if(req.isPut())
{
if (enabled && !serv_perms.peerHasPermission(peer_id))
{
serv_perms.setPermission(peer_id);
} else if (!enabled && serv_perms.peerHasPermission(peer_id)){
serv_perms.resetPermission(peer_id);
} else {
//nothing todo
resp.setOk();
return;
}
} else {
resp.setFail("only POST supported.");
return;
}
ok = rsServiceControl->updateServicePermissions(serviceid,serv_perms);
if (!ok) {
resp.setFail("updateServicePermissions failed");
return;
}
resp.setOk();
}
} // namespace resource_api

View File

@ -1,42 +0,0 @@
/*******************************************************************************
* libresapi/api/ServiceControlHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ApiTypes.h"
#include "ResourceRouter.h"
class RsServiceControl;
namespace resource_api
{
class ServiceControlHandler: public ResourceRouter
{
public:
ServiceControlHandler(RsServiceControl* control);
private:
RsServiceControl* mRsServiceControl;
void handleWildcard(Request& req, Response& resp);
void handleUser(Request& req, Response& resp);
};
} // namespace resource_api

View File

@ -1,203 +0,0 @@
/*******************************************************************************
* libresapi/api/SettingsHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "SettingsHandler.h"
#include <iostream>
#include <retroshare/rsinit.h>
namespace resource_api
{
#define SETTINGS_FILE (QString::fromUtf8(RsAccounts::AccountDirectory().c_str()) + "/Sonet.conf")
SettingsHandler::SettingsHandler(StateTokenServer *sts, const QString settingsGroup) :
QSettings(SETTINGS_FILE, QSettings::IniFormat),
mStateTokenServer(sts),
mMtx("SettingsHandler Mutex"),
mStateToken(sts->getNewToken())
{
RsPeerId sPreferedId;
m_bValid = RsAccounts::GetPreferredAccountId(sPreferedId);
if (!settingsGroup.isEmpty())
beginGroup(settingsGroup);
addResourceHandler("*", this, &SettingsHandler::handleSettingsRequest);
addResourceHandler("get_advanced_mode", this, &SettingsHandler::handleGetAdvancedMode);
addResourceHandler("set_advanced_mode", this, &SettingsHandler::handleSetAdvancedMode);
addResourceHandler("get_flickable_grid_mode", this, &SettingsHandler::handleGetFlickableGridMode);
addResourceHandler("set_flickable_grid_mode", this, &SettingsHandler::handleSetFlickableGridMode);
addResourceHandler("get_auto_login", this, &SettingsHandler::handleGetAutoLogin);
addResourceHandler("set_auto_login", this, &SettingsHandler::handleSetAutoLogin);
}
SettingsHandler::~SettingsHandler()
{
sync();
}
void SettingsHandler::handleSettingsRequest(Request &/*req*/, Response &resp)
{
}
void SettingsHandler::handleGetAdvancedMode(Request &/*req*/, Response &resp)
{
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
bool advanced_mode = valueFromGroup("General", "Advanced", false).toBool();
resp.mDataStream << makeKeyValueReference("advanced_mode", advanced_mode);
resp.setOk();
sync();
}
void SettingsHandler::handleSetAdvancedMode(Request &req, Response &resp)
{
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
bool advanced_mode;
req.mStream << makeKeyValueReference("advanced_mode", advanced_mode);
setValueToGroup("General", "Advanced", advanced_mode);
resp.setOk();
sync();
}
void SettingsHandler::handleGetFlickableGridMode(Request &/*req*/, Response &resp)
{
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
bool flickable_grid_mode = valueFromGroup("General", "FlickableGrid", false).toBool();
resp.mDataStream << makeKeyValueReference("flickable_grid_mode", flickable_grid_mode);
resp.setOk();
sync();
}
void SettingsHandler::handleSetFlickableGridMode(Request &req, Response &resp)
{
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
bool flickable_grid_mode;
req.mStream << makeKeyValueReference("flickable_grid_mode", flickable_grid_mode);
setValueToGroup("General", "FlickableGrid", flickable_grid_mode);
resp.setOk();
sync();
}
void SettingsHandler::handleGetAutoLogin(Request &/*req*/, Response &resp)
{
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
bool autoLogin = RsInit::getAutoLogin();;
resp.mDataStream << makeKeyValueReference("auto_login", autoLogin);
resp.setOk();
sync();
}
void SettingsHandler::handleSetAutoLogin(Request &req, Response &resp)
{
{
RS_STACK_MUTEX(mMtx);
resp.mStateToken = mStateToken;
}
bool autoLogin;
req.mStream << makeKeyValueReference("auto_login", autoLogin);
RsInit::setAutoLogin(autoLogin);
resp.setOk();
sync();
}
QVariant SettingsHandler::value(const QString &key, const QVariant &defaultVal) const
{
if (m_bValid == false)
{
return defaultVal.isNull() ? defaultValue(key) : defaultVal;
}
return QSettings::value(key, defaultVal.isNull() ? defaultValue(key) : defaultVal);
}
void SettingsHandler::setValue(const QString &key, const QVariant &val)
{
if (m_bValid == false)
{
std::cerr << "RSettings::setValue() Calling on invalid object, key = " << key.toStdString() << std::endl;
return;
}
if (val == defaultValue(key))
QSettings::remove(key);
else if (val != value(key))
QSettings::setValue(key, val);
}
QVariant SettingsHandler::valueFromGroup(const QString &group, const QString &key, const QVariant &defaultVal)
{
beginGroup(group);
QVariant val = value(key, defaultVal);
endGroup();
return val;
}
void SettingsHandler::setValueToGroup(const QString &group, const QString &key, const QVariant &val)
{
beginGroup(group);
setValue(key, val);
endGroup();
}
void SettingsHandler::setDefault(const QString &key, const QVariant &val)
{
_defaults.insert(key, val);
}
QVariant SettingsHandler::defaultValue(const QString &key) const
{
if (_defaults.contains(key))
return _defaults.value(key);
return QVariant();
}
void SettingsHandler::reset()
{
/* Static method, so we have to create a QSettings object. */
QSettings settings(SETTINGS_FILE, QSettings::IniFormat);
settings.clear();
}
} // namespace resource_api

View File

@ -1,80 +0,0 @@
/*******************************************************************************
* libresapi/api/SettingsHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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 SETTINGSHANDLER_H
#define SETTINGSHANDLER_H
#include <QSettings>
#include <util/rsthreads.h>
#include "ResourceRouter.h"
#include "StateTokenServer.h"
/* Reimplemented class RSettings*/
namespace resource_api
{
class SettingsHandler : public ResourceRouter, public QSettings
{
public:
SettingsHandler(StateTokenServer* sts, const QString group = QString());
~SettingsHandler();
static void reset();
QVariant value(const QString &key,
const QVariant &defaultVal = QVariant()) const;
void setValue(const QString &key, const QVariant &val);
QVariant valueFromGroup(const QString &group, const QString &key,
const QVariant &defaultVal = QVariant());
void setValueToGroup(const QString &group, const QString &key,
const QVariant &val);
protected:
void setDefault(const QString &key, const QVariant &val);
QVariant defaultValue(const QString &key) const;
bool m_bValid;
private:
void handleSettingsRequest(Request& req, Response& resp);
void handleGetAdvancedMode(Request& req, Response& resp);
void handleSetAdvancedMode(Request& req, Response& resp);
void handleGetFlickableGridMode(Request& req, Response& resp);
void handleSetFlickableGridMode(Request& req, Response& resp);
void handleGetAutoLogin(Request& req, Response& resp);
void handleSetAutoLogin(Request& req, Response& resp);
QHash<QString, QVariant> _defaults;
StateTokenServer* mStateTokenServer;
RsMutex mMtx;
StateToken mStateToken; // mutex protected
};
} // namespace resource_api
#endif // SETTINGSHANDLER_H

View File

@ -1,172 +0,0 @@
/*******************************************************************************
* libresapi/api/StateTokenServer.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "StateTokenServer.h"
#include <algorithm>
namespace resource_api
{
// maybe it would be good to make this part of state token or friend, to be able to directly access the value
StreamBase& operator <<(StreamBase& left, KeyValueReference<StateToken> kv)
{
if(left.serialise())
{
// have to make a variable, to be able to pass it by reference
// (cant pass return value of a function by refernce to another function)
int value = kv.value.getValue();
left << makeKeyValueReference(kv.key, value);
}
else
{
int value;
left << makeKeyValueReference(kv.key, value);
kv.value = StateToken(value);
}
return left;
}
StreamBase& operator<<(StreamBase& left, StateToken& token)
{
if(left.serialise())
{
// have to make a variable, to be able to pass it by reference
// (cant pass return value of a function by refernce to another function)
int value = token.getValue();
left << value;
}
else
{
int value;
left << value;
token = StateToken(value);
}
return left;
}
bool operator==(const StateToken& left, const StateToken& right)
{
if(left.getValue() == right.getValue())
{
return true;
}
else
{
return false;
}
}
StateTokenServer::StateTokenServer():
mMtx("StateTokenServer mMtx"),
mNextToken(1),
mClientsMtx("StateTokenServer mClientsMtx")
{
addResourceHandler("*", this, &StateTokenServer::handleWildcard);
}
StateToken StateTokenServer::getNewToken()
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
return locked_getNewToken();
}
void StateTokenServer::discardToken(StateToken token)
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
locked_discardToken(token);
}
void StateTokenServer::replaceToken(StateToken &token)
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
locked_discardToken(token);
token = locked_getNewToken();
}
void StateTokenServer::registerTickClient(Tickable *c)
{
// extra service: tick it to let it init its ticking stuff
c->tick();
// avoid double registration
unregisterTickClient(c);
RS_STACK_MUTEX(mClientsMtx); // ********** LOCKED **********
mTickClients.push_back(c);
}
void StateTokenServer::unregisterTickClient(Tickable *c)
{
RS_STACK_MUTEX(mClientsMtx); // ********** LOCKED **********
std::vector<Tickable*>::iterator vit = std::find(mTickClients.begin(), mTickClients.end(), c);
if(vit != mTickClients.end())
mTickClients.erase(vit);
}
void StateTokenServer::handleWildcard(Request &req, Response &resp)
{
{
RS_STACK_MUTEX(mClientsMtx); // ********** LOCKED **********
for(std::vector<Tickable*>::iterator vit = mTickClients.begin(); vit != mTickClients.end(); ++vit)
{
(*vit)->tick();
}
}
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
// want to lookpup many tokens at once, return a list of invalid tokens
// TODO: make generic list serialiser/deserialiser
resp.mDataStream.getStreamToMember();
while(req.mStream.hasMore())
{
StateToken token;
req.mStream << token;
// lookup if token is valid
if(std::find(mValidTokens.begin(), mValidTokens.end(), token) == mValidTokens.end())
{
// if invalid, add to response list
resp.mDataStream << token;
}
}
resp.setOk();
}
StateToken StateTokenServer::locked_getNewToken()
{
StateToken token(mNextToken);
mValidTokens.push_back(token);
mNextToken++;
if(mNextToken == 0) // 0 is a reserved value, don't ever use it
mNextToken = 1;
return token;
}
void StateTokenServer::locked_discardToken(StateToken token)
{
std::vector<StateToken>::iterator toDelete = std::find(mValidTokens.begin(), mValidTokens.end(), token);
if(toDelete != mValidTokens.end())
{
mValidTokens.erase(toDelete);
}
}
} // namespace resource_api

View File

@ -1,90 +0,0 @@
/*******************************************************************************
* libresapi/api/StateTokenServer.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include <util/rsthreads.h>
#include "ResourceRouter.h"
namespace resource_api
{
//class StreamBase;
//class StateToken;
// serialiser/deserialiser (depends on stream type)
// for single value
StreamBase& operator <<(StreamBase& left, KeyValueReference<StateToken> kv);
// for lists
StreamBase& operator <<(StreamBase& left, StateToken& token);
bool operator ==(const StateToken& left, const StateToken& right);
class Tickable{
public:
virtual void tick() = 0;
};
class StateTokenServer: public ResourceRouter
{
public:
StateTokenServer();
// thread safe
// this allows tokens to be created and destroyed from arbitrary threads
StateToken getNewToken();
void discardToken(StateToken token);
// discard the token and fill in a new one
void replaceToken(StateToken& token);
void registerTickClient(Tickable* c);
void unregisterTickClient(Tickable* c);
private:
void handleWildcard(Request& req, Response& resp);
StateToken locked_getNewToken();
void locked_discardToken(StateToken token);
RsMutex mMtx;
uint32_t mNextToken;
// not sure what the most efficient data structure for simple token storage is
// have to:
// - add elements
// - remove elements by value
// - store many values
// vector: expensive token erase, could make this better with a invalidate flag
// and periodic cleanup
// list: lots of overhead for pointers
// a set would offer cheap lookup
// we have to lookup often, so maybe this would be an option
// have to see where the bottleneck is in practice
// idea: invalidate all tokens after x minutes/hours, to limit the range of the token values
// then store the token states in a circular bitbuffer
std::vector<StateToken> mValidTokens;
// classes which want to be ticked
RsMutex mClientsMtx; // needs extra mutex, because clients may call back to modify get/delete tokens
std::vector<Tickable*> mTickClients;
};
} // namespace resource_api

View File

@ -1,71 +0,0 @@
/*******************************************************************************
* libresapi/api/StatsHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "StatsHandler.h"
#include "Operators.h"
#include <retroshare/rsconfig.h>
#include <retroshare/rspeers.h>
#include <pqi/authssl.h>
namespace resource_api
{
StatsHandler::StatsHandler()
{
addResourceHandler("*", this, &StatsHandler::handleStatsRequest);
}
void StatsHandler::handleStatsRequest(Request &/*req*/, Response &resp)
{
StreamBase& itemStream = resp.mDataStream.getStreamToMember();
// location info
itemStream << makeKeyValue("name", rsPeers->getGPGName(rsPeers->getGPGOwnId()));
itemStream << makeKeyValue("location", AuthSSL::getAuthSSL()->getOwnLocation());
// peer info
unsigned int all, online;
rsPeers->getPeerCount(&all, &online, false);
itemStream << makeKeyValue("peers_all", all);
itemStream << makeKeyValue("peers_connected", online);
// bandwidth info
float downKb, upKb;
rsConfig->GetCurrentDataRates(downKb, upKb);
itemStream << makeKeyValue("bandwidth_up_kb", (double)upKb);
itemStream << makeKeyValue("bandwidth_down_kb", (double)downKb);
// DHT/NAT info
RsConfigNetStatus config;
rsConfig->getConfigNetStatus(config);
itemStream << makeKeyValue("dht_active", config.DHTActive);
itemStream << makeKeyValue("dht_ok", config.netDhtOk);
itemStream << makeKeyValue("dht_size_all", config.netDhtNetSize);
itemStream << makeKeyValue("dht_size_rs", config.netDhtRsNetSize);
uint32_t netState = rsConfig -> getNetState();
itemStream << makeKeyValue("nat_state", netState);
// ok
resp.setOk();
}
} // namespace resource_api

View File

@ -1,46 +0,0 @@
/*******************************************************************************
* libresapi/api/StatsHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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 STATSHANDLER_H
#define STATSHANDLER_H
/*
* simple class to output some basic stats about RS
* like bandwidth, connected peers, ...
*/
#include "ResourceRouter.h"
namespace resource_api
{
class StatsHandler : public ResourceRouter
{
public:
StatsHandler();
private:
void handleStatsRequest(Request& req, Response& resp);
};
} // namespace resource_api
#endif // STATSHANDLER_H

View File

@ -1,141 +0,0 @@
/*******************************************************************************
* libresapi/api/TmpBLogStore.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "TmpBlobStore.h"
#include <util/rsrandom.h>
#include <time.h>
namespace resource_api{
TmpBlobStore::TmpBlobStore(StateTokenServer* sts):
mStateTokenServer(sts), mMtx("TmpBlobStore::mMtx"), mBlobs(0)
{
mStateTokenServer->registerTickClient(this);
}
TmpBlobStore::~TmpBlobStore()
{
mStateTokenServer->unregisterTickClient(this);
RS_STACK_MUTEX(mMtx);
Blob* blob = mBlobs;
while(blob)
{
Blob* next = blob->next;
delete blob;
blob = next;
}
}
void TmpBlobStore::tick()
{
RS_STACK_MUTEX(mMtx);
Blob* prev = 0;
Blob* current = mBlobs;
time_t now = time(NULL);
for(; current != 0; current = current->next)
{
if(current->timestamp + BLOB_STORE_TIME_SECS < now)
{
if(prev)
prev->next = current->next;
else
mBlobs = current->next;
delete current;
return;
}
prev = current;
}
}
uint32_t TmpBlobStore::storeBlob(std::vector<uint8_t>& bytes)
{
if(bytes.size() > MAX_BLOBSIZE)
return 0;
RS_STACK_MUTEX(mMtx);
Blob* blob = new Blob();
blob->bytes.swap(bytes);
blob->timestamp = time(NULL);
blob->id = locked_make_id();
blob->next = 0;
if(!mBlobs)
{
mBlobs = blob;
return blob->id;
}
Blob* blob2 = mBlobs;
while(blob2->next)
blob2 = blob2->next;
blob2->next = blob;
return blob->id;
}
bool TmpBlobStore::fetchBlob(uint32_t blobid, std::vector<uint8_t>& bytes)
{
RS_STACK_MUTEX(mMtx);
Blob* prev = 0;
Blob* current = mBlobs;
for(; current != 0; current = current->next)
{
if(current->id == blobid)
{
bytes.swap(current->bytes);
if(prev)
prev->next = current->next;
else
mBlobs = current->next;
delete current;
return true;
}
prev = current;
}
return false;
}
TmpBlobStore::Blob* TmpBlobStore::locked_findblob(uint32_t id)
{
Blob* blob;
for(blob = mBlobs; blob != 0; blob = blob->next)
{
if(blob->id == id)
return blob;
}
return 0;
}
uint32_t TmpBlobStore::locked_make_id()
{
uint32_t id = RSRandom::random_u32();
// make sure the id is not in use already
while(locked_findblob(id))
id = RSRandom::random_u32();
return id;
}
} // namespace resource_api

View File

@ -1,74 +0,0 @@
/*******************************************************************************
* libresapi/api/TmpBLogStore.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "StateTokenServer.h"
namespace resource_api{
// store for temporary binary data
// use cases: upload of avatar image, upload of certificate file
// those files are first store in this class, then and a handler will fetch the datas later
// blobs are removed if they are not used after xx hours
class TmpBlobStore: private Tickable
{
public:
TmpBlobStore(StateTokenServer* sts);
virtual ~TmpBlobStore();
// from Tickable
virtual void tick();
// 30MB should be enough for avatars pictures, pdfs and mp3s
// can remove this limit, once we can store data on disk
static const int MAX_BLOBSIZE = 30*1000*1000;
static const int BLOB_STORE_TIME_SECS = 12*60*60;
// steals the bytes
// returns a blob number as identifier
// returns null on failure
uint32_t storeBlob(std::vector<uint8_t>& bytes);
// fetch blob with given id
// the blob is removed from the store
// return true on success
bool fetchBlob(uint32_t blobid, std::vector<uint8_t>& bytes);
private:
class Blob{
public:
std::vector<uint8_t> bytes;
time_t timestamp;
uint32_t id;
Blob* next;
};
Blob* locked_findblob(uint32_t id);
uint32_t locked_make_id();
StateTokenServer* const mStateTokenServer;
RsMutex mMtx;
Blob* mBlobs;
};
} // namespace resource_api

View File

@ -1,374 +0,0 @@
/*******************************************************************************
* libresapi/api/TransfersHandler.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "TransfersHandler.h"
#include "Operators.h"
#include <algorithm>
#include <time.h>
namespace resource_api
{
TransfersHandler::TransfersHandler(StateTokenServer *sts, RsFiles *files, RsPeers *peers,
RsNotify& notify):
mStateTokenServer(sts), mFiles(files), mRsPeers(peers), mNotify(notify),
mMtx("TransfersHandler"), mLastUpdateTS(0)
{
addResourceHandler("*", this, &TransfersHandler::handleWildcard);
addResourceHandler("downloads", this, &TransfersHandler::handleDownloads);
addResourceHandler("uploads", this, &TransfersHandler::handleUploads);
addResourceHandler("control_download", this, &TransfersHandler::handleControlDownload);
addResourceHandler( "set_file_destination_directory", this,
&TransfersHandler::handleSetFileDestinationDirectory );
addResourceHandler( "set_file_destination_name", this,
&TransfersHandler::handleSetFileDestinationName );
addResourceHandler( "set_file_chunk_strategy", this,
&TransfersHandler::handleSetFileChunkStrategy );
mStateToken = mStateTokenServer->getNewToken();
mStateTokenServer->registerTickClient(this);
mNotify.registerNotifyClient(this);
}
TransfersHandler::~TransfersHandler()
{
mStateTokenServer->unregisterTickClient(this);
mNotify.unregisterNotifyClient(this);
}
void TransfersHandler::notifyListChange(int list, int /* type */)
{
if(list == NOTIFY_LIST_TRANSFERLIST)
{
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
mStateTokenServer->discardToken(mStateToken);
mStateToken = mStateTokenServer->getNewToken();
}
}
const int UPDATE_PERIOD_SECONDS = 5;
void TransfersHandler::tick()
{
if(time(0) > (mLastUpdateTS + UPDATE_PERIOD_SECONDS))
mStateTokenServer->replaceToken(mStateToken);
bool replace = false;
// extra check: was the list of files changed?
// if yes, replace state token immediately
std::list<RsFileHash> dls;
mFiles->FileDownloads(dls);
// there is no guarantee of the order
// so have to sort before comparing the lists
dls.sort();
if(!std::equal(dls.begin(), dls.end(), mDownloadsAtLastCheck.begin()))
mDownloadsAtLastCheck.swap(dls);
std::list<RsFileHash> upls;
mFiles->FileUploads(upls);
upls.sort();
if(!std::equal(upls.begin(), upls.end(), mUploadsAtLastCheck.begin()))
mUploadsAtLastCheck.swap(upls);
if(replace)
mStateTokenServer->replaceToken(mStateToken);
}
void TransfersHandler::handleWildcard(Request & /*req*/, Response & /*resp*/)
{
}
void TransfersHandler::handleControlDownload(Request &req, Response &resp)
{
mStateTokenServer->replaceToken(mStateToken);
std::string hashString;
std::string action;
req.mStream << makeKeyValueReference("action", action);
req.mStream << makeKeyValueReference("hash", hashString);
RsFileHash hash(hashString);
if(action == "begin")
{
std::string fname;
double size;
req.mStream << makeKeyValueReference("name", fname);
req.mStream << makeKeyValueReference("size", size);
std::list<RsPeerId> srcIds;
FileInfo finfo;
mFiles->FileDetails(hash, RS_FILE_HINTS_REMOTE, finfo);
for(std::vector<TransferInfo>::const_iterator it(finfo.peers.begin());it!=finfo.peers.end();++it)
srcIds.push_back((*it).peerId);
bool ok = req.mStream.isOK();
if(ok)
ok = mFiles->FileRequest(fname, hash, size, "", RS_FILE_REQ_ANONYMOUS_ROUTING, srcIds);
if(ok)
resp.setOk();
else
resp.setFail("something went wrong. are all fields filled in? is the file already downloaded?");
return;
}
if(!req.mStream.isOK())
{
resp.setFail("error: could not deserialise the request");
return;
}
bool ok = false;
bool handled = false;
if(action == "pause")
{
handled = true;
ok = mFiles->FileControl(hash, RS_FILE_CTRL_PAUSE);
}
if(action == "start")
{
handled = true;
ok = mFiles->FileControl(hash, RS_FILE_CTRL_START);
}
if(action == "check")
{
handled = true;
ok = mFiles->FileControl(hash, RS_FILE_CTRL_FORCE_CHECK);
}
if(action == "cancel")
{
handled = true;
ok = mFiles->FileCancel(hash);
}
if(ok)
resp.setOk();
else
resp.setFail("something went wrong. not sure what or why.");
if(handled)
return;
resp.setFail("error: action not handled");
}
void TransfersHandler::handleDownloads(Request & /* req */, Response &resp)
{
tick();
resp.mStateToken = mStateToken;
resp.mDataStream.getStreamToMember();
for(std::list<RsFileHash>::iterator lit = mDownloadsAtLastCheck.begin();
lit != mDownloadsAtLastCheck.end(); ++lit)
{
FileInfo fi;
if(mFiles->FileDetails(*lit, RS_FILE_HINTS_DOWNLOAD, fi))
{
StreamBase& stream = resp.mDataStream.getStreamToMember();
stream << makeKeyValueReference("id", fi.hash)
<< makeKeyValueReference("hash", fi.hash)
<< makeKeyValueReference("name", fi.fname);
double size = fi.size;
double transfered = fi.transfered;
stream << makeKeyValueReference("size", size)
<< makeKeyValueReference("transferred", transfered)
<< makeKeyValue("transfer_rate", fi.tfRate);
std::string dl_status;
switch(fi.downloadStatus)
{
case FT_STATE_FAILED:
dl_status = "failed";
break;
case FT_STATE_OKAY:
dl_status = "okay";
break;
case FT_STATE_WAITING:
dl_status = "waiting";
break;
case FT_STATE_DOWNLOADING:
dl_status = "downloading";
break;
case FT_STATE_COMPLETE:
dl_status = "complete";
break;
case FT_STATE_QUEUED:
dl_status = "queued";
break;
case FT_STATE_PAUSED:
dl_status = "paused";
break;
case FT_STATE_CHECKING_HASH:
dl_status = "checking";
break;
default:
dl_status = "error_unknown";
}
stream << makeKeyValueReference("download_status", dl_status);
}
}
resp.setOk();
}
void TransfersHandler::handleUploads(Request & /* req */, Response &resp)
{
tick();
resp.mStateToken = mStateToken;
resp.mDataStream.getStreamToMember();
RsPeerId ownId = mRsPeers->getOwnId();
for(std::list<RsFileHash>::iterator lit = mUploadsAtLastCheck.begin();
lit != mUploadsAtLastCheck.end(); ++lit)
{
FileInfo fi;
if(mFiles->FileDetails(*lit, RS_FILE_HINTS_UPLOAD, fi))
{
for( std::vector<TransferInfo>::iterator pit = fi.peers.begin(); pit != fi.peers.end(); ++pit)
{
if (pit->peerId == ownId) //don't display transfer to ourselves
continue ;
std::string sourceName = mRsPeers->getPeerName(pit->peerId);
bool isAnon = false;
bool isEncryptedE2E = false;
if(sourceName == "")
{
isAnon = true;
sourceName = pit->peerId.toStdString();
if(rsFiles->isEncryptedSource(pit->peerId))
isEncryptedE2E = true;
}
std::string status;
switch(pit->status)
{
case FT_STATE_FAILED:
status = "Failed";
break;
case FT_STATE_OKAY:
status = "Okay";
break;
case FT_STATE_WAITING:
status = "Waiting";
break;
case FT_STATE_DOWNLOADING:
status = "Uploading";
break;
case FT_STATE_COMPLETE:
status = "Complete";
break;
default:
status = "Complete";
break;
}
CompressedChunkMap cChunkMap;
if(!rsFiles->FileUploadChunksDetails(*lit, pit->peerId, cChunkMap))
continue;
double dlspeed = pit->tfRate;
double fileSize = fi.size;
double completed = pit->transfered;
uint32_t chunk_size = 1024*1024;
uint32_t nb_chunks = (uint32_t)((fi.size + (uint64_t)chunk_size - 1) / (uint64_t)(chunk_size));
uint32_t filled_chunks = cChunkMap.filledChunks(nb_chunks);
if(filled_chunks > 0 && nb_chunks > 0)
completed = cChunkMap.computeProgress(fi.size, chunk_size);
else
completed = pit->transfered % chunk_size;
resp.mDataStream.getStreamToMember()
<< makeKeyValueReference("hash", fi.hash)
<< makeKeyValueReference("name", fi.fname)
<< makeKeyValueReference("source", sourceName)
<< makeKeyValueReference("size", fileSize)
<< makeKeyValueReference("transferred", completed)
<< makeKeyValueReference("is_anonymous", isAnon)
<< makeKeyValueReference("is_encrypted_e2e", isEncryptedE2E)
<< makeKeyValueReference("transfer_rate", dlspeed)
<< makeKeyValueReference("status", status);
}
}
}
resp.setOk();
}
void TransfersHandler::handleSetFileDestinationDirectory( Request& req,
Response& resp )
{
mStateTokenServer->replaceToken(mStateToken);
std::string hashString;
std::string newPath;
req.mStream << makeKeyValueReference("path", newPath);
req.mStream << makeKeyValueReference("hash", hashString);
RsFileHash hash(hashString);
if (mFiles->setDestinationDirectory(hash, newPath)) resp.setOk();
else resp.setFail();
}
void TransfersHandler::handleSetFileDestinationName( Request& req,
Response& resp )
{
mStateTokenServer->replaceToken(mStateToken);
std::string hashString;
std::string newName;
req.mStream << makeKeyValueReference("name", newName);
req.mStream << makeKeyValueReference("hash", hashString);
RsFileHash hash(hashString);
if (mFiles->setDestinationName(hash, newName)) resp.setOk();
else resp.setFail();
}
void TransfersHandler::handleSetFileChunkStrategy(Request& req, Response& resp)
{
mStateTokenServer->replaceToken(mStateToken);
std::string hashString;
std::string newChunkStrategyStr;
req.mStream << makeKeyValueReference("chuck_stategy", newChunkStrategyStr);
req.mStream << makeKeyValueReference("hash", hashString);
RsFileHash hash(hashString);
FileChunksInfo::ChunkStrategy newStrategy =
FileChunksInfo::CHUNK_STRATEGY_PROGRESSIVE;
if ( newChunkStrategyStr == "streaming" )
newStrategy = FileChunksInfo::CHUNK_STRATEGY_STREAMING;
else if ( newChunkStrategyStr == "random" )
newStrategy = FileChunksInfo::CHUNK_STRATEGY_RANDOM;
else if ( newChunkStrategyStr == "progressive" )
newStrategy = FileChunksInfo::CHUNK_STRATEGY_PROGRESSIVE;
if (mFiles->setChunkStrategy(hash, newStrategy)) resp.setOk();
else resp.setFail();
}
} // namespace resource_api

View File

@ -1,76 +0,0 @@
/*******************************************************************************
* libresapi/api/TransfersHandler.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#pragma once
#include "ResourceRouter.h"
#include "StateTokenServer.h"
#include <retroshare/rsfiles.h>
#include <retroshare/rspeers.h>
#include <retroshare/rsnotify.h>
namespace resource_api
{
class TransfersHandler: public ResourceRouter, Tickable, NotifyClient
{
public:
TransfersHandler(StateTokenServer* sts, RsFiles* files, RsPeers *peers, RsNotify& notify);
virtual ~TransfersHandler();
/**
Derived from NotifyClient
This function may be called from foreign thread
*/
virtual void notifyListChange(int list, int type);
// from Tickable
virtual void tick();
private:
void handleWildcard(Request& req, Response& resp);
void handleControlDownload(Request& req, Response& resp);
void handleDownloads(Request& req, Response& resp);
void handleUploads(Request& req, Response& resp);
void handleSetFileDestinationDirectory(Request& req, Response& resp);
void handleSetFileDestinationName(Request& req, Response& resp);
void handleSetFileChunkStrategy(Request& req, Response& resp);
StateTokenServer* mStateTokenServer;
RsFiles* mFiles;
RsPeers* mRsPeers;
RsNotify& mNotify;
/**
Protects mStateToken that may be changed in foreign thread
@see TransfersHandler::notifyListChange(...)
*/
RsMutex mMtx;
StateToken mStateToken;
time_t mLastUpdateTS;
std::list<RsFileHash> mDownloadsAtLastCheck;
std::list<RsFileHash> mUploadsAtLastCheck;
};
} // namespace resource_api

File diff suppressed because it is too large Load Diff

View File

@ -1,617 +0,0 @@
/*
SuperEasyJSON
http://www.sourceforge.net/p/supereasyjson
The MIT License (MIT)
Copyright (c) 2013 Jeff Weinstein (jeff.weinstein at gmail)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
CHANGELOG:
==========
8/31/2014:
---------
* Fixed bug from last update that broke false/true boolean usage. Courtesy of Vasi B.
* Change postfix increment of iterators in Serialize to prefix, courtesy of Vasi B.
* More improvements to validity checking of non string/object/array types. Should
catch even more invalid usage types such as -1jE5, falsee, trueeeeeee
{"key" : potato} (that should be {"key" : "potato"}), etc.
* Switched to strtol and strtod from atof/atoi in Serialize for better error handling.
* Fix for GCC order of initialization warnings, courtsey of Vasi B.
8/17/2014:
----------
* Better error handling (and bug fixing) for invalid JSON. Previously, something such as:
{"def": j[{"a": 100}],"abc": 123}
would result in at best, a crash, and at worst, nothing when this was passed to
the Deserialize method. Note that the "j" is invalid in this example. This led
to further fixes for other invalid syntax:
- Use of multiple 'e', for example: 1ee4 is not valid
- Use of '.' when not preceded by a digit is invalid. For example: .1 is
incorrect, but 0.1 is fine.
- Using 'e' when not preceded by a digit. For example, e4 isn't valid but 1e4 is.
The deserialize method should properly handle these problems and when there's an
error, it returns a Value object with the NULLVal type. Check this type to see
if there's an error.
Issue reported by Imre Pechan.
7/21/2014:
----------
* All asserts removed and replaced with exceptions, as per request from many users.
Instead of asserting, functions will throw a std::runtime_error with
appropriate error message.
* Added versions of the Value::To* functions that take a default parameter.
In the event of an error (like calling Value::ToInt() when it's type is an Object),
the default value you specified will be returned. Courtesy of PeterSvP
* Fixed type mismatch warning, courtesy of Per Rovegård
* Initialized some variables in the various Value constructors to defaults for
better support with full blast g++ warnings, courtesy of Mark Odell.
* Changed Value::ToString to return a const std::string& instead of std::string
to avoid unnecessary copying.
* Improved some commenting
* Fixed a bug where a capital E for scientific notation numbers wasn't
recognized, only lowercase e.
* VASTLY OVERHAULED AND IMPROVED THE README FILE, PLEASE CONSULT IT FOR
IN DEPTH USAGE AND EXAMPLES.
2/8/2014:
---------
MAJOR BUG FIXES, all courtesy of Per Rovegård, Ph.D.
* Feature request: HasKey and HasKeys added to Value for convenience and
to avoid having to make a temporary object.
* Strings should now be properly unescaped. Previously, as an example, the
string "\/Date(1390431949211+0100)\/\" would be parsed as
\/Date(1390431949211+0100)\/. The string is now properly parsed as
/Date(1390431949211+0100)/.
As per http://www.json.org the other escape characters including
\u+4 hex digits will now be properly unescaped. So for example,
\u0061 now becomes "A".
* Serialize now supports serializing a toplevel array (which is valid JSON).
The parameter it takes is now a Value, but existing code doesn't
need to be changed.
* Fixed bug with checking for proper opening/closing sequence for braces/brackets.
Previously, this code:
const char *json = "{\"arr\":[{}}]}";
auto val = json::Deserialize(json);
worked fine with no errors. That's a bug. I did a major overhaul so that
now improperly formatted pairs will now correctly result in an error.
* Made internal deserialize methods static
1/30/2014:
----------
* Changed #pragma once to the standard #ifndef header guard style for
better compatibility.
* Added a [] operator for Value that takes a const char* as an argument
to avoid having to explicitly (and annoyingly) cast to std::string.
Thus, my_value["asdf"] = "a string" should now work fine.
The same has been added to the Object class.
* Added non-operator methods of casting a Value to int/string/bool/etc.
Implicitly casting a Value to a std::string doesn't work as per C++
rules. As such, previously to assign a Value to a std::string you
had to do:
my_std_string = (std::string)my_value;
You can now instead do:
my_std_string = my_value.ToString();
If you want more information on why this can't be done, please read
this topic for more details:
http://stackoverflow.com/questions/3518145/c-overloading-conversion-operator-for-custom-type-to-stdstring
1/27/2014
----------
* Deserialize will now return a NULLVal Value instance if there was an
error instead of asserting. This way you can handle however you want to
invalid JSON being passed in. As a top level object must be either an
array or an object, a NULL value return indicates an invalid result.
1/11/2014
---------
* Major bug fix: Strings containing []{} characters could cause
parsing errors under certain conditions. I've just tested
the class parsing a 300KB JSON file with all manner of bizarre
characters and permutations and it worked, so hopefully this should
be the end of "major bug" fixes.
1/10/2014
---------
Bug fixes courtesy of Gerry Beauregard:
* Pretty big bug: was using wrong string paramter in ::Deserialize
and furthermore it wasn't being trimmed.
* Object::HasKeys now casts the return value to avoid compiler warnings.
* Slight optimization to the Trim function
* Made asserts in ::Deserialize easier to read
1/9/2014
--------
* Major bug fix: for JSON strings containing \" (as in, two characters,
not the escaped " character), the lib would mess up and not parse
correctly.
* Major bug fix: I erroneously was assuming that all root JSON types
had to be an object. This was an oversight, as a root JSON
object can be an array. I have therefore changed the Deserialize
method to return a json::Value rather than a json::Object. This
will NOT impact any existing code you have, as a json::Value will
cast to a json::Object (if it is indeed an object). But for
correctness, you should be using json::Value = Deserialize...
The Value type can be checked if it's an array (or any other type),
and furthermore can even be accessed with the [] operator for
convenience.
* I've made the NULL value type set numeric fields to 0 and bool to false.
This is for convenience for using the NULL type as a default return
value in your code.
* asserts added to casting (Gerry Beauregard)
* Added method HasKeys to json::Object which will check if all the keys
specified are in the object, returning the index of the first key
not found or -1 if all found (hoppe).
1/4/2014
--------
* Fixed bug where booleans were being parsed as doubles (Gerry Beauregard).
1/2/2014 v3
------------
* More missing headers added for VisualStudio 2012
* Switched to snprintf instead of sprintf (or sprintf_s in MSVC)
1/2/2014 v2
-----------
* Added yet more missing headers for compiling on GNU and Linux systems
* Made Deserialize copy the passed in string so it won't mangle it
1/2/2014
--------
* Fixed previous changelog years. Got ahead of myself and marked them
as 2014 when they were in fact done in 2013.
* Added const version of [] to Array/Object/Value
* Removed C++11 requirements, should work with older compilers
(thanks to Meng Wang for pointing that out)
* Made ValueMap and ValueVector typedefs in Object/Value public
so you can actually iterate over the class
* Added HasKey and HasValue to Object/Array for convenience
(note this could have been done comparing .find to .end)
12/29/2013 v2
-------------
* Added .size() field to Value. Returns 1 for non Array/Object types,
otherwise the number of elements contained.
* Added .find() to Object to search for a key. Returns Object::end()
if not found, otherwise the Value.
Example: bool found = my_obj.find("some key") != my_obj.end();
* Added .find() to Array to search for a value. Just a convenience
wrapper for std::find(Array::begin(), Array::end(), Value)
* Added ==, !=, <, >, <=, >= operators to Object/Array/Value.
For Objects/Arrays, the operators function just like they do for a
std::map and std::vector, respectively.
* Added IsNumeric to Value to indicate if it's an int/float/double type.
12/29/2013
----------
* Added the DoubleVal type which stores, you guessed it, double values.
* Bug fix for floats with an exact integer value. Now, setting any numerical
field will also set the fields for the other numerical types. So if you
have obj["value"] = 12, then the int/float/double cast methods will
return 12/12.0f/12.0. Previously, in the example above, only the int
value was set, making a cast to float return 0.
* Bug fix for deserializing JSON strings that contained large integer values.
Now if the numerical value of a key in a JSON string contains a number
less than INT_MIN or greater than INT_MAX it will be stored as a double.
Note that as mentioned above, all numerical fields are set.
* Should work fine with scientific notation values now.
12/28/2013
----------
* Fixed a bug where if there were spaces around values or key names in a JSON
string passed in to Deserialize, invalid results or asserts would occur.
(Fix courtesy of Gerry Beauregard)
* Added method named "Clear()" to Object/Array/Value to reset state
* Added license to header file for easyness (totally valid word).
*/
#ifndef __SUPER_EASY_JSON_H__
#define __SUPER_EASY_JSON_H__
#include <vector>
#include <map>
#include <string>
#include <stdexcept>
// PLEASE SEE THE README FOR USAGE INFORMATION AND EXAMPLES. Comments will be kept to a minimum to reduce clutter.
namespace json
{
enum ValueType
{
NULLVal,
StringVal,
IntVal,
FloatVal,
DoubleVal,
ObjectVal,
ArrayVal,
BoolVal
};
class Value;
// Represents a JSON object which is of the form {string:value, string:value, ...} Where string is the "key" name and is
// of the form "" or "characters". Value is either of: string, number, object, array, boolean, null
class Object
{
public:
// This is the type used to store key/value pairs. If you want to get an iterator for this class to iterate over its members,
// use this.
// For example: Object::ValueMap::iterator my_iterator;
typedef std::map<std::string, Value> ValueMap;
protected:
ValueMap mValues;
public:
Object();
Object(const Object& obj);
Object& operator =(const Object& obj);
friend bool operator ==(const Object& lhs, const Object& rhs);
inline friend bool operator !=(const Object& lhs, const Object& rhs) {return !(lhs == rhs);}
friend bool operator <(const Object& lhs, const Object& rhs);
inline friend bool operator >(const Object& lhs, const Object& rhs) {return operator<(rhs, lhs);}
inline friend bool operator <=(const Object& lhs, const Object& rhs) {return !operator>(lhs, rhs);}
inline friend bool operator >=(const Object& lhs, const Object& rhs) {return !operator<(lhs, rhs);}
// Just like a std::map, you can get the value for a key by using the index operator. You could also
// use this to insert a value if it doesn't exist, or overwrite it if it does. Example:
// Value my_val = my_object["some key name"];
// my_object["some key name"] = "overwriting the value with this new string value";
// my_object["new key name"] = "a new key being inserted";
Value& operator [](const std::string& key);
const Value& operator [](const std::string& key) const;
Value& operator [](const char* key);
const Value& operator [](const char* key) const;
ValueMap::const_iterator begin() const;
ValueMap::const_iterator end() const;
ValueMap::iterator begin();
ValueMap::iterator end();
// Find will return end() if the key can't be found, just like std::map does. ->first will be the key (a std::string),
// ->second will be the Value.
ValueMap::iterator find(const std::string& key);
ValueMap::const_iterator find(const std::string& key) const;
// Convenience wrapper to search for a key
bool HasKey(const std::string& key) const;
// Checks if the object contains all the keys in the array. If it does, returns -1.
// If it doesn't, returns the index of the first key it couldn't find.
int HasKeys(const std::vector<std::string>& keys) const;
int HasKeys(const char* keys[], int key_count) const;
// Removes all values and resets the state back to default
void Clear();
size_t size() const {return mValues.size();}
};
// Represents a JSON Array which is of the form [value, value, ...] where value is either of: string, number, object, array, boolean, null
class Array
{
public:
// This is the type used to store values. If you want to get an iterator for this class to iterate over its members,
// use this.
// For example: Array::ValueVector::iterator my_array_iterator;
typedef std::vector<Value> ValueVector;
protected:
ValueVector mValues;
public:
Array();
Array(const Array& a);
Array& operator =(const Array& a);
friend bool operator ==(const Array& lhs, const Array& rhs);
inline friend bool operator !=(const Array& lhs, const Array& rhs) {return !(lhs == rhs);}
friend bool operator <(const Array& lhs, const Array& rhs);
inline friend bool operator >(const Array& lhs, const Array& rhs) {return operator<(rhs, lhs);}
inline friend bool operator <=(const Array& lhs, const Array& rhs) {return !operator>(lhs, rhs);}
inline friend bool operator >=(const Array& lhs, const Array& rhs) {return !operator<(lhs, rhs);}
Value& operator[] (size_t i);
const Value& operator[] (size_t i) const;
ValueVector::const_iterator begin() const;
ValueVector::const_iterator end() const;
ValueVector::iterator begin();
ValueVector::iterator end();
// Just a convenience wrapper for doing a std::find(Array::begin(), Array::end(), Value)
ValueVector::iterator find(const Value& v);
ValueVector::const_iterator find(const Value& v) const;
// Convenience wrapper to check if a value is in the array
bool HasValue(const Value& v) const;
// Removes all values and resets the state back to default
void Clear();
void push_back(const Value& v);
void insert(size_t index, const Value& v);
size_t size() const;
};
// Represents a JSON value which is either of: string, number, object, array, boolean, null
class Value
{
protected:
ValueType mValueType;
int mIntVal;
float mFloatVal;
double mDoubleVal;
std::string mStringVal;
Object mObjectVal;
Array mArrayVal;
bool mBoolVal;
public:
Value() : mValueType(NULLVal), mIntVal(0), mFloatVal(0), mDoubleVal(0), mBoolVal(false) {}
Value(int v) : mValueType(IntVal), mIntVal(v), mFloatVal((float)v), mDoubleVal((double)v), mBoolVal(false) {}
Value(float v) : mValueType(FloatVal), mIntVal((int)v), mFloatVal(v), mDoubleVal((double)v), mBoolVal(false) {}
Value(double v) : mValueType(DoubleVal), mIntVal((int)v), mFloatVal((float)v), mDoubleVal(v), mBoolVal(false) {}
Value(const std::string& v) : mValueType(StringVal), mIntVal(), mFloatVal(), mDoubleVal(), mStringVal(v), mBoolVal(false) {}
Value(const char* v) : mValueType(StringVal), mIntVal(), mFloatVal(), mDoubleVal(), mStringVal(v), mBoolVal(false) {}
Value(const Object& v) : mValueType(ObjectVal), mIntVal(), mFloatVal(), mDoubleVal(), mObjectVal(v), mBoolVal(false) {}
Value(const Array& v) : mValueType(ArrayVal), mIntVal(), mFloatVal(), mDoubleVal(), mArrayVal(v), mBoolVal(false) {}
Value(bool v) : mValueType(BoolVal), mIntVal(), mFloatVal(), mDoubleVal(), mBoolVal(v) {}
Value(const Value& v);
// Use this to determine the underlying type that this Value class represents. It will be one of the
// ValueType enums as defined at the top of this file.
ValueType GetType() const {return mValueType;}
// Convenience method that checks if this type is an int/double/float
bool IsNumeric() const {return (mValueType == IntVal) || (mValueType == DoubleVal) || (mValueType == FloatVal);}
Value& operator =(const Value& v);
friend bool operator ==(const Value& lhs, const Value& rhs);
inline friend bool operator !=(const Value& lhs, const Value& rhs) {return !(lhs == rhs);}
friend bool operator <(const Value& lhs, const Value& rhs);
inline friend bool operator >(const Value& lhs, const Value& rhs) {return operator<(rhs, lhs);}
inline friend bool operator <=(const Value& lhs, const Value& rhs) {return !operator>(lhs, rhs);}
inline friend bool operator >=(const Value& lhs, const Value& rhs) {return !operator<(lhs, rhs);}
// If this value represents an object or array, you can use the [] indexing operator
// just like you would with the native json::Array or json::Object classes.
// THROWS A std::runtime_error IF NOT AN ARRAY OR OBJECT.
Value& operator [](size_t idx);
const Value& operator [](size_t idx) const;
Value& operator [](const std::string& key);
const Value& operator [](const std::string& key) const;
Value& operator [](const char* key);
const Value& operator [](const char* key) const;
// If this value represents an object, these methods let you check if a single key or an array of
// keys is contained within it.
// THROWS A std::runtime_error IF NOT AN OBJECT.
bool HasKey(const std::string& key) const;
int HasKeys(const std::vector<std::string>& keys) const;
int HasKeys(const char* keys[], int key_count) const;
// non-operator versions, **will throw a std::runtime_error if invalid with an appropriate error message**
int ToInt() const;
float ToFloat() const;
double ToDouble() const;
bool ToBool() const;
const std::string& ToString() const;
Object ToObject() const;
Array ToArray() const;
// These versions do the same as above but will return your specified default value in the event there's an error, and thus **don't** throw an exception.
int ToInt(int def) const {return IsNumeric() ? mIntVal : def;}
float ToFloat(float def) const {return IsNumeric() ? mFloatVal : def;}
double ToDouble(double def) const {return IsNumeric() ? mDoubleVal : def;}
bool ToBool(bool def) const {return (mValueType == BoolVal) ? mBoolVal : def;}
const std::string& ToString(const std::string& def) const {return (mValueType == StringVal) ? mStringVal : def;}
// Please note that as per C++ rules, implicitly casting a Value to a std::string won't work.
// This is because it could use the int/float/double/bool operators as well. So to assign a
// Value to a std::string you can either do:
// my_string = (std::string)my_value
// Or you can now do:
// my_string = my_value.ToString();
//
operator int() const;
operator float() const;
operator double() const;
operator bool() const;
operator std::string() const;
operator Object() const;
operator Array() const;
// Returns 1 for anything not an Array/ObjectVal
size_t size() const;
// Resets the state back to default, aka NULLVal
void Clear();
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Converts a JSON Object or Array instance into a JSON string representing it. RETURNS EMPTY STRING ON ERROR.
// As per JSON specification, a JSON data structure must be an array or an object. Thus, you must either pass in a
// json::Array, json::Object, or a json::Value that has an Array or Object as its underlying type.
std::string Serialize(const Value& obj);
// If there is an error, Value will be NULLVal. Pass in a valid JSON string (such as one returned from Serialize, or obtained
// elsewhere) to receive a Value in return that represents the JSON structure. Check the type of Value by calling GetType().
// It will be ObjectVal or ArrayVal (or NULLVal if invalid JSON). The Value class contains the operator [] for indexing in the
// case that the underlying type is an object or array. You may, if you prefer, create an object or array from the Value returned
// by this method by simply passing it into the constructor.
Value Deserialize(const std::string& str);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline bool operator ==(const Object& lhs, const Object& rhs)
{
return lhs.mValues == rhs.mValues;
}
inline bool operator <(const Object& lhs, const Object& rhs)
{
return lhs.mValues < rhs.mValues;
}
inline bool operator ==(const Array& lhs, const Array& rhs)
{
return lhs.mValues == rhs.mValues;
}
inline bool operator <(const Array& lhs, const Array& rhs)
{
return lhs.mValues < rhs.mValues;
}
/* When comparing different numeric types, this method works the same as if you compared different numeric types
on your own. Thus it performs the same as if you, for example, did this:
int a = 1;
float b = 1.1f;
bool equivalent = a == b;
The same logic applies to the other comparison operators.
*/
inline bool operator ==(const Value& lhs, const Value& rhs)
{
if ((lhs.mValueType != rhs.mValueType) && !lhs.IsNumeric() && !rhs.IsNumeric())
return false;
switch (lhs.mValueType)
{
case StringVal : return lhs.mStringVal == rhs.mStringVal;
case IntVal : if (rhs.GetType() == FloatVal)
return lhs.mIntVal == rhs.mFloatVal;
else if (rhs.GetType() == DoubleVal)
return lhs.mIntVal == rhs.mDoubleVal;
else if (rhs.GetType() == IntVal)
return lhs.mIntVal == rhs.mIntVal;
else
return false;
case FloatVal : if (rhs.GetType() == FloatVal)
return lhs.mFloatVal == rhs.mFloatVal;
else if (rhs.GetType() == DoubleVal)
return lhs.mFloatVal == rhs.mDoubleVal;
else if (rhs.GetType() == IntVal)
return lhs.mFloatVal == rhs.mIntVal;
else
return false;
case DoubleVal : if (rhs.GetType() == FloatVal)
return lhs.mDoubleVal == rhs.mFloatVal;
else if (rhs.GetType() == DoubleVal)
return lhs.mDoubleVal == rhs.mDoubleVal;
else if (rhs.GetType() == IntVal)
return lhs.mDoubleVal == rhs.mIntVal;
else
return false;
case BoolVal : return lhs.mBoolVal == rhs.mBoolVal;
case ObjectVal : return lhs.mObjectVal == rhs.mObjectVal;
case ArrayVal : return lhs.mArrayVal == rhs.mArrayVal;
default:
return true;
}
}
inline bool operator <(const Value& lhs, const Value& rhs)
{
if ((lhs.mValueType != rhs.mValueType) && !lhs.IsNumeric() && !rhs.IsNumeric())
return false;
switch (lhs.mValueType)
{
case StringVal : return lhs.mStringVal < rhs.mStringVal;
case IntVal : if (rhs.GetType() == FloatVal)
return lhs.mIntVal < rhs.mFloatVal;
else if (rhs.GetType() == DoubleVal)
return lhs.mIntVal < rhs.mDoubleVal;
else if (rhs.GetType() == IntVal)
return lhs.mIntVal < rhs.mIntVal;
else
return false;
case FloatVal : if (rhs.GetType() == FloatVal)
return lhs.mFloatVal < rhs.mFloatVal;
else if (rhs.GetType() == DoubleVal)
return lhs.mFloatVal < rhs.mDoubleVal;
else if (rhs.GetType() == IntVal)
return lhs.mFloatVal < rhs.mIntVal;
else
return false;
case DoubleVal : if (rhs.GetType() == FloatVal)
return lhs.mDoubleVal < rhs.mFloatVal;
else if (rhs.GetType() == DoubleVal)
return lhs.mDoubleVal < rhs.mDoubleVal;
else if (rhs.GetType() == IntVal)
return lhs.mDoubleVal < rhs.mIntVal;
else
return false;
case BoolVal : return lhs.mBoolVal < rhs.mBoolVal;
case ObjectVal : return lhs.mObjectVal < rhs.mObjectVal;
case ArrayVal : return lhs.mArrayVal < rhs.mArrayVal;
default:
return true;
}
}
}
#endif //__SUPER_EASY_JSON_H__

View File

@ -1,212 +0,0 @@
################################################################################
# libresapi.pro #
# Copyright (C) 2018, 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/>. #
################################################################################
!include("../../retroshare.pri"): error("Could not include file ../../retroshare.pri")
TEMPLATE = lib
CONFIG += staticlib
CONFIG -= qt
TARGET = resapi
TARGET_PRL = libresapi
DESTDIR = lib
!include(use_libresapi.pri):error("Including")
INCLUDEPATH += ../../libretroshare/src
libresapilocalserver {
SOURCES *= api/ApiServerLocal.cpp
HEADERS *= api/ApiServerLocal.h
}
libresapi_settings {
SOURCES += api/SettingsHandler.cpp
HEADERS += api/SettingsHandler.h
}
libresapihttpserver {
linux-* {
webui_files.path = "$${DATA_DIR}/webui"
webui_files.files = webui/app.js webui/app.css webui/index.html
INSTALLS += webui_files
webui_img_files.path = "$${DATA_DIR}/webui/img"
webui_img_files.files = ../../retroshare-gui/src/gui/images/logo/logo_splash.png
INSTALLS += webui_img_files
# create dummy files, we need it to include files on first try
system(webui-src/make-src/build.sh .)
WEBUI_SRC_SCRIPT = webui-src/make-src/build.sh
WEBUI_SRC_HTML = $$WEBUI_SRC_SCRIPT
WEBUI_SRC_HTML += webui-src/app/assets/index.html
WEBUI_SRC_JS = $$WEBUI_SRC_SCRIPT
WEBUI_SRC_JS += webui-src/app/accountselect.js
WEBUI_SRC_JS += webui-src/app/adddownloads.js
WEBUI_SRC_JS += webui-src/app/addidentity.js
WEBUI_SRC_JS += webui-src/app/addpeer.js
WEBUI_SRC_JS += webui-src/app/chat.js
WEBUI_SRC_JS += webui-src/app/createlogin.js
WEBUI_SRC_JS += webui-src/app/downloads.js
WEBUI_SRC_JS += webui-src/app/forums.js
WEBUI_SRC_JS += webui-src/app/home.js
WEBUI_SRC_JS += webui-src/app/identities.js
WEBUI_SRC_JS += webui-src/app/main.js
WEBUI_SRC_JS += webui-src/app/menudef.js
WEBUI_SRC_JS += webui-src/app/menu.js
WEBUI_SRC_JS += webui-src/app/mithril.js
WEBUI_SRC_JS += webui-src/app/mithril.min.js
WEBUI_SRC_JS += webui-src/app/peers.js
WEBUI_SRC_JS += webui-src/app/retroshare.js
WEBUI_SRC_JS += webui-src/app/search.js
WEBUI_SRC_JS += webui-src/app/searchresult.js
WEBUI_SRC_JS += webui-src/app/servicecontrol.js
WEBUI_SRC_JS += webui-src/app/settings.js
WEBUI_SRC_JS += webui-src/app/waiting.js
WEBUI_SRC_CSS = $$WEBUI_SRC_SCRIPT
WEBUI_SRC_CSS += webui-src/app/green-black.scss
WEBUI_SRC_CSS += webui-src/app/_reset.scss
WEBUI_SRC_CSS += webui-src/app/_chat.sass
WEBUI_SRC_CSS += webui-src/app/main.sass
create_webfiles_html.output = webui/index.html
create_webfiles_html.input = WEBUI_SRC_HTML
create_webfiles_html.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ index.html .
create_webfiles_html.variable_out = JUNK
create_webfiles_html.CONFIG = combine no_link
create_webfiles_js.output = webui/app.js
create_webfiles_js.input = WEBUI_SRC_JS
create_webfiles_js.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ app.js .
create_webfiles_js.variable_out = JUNK
create_webfiles_js.CONFIG = combine no_link
create_webfiles_css.output = webui/app.css
create_webfiles_css.input = WEBUI_SRC_CSS
create_webfiles_css.commands = sh $$_PRO_FILE_PWD_/webui-src/make-src/build.sh $$_PRO_FILE_PWD_ app.css .
create_webfiles_css.variable_out = JUNK
create_webfiles_css.CONFIG = combine no_link
QMAKE_EXTRA_COMPILERS += create_webfiles_html create_webfiles_js create_webfiles_css
}
appveyor {
DEFINES *= WINDOWS_SYS
INCLUDEPATH += . $$INC_DIR
PRO_PATH=$$shell_path($$_PRO_FILE_PWD_)
MAKE_SRC=$$shell_path($$PRO_PATH/webui-src/make-src)
#create_webfiles.commands = $$MAKE_SRC\\build.bat $$PRO_PATH
#QMAKE_EXTRA_TARGETS += create_webfiles
#PRE_TARGETDEPS += create_webfiles
QMAKE_POST_LINK=$$MAKE_SRC\\build.bat $$PRO_PATH
# create dummy files
system($$MAKE_SRC\\init.bat .)
}
win32 {
DEFINES *= WINDOWS_SYS
INCLUDEPATH += . $$INC_DIR
PRO_PATH=$$shell_path($$_PRO_FILE_PWD_)
MAKE_SRC=$$shell_path($$PRO_PATH/webui-src/make-src)
QMAKE_POST_LINK=$$MAKE_SRC/build.sh $$PRO_PATH
# create dummy files
system($$MAKE_SRC/init.sh .)
}
linux {
CONFIG += link_pkgconfig
PKGCONFIG *= libmicrohttpd
} else {
mac {
INCLUDEPATH += . $$INC_DIR
#for(lib, LIB_DIR):exists($$lib/libmicrohttpd.a){ LIBS *= $$lib/libmicrohttpd.a}
LIBS *= -lmicrohttpd
} else {
LIBS *= -lmicrohttpd
}
}
SOURCES += \
api/ApiServerMHD.cpp
HEADERS += \
api/ApiServerMHD.h
}
SOURCES += \
api/ApiServer.cpp \
api/json.cpp \
api/JsonStream.cpp \
api/ResourceRouter.cpp \
api/PeersHandler.cpp \
api/Operators.cpp \
api/IdentityHandler.cpp \
api/ForumHandler.cpp \
api/ServiceControlHandler.cpp \
api/StateTokenServer.cpp \
api/GxsResponseTask.cpp \
api/FileSearchHandler.cpp \
api/TransfersHandler.cpp \
api/RsControlModule.cpp \
api/GetPluginInterfaces.cpp \
api/ChatHandler.cpp \
api/LivereloadHandler.cpp \
api/TmpBlobStore.cpp \
util/ContentTypes.cpp \
api/ApiPluginHandler.cpp \
api/ChannelsHandler.cpp \
api/StatsHandler.cpp \
api/FileSharingHandler.cpp
HEADERS += \
api/ApiServer.h \
api/json.h \
api/JsonStream.h \
api/ApiTypes.h \
api/ResourceRouter.h \
api/PeersHandler.h \
api/Operators.h \
api/IdentityHandler.h \
api/ForumHandler.h \
api/ServiceControlHandler.h \
api/GxsMetaOperators.h \
api/StateTokenServer.h \
api/GxsResponseTask.h \
api/Pagination.h \
api/FileSearchHandler.h \
api/TransfersHandler.h \
api/RsControlModule.h \
api/GetPluginInterfaces.h \
api/ChatHandler.h \
api/LivereloadHandler.h \
api/TmpBlobStore.h \
util/ContentTypes.h \
api/ApiPluginHandler.h \
api/ChannelsHandler.h \
api/StatsHandler.h \
api/FileSharingHandler.h

View File

@ -1,54 +0,0 @@
################################################################################
# uselibresapi.pri #
# Copyright (C) 2018, 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/>. #
################################################################################
DEPENDPATH *= $$system_path($$clean_path($$PWD/../../libresapi/src))
INCLUDEPATH *= $$system_path($$clean_path($${PWD}/../../libresapi/src))
LIBS *= -L$$system_path($$clean_path($${OUT_PWD}/../../libresapi/src/lib/)) -lresapi
!equals(TARGET, resapi):PRE_TARGETDEPS *= $$system_path($$clean_path($${OUT_PWD}/../../libresapi/src/lib/libresapi.a))
!include("../../libretroshare/src/use_libretroshare.pri"):error("Including")
sLibs =
mLibs =
dLibs =
libresapilocalserver {
CONFIG *= qt
QT *= network
}
libresapi_settings {
CONFIG *= qt
QT *= core
}
libresapihttpserver {
mLibs *= microhttpd
}
static {
sLibs *= $$mLibs
} else {
dLibs *= $$mLibs
}
LIBS += $$linkStaticLibs(sLibs)
PRE_TARGETDEPS += $$pretargetStaticLibs(sLibs)
LIBS += $$linkDynamicLibs(dLibs)

View File

@ -1,109 +0,0 @@
/*******************************************************************************
* libresapi/util/ContentTypes.cpp *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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/>. *
* *
*******************************************************************************/
#include "ContentTypes.h"
#include <fstream>
#include <cctype>
#include <algorithm>
RsMutex ContentTypes::ctmtx = RsMutex("CTMTX");
std::map<std::string, std::string> ContentTypes::cache;
bool ContentTypes::inited = false;
#ifdef WINDOWS_SYS
//Next to the executable
const char* ContentTypes::filename = ".\\mime.types";
#else
const char* ContentTypes::filename = "/etc/mime.types";
#endif
std::string ContentTypes::cTypeFromExt(const std::string &extension)
{
if(extension.empty())
return DEFAULTCT;
RsStackMutex mtx(ctmtx);
if(!inited)
addBasic();
std::string extension2(extension); //lower case
std::transform(extension2.begin(), extension2.end(), extension2.begin(),::tolower);
//looking into the cache
std::map<std::string,std::string>::iterator it;
it = cache.find(extension2);
if (it != cache.end())
{
std::cout << "Mime " + it->second + " for extension ." + extension2 + " was found in cache" << std::endl;
return it->second;
}
//looking into mime.types
std::string line;
std::string ext;
std::ifstream file(filename);
while(getline(file, line))
{
if(line.empty() || line[0] == '#') continue;
size_t i = line.find_first_of("\t ");
size_t j;
while(i != std::string::npos) //tokenize
{
j = i;
i = line.find_first_of("\t ", i+1);
if(i == std::string::npos)
ext = line.substr(j+1);
else
ext = line.substr(j+1, i-j-1);
if(extension2 == ext)
{
std::string mime = line.substr(0, line.find_first_of("\t "));
cache[extension2] = mime;
std::cout << "Mime " + mime + " for extension ." + extension2 + " was found in mime.types" << std::endl;
return mime;
}
}
}
//nothing found
std::cout << "Mime for " + extension2 + " was not found in " + filename + " falling back to " << DEFAULTCT << std::endl;
cache[extension2] = DEFAULTCT;
return DEFAULTCT;
}
//Add some basic content-types before first use.
//It keeps webui usable in the case of mime.types file not exists.
void ContentTypes::addBasic()
{
inited = true;
cache["html"] = "text/html";
cache["css"] = "text/css";
cache["js"] = "text/javascript";
cache["jsx"] = "text/jsx";
cache["png"] = "image/png";
cache["jpg"] = "image/jpeg";
cache["jpeg"] = "image/jpeg";
cache["gif"] = "image/gif";
}

View File

@ -1,44 +0,0 @@
/*******************************************************************************
* libresapi/util/ContentTypes.h *
* *
* LibResAPI: API for local socket server *
* *
* Copyright 2018 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 CONTENTTYPES_H
#define CONTENTTYPES_H
#include <util/rsthreads.h>
#include <map>
#include <string>
#define DEFAULTCT "application/octet-stream"
class ContentTypes
{
public:
static std::string cTypeFromExt(const std::string& extension);
private:
static std::map<std::string, std::string> cache;
static RsMutex ctmtx;
static const char* filename;
static bool inited;
static void addBasic();
};
#endif // CONTENTTYPES_H

View File

@ -1,2 +0,0 @@
node_modules/*
public/*

View File

@ -1,78 +0,0 @@
A new approach to build a webinterface for RS
=============================================
1. get JSON encoded data from the backend, data contains a state token
2. render data with mithril.js
3. ask the backend if the state token from step 1 expired. If yes, then start again with step 1.
Steps 1. and 3. are common for most things, only Step 2. differs. This allows to re-use code for steps 1. and 3.
BUILD / DEVELOPMENT
------------
- install tools
sudo apt-get install TODO (insert package names for nodejs, ruby, sass here)
- run this once in webui-src directory, to install more tools
npm install
- start build
npm run watch
- the build process watches files for changes, and rebuilds and reloads the page. Build output is in ./public
- use the --webinterface 9090 command line parameter to enable webui in retroshare-nogui
- set the --docroot parameter of retroshare-nogui to point to the "libresapi/src/webui-src/public" directory
(or symlink from /usr/share/retroshare/webui on Linux, ./webui on Windows)
- retroshare-gui does not have a --docroot parameter. Use symlinks then.
CONTRIBUTE
----------
- if you are a web developer or want to become one
get in contact!
- lots of work to do, i need you!
TODO
----
- [ ] make stylesheets or find reusable sass/css components
google material design has nice rules for color, spacing and everything: https://www.google.de/design/spec/material-design/introduction.html
- [ ] find icons, maybe use google material design iconfont
- [X] use urls/mithril routing for the menu. urls could replace state stored in rs.content
- [X] drag and drop private key upload and import
- [X] link from peer location to chat (use urls and mithril routing)
- [X] add/remove friend, own cert
- [X] downloads, search
- [ ] make reusable infinite list controller, the js part to load data from Pagination.h (tweak Pagination.h to make everything work)
should provide forward, backward and follow-list-end
- [ ] backend: view/create identities
- [ ] backend: chat lobby participants list
- [X] chat: send_message
- [ ] backend: chat typing notifications
- [ ] make routines to handle retroshare links
- [ ] backend: edit shared folders
- [ ] backend: view shared files
- [ ] redirect if a url is not usable in the current runstate (e.g. redirect from login page to home page, after login)
- [X] sort friendslist
need 4 master
-------------
- [X] unsubscribe lobby
- [X] unread chat message counter in menu
- [X] list chat-lobby participants
- [X] creating app.js on build (no need for npm on regulary build)
url-handling (brainstorming)
----------------------------
* normal weblinks (bbcode? => only with gui support)
* rslinks
- files
- (chatrooms)
- forum retroshare://forum?name=Developers%27%20Discussions&id=8fd22bd8f99754461e7ba1ca8a727995
- own cert link (paste)
- cert-links
- searches
- [X] downloads pasten
- uploads?
* enter / display urls
- use urls in href like used for input (so it can be copy-link)
- handle RS-urls with javascript, other with target _blank
* smilies
* Bilder
* KEEP IT SIMPLE

View File

@ -1,81 +0,0 @@
.chat
$color: black
$header_height: 50px
$left_width: 200px
$right_width: 200px
$input_height: 100px
padding: 15px
&.container
height: 100%
padding: 0px
position: relative
box-sizing: border-box
&.header
position: absolute
top: 0px
left: 0px
right: 0px
height: $header_height
background-color: $color
border-bottom: solid 1px gray
box-sizing: border-box
&.left
position: absolute
top: $header_height
bottom: 0px
left: 0px
width: $left_width
//border-right: solid 1px gray
box-sizing: border-box
background-color: black
&.right
position: absolute
top: $header_height
right: 0px
bottom: 0px
width: $right_width
box-sizing: border-box
//border-left: solid 1px gray
&.middle
//background-color: blue
position: absolute
top: 0px
margin-top: $header_height
left: $left_width
right: $right_width
box-sizing: border-box
padding: 0px
height: 100%
overflow-y: scroll
&.bottom
position: absolute
bottom: 0px
right: $right_width
left: $left_width
padding: 5px
&.msg
padding: 0px
$author_width: 100px
&.container
position: relative
border-bottom: solid 1px lightgray
padding: 10px
height: unset
//background-color: lime
&.from
position: absolute
width: $author_width
top: 10px
left: 0px
color: white
text-align: right
&.when
float: right
color: lightgray
margin-bottom: 10px
&.text
padding-left: $author_width
top: 0px
left: $author_width
white-space: pre-wrap
height: initial

View File

@ -1,43 +0,0 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@ -1,68 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
function cancel(){
rs.memory("control/locations").curraccount=null;
m.redraw();
}
function selAccount(account){
rs.memory("control/locations").curraccount=account;
m.redraw();
rs.request("control/login", {id: account.id}, function(){
console.log("login sent");
});
}
function curraccount() {
var mem;
mem = rs.memory("control/locations");
if (mem.curraccount === undefined) {
return null;
}
return mem.curraccount;
}
module.exports = {view: function(){
var accounts = rs("control/locations");
if(accounts === undefined || accounts == null){
return m("div", "accounts: waiting_server");
}
if (curraccount() == null) {
return m("div", [
m("h2","login:"),
m("hr"),
accounts.map(function(account){
return [
m("div.btn2", {
onclick: function(){
selAccount(account)
}
},
account.location + " (" + account.name + ")"),
m("br")
]
})
]);
} else {
// rs.untoken("control/password");
return m("div", [
m("div", [
"logging in ...",
m("br"),
"(waiting for password-request)",
]),
/*
m("hr"),
m(".btn2", {
onclick: function() {
cancel();
}
},"Cancel " + curraccount().name + " login "),
*/
]);
}
}
};

View File

@ -1,299 +0,0 @@
var m = require("mithril");
var rs = require("retroshare");
var me = {
toParse: [], // links to parse ( = pasted content)
toConfirm: [], // links to confirm
toAdd: [], // links to add
toResult: [], // Result to show
index: 0,
view: function(){
return m("div", {
style: {
height:"100%",
boxSizing: "border-box",
paddingBottom: "130px",
}
},[
m("h2","add downloads"),
m("hr"),
this.toParse.length
? step2()
: this.toConfirm.length
? step3()
: this.toAdd.length
? step4()
: this.toResult.length
? step5()
: step1()
,
]);
},
parseOne: function(){
if (me.index == null) {
return null;
}
var startindex = me.index;
while (me.toParse.length > me.index && me.index - startindex < 10) {
var src = me.toParse[me.index].split("?",2);
console.log(src);
if (src[0] == "retroshare://file" && src.length == 2) {
var target = {action: "begin"};
var errText = "Error: link missing name and/or hash"
src[1].split("&").map(function(parm){
var pos = parm.indexOf("=");
if (pos >0){
if (parm.substr(0,pos) == "name") {
var sname=decodeURIComponent(parm.substr(pos+1))
if (sname.match("[\\\\/]")) {
errText="name contains illegal char "
+ sname.match("[\\\\/]");
} else {
target.name=sname;
}
} else if (parm.substr(0,pos) == "size") {
target.size=parseFloat(parm.substr(pos+1));
} else if (parm.substr(0,pos) == "hash") {
target.hash=parm.substr(pos+1);
}
}
});
if (target['name'] && target['hash']){
me.toConfirm.push({
text: target.name,
target: target,
confirmed: true,
});
} else {
me.toConfirm.push({
text:errText,
});
}
} else {
me.toConfirm.push({ text: "Error: no Retroshare-file link"})
}
me.index++;
}
if (me.toParse.length > me.index) {
window.setTimeout("require(\"adddownloads\").parseOne()",1);
} else {
me.toParse = [];
console.log(me.toConfirm.length);
}
refresh();
},
addOne: function(){
if (me.index == null) {
cancel();
} else if (me.index >= me.toAdd.length) {
me.toResult=me.toAdd;
me.toAdd=[];
refresh();
} else {
console.log([
me.toAdd[me.index].action,
me.toAdd[me.index].name,
me.toAdd[me.index].size,
me.toAdd[me.index].hash,
]);
refresh();
rs.request("transfers/control_download", me.toAdd[me.index],
function(data,statetoken){
if (me.index != null) {
me.toAdd[me.index].ok=true;
me.index++;
me.addOne();
}
}, {
onfail: function(value){
me.toAdd[me.index].ok=false;
me.toAdd[me.index].debug_msg=value;
me.index++;
me.addOne();
},
onmismatch: function(response){
me.toAdd[me.index].ok=false;
me.toAdd[me.index].debug_msg=response.debug_msg;
me.index++;
me.addOne();
},
}
);
}
}
};
function cancel() {
me.toAdd=[];
me.toConfirm=[];
me.toParse=[];
me.toResult=[];
me.index=null;
refresh();
}
function parseDownloads(){
me.toParse = document.getElementById("txtInput").value.replace("\r\n","\n").split("\n");
var pos;
while ((pos=me.toParse.indexOf(""))>=0) {
me.toParse.splice(pos,1);
}
var parser = document.createElement('a');
me.toConfirm = [];
me.index = 0;
if (me.toParse.length > me.index){
window.setTimeout("require(\"adddownloads\").parseOne()",1);
}
}
function addDownloads(){
me.toConfirm.map(function(item){
if (item.confirmed) {
item.debug_msg="";
me.toAdd.push(item.target);
}
});
me.toConfirm=[];
if (me.toAdd.length > 0){
me.index=0;
window.setTimeout("require(\"adddownloads\").addOne()",1);
} else {
cancel();
}
refresh();
}
function refresh(){
m.startComputation();
m.endComputation();
}
function cancelBtn(){
return m("div.btn2", {
style:{
textAlign: "center",
color: "red",
borderColor: "red",
},
onclick:cancel,
},"cancel");
}
// paste links
function step1(){
m.initControl = "txtInput";
return [
m("h3","step 1 / 5: paste retroshare-links:"),
m("textarea[id=txtInput]", {
style: {
height:"100%",
},
onkeydown: function(event){
if (event.keyCode == 13){
parseDownloads();
}
}
}),
m("div.btn2", {
style:{
textAlign:"center",
},
onclick:parseDownloads,
},"add downloads")
]
}
// parsing links
function step2(){
return [
m("h3","step 2 / 5: parsing input ..."),
m("p",
"parsing " + (me.index) + " / " + me.toParse.length),
cancelBtn(),
]
}
// parsing confirm
function step3(){
return [
m("h3","step 3 / 5: confirm-links:"),
m("ul",
me.toConfirm.map(function(item){
return m("li", {
style:{
color: item.confirmed
? "lime"
: "red"
},
}, item.text);
})
),
m("div.btn2", {
style:{
textAlign:"center",
},
onclick:addDownloads,
},"add green listed downloads"),
cancelBtn(),
]
}
// adding links
function step4(){
return [
m("h3","step 4 / 5: adding downloads:"),
m("p",
"adding " + (me.index) + " / " + me.toParse.length),
m("ul",
me.toAdd.map(function(item){
return m("li", {
style:{
color: item.ok === undefined
? "white"
: item.ok == null
? "grey"
: item.ok
? "lime"
: "red"
},
}, (item.debug_msg ? item.debug_msg + ": " : "") + item.name
+ " " + item.size + " " + item.hash);
})
),
cancelBtn(),
]
}
// show result
function step5(){
return [
m("h3","step 5 / 5: Result:"),
m("p",
"verarbeitet: " + me.toResult.length),
m("ul",
me.toResult.map(function(item){
return m("li", {
style:{
color: item.ok === undefined
? "white"
: item.ok == null
? "grey"
: item.ok
? "lime"
: "red"
},
}, (item.debug_msg ? item.debug_msg + ": " : "") + item.name);
})
),
m("div.btn2", {
style:{
textAlign: "center",
},
onclick: cancel,
},"ok"),
]
}
module.exports = me;

View File

@ -1,54 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
function createidentity(){
var data = {
name: document.getElementById("txtname").value,
pgp_linked: false,
//document.getElementById("chklinked").checked,
};
m.route("/waiting");
rs.request("identity/create_identity",data, function(){
m.route("/identities");
})
}
module.exports = {view: function(){
m.initControl = "txtname";
return m("div",
m("h2","create identity"),
m("hr"),
m("h3","name"),
m("input", {
type: "text",
id: "txtname",
/*
onkeydown: function(event){
if (event.keyCode == 13){
setPasswd(this.value);
sendPassword(needpasswd);
}
}
*/
}),
/*
m("b","linked with pgp-id: "),
m("input", {
type: "checkbox",
id: "chklinked",
style: {
fontweight:"bold",
width: "0%",
}
}),
*/
m("p"," "),
m("input.btn2", {
onclick: createidentity,
type: "button",
value: "create new identity",
})
)
}}

View File

@ -1,151 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
var newkey = "";
var remote = "";
module.exports = {
view: function(){
var key = m.route.param("radix");
var pgp = m.route.param("pgp_id");
var peer_id =m.route.param("peer_id");
if (key===undefined && pgp === undefined) {
var owncert = rs("peers/self/certificate");
if (owncert === undefined ) {
owncert = {cert_string:"< waiting for server ... >"}
}
return m("div", [
m("h2","add new friend (Step 1/3)"),
m("p","Your own key, give it to your friends"),
m("pre", owncert.cert_string),
m("p","paste your friends key below"),
m("textarea", {
ref:"cert",
cols:"70",
rows:"16",
onchange: m.withAttr("value", function(value){newkey=value;})
}),
m("br"),
m("input.btn2",{
type:"button",
value:"read",
onclick: function (){
m.route("/addpeer",{radix:newkey})
},
})
]);
} else if (pgp === undefined) {
rs.request("peers/examine_cert",{cert_string:key},
function(data,responsetoken){
data.radix=key;
m.route("/addpeer", data);
}
);
return m("div", [
m("h2","add new friend (Step 2/3)"),
m("div", "analyse cert, please wait for server ...")
// { data: null, debug_msg: "failed to load certificate ", returncode: "fail" }
]);
} else {
var result = {
cert_string:key ,
flags:{
allow_direct_download:false,
allow_push:false,
// set to false, until the webinterface supports managment of the blacklist/whitelist
require_whitelist: false,
}
};
return m("div",[
m("h2","add new friend (Step 3/3)"),
m("p","Do you want to add "
+ m.route.param("name")
+ " (" + m.route.param("location") + ")"
+ " to your friendslist?"),
m("input.checkbox[type=checkbox]", {
onchange: m.withAttr("checked", function(checked){
result.flags.allow_direct_download=checked;
})
}), "Allow direct downloads from this node",
m("br"),
m("input.checkbox[type=checkbox]", {
onchange: m.withAttr("checked", function(checked){
result.flags.allow_push=checked;
})
}), "Auto download recommended files from this node",
m("div.btn2",{
onclick: function(){
m.route("/waiting");
rs.request("peers",result,function(data, responsetoken){
m.route("/peers");
})
}
},"add to friendslist")
])
}
}
};
/*
return "peers/self/certificate"
RS.request({path: "peers/examine_cert", data: {cert_string: cert_string}}, this.examine_cert_callback);
this.setState({page:"waiting", cert_string: cert_string});
RS.request(
{
path: "peers",
data: {
cert_string: this.state.cert_string,
flags:{
allow_direct_download: this.refs.cb_direct_dl.getDOMNode().checked,
allow_push: this.refs.cb_push.getDOMNode().checked,
// set to false, until the webinterface supports managment of the blacklist/whitelist
require_whitelist: false,
}
}
});
render: function(){
if(this.state.page === "start")
return(
<div>
<p>Your own key, give it to your friends</p>
<OwnCert/>
<p>paste your friends key below</p>
<textarea ref="cert" cols="70" rows="16"></textarea><br/>
<input
type="button"
value="read key"
onClick={this.add_friend_handler}
/>
</div>
);
if(this.state.page === "waiting")
return(
<div>
waiting for response from server...
</div>
);
if(this.state.page === "peer")
return(
<div>
<p>Do you want to add {this.state.data.name} to your friendslist?</p>
<input className="checkbox" type="checkbox" ref="cb_direct_dl"/> Allow direct downloads from this node<br/>
<input className="checkbox" type="checkbox" ref="cb_push"/> Auto download recommended files from this node<br/>
<div onClick={this.final_add_handler} className="btn2">add to friendslist</div>
</div>
);
},
*/

View File

@ -1,23 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>rswebui6</title>
<link rel="stylesheet" href="app.css">
<script src="app.js"></script>
</head>
<body onload="load_ui();">
<div id="main">if app does not load, enable JavaScript!</div>
<script type="text/javascript">
function load_ui(){
var m = require("mithril");
var ui = require("main");
var main = document.getElementById("main");
ui.init(main);
if (m.initControl != undefined) {
m.initControl.focus();
}
}
</script>
</body>
</html>

View File

@ -1,434 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
var msg = null;
var particips = [];
function dspmsg(from, when, text){
return m(".chat.msg.container",[
m(".chat.msg.from", from),
m(".chat.msg.when", when),
m(".chat.msg.text", text),
]);
}
function lobbies(){
return [
rs.list("chat/lobbies",function(lobby){
return m("div.btn",{
title: "topic: " + lobby.topic + "\n"
+ "subscribed: " + lobby.subscribed,
style: {
backgroundColor: lobby.subscribed ? 'blue' : 'darkred',
},
onclick: function(){
m.route("/chat?lobby=" + lobby.chat_id);
}
},
lobby.name + (
lobby.unread_msg_count > 0
? ("(" + lobby.unread_msg_count + ")")
: "")
);
},
rs.sort.bool("is_broadcast",
rs.sort.bool("subscribed",
rs.sort("name")))
),
m("br"),
m("h3","peers:"),
rs.list("peers",function(peer){
return peer.locations.map(function(loc){
if (loc.location == "") {
return [];
};
return m("div.btn",{
style: {
backgroundColor: loc.is_online ? 'blue' : 'darkred',
},
onclick: function(){
m.route("/chat?lobby=" + loc.chat_id);
}
},
peer.name + " / " + loc.location + (
loc.unread_msgs > 0
? ("(" + loc.unread_msgs + ")")
: "")
);
})
})
];
}
function getLobbyDetails(lobbyid){
var lobs = rs("chat/lobbies");
if (lobs === undefined) {
return null;
};
for(var i = 0, l = lobs.length; i < l; ++i) {
if (lobs[i].chat_id == lobbyid) {
return lobs[i];
}
}
var peers = rs("peers");
if (peers === undefined) {
return null;
};
for(var i = 0, l = peers.length; i < l; ++i) {
var peer = peers[i];
for(var i1 = 0, l1 = peer.locations.length; i1 < l1; ++i1) {
if (peer.locations[i1].chat_id == lobbyid) {
return peer.locations[i1];
}
}
}
return null;
}
function find_next(searchValue){
var els = document.getElementsByClassName("chat msg text");
var middle = document.getElementsByClassName("chat middle")[0];
var find_hidden = document.getElementById("LastWordPos");
var start_index = Number(find_hidden.innerText);
if(!Number.isInteger(start_index)){
console.log(start_index + " is Not Integer");
start_index = 0;
}
if(start_index > els.length)
start_index = 0;
console.log(start_index);
for (var i = start_index; i < els.length; i++) {
var start = els[i].innerHTML.indexOf(searchValue);
if ( start > -1) {
//match has been made
middle.scrollTop = els[i].parentElement.offsetTop - middle.clientHeight/2;
var end = searchValue.length + start;
setSelectionRange(els[i], start, end);
find_hidden.innerText = i + 1;
break;
}
}
}
function setSelectionRange(el, start, end) {
if (document.createRange && window.getSelection) {
var range = document.createRange();
range.selectNodeContents(el);
var textNodes = getTextNodesIn(el);
var foundStart = false;
var charCount = 0, endCharCount;
for (var i = 0, textNode; textNode = textNodes[i++]; ) {
endCharCount = charCount + textNode.length;
if (!foundStart && start >= charCount && (start < endCharCount || (start == endCharCount && i <= textNodes.length))) {
range.setStart(textNode, start - charCount);
foundStart = true;
}
if (foundStart && end <= endCharCount) {
range.setEnd(textNode, end - charCount);
break;
}
charCount = endCharCount;
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && document.body.createTextRange) {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(true);
textRange.moveEnd("character", end);
textRange.moveStart("character", start);
textRange.select();
}
}
function getTextNodesIn(node) {
var textNodes = [];
if (node.nodeType == 3) {
textNodes.push(node);
} else {
var children = node.childNodes;
for (var i = 0, len = children.length; i < len; ++i) {
textNodes.push.apply(textNodes, getTextNodesIn(children[i]));
}
}
return textNodes;
}
function sendmsg(msgid){
var txtmsg = document.getElementById("txtNewMsg");
var remove_whitespace = txtmsg.value.replace(/(\r\n|\n|\r|\s)+/g,'');
if( remove_whitespace == '')
return;
rs.request("chat/send_message", {
chat_id: msgid,
msg: txtmsg.value
});
txtmsg.value="";
}
function lobby(lobbyid){
var msgs;
var lobdt = getLobbyDetails(lobbyid);
var info = rs("chat/info/" + lobbyid);
if (lobdt == null || info === undefined) {
return m("div","waiting ...");
}
var mem = rs.memory("chat/info/" + lobbyid);
if (mem.msg === undefined) {
mem.msg = [];
};
var reqData = {};
if (mem.lastKnownMsg != undefined) {
reqData.begin_after = mem.lastKnownMsg;
}
rs.request("chat/messages/" + lobbyid, reqData, function (data) {
if (data.length > 0 ) {
mem.msg = mem.msg.concat(data);
if (mem.msg.length > 0) {
mem.lastKnownMsg = mem.msg[mem.msg.length -1].id;
}
rs.request("chat/mark_chat_as_read/" + lobbyid,{}, null,
{allow: "ok|not_set"});
} else {
mem.msg = [];
}
}, {
onmismatch: function (){},
log:function(){} //no logging (pulling)
});
var intro = [
//m("h2",lobdt.name),
m("p",lobdt.topic ? lobdt.topic: lobdt.location),
m("hr")
]
if (lobdt.subscribed != undefined && !lobdt.subscribed) {
return [
intro,
m("b","select subscribe identity:"),
m("p"),
rs.list("identity/own", function(item){
return m("div.btn2",{
onclick:function(){
console.log("subscribe - id:" + lobdt.id +", "
+ "gxs_id:" + item.gxs_id)
rs.request("chat/subscribe_lobby",{
id:lobdt.id,
gxs_id:item.gxs_id
})
}
},"subscribe as " + item.name);
}),
];
} else {
var cln = document.getElementById("ChatLobbyName");
if (cln != null) {
cln.innerText = lobdt.name;
}
msg = m(".chat.bottom",[
m("div","enter new message, Ctrl+Enter to submit:"),
m("textarea",{
id:"txtNewMsg",
onkeydown: function(event){
if (event.ctrlKey && event.keyCode == 13){
sendmsg(lobbyid);
}
}
}),
m("table.noBorderTable", [
m("tr",[
m("td.noBorderTD",[
m("div.btnSmall", {
style: {textAlign:"center"},
onclick: function(){
var els = document.getElementsByClassName("chat middle");
var el = els[0];
el.scrollTop = el.scrollHeight - el.clientHeight;
}
},"bottom")
]),
m("td.noBorderTD",[
m("div.btnSmall", {
style: {textAlign:"center"},
onclick: function(){
sendmsg(lobbyid);
}
},"submit")
]),
m("td.noBorderTD",[
m("input",{
id:"txtMsgKeyword"
})
]),
m("td.noBorderTD", [
m("div.btnSmall", {
style: {textAlign:"center"},
onclick: function(){
var key = document.getElementById("txtMsgKeyword");
var txtkeyword = key.value;
find_next(txtkeyword);
}
},"Find")
])
])
]),
m("div.hidden", {
id: "LastWordPos"
},""
),
]);
}
if (lobdt.subscribed != undefined
&& lobdt.subscribed
&& !lobdt.is_broadcast
) {
//set participants
particips = [
m("div.btn", {
style: {
"text-align":"center"
},
onclick: function (){
rs.request("chat/unsubscribe_lobby",{
id:lobdt.id,
});
m.route("/chat");
}
},"unsubscribe"),
m("div.btn", {
style: {
"text-align":"center"
},
onclick: function (){
rs.request("chat/clear_lobby",{
id:lobdt.id,
});
m.route("/chat?lobby=" + lobbyid);
}
},"clear"),
m("h3","participants:"),
rs.list(
"chat/lobby_participants/" + lobbyid,
function(item) {
return m("div",item.identity.name);
},
function (a,b){
return rs.stringSort(a.identity.name,b.identity.name);
}
)
]
} else {
if (lobdt.subscribed != undefined
&& lobdt.subscribed
&& lobdt.is_broadcast
) {
//set participants
particips = [
m("div.btn", {
style: {
"text-align":"center"
},
onclick: function (){
rs.request("chat/clear_lobby",{
lobbyid,
});
m.route("/chat?lobby=" + lobbyid);
}
},"clear"),
]
}
}
return [
intro,
mem.msg.map(function(item){
var d = new Date(new Number(item.send_time)*1000);
return dspmsg(
item.author_name,
d.toLocaleDateString() + " " + d.toLocaleTimeString(),
item.msg
);
})
];
}
module.exports = {
frame: function(content, right){
return m("div", {
style: {
"height": "100%",
"box-sizing": "border-box",
"padding-bottom": "170px",
}
},[
m(".chat.container", [
m(".chat.header", [
m("table.noBorderTable",[
m("tr",[
m("td.noBorderTD",[
m(
"h2",
{style:{margin:"0px"}},
"chat"
)
]),
m("td.noBorderTD",[
m(
"h2",
{
style:{margin:"0px"},
id:"ChatLobbyName"
},
"Lobby Name"
)
])
])
])
]),
m(".chat.left", [
m("div.chat.header[style=position:relative]","lobbies:"),
m("br"),
lobbies(),
]),
m(".chat.right", right),
m(".chat.middle", content),
m(".chat.clear", ""),
]),
msg != null
? msg
: [],
]);
},
view: function(){
var lobbyid = m.route.param("lobby");
msg = null;
if (lobbyid != undefined ) {
particips = [];
return this.frame(
lobby(lobbyid),
particips
);
};
return this.frame(
m(
"div",
{style: {margin:"10px"}},
"please select lobby"
),
m("div","")
);
}
}

View File

@ -1,254 +0,0 @@
var m = require("mithril");
var rs = require("retroshare");
var locationName = "";
var password ="";
var ssl_name = "";
var newName = "";
function listprofiles(){
var locations = rs("control/locations");
var knownProfileIds = [];
var result = [];
if(locations === undefined || locations == null){
return m("div", "profiles: waiting_server");
}
locations.map(function(location) {
if (knownProfileIds.indexOf(location.pgp_id)<0){
knownProfileIds.push(location.pgp_id);
result.push(m(
"div.btn2",{
onclick: function(){
m.route("/createlogin",{
id: location.pgp_id,
name: location.name,
})
}
},
location.name
));
}
});
return result;
}
function setLocationName(location) {
locationName = location;
}
function setPasswd(passwd) {
password = passwd;
}
function setSslName(ssl) {
ssl_name = ssl;
}
function setNewName(name) {
newName = name;
}
function checkpasswd(){
var status = "";
var color = "red";
var lbl = document.getElementById("lblpwdinfo");
var passwd2 = document.getElementById("txtpasswd2").value;
if (passwd2 == password && passwd2 != "") {
color = "lime";
status = "password ok";
} else if (passwd2 == "") {
color = "yellow";
status = "password required";
} else {
color = "red";
status = "passwords don't match";
}
lbl.textContent = status;
lbl.style.color=color;
}
function createLocation() {
var profile = m.route.param("id");
var profname = m.route.param("name");
var loc ={
ssl_name: document.getElementById("txtlocation").value,
pgp_password: password,
};
if (profile != undefined) {
loc.pgp_id= profile;
} else {
loc.pgp_name = newName;
};
rs.request("control/create_location",loc,function(data){
m.route("/accountselect", {});
});
m.route("/createlogin",{state:wait});
}
function certDrop(event)
{
console.log("onDrop()");
console.log(event.dataTransfer.files);
event.preventDefault();
var reader = new FileReader();
var widget = this;
reader.onload = function(evt) {
console.log("onDrop(): file loaded");
rs.request(
"control/import_pgp",{
key_string:evt.target.result,
}, importCallback);
};
reader.readAsText(event.dataTransfer.files[0]);
m.route("/createlogin",{state:"waiting"});
}
function importCallback(resp)
{
console.log("importCallback()" + resp);
m.route("/createlogin",{
id:resp.pgp_id,
name:"",
});
}
module.exports = {
view: function(){
var profile = m.route.param("id");
var state = m.route.param("state");
var profname = m.route.param("name");
var hidden = m.route.param("hidden");
if (state == "wait"){
return m("div","waiting ...");
} if (state == "newid"){
m.initControl = "txtnewname";
return m("div",[
m("h2","create login - Step 2 / 2: create location"),
m("h3","- for new profile "),
m("hr"),
m("h2","PGP-profile name:"),
m("input",{
id: "txtnewname",
type:"text",
onchange:m.withAttr("value", setNewName),
onkeydown: function(event){
if (event.keyCode == 13){
document.getElementById("txtpasswd").focus();
}
},
}),
m("h2","enter password:"),
m("input", {
id: "txtpasswd",
type:"password",
onchange: m.withAttr("value",setPasswd),
onkeydown: function(event){
if (event.keyCode == 13){
setPasswd(this.value);
document.getElementById("txtpasswd2").focus();
};
checkpasswd;
}
}),
m("h2", "re-enter password:"),
m("input", {
id: "txtpasswd2",
type:"password",
onfocus: checkpasswd,
onchange: checkpasswd,
onkeyup: function(event){
if (event.keyCode == 13){
document.getElementById("txtlocation").focus();
}
checkpasswd();
}
}),
m("h3",{
id: "lblpwdinfo",
style:"color:yellow",
}, "password required"),
m("h2","location name:"),
m("input",{
id: "txtlocation",
type:"text",
onchange:m.withAttr("value", setLocationName),
onkeydown: function(event){
if (event.keyCode == 13){
setSslName(this.value);
createLocation();
}
},
}),
m("br"),
m("input",{
type: "button",
onclick: createLocation,
value: "create location",
}),
]);
} else if (profile != undefined) {
m.initControl = "txtpasswd";
return m("div",[
m("h2","create login - Step 2 / 2: create location"),
m("h3","- for " + profname + " (" +profile + ")"),
m("hr"),
m("h2","enter password:"),
m("input", {
id: "txtpasswd",
type:"password",
onchange: m.withAttr("value",setPasswd),
onkeydown: function(event){
if (event.keyCode == 13){
setPasswd(this.value);
document.getElementById("txtlocation").focus();
}
}
}),
m("h2","location name:"),
m("input",{
id: "txtlocation",
type:"text",
onchange:m.withAttr("value", setLocationName),
onkeydown: function(event){
if (event.keyCode == 13){
setSslName(this.value);
createLocation();
}
},
}),
m("br"),
m("input",{
type: "button",
onclick: createLocation,
value: "create location",
}),
]);
} else {
return m("div",[
m("h2","create login - Step 1 / 2: select profile(PGP-ID)"),
m("hr"),
m("div.btn2",{
onclick: function(){
m.route("/createlogin", {state: "newid"});
},
} ,"<create new profile>"),
m("div.btn2",{
ondragover:function(event){
/*important: block default event*/
event.preventDefault();
},
ondrop: certDrop,
} ,"<import profile (drag and drop a profile here)>"),
listprofiles()
]);
};
}
}

View File

@ -1,96 +0,0 @@
var m = require("mithril");
var rs = require("retroshare");
function makeFriendlyUnit(bytes)
{
if(bytes < 1e3)
return bytes.toFixed(1) + "B";
if(bytes < 1e6)
return (bytes/1e3).toFixed(1) + "kB";
if(bytes < 1e9)
return (bytes/1e6).toFixed(1) + "MB";
if(bytes < 1e12)
return (bytes/1e9).toFixed(1) + "GB";
return (bytes/1e12).toFixed(1) + "TB";
}
function progressBar(file){
return m("div[style=border:5px solid lime;"
+ 'border-radius:3mm;'
+ 'padding:2mm;'
+ 'height:5mm'
+ "]", [
m("div[style="
+ 'background-color:lime;'
+ 'height:100%;'
+ 'width:' + (file.transferred / file.size * 100)+'%'
+ ']'
,"")
]);
};
function cntrlBtn(file, act) {
return(
m("div.btn",{
onclick: function(){
rs.request("transfers/control_download",{action: act, hash: file.hash});
}
},
act)
)
}
module.exports = {
view: function(){
var paths = rs("transfers/downloads");
var filestreamer_url = "/fstream/";
if (paths === undefined) {
return m("div", "Downloads ... please wait ...");
}
return m("div", [
m("h2","Downloads (" + paths.length +")"),
m("div.btn2", {
onclick: function(){
m.route("/downloads/add");
}
}, "add retrohare downloads"),
m("hr"),
m('table', [
m("tr",[
m("th","name"),
m("th","size"),
m("th","progress"),
m("th","transfer rate"),
m("th","status"),
m("th","progress"),
m("th","action")
]),
paths.map(function (file){
var ctrlBtn = m("div","");
var progress = file.transferred / file.size * 100;
return m("tr",[
m("td",[
m("a.filelink",
{
target: "blank",
href: filestreamer_url + file.hash + "/" + encodeURIComponent(file.name)
},
file.name
)
]),
m("td", makeFriendlyUnit(file.size)),
m("td", progress.toPrecision(3) + "%"),
m("td", makeFriendlyUnit(file.transfer_rate*1e3)+"/s"),
m("td", file.download_status),
m("td", progressBar(file)),
m("td", [
cntrlBtn(file, file.download_status==="paused"?"start":"pause"),
cntrlBtn(file, "cancel")]
)
])
})
])
]);
}
};

View File

@ -1,65 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
module.exports = {view: function(){
return m("div",[
m("h2","forums"),
m("p","(work in progress, currently only listing)"),
m("hr"),
/*
m("div.btn2", {
onclick: function (){
m.route("/addforum");
}
},"< create new forum >"),
*/
m("ul",
rs.list("forums",
function(item){
return m("li",[
m("h2",item.name),
m("div",{style:{margin:"10px"}},
[
item.description != ""
? [
m("span", "Description: "
+ item.description),
m("br")]
: [],
m("span","messages visible: "
+ item.visible_msg_count),
]
),
/*
item.subscribed
? [
m(
"span.btn2",
{style:{padding:"0px"}},
"unsubscribe"
),
" ",
m(
"span.btn2",
{style:{padding:"0px", margin:"10px"}},
"enter"
),
m("hr", {style: {color:"silver"}}),
]
: [
m(
"span.btn2",
{style:{padding:"0px", margin:"10px"}},
"subscribe"
),
]
*/
]);
},
rs.sort("name")
)
)
]);
}}

View File

@ -1,214 +0,0 @@
body {
background-color: black;
color: lime;
font-family: monospace;
margin: 0em;
/*padding: 1.5em;*/
padding: 2mm;
font-size: 1.1em;
box-sizing: border-box;
}
#overlay{
z-index: 10;
position: fixed;
top:0;
left:0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.8);
}
.paddingbox{
padding:2mm;
}
.nav{
list-style-type: none;
padding: 0em;
margin: 0em;
}
.nav li{
display: inline;
padding: 0.1em;
margin-right: 1em;
border-width: 0.1em;
border-color: blue;
border-bottom-style: solid;
cursor: pointer;
}
td{
padding: 0.3em;
border-style: solid;
border-width: 0.1em;
border-color: lime;
}
hr {
color: lime;
}
.menu{
border-style: solid;
border-color: lime;
border-width: 0.1em;
cursor: pointer;
padding: 0.0em;
}
.btn{
border-style: solid;
border-color: lime;
border-width: 0.1em;
cursor: pointer;
padding: 0.1em;
}
.btn2, .box{
border-style: solid;
/*border-color: lime;*/
border-color: limeGreen;
/*border-width: 1px;*/
border-radius: 3mm;
padding: 2mm;
font-size: 10mm;
cursor: pointer;
margin-bottom: 2mm;
}
.btn2:hover{
background-color: midnightblue;
}
.btnSmall{
border-style: solid;
/*border-color: lime;*/
border-color: limeGreen;
/*border-width: 1px;*/
border-radius: 3mm;
padding: 1mm;
font-size: 100%;
cursor: pointer;
margin-bottom: 0mm;
}
.hidden{
display:none;
}
.noBorderTable{
width: 100%;
border: none;
border-collapse: collapse;
}
.noBorderTD{
border: none;
border-collapse: collapse;
vertical-align: center;
}
.filelink{
color: inherit;
}
input,textarea{
color: lime;
font-family: monospace;
background-color: black;
border-color: lime;
font-size: 10mm;
border-radius: 3mm;
border-width: 1mm;
padding: 2mm;
margin-bottom: 1mm;
margin-right: 1mm;
/* make the button the whole screen width */
width: 100%;
/* make the text input fit small screens*/
box-sizing: border-box;
}
input:hover{
background-color: midnightblue;
}
textarea#txtNewMsg{
color: lime;
font-family: monospace;
background-color: black;
border-color: lime;
font-size: 100%;
border-radius: 3mm;
border-width: 1mm;
padding: 2mm;
margin-bottom: 0mm;
margin-right: 1mm;
height:110px;
resize: none;
/* make the button the whole screen width */
width: 100%;
/*height: 100%;*/
/* make the text input fit small screens*/
box-sizing: border-box;
}
input#txtMsgKeyword{
color: lime;
font-family: monospace;
background-color: black;
border-color: lime;
font-size: 100%;
border-radius: 3mm;
border-width: 1mm;
padding: 1mm;
margin-bottom: 0mm;
margin-right: 1mm;
/* make the button the whole screen width */
width: 100%;
/* make the text input fit small screens*/
box-sizing: border-box;
}
.checkbox {
width: auto;
}
.flexbox{
display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */
display: -ms-flexbox; /* TWEENER - IE 10 */
display: -webkit-flex; /* NEW - Chrome */
display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
.flexwidemember{
-webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-flex: 1; /* OLD - Firefox 19- */
width: 20%; /* For old syntax, otherwise collapses. */
-webkit-flex: 1; /* Chrome */
-ms-flex: 1; /* IE 10 */
flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */
}
#logo_splash{
-webkit-animation-fill-mode: forwards; /* Chrome, Safari, Opera */
animation-fill-mode: forwards;
-webkit-animation-name: logo_splash; /* Chrome, Safari, Opera */
-webkit-animation-duration: 3s; /* Chrome, Safari, Opera */
animation-name: logo_splash;
animation-duration: 3s;
text-align: center;
}
/* Chrome, Safari, Opera */
@-webkit-keyframes logo_splash {
from {opacity: 0;}
to {opacity: 1;}
}
/* Standard syntax */
@keyframes logo_splash {
from {opacity: 0;}
to {opacity: 1;}
}

View File

@ -1,6 +0,0 @@
var m = require("mithril");
module.exports = {view: function(){
return m("div","RetroShare - WebClient - Welcome");
}
};

View File

@ -1,22 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
module.exports = {view: function(){
return m("div",[
m("h2","identities"),
m("hr"),
m("div.btn2", {
onclick: function (){
m.route("/addidentity");
}
},"< create new identity >"),
m("ul",
rs.list("identity/own", function(item){
return m("li",[m("h2",item.name)]);
},
rs.sort("name"))
)
]);
}}

View File

@ -1,122 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
var menu =require("menu");
var currentpasswd = null;
function setPasswd(password) {
currentpasswd = password
}
function sendPassword(data) {
console.log("sending pwd for " + data.key_name + "...");
rs.request("control/password", {password: currentpasswd}, function(){
m.redraw();
});
}
function Page(menu){
this.menu = menu;
this.module = (menu.module != undefined) ? menu.module : menu.name;
this.path = menu.path != undefined ? menu.path : ("/" + menu.name);
var runst = menu.runstate;
var content = require(this.module);
var mm = require("menu");
this.view = function(){
var runstate = rs("control/runstate");
var needpasswd = rs("control/password");
//console.log("runstate: " + (runstate === undefined ? runstate : runstate.runstate));
if(runstate === undefined){
return m("h2", "waiting_server ... ");
} else if (runstate.runstate == null){
// try clean reboot ...
rs.clearCache();
rs("control/runstate"); //reboot detector
console.log("i'm down");
return m("h2", "server down");
} else if (needpasswd != undefined && needpasswd.want_password === true){
m.initControl = "txtpasswd";
return m("div",[
m("h2","password required"),
m("h3",needpasswd.key_name),
m("input",{
id: "txtpasswd",
type:"password",
onchange:m.withAttr("value", setPasswd),
onkeydown: function(event){
if (event.keyCode == 13){
setPasswd(this.value);
sendPassword(needpasswd);
}
}
}),
m("br"),
m("input[type=button][value=send password]",{
onclick: function(){
sendPassword(needpasswd);
}
}),
]);
} else {
if (runstate.runstate.match("waiting_init|waiting_startup")) {
return m("h2","server starting ...")
} else if(runstate.runstate.match("waiting_account_select|running_ok.*")) {
if (runst === undefined || runstate.runstate.match(runst)) {
return m("div", {
style: {
height: "100%",
"box-sizing": "border-box",
"padding-bottom": "40px"
}
}, [
m("div", mm.view()),
m("hr"),
m("div", {
style: {
height: "100%",
"box-sizing": "border-box",
"padding-bottom":"40px"
}
}, content)
]);
} else {
// funktion currently not available
m.route("/");
return m("div", [
m("div", mm.view()),
m("hr"),
m("div", require("home").view())
]);
};
} else {
return m("div", "unknown runstate: " + runstate.runstate);
}
}
}
};
module.exports = {
init:function(main){
console.log("start init ...");
var menudef = require("menudef");
var maps = {};
var m = require("mithril");
menudef.nodes.map(function(menu){
if (menu.action === undefined) {
var p = new Page(menu)
console.log("adding route " + menu.name + " for " + p.path + " with module " + p.module);
maps[p.path] = p;
}
});
m.route.mode = "hash";
m.route(main,"/",maps);
console.log("init done.");
}
};

View File

@ -1,12 +0,0 @@
/*@import "reset" */
html, body, #main
height: 100%
/*body */
/* font-family: "Sans-serif" */
@import "chat"
@import "green-black"

View File

@ -1,65 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
var mnodes = require("menudef");
function goback(){
rs.content=null;
m.redraw();
}
function buildmenu(menu, tagname, runstate, ignore){
if (
(menu.runstate === undefined
|| runstate.match("^(" + menu.runstate + ")$")!=null)
&& (!menu.name.match(ignore))
&& (menu.path === undefined || menu.path.match(":")==null)
&& (menu.show === undefined || menu.show)
) {
var name = menu.name;
if (menu.counter != undefined) {
name += menu.counter();
}
if (menu.action === undefined) {
return m(tagname , {
onclick: function(){
m.route(
menu.path != undefined ? menu.path : "/" + menu.name
)
}
}, name);
} else {
return m(tagname, {onclick: function(){menu.action(m)}}, name);
}
}
}
module.exports = {view: function(){
var runstate = rs("control/runstate");
if (runstate === undefined
|| runstate.runstate === undefined
|| runstate.runstate == null)
return m("div.nav","menu: waiting for server ...");
if (m.route() != "/")
return m("span",[
m("span"," | "),
mnodes.nodes.map(function(menu){
var item = buildmenu(menu,"span.menu", runstate.runstate, "-");
if (item != null){
return [
item,
m("span"," | ")
]
}
})
]);
return m("div", [
m("h2","home"),
m("hr"),
mnodes.nodes.map(function(menu){
return buildmenu(menu,"div.btn2", runstate.runstate, "home");
})
]);
}
};

View File

@ -1,119 +0,0 @@
var rs=require("retroshare");
module.exports = { nodes: [
{
name: "home",
path: "/"
},
{
name: "login",
module: "accountselect",
runstate: "waiting_account_select",
counter: rs.counting("control/locations"),
},
{
name: "create login",
path: "/createlogin",
module: "createlogin",
runstate: "waiting_account_select",
},
{
name: "peers",
runstate: "running_ok.*",
counter: rs.counting("peers", function(data){
var onlinecount = 0;
data.map(function(peer) {
var is_online = false;
peer.locations.map(function (location){
if (location.is_online) {
is_online=true;
}
});
if (is_online) {
onlinecount +=1;
}
});
return onlinecount + "/" + data.length;
})
},
{
name: "addpeer",
runstate: "running_ok.*",
show: false,
},
{
name: "identities",
runstate: "running_ok.*",
counter: rs.counting("identity/own"),
},
{
name: "addidentity",
runstate: "running_ok.*",
show: false,
},
{
name:"searchresult",
path: "/search/:id",
runstate: "running_ok.*",
},
{
name: "search",
runstate: "running_ok.*",
},
{
name: "downloads",
runstate: "running_ok.*",
counter: rs.counting("transfers/downloads")
},
{
name: "adddownloads",
runstate: "running_ok.*",
path: "/downloads/add",
show: false,
},
{
name: "forums",
runstate: "running_ok.*",
},
{
name: "chat",
runstate: "running_ok.*",
counter: rs.counting2({
"peers": function(peer) {
var sum = 0;
peer.locations.map(function (loc) {
sum += parseInt(loc.unread_msgs);
});
return sum;
},
"chat/lobbies": function(lobby) {
return lobby.unread_msg_count;
}
})
},
{
name:"settings",
runstate: "running_ok.*",
},
{
name:"servicecontrol",
runstate: "running_ok.*",
path:"/settings/servicecontrol",
show: false,
},
{
name: "shutdown",
runstate: "running_ok|waiting_account_select",
action: function(m){
rs.request("control/shutdown",null,function(){
rs("control/runstate").runstate=null;
rs.forceUpdate("control/runstate");
m.redraw();
});
}
},
{
name: "waiting",
show: false,
},
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,294 +0,0 @@
body {
background-color: #CCC;
color: #666;
font-family: sans;
margin: 0em;
/*padding: 1.5em;*/
padding: 2mm;
font-size: 1.1em;
box-sizing: border-box;
}
h2 {
text-transform: uppercase;
}
#overlay {
z-index: 10;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
}
.paddingbox {
padding: 2mm;
}
.nav {
list-style-type: none;
padding: 0em;
margin: 0em;
}
.nav li {
display: inline;
padding: 0.1em;
margin-right: 1em;
border-width: 0.1em;
border-color: blue;
border-bottom-style: solid;
cursor: pointer;
}
td {
padding: 0.3em;
border-style: none;
border-width: 0.1em;
border-color: orange;
}
li {
list-style: none;
}
.btn {
cursor: pointer;
padding: 0.1em;
}
input[type="button"] {
border: solid 1px rgba(0, 0, 0, 0);
border-radius: 10px;
font-size: 6mm;
cursor: pointer;
color: #333;
background-color: #f79e3a;
width: 26vw;
display: inline-grid;
margin: 0.2rem;
padding: 0.5rem;
text-align: center;
text-transform: capitalize;
box-shadow: 5px 5px 9px #aaa;
word-break: break-all;
}
.btn2,
.box,
.btnSmall {
border: solid 1px rgba(0, 0, 0, 0);
border-radius: 10px;
font-size: 6mm;
cursor: pointer;
color: #333;
background-color: #f79e3a;
display: inline-grid;
margin: 0.2rem;
padding: 0.5rem;
text-align: center;
text-transform: capitalize;
box-shadow: 5px 5px 9px #aaa;
}
.btn2,
.box
{
width: 12rem;
}
.btnSmall {
width: 7rem;
}
.menu {
border: solid 1px rgba(0, 0, 0, 0);
border-radius: 10px;
font-size: 0.8em;
cursor: pointer;
background-color: #f79e3a;
display: inline-grid;
margin: 0.2rem;
padding: 0.5rem;
text-align: center;
text-transform: capitalize;
width: 7rem;
}
span.menu:nth-child(2) {
background-color: rgba(0, 0, 0, 0);
width: 6.3rem;
}
span.menu:nth-child(16) {
background-color: rgba(0, 0, 0, 0);
}
li .menu {
background-color: rgba(0,0,0,0.3);
}
div.btn2:nth-child(9) {
border: rgba(0, 0, 0, 0);
background-color: rgba(0, 0, 0, 0);
text-decoration: underline;
box-shadow: unset;
margin-top: 1em;
text-align: left !important;
font-size: 1em;
}
div.btn2:nth-child(9):hover {
color: #666;
}
.btn2:hover {
background-color: orangered;
color: white;
}
.hidden {
display: none;
}
.noBorderTable {
width: 100%;
border: none;
border-collapse: collapse;
}
.noBorderTD {
border: none;
border-collapse: collapse;
vertical-align: center;
}
.filelink {
color: inherit;
}
input,
textarea {
border-color: orange;
font-size: 10mm;
border-radius: 3mm;
border-width: 1mm;
padding: 2mm;
margin-bottom: 1mm;
margin-right: 1mm;
/* make the button the whole screen width */
width: 100%;
/* make the text input fit small screens*/
box-sizing: border-box;
}
input[type="button"]:hover {
background-color: orangered;
}
/*chat*/
textarea#txtNewMsg {
font-size: 100%;
border-radius: 3mm;
border-width: 1mm;
padding: 2mm;
margin-bottom: 0mm;
margin-right: 1mm;
height: 110px;
resize: none;
/* make the button the whole screen width */
width: 100%;
/*height: 100%;*/
/* make the text input fit small screens*/
box-sizing: border-box;
}
input#txtMsgKeyword {
/* color: orange;
font-family: monospace;
background-color: black;*/
border-color: orange;
font-size: 100%;
border-radius: 3mm;
border-width: 1mm;
padding: 1mm;
margin-bottom: 0mm;
margin-right: 1mm;
/* make the button the whole screen width */
width: 100%;
/* make the text input fit small screens*/
box-sizing: border-box;
}
.checkbox {
width: auto;
}
.flexbox {
display: -webkit-box;
/* OLD - iOS 6-, Safari 3.1-6 */
display: -moz-box;
/* OLD - Firefox 19- (buggy but mostly works) */
display: -ms-flexbox;
/* TWEENER - IE 10 */
display: -webkit-flex;
/* NEW - Chrome */
display: flex;
/* NEW, Spec - Opera 12.1, Firefox 20+ */
}
.flexwidemember {
-webkit-box-flex: 1;
/* OLD - iOS 6-, Safari 3.1-6 */
-moz-box-flex: 1;
/* OLD - Firefox 19- */
width: 20%;
/* For old syntax, otherwise collapses. */
-webkit-flex: 1;
/* Chrome */
-ms-flex: 1;
/* IE 10 */
flex: 1;
/* NEW, Spec - Opera 12.1, Firefox 20+ */
}
#logo_splash {
-webkit-animation-fill-mode: forwards;
/* Chrome, Safari, Opera */
animation-fill-mode: forwards;
-webkit-animation-name: logo_splash;
/* Chrome, Safari, Opera */
-webkit-animation-duration: 3s;
/* Chrome, Safari, Opera */
animation-name: logo_splash;
animation-duration: 3s;
text-align: center;
}
/* Chrome, Safari, Opera */
@-webkit-keyframes logo_splash {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Standard syntax */
@keyframes logo_splash {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

View File

@ -1,89 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
module.exports = {view: function(){
var peers = rs("peers");
//console.log("peers:" + peers);
//waiting for peerlist ...
if(peers === undefined || peers == null){
return m("div",[
m("h2","peers"),
m("h3","waiting_server"),
]);
};
peers = peers.sort(rs.sort("name"));
//building peerlist (prebuild for counting)
var online = 0;
var peerlist = peers.map(function(peer){
var isonline = false;
var avatar_address ="";
//building location list (prebuild for state + icon)
var loclist = peer.locations.map(function(location){
if (location.is_online && ! isonline){
online +=1;
isonline = true;
}
if (location.avatar_address != "" && avatar_address =="") {
avatar_address=location.avatar_address;
}
return m("li",{
style:"color:" + (location.is_online ? "lime": "grey")
+ ";cursor:pointer",
onclick: function(){
m.route("/chat?lobby=" + location.chat_id)
}
}, location.location);
});
//return friend (peer + locations)
return m("div.flexbox[style=color:lime]",[
// avatar-icon
m("div", [
avatar_address == "" ? "" : (
m("img",{
src: rs.apiurl("peers" + avatar_address),
style:"border-radius:3mm;margin:2mm;",
})
)
]),
//peername + locations
m("div.flexwidemember",[
m("h1[style=margin-bottom:1mm;]",
{style:"color:" + (isonline ? "lime": "grey")} ,
peer.name
),
m("ul", loclist ),
]),
//remove-button
m("div", {
style: "color:red;" +
"font-size:1.5em;" +
"padding:0.2em;" +
"cursor:pointer",
onclick: function (){
var yes = window.confirm(
"Remove " + peer.name + " from friendslist?");
if(yes){
rs.request("peers/" + peer.pgp_id +"/delete");
}
}
}, "X")
]);
});
// return add-peer-button + peerlist
return m("div",[
m("div.btn2",{onclick: function(){m.route("/addpeer")}},"add new friend"),
m("h2","peers (online: " + online +" / " + peers.length + "):"),
m("div", [
peerlist,
]),
]);
}
};

View File

@ -1,410 +0,0 @@
/*
var rs = requires("rs");
var m = require("mithril");
function main(){
var state = rs("runstate");
if(state=== undefined){
return m("div", "waiting for server");
}
if(state === "waiting_login"){
return require("login")();
}
if(state === "running_ok"){
return require("mainwindow")();
}
}
*/
/*
idea: statetokenservice could just send the date instead of the token
*/
"use strict";
var m = require("mithril");
var api_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/api/v2/";
var filestreamer_url = window.location.protocol + "//" +window.location.hostname + ":" + window.location.port + "/fstream/";
var upload_url = window.location.protocol + "//" + window.location.hostname + ":" + window.location.port + "/upload/";
function for_key_in_obj(obj, callback){
var key;
for(key in obj){
callback(key, obj[key]);
}
}
var cache = {};
var last_update_ts = 0;
function check_for_changes(){
var tokens = [];
var paths_to_fetch = [];
// console.log("start-check " + Object.keys(cache));
for_key_in_obj(cache, function(path, item){
var token = item.statetoken;
if(token === undefined || token== null) {
paths_to_fetch.push(path)
} else if (tokens.indexOf(token)<0) {
tokens.push(token);
}
});
// console.log("tokens found: " + tokens);
var req = m.request({
method: "POST",
url: api_url + "statetokenservice",
background: true,
data: tokens,
});
req.then(function handle_statetoken_response(response){
// console.log("checking result " + response.data ? Object.keys(response.data) : "<null>") ;
for_key_in_obj(cache, function(path, item){
var found = false;
for(var i = 0; i < response.data.length; i++){
if(response.data[i] === item.statetoken){
found = true;
}
}
if(found){
paths_to_fetch.push(path);
}
});
// console.log("generating Results for paths " + paths_to_fetch);
var requests = [];
paths_to_fetch.map(function request_it(path){
var req2 = m.request({
method: "GET",
url: api_url + path,
background: true,
});
req2 = req2.then(function fill_in_result(response){
cache[path].data = response.data;
cache[path].statetoken = response.statetoken;
});
requests.push(req2);
});
if(requests.length > 0){
// console.log("requesting " + requests.length + " requests");
m.sync(requests).then(function trigger_render(){
m.startComputation();
m.endComputation();
checkFocus();
setTimeout(check_for_changes, 500);
});
}
else{
// console.log("no requests");
setTimeout(check_for_changes, 500);
}
}, function errhandling(value){
// console.log("server disconnected " + value);
setTimeout(check_for_changes, 500);
});
}
check_for_changes();
var update_scheduled = false;
function schedule_request_missing(){
if(update_scheduled)
return;
update_scheduled = true;
// place update logic outside of render loop, this way we can fetch multiple things at once
// (because after the render loop everything we should fetch is in the list)
// if we fetch multiple things at once, we can delay a re-rende runtil everything is done
// so we need only one re-render for multiple updates
setTimeout(function request_missing(){
update_scheduled = false;
var requests = [];
for_key_in_obj(cache, function(path, item){
if(!item.requested){
var req = m.request({
method: "GET",
url: api_url + path,
background: true,
});
req.then(function fill_data(response){
// TODO: add errorhandling
item.data = response.data;
item.statetoken = response.statetoken;
if (item.then != undefined && item.then != null) {
try {
item.then(response);
} catch (ex) {
if (item.errorCallback != undefined && item.errorCallback != null) {
item.errorCallback(ex);
};
}
};
}, function errhandling(value){
if (item.errorCallback != undefined && item.errorCallback != null) {
item.errorCallback(value);
}
});
requests.push(req);
}
item.requested = true;
});
m.sync(requests).then(function trigger_render(){
m.startComputation();
m.endComputation();
checkFocus();
});
});
}
function checkFocus(){
if (m.initControl != undefined) {
var ctrl = document.getElementById(m.initControl);
if (ctrl!= null) {
ctrl.focus();
m.initControl = undefined;
} else {
console.log("focus-control '" + m.initControl + "' not found!");
m.initControl = undefined;
}
}
}
// called every time, rs or rs.request failed, only response or value is set
function requestFail(path, response, value) {
rs.error = "error on " + path;
console.log("Error on " + path +
(response == null ? ", value: " + value : (", response: " +
(response.debug_msg === undefined ? response : response.debug_msg)
))
);
}
function rs(path, args, callback, options){
if(cache[path] === undefined){
options=optionsPrep(options,path);
var req = {
data: args,
statetoken: undefined,
requested: false,
allow: options.allow,
then: function(response){
options.log(path + ": response: " + response.returncode);
if (!this.allow.match(response.returncode)) {
options.onmismatch(response);
} else if (callback != undefined && callback != null) {
callback(response.data, response.statetoken);
}
},
errorCallback: options.onfail
};
cache[path] = req;
schedule_request_missing();
}
return cache[path].data;
}
module.exports = rs;
rs.for_key_in_obj = for_key_in_obj;
// single request for action
rs.request=function(path, args, callback, options){
options = optionsPrep(options, path);
var req = m.request({
method: options.method === undefined ? "POST" : options.method,
url: api_url + path,
data: args,
background: true
});
req.then(function checkResponseAndCallback(response){
options.log(path + ": response: " + response.returncode);
if (!options.allow.match(response.returncode)) {
options.onmismatch(response);
} else if (callback != undefined && callback != null) {
callback(response.data, response.statetoken);
}
}, options.onfail);
return req;
};
//set default-values for shared options in rs() and rs.request()
function optionsPrep(options, path) {
if (options === undefined) {
options = {};
}
if (options.onfail === undefined) {
options.onfail = function errhandling(value){
requestFail(path, null, value);
}
};
if (options.onmismatch === undefined) {
options.onmismatch = function errhandling(response){
requestFail(path, response,null);
}
};
if (options.log === undefined) {
options.log = function(message) {
console.log(message);
}
}
if (options.allow === undefined) {
options.allow = "ok";
};
return options;
}
// force reload for path
rs.forceUpdate = function(path, removeCache){
if (removeCache === undefined || !removeCache) {
cache[path].requested=false;
} else {
delete cache[path];
}
}
// force reload for all
rs.clearCache = function(path, removeCache){
console.log("clearing Cache ...")
cache = {};
console.log("update_scheduled: " + update_scheduled);
update_scheduled = false;
check_for_changes();
console.log("Cache cleared.")
}
// dismiss statetoken (= force reload)
rs.untoken = function(path) {
cache[path].statetoken = null;
}
//return api-path
rs.apiurl = function(path) {
if (path === undefined) {
path="";
}
if (path.length > 0 && "^\\\\|\\/".match(path)) {
path=path.substr(1);
}
return api_url + path;
}
// counting in menu
rs.counting = function(path, counterfnkt) {
return function () {
var data=rs(path);
if (data != undefined) {
if (counterfnkt === undefined) {
return " (" + data.length + ")";
}
return " (" + counterfnkt(data) + ")";
}
return "";
}
};
// counting in menu
rs.counting2 = function(targets) {
return function () {
var sum = 0;
for (var path in targets) {
var data=rs(path);
if (data != undefined) {
data.map(function(item){
sum += parseInt(targets[path](item));
});
};
};
if (sum > 0) {
return " (" + sum + ")";
}
return "";
}
};
// listing data-elements
rs.list = function(path, buildfktn, sortfktn){
var list = rs(path);
if (list === undefined|| list == null) {
return "< waiting for server ... >"
};
if (sortfktn != undefined && sortfktn != null) {
list=list.sort(sortfktn);
}
return list.map(buildfktn);
};
//remember additional data (feature of last resort)
rs.memory = function(path, args){
var item = cache[path];
if (item === undefined) {
rs(path, args);
item = cache[path];
}
if (item.memory === undefined) {
item.memory = {};
}
return item.memory;
};
// Sortierfunktion für Texte von Objekten,
// falls einfache Namen nicht funktionieren
rs.stringSort = function(textA,textB, innersort, objectA, objectB){
if (textA.toLowerCase() == textB.toLowerCase()) {
if (innersort === undefined) {
return 0
}
return innersort(objectA,objectB);
} else if (textA.toLowerCase() < textB.toLowerCase()) {
return -1
} else {
return 1
}
}
//return sorting-function for string, based on property name
//using: list.sort(rs.sort("name"));
// -----
//innersort: cascading sorting - using:
//list.sort(rs.sort("type",rs.sort("name")))
rs.sort = function(name, innersort){
return function(a,b) {
return rs.stringSort(a[name],b[name],innersort,a,b);
}
}
//return sorting-function for boolean, based on property name
rs.sort.bool = function(name, innersort){
return function(a,b){
if (a[name] == b[name]) {
if (innersort === undefined) {
return 0
}
return innersort(a,b);
} else if (a[name]) {
return -1
} else {
return 1
}
}
}
// searching a element in a list
// items: list to search in
// name: name of attribute to lookup
// value: attribute's value to compare
rs.find = function(items, name, value) {
if (items === undefined||items == null) {
return null;
};
for(var i = 0, l = items.length; i < l; ++i) {
if (items[i][name] == value) {
return items[i];
}
}
return null;
}

View File

@ -1,54 +0,0 @@
var m = require("mithril");
var rs = require("retroshare");
var state = {};
var searchText = "";
function updateText(newText) {
searchText = newText;
}
function dosearch(){
console.log("searching for: "+searchText);
rs.request(
"filesearch/create_search", {
distant: true,
search_string: searchText
},
function(resp){
m.route("/search/" + resp.search_id);
}
);
}
module.exports = {
view: function(){
var results = rs("filesearch");
if (results === undefined||results == null) {
results = [];
};
return m("div",[
m("h2","turtle file search"),
m("div", [
m("input[type=text]", {onchange:m.withAttr("value", updateText)}),
m("input[type=button][value=search]",{onclick:dosearch})
]),
m("hr"),
m("h2","previous searches:"),
m("div", [
results.map(function(item){
var res = rs("filesearch/" + item.id,{},null,{allow:"not_set|ok"});
if (res === undefined) {
res =[];
};
return m("div.btn2",{
onclick:function(){
m.route("/search/" + item.id);
}
}, item.search_string + " (" + res.length + ")");
})
])
])
}
}

View File

@ -1,71 +0,0 @@
var m = require("mithril");
var rs = require("retroshare");
module.exports = {
view: function(){
var id=m.route.param("id");
var results = rs("filesearch/" + id ,{},null,{allow:"not_set|ok"});
if (results === undefined || results.length == undefined) {
results = [];
}
var searches = rs("filesearch");
var searchdetail = "<unknown>";
if (!(searches === undefined) && !(searches.length === undefined)) {
searches.forEach(function(s){
if (s.id == id) {
searchdetail = s.search_string;
}
});
}
var dl_ids = [];
var downloads =rs("transfers/downloads");
if (downloads !== undefined) {
downloads.map(function(item){
dl_ids.push(item.hash);
})
}
return m("div",[
m("h2","turtle file search results"),
m("h3", "searchtext: " + searchdetail + " (" + results.length + ")"),
m("hr"),
m("table", [
m("tr" ,[
m("th","name"),
m("th","size"),
m("th",""),
]),
results.map(function(file){
if (dl_ids.indexOf(file.hash)>=0) {
file.state="in download queue"
}
return m("tr",[
m("th",file.name),
m("th",file.size),
m("th",[
file.state === undefined
? m("span.btn", {
onclick:function(){
rs.request("transfers/control_download", {
action: "begin",
name: file.name,
size: file.size,
hash: file.hash,
}, function(){
result="added";
});
m.startComputation();
m.endComputation();
}
}, "download")
: file.state
]),
])
})
])
])
}
}

View File

@ -1,264 +0,0 @@
"use strict";
var m = require("mithril");
var rs = require("retroshare");
function setOption(id,value) {
return function(){
rs.request("servicecontrol", {
service_id: id,
default_allowed: value,
});
rs.forceUpdate("servicecontrol", true);
}
}
function setUserOption(serviceid, userid, value) {
return function(){
rs.request("servicecontrol/user", {
service_id: serviceid,
peer_id: userid,
enabled: value
}, function(){
rs.forceUpdate("servicecontrol", true)
});
}
}
function createSwitch(isOn, width) {
if (width === undefined) {
width = "2.1em";
}
return [
m("div.menu", {
style: {
float:"left",
width: width,
textAlign: "center",
color: "#303030",
borderColor: isOn
? "lime"
: "red",
backgroundColor: !isOn
? "black"
: "lime",
}
}, "ON"),
m("div.menu",{
style: {
float:"left",
width: width,
textAlign: "center",
marginRight:"5px",
color: "#303030",
borderColor: isOn
? "lime"
: "red",
backgroundColor: isOn
? "black"
: "red",
}
}, "OFF"),
];
}
function breadcrums(name, parts){
var result = [];
rs.for_key_in_obj(parts, function(partname,item){
result.push(
m("span.btn",{
onclick: function(){
m.route(item)
}
},partname)
);
result.push(" / ");
});
result.push(name);
return result;
}
function serviceView(serviceid) {
var service, liste;
service = rs.find(rs("servicecontrol"),"service_id",serviceid);
if (service == null) {
return m("h3","<please wait ... >");
}
liste = service.default_allowed
? service.peers_denied
: service.peers_allowed;
return m("div", [
m("h2", breadcrums(service.service_name, {
settings:"/settings",
rights: "/settings/servicecontrol",
})),
m("hr"),
m("h2",{
style:{
float:"left",
}
},[
m("div",{
style:{
float:"left",
}
},"user rights for: " + service.service_name + ", default: "),
m("div", {
onclick: setOption(
serviceid,
!service.default_allowed
),
style: {
float:"left",
marginLeft: "0.4em",
marginRight: "0.4em",
}
},createSwitch(service.default_allowed)),
]),
m("div", {
style: {
clear:"left",
}
}),
m("ul", rs.list("peers",function(peer){
var locs;
locs = peer.locations;
locs.sort(rs.sort("location"));
return peer.locations.map(function(location){
var isExcept, isOn;
isExcept = liste != null
&& liste.indexOf(location.peer_id)>=0;
isOn = service.default_allowed ? !isExcept: isExcept;
return m("li", {
style: {
margin: "5px",
color: isOn ? "lime" :"red",
}
}, [
m("div"),
m("div", {
onclick: setUserOption(
serviceid,
location.peer_id,
!isOn
),
style: {
float:"left",
},
},createSwitch(isOn)),
m("div",
{
style: {
//color: "lime",
float:"left",
marginLeft: "5px",
marginRight: "5px",
fontWeight: "bold",
}
},
peer.name + (location.location
? " (" + location.location + ")"
: "")
),
m("div", {
style: {
clear: "left"
}
}),
]);
})
}, rs.sort("name")))
]);
}
module.exports = {
view: function(){
if (m.route.param("service_id")) {
return serviceView(m.route.param("service_id"));
}
return m("div", [
m("h2", breadcrums("rights", {
settings:"/settings",
})),
m("hr"),
m("ul", rs.list("servicecontrol", function(item){
return m("li", {
style: {
margin: "5px",
color: item.default_allowed ? "lime" :"red",
}
}, [
m("div"),
m("div", {
onclick: setOption(
item.service_id,
!item.default_allowed
),
style: {
float:"left",
}
},createSwitch(item.default_allowed)),
m("div.menu",
{
style: {
// color: "lime",
borderColor: item.default_allowed
? "lime"
: "red",
float: "left",
marginLeft: "5px",
marginRight: "5px",
paddingLeft: "2px",
paddingRight: "2px",
},
onclick: function(){
m.route("/settings/servicecontrol/", {
service_id: item.service_id,
})
}
}, "more"
),
m("div",
{
style: {
// color: "lime",
float:"left",
marginLeft: "5px",
marginRight: "5px",
fontWeight: "bold",
}
},
item.service_name
),
m("div",
{
style: {
color: "lime",
float:"left",
marginLeft: "5px",
marginRight: "5px",
}
},
(
item.default_allowed
? ( item.peers_denied != null
? "(" + item.peers_denied.length + " denied)"
: "")
: ( item.peers_allowed != null
? "(" + item.peers_allowed.length + " allowed)"
: "")
)
),
m("div", {
style: {
clear: "left"
}
}),
]);
})
)
]);
}
}

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