mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
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:
parent
887e3ad394
commit
c73bdb83cd
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -11,3 +11,6 @@
|
|||||||
path = supportlibs/udp-discovery-cpp
|
path = supportlibs/udp-discovery-cpp
|
||||||
url = https://github.com/truvorskameikin/udp-discovery-cpp.git
|
url = https://github.com/truvorskameikin/udp-discovery-cpp.git
|
||||||
branch = develop
|
branch = develop
|
||||||
|
[submodule "supportlibs/rapidjson"]
|
||||||
|
path = supportlibs/rapidjson
|
||||||
|
url = https://github.com/Tencent/rapidjson.git
|
||||||
|
49
README.asciidoc
Normal file
49
README.asciidoc
Normal 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
151
README.md
@ -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 don’t 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
|
|
@ -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
135
TODO.txt
@ -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.
|
|
||||||
|
|
||||||
|
|
@ -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}
|
|
@ -23,10 +23,7 @@ Files: src/retroshare-gui/src/TorControl/
|
|||||||
Copyright: 2014, John Brooks <john.brooks@dereferenced.net>
|
Copyright: 2014, John Brooks <john.brooks@dereferenced.net>
|
||||||
License: BSD-New
|
License: BSD-New
|
||||||
|
|
||||||
Files: src/libresapi/src/api/json.h \
|
Files: retroshare-gui/src/gui/common/PictureFlow.* \
|
||||||
libresapi/src/webui-src/app/mithril.min.js \
|
|
||||||
src/rapidjson-1.1.0/* \
|
|
||||||
retroshare-gui/src/gui/common/PictureFlow.* \
|
|
||||||
src/retroshare-gui/src/qss/qdarkstyle.qss
|
src/retroshare-gui/src/qss/qdarkstyle.qss
|
||||||
Copyright: 2013 Jeff Weinstein <jeff.weinstein@gmail.com>
|
Copyright: 2013 Jeff Weinstein <jeff.weinstein@gmail.com>
|
||||||
License: MIT
|
License: MIT
|
||||||
|
1
libresapi/src/.gitignore
vendored
1
libresapi/src/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
webui/*
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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 ""+mRootDir+"" exists";
|
|
||||||
else
|
|
||||||
direxists = "directory ""+mRootDir+"" does not exist!";
|
|
||||||
std::string msg = "<html><body><p>Error: can't open the requested file. path=""+escape_html(filename)+""</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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,6 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <retroshare/rsgxsifacetypes.h>
|
|
||||||
|
|
||||||
// operators for rsgxsgrpmeta and rsgxsmsgmeta
|
|
||||||
//
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
@ -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
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
@ -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__
|
|
@ -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
|
|
@ -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)
|
|
||||||
|
|
@ -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";
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
2
libresapi/src/webui-src/.gitignore
vendored
2
libresapi/src/webui-src/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
node_modules/*
|
|
||||||
public/*
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
||||||
}
|
|
@ -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 "),
|
|
||||||
*/
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -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;
|
|
@ -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",
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}}
|
|
@ -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>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
*/
|
|
@ -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>
|
|
@ -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","")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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()
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -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")]
|
|
||||||
)
|
|
||||||
])
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
};
|
|
@ -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")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}}
|
|
@ -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;}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
var m = require("mithril");
|
|
||||||
|
|
||||||
module.exports = {view: function(){
|
|
||||||
return m("div","RetroShare - WebClient - Welcome");
|
|
||||||
}
|
|
||||||
};
|
|
@ -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"))
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}}
|
|
@ -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.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
/*@import "reset" */
|
|
||||||
|
|
||||||
html, body, #main
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
|
|
||||||
/*body */
|
|
||||||
/* font-family: "Sans-serif" */
|
|
||||||
|
|
||||||
|
|
||||||
@import "chat"
|
|
||||||
@import "green-black"
|
|
@ -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");
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
};
|
|
@ -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
8
libresapi/src/webui-src/app/mithril.min.js
vendored
8
libresapi/src/webui-src/app/mithril.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,
|
|
||||||
]),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
};
|
|
@ -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;
|
|
||||||
}
|
|
@ -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 + ")");
|
|
||||||
})
|
|
||||||
])
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
]),
|
|
||||||
])
|
|
||||||
})
|
|
||||||
])
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
Loading…
Reference in New Issue
Block a user