...
 
Commits (50)
This diff is collapsed.
......@@ -8,13 +8,13 @@ members = [
"libqaul/http",
"libqaul/rpc",
"libqaul/ipc",
"libqaul/ws",
"libqaul/ws",
# qaul.net main services
"libqaul/service/chat",
# "libqaul/service/feed",
"libqaul/service/files",
"libqaul/service/voices",
"libqaul/service/voices",
# decentralised routing protocol
"ratman",
......@@ -25,12 +25,16 @@ members = [
"clockctrl",
"permute",
"visn",
"access-notifier",
"async-notify",
# Available netmod drivers
"netmod-mem",
"netmod-udp",
"netmod-wd",
"netmod-overlay",
# infrastructure crates
"qaulhubd",
# Client specific targets
"clients/linux",
......
[package]
name = "access-notifier"
name = "async-notify"
version = "0.1.0"
authors = ["Leonora Tindall <nora@nora.codes>"]
authors = ["Leonora Tindall <nora@nora.codes>", "Katharina Fey <kookie@spacekookie.de>"]
edition = "2018"
description = "Notify async tasks when someone mutates data they're interested in."
license = "AGPL-3.0"
......@@ -8,46 +8,47 @@ use std::task::Waker;
/// should, for example, check for more work in a queue or try again to
/// acquire a lock.
#[derive(Default, Debug, Clone)]
pub struct AccessNotifier<T> {
pub struct Notify<T> {
inner: T,
waker: Option<Waker>,
}
impl<T> Deref for AccessNotifier<T> {
impl<T> Deref for Notify<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for AccessNotifier<T> {
impl<T> DerefMut for Notify<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.waker.as_ref().map(|w| w.wake_by_ref());
&mut self.inner
}
}
impl<T> AccessNotifier<T> {
/// Check whether or not this `AccessNotifier` has a registered `Waker`.
impl<T> Notify<T> {
/// Check whether or not this `Notify` has a registered `Waker`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `AccessNotifier::has_waker()`.
pub fn has_waker(ptr: &AccessNotifier<T>) -> bool {
/// Call it as `Notify::has_waker()`.
pub fn has_waker(ptr: &Notify<T>) -> bool {
ptr.waker.is_some()
}
/// Get a copy of the registered `Waker` for this `AccessNotifier`.
/// Get a copy of the registered `Waker` for this `Notify`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `AccessNotifier::waker()`.
pub fn waker(ptr: &mut AccessNotifier<T>) -> Option<Waker> {
/// Call it as `Notify::waker()`.
pub fn waker(ptr: &mut Notify<T>) -> Option<Waker> {
ptr.waker.as_ref().map(|w| w.clone())
}
/// Call wake on the waker, if it's a waker, yehaa!
pub fn wake_if_waker(ptr: &mut AccessNotifier<T>) {
#[inline]
pub fn wake(ptr: &mut Notify<T>) {
if let Some(ref w) = ptr.waker {
w.clone().wake();
}
......@@ -57,35 +58,34 @@ impl<T> AccessNotifier<T> {
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `AccessNotifier::register_waker()`.
/// Call it as `Notify::register_waker()`.
///
/// # Panics
/// Panics if there is an already registered `Waker`.
/// Use `AccessNotifier::has_waker` to check the state before using this.
pub fn register_waker(ptr: &mut AccessNotifier<T>, waker: &Waker) {
if AccessNotifier::has_waker(ptr) {
panic!("Tried to register a Waker on an AccessWaker with an already registered Waker.");
} else {
/// Use `Notify::has_waker` to check the state before using this.
#[inline]
pub fn register_waker(ptr: &mut Notify<T>, waker: &Waker) {
if !Notify::has_waker(ptr) {
ptr.waker = Some(waker.clone())
}
}
/// Removes and returns the `Waker` registered to this `AccessNotifier`.
/// Removes and returns the `Waker` registered to this `Notify`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `AccessNotifier::clear_waker()`.
pub fn clear_waker(ptr: &mut AccessNotifier<T>) -> Option<Waker> {
/// Call it as `Notify::clear_waker()`.
pub fn clear_waker(ptr: &mut Notify<T>) -> Option<Waker> {
ptr.waker.take()
}
/// Consumes the `AccessNotifier`, dropping any associated `Waker` and
/// Consumes the `Notify`, dropping any associated `Waker` and
/// returning the inner value without notifying the `Waker`.
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `AccessNotifier::into_inner()`.
pub fn into_inner(ptr: AccessNotifier<T>) -> T {
/// Call it as `Notify::into_inner()`.
pub fn into_inner(ptr: Notify<T>) -> T {
ptr.inner
}
......@@ -93,8 +93,8 @@ impl<T> AccessNotifier<T> {
///
/// This function is implemented as an associated function rather than a
/// method to avoid conflicts with methods on the wrapped type.
/// Call it as `AccessNotifier::notify()`.
pub fn notify(ptr: &AccessNotifier<T>) {
/// Call it as `Notify::notify()`.
pub fn notify(ptr: &Notify<T>) {
ptr.waker.as_ref().map(|w| w.wake_by_ref());
}
}
......@@ -8,7 +8,7 @@ use std::{env, process};
fn main() {
let assets = match env::args().nth(0) {
let assets = match env::args().nth(1) {
Some(p) => p,
None => {
eprintln!("Usage: linux-http-test <path>");
......@@ -22,6 +22,10 @@ fn main() {
let chat = Chat::new(Arc::clone(&qaul)).unwrap();
let voices = Voices::new(Arc::clone(&qaul)).unwrap();
// print the path to the static
println!("Path to static web content: {}", assets);
println!("Open http://127.0.0.1:9900 in your web browser");
// Start the websocket server
HttpServer::block("127.0.0.1:9900", assets, Responder { qaul, chat, voices });
}
......@@ -27,6 +27,11 @@ After installing Rust you can install mdbook as follows:
cargo install mdbook
```
### Further Dependencies (Optional)
In order to be able to build the graph SVG of the program structure you
need to install the CLI program `graphviz`.
## Develop
......
......@@ -2,4 +2,12 @@
authors = ["Katharina Fey", "Mathias Jud", "Jess 3Jane"]
multilingual = false
src = "src"
title = "Contribution Guide"
title = "qaul.net Contributors Guide"
[build]
create-missing = false
[output.html]
preferred-dark-theme = "navy"
git-repository-url = "https://git.open-communication.net/qaul/qaul.net/-/tree/master/docs%2Fcontributors"
git-repository-icon = "fa-git"
#!/bin/sh
# build this mdbook
# crate the structural dependencies SVG image
dot -Tsvg src/assets/dependencies.dot -o src/assets/dependencies.svg
# build this mdbook
mdbook build
......@@ -6,30 +6,38 @@
- [Contributions](./social/contributions.md)
- [Code of Conduct](./social/code-of-conduct.md)
- [Servers & Web Services](./web-services/_intro.md)
- [Licenses](./legal/README.md)
- [GNU Affero General Public License version 3 or later](./legal/agpl-3.0.md)
- [Creative Commons Attribution 4.0 International License](./legal/cc-by.md)
- [Mozilla Public License version 2.0](./legal/mpl-v2.0.md)
- [GNU GENERAL PUBLIC LICENSE](./legal/gpl-3.0.md)
- [Build & Install](./install/_intro.md)
- [Linux](./install/linux.md)
- [Technical](./technical/index.md)
- [qaul.net](./technical/qaul.net/index.md)
- [services](./technical/qaul.net/services.md)
- [Services](./technical/qaul.net/services.md)
- [Overview of Crates](./technical/crates.md)
- [libqaul](./technical/libqaul/index.md)
- [api](./technical/libqaul/api.md)
- [ipc](./technical/libqaul/ipc/index.md)
- [libqaul Internals](./technical/libqaul/internals.md)
- [Service API](./technical/libqaul/api.md)
- [libqaul IPC Interfaces](./technical/libqaul/ipc/index.md)
- [socket-ipc](./technical/libqaul/ipc/socket.md)
- [android-ipc](./technical/libqaul/ipc/android.md)
- [internals](./technical/libqaul/internals.md)
- [ratman](./technical/ratman/index.md)
- [api](./technical/ratman/api.md)
- [netmod](./technical/ratman/netmod.md)
- [internals](./technical/ratman/internals/index.md)
- [routing](./technical/ratman/internals/routing.md)
- [journal](./technical/ratman/internals/journal.md)
- [Ratman](./technical/ratman/index.md)
- [Ratman API](./technical/ratman/api.md)
- [Netmod](./technical/ratman/netmod.md)
- [Ratman Internals](./technical/ratman/internals/index.md)
- [Routing](./technical/ratman/internals/routing.md)
- [Journal](./technical/ratman/internals/journal.md)
- [Development Interfaces](./technical/api/_intro.md)
- [Web GUI](./technical/webgui/_intro.md)
- [Install EmberJS & Test Web GUI](./technical/webgui/install.md)
- [Beta](./technical/beta/_intro.md)
- [TODO](./technical/beta/challenges.md)
- [Challenges & TODO's](./technical/beta/challenges.md)
- [Test](./test/index.md)
- [linux-http-test](./test/linux-http-test.md)
- [Translations](./translations/_intro.md)
- [Design](./design/_intro.md)
- [qaul.net Web Site](./web-site/_intro.md)
- [How to Create a Tutorial](./web-site/tutorials.md)
- [Translate the Web Site](./web-site/translate.md)
- [Legal](./legal/_intro.md)
- [GPL License (Version 3)](./legal/gpl-3.0.md)
- [Creative Commons Attribution 4.0 International License](./legal/cc-by.md)
# Build and Install qaul.net
This chapter will guide you through the build and
installation of qaul.net.
## Build qaul.net for your Operating Systems
* [Linux](linux.md)
## Special Parts of qaul.net
* **[WebGui]:** The WebGui is the crossplatform GUI to interact with the qaul.net app. How
* **[Website]:** How to build and edit the the https://qaul.net website can you read in the [Website] chapter.
[WebGui]: ../technical/webgui/install.md
[Website]; ,,/web-site/_intro.md
# Build qaul.net on Linux
## Prerequisites
The core of qaul.net is written in the programming language `Rust`.
In order to build libqaul, you need to install `rust` on your Computer.
You'll find the installation instructions here:
https://www.rust-lang.org/tools/install
## Get and Compile the Sources
Clone the [qaul.net git repository](https://git.open-communication.net/qaul/qaul.net.git) from the terminal.
```bash
git clone https://git.open-communication.net/qaul/qaul.net.git
```
Now you can move into the repository and build the application.
```bash
# move into the qaul.net project folder
cd qaul.net
# build the application via the rust build tool `cargo`
cargo build
```
The rust build created the following binaries:
* `qaul-linux`
* `linux-cli`
* `linux-http-test`
## Run and Test qaul.net
See the [test chapter](../test/index.md).
# Introduction
# qaul.net Contributors Guide
Welcome to the `qaul.net` contributors' manual!
Welcome to the *qaul.net Contributors Guide*!
This guide is open and publicly editable. You can find it's sources
in our [gitlab repository].
......
../../../licenses
\ No newline at end of file
# Legal
qaul.net is a fully free and open source software. It is licensed
under the [GPL license (Version 3)](gpl-3.0.md).
The documentation & web site of qaul.net stay under the
[Creative Commons Attribution 4.0 International](cc-by.md) license.
This diff is collapsed.
# Api
# Development Interfaces
## HTTP Api
This document will change quite a lot during the development,
especially because there are several layers of interfaces that need to
be documented. In the future we might want to move some of them into
their dedicated books.
The HTTP Api is documented in a seperate book:
For now, this document is an outline for the json RPC interface of
`libqaul`. It's being made available via the `libqaul-http` crate.
* [Online book](https://docs.qaul.net/http-api/)
* [Book repository](https://git.open-communication.net/qaul/qaul.net/tree/master/docs/http-api)
## Envelope
Every message is packed into an envelope. It contains metadata that
might be present in some transports, but not others. The envelope was
generally built to be streamed via sockets, where message association
isn't handled via the transport protocol (like http).
```json
{
"id": "1",
"auth": {
"id": "7F9B BAA9 DF90 0F48 CD41 7FD4 2C9C E432 1309 FB79 8CF9 C56F FB9A 8F2C 29C1 12D9",
"token": "56C0 F4C0 BD0D E822 B21D 4120 CC43 5C73 1F92 B246 C988 3335 72F5 8F1A 2701 3FD7"
},
"page": "1",
"method": "query",
"kind": "messages",
"data": {
"query": {
"tag": {
"key": "room-id",
"val": "A88A 7CC6 DF68 CE60 2D63 64CB 7393 8924 653D 52FB DBC6 2A39 D892 A9BD 6FA4 FD5D"
}
},
"service": "net.qaul.chat"
}
}
```
This type can be found in `libqaul/rpc/src/json/mod.rs`. The general
structure of requests stays the same: there's an ID, some auth data,
the page (which isn't implemented yet), `method` and `kind` the
request operates on.
There are several types available (`kind`):
- **users**, either a local or remote user
- **contacts**, some user-specific contact data for another user
- **chat-rooms**, a chat room via service (requires `qaul` feature)
- **chat-messages**, a chat room message (requires `qaul` feature)
Possible `methods` are (not all combinations exist though!):
- **list**, get a list of all, if available
- **create**, create a new (with side-effects, such as sending)
- **delete**, remove from local stores
- **get**, return single item by ID
- **query**, return a set of items, according to some query
- **modify**, change an existing item in some way
- **subscribe**, (not implemented yet)
- **unsubscribe**, (not implemented yet)
Three special `methods` only exist for users:
- **login**, validate password and receive auth tokens
- **logout**, end session token
- **repass**, change user passphrase
Following is a list of examples of how to construct valid requests.
If in doubt, the code that parses these is in
`libqaul/rpc/src/json/parser.rs`. You can always look it up there.
# Overview of crates
# Overview of Crates
The qaul.net project mostly uses a monorepo approach, meaning that all
libraries relevant to the application, library, and service ecosystem
......
# libqaul internals
# libqaul Internals
This section of the manual covers parts of libqaul not exposed
directly via the API. It's primarily useful to learn to debug
......@@ -10,7 +10,7 @@ that acts as a strongly typed store (messages, users, contacts, files,
...). Some more interesting components are those that are not exposed.
## Key store
## Key Store
Similar to the user and message store, libqaul stores public keys that
it comes across, for later. This is meant to opportunisticly fill the
......@@ -43,7 +43,7 @@ service handler was found.
## Persistence
The presistence module is implemented mostly by wrapping internal
libqaul types with [alexantria] storage callbacks, which is the
libqaul types with [alexandria] storage callbacks, which is the
library which implements all of the persistence logic and at-rest
encryption. It is developed as part of qaul.net, but pulled out of
the main tree to make it easier to use in other projects.
......
# libqaul IPC interfaces
# libqaul IPC Interfaces
While libqaul and all of the qaul.net services are written in Rust,
that doens't mean that you need to write your application in Rust.
......
# qaul.net services
Following is a list of services that are bundled in with qaul.net, and
Following is a list of services that are bundled with qaul.net, and
what they can do for you.
**Important** because we're still in development, some of these
......
# internals
# Internals
The routing layer of qaul.net is made up of the `R.A.T.M.A.N.` routing
protocol. It provides fully delay tolerant, distance vector based
cross-network mesh routing. Check the graph below for a rough
overview of components.
![](/assets/dependencies.svg)
Currently `R.A.T.M.A.N.` has the following components.
## Core
Handles the routing table and frame routing workers. A thread listens
for `Frame`s to be sent out, while also polling for incoming frames
and sending them to a management thread.
The routing table can be modified behind an `Arc<Mutex<_>>`, allowing
other threads to inject user identity information into the worker
thread.
It also provides a `send` function which will queue a new set of
`Frame`s for sending. The `Core` does not handle `Message` slicing.
## Journal
This thread manages all `Frame`s that are being received by the
`Core`. `Frame`s that are addressed to a local user are buffered in a
cache until they can be re-merged into their respective
`Message`s. Foreign `Frame`s are being re-sent to the `Core` worker
thread, which will do an interface lookup and re-transmit them to the
net hop.
The local cache is is addressed by next-`Frame` signatures. The idea
here is that each `Frame` contains the signature of the next `Frame`
in the sequence (or `None`). This means that for each `Frame` it is
possible to tell if a following incoming `Frame` should be associated
to it, or if it might be wrong or even malicious.
## Slicer
This module doesn't provide it's own thread and instead only some
utilities for taking a `Message` and size hint, and turning that into
a set of `Frame` objects.
# Routing Layer
The routing layer of qaul.net is made up of the `R.A.T.M.A.N.` routing
protocol. It provides fully delay tolerant, distance vector based
cross-network mesh routing. Check the graph below for a rough
overview of components.
![](/assets/dependencies.svg)
Currently `R.A.T.M.A.N.` has the following components.
## Core
Handles the routing table and frame routing workers. A thread listens
for `Frame`s to be sent out, while also polling for incoming frames
and sending them to a management thread.
The routing table can be modified behind an `Arc<Mutex<_>>`, allowing
other threads to inject user identity information into the worker
thread.
It also provides a `send` function which will queue a new set of
`Frame`s for sending. The `Core` does not handle `Message` slicing.
## Journal
This thread manages all `Frame`s that are being received by the
`Core`. `Frame`s that are addressed to a local user are buffered in a
cache until they can be re-merged into their respective
`Message`s. Foreign `Frame`s are being re-sent to the `Core` worker
thread, which will do an interface lookup and re-transmit them to the
net hop.
The local cache is is addressed by next-`Frame` signatures. The idea
here is that each `Frame` contains the signature of the next `Frame`
in the sequence (or `None`). This means that for each `Frame` it is
possible to tell if a following incoming `Frame` should be associated
to it, or if it might be wrong or even malicious.
## Slicer
This module doesn't provide it's own thread and instead only some
utilities for taking a `Message` and size hint, and turning that into
a set of `Frame` objects.
# Test qaul.net Application
**This Chapter expains how to test the qaul.net application. It will change rapidly until alpha.**
## Tests
* [linux-http-test](linux-http-test.md)
\ No newline at end of file
# Test qaul.net on Linux with *linux-http-test* Executable
**Executable to test the qaul.net application with it's WebGui on linux.**
## Prerequisites
To be able to run the test you need to build the following:
* [Build the `linux-http-test` executable](../install/linux.md)
* [Build the ember WebGUI](../technical/webgui/install.md)
## Test
From the qaul.net project folder you can invoke the following command to start the test
```bash
# start linux-http-test with the path to the built webui content as parameter.
target/debug/linux-http-test webgui/dist
# or alternatively
cargo run --bin linux-http-test webgui/dist
```
Now you can now open the WebGUI in your web browser via [http://127.0.0.1:9900](http://127.0.0.1:9900)
# Web GUI
The **web GUI** is the **HTML5 GUI** for qaul.net. It is
built using [EmberJS](https://emberjs.com/).
* [Install EmberJS & Test Web GUI](install.md)
# Install EmberJS & Test Web GUI
In order to develop, build and test the qaul.net web GUI
you need to install EmberJS. This is a step by step guide
to get you up and running.
## Prerequisites
You will need the following things properly installed on your computer.
* [Git](https://git-scm.com/)
* [Node.js](https://nodejs.org/) *(you need node version <10 due dependencies..., 2018/05/07)*
* [Yarn](https://yarnpkg.com/)
* [Ember CLI](https://ember-cli.com/)
* [Google Chrome](https://google.com/chrome/)
## Installation
The qaul.net web GUI code is in the `webgui` foder of the qaul.net source code repository.
* `git clone https://git.open-communication.net/qaul/qaul.net.git`
* `cd qaul.net/webgui`
* `yarn`
## Running / Development
* `ember serve`
* Visit your app at [http://localhost:4200](http://localhost:4200).
* Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
### Code Generators
Make use of the many generators for code, try `ember help generate` for more details
### Running Tests
* `ember test`
* `ember test --server`
### Linting
* `npm run lint:hbs`
* `npm run lint:js`
* `npm run lint:js -- --fix`
### Building
* `ember build` (development)
* `ember build --environment production` (production)
### Deploying
* `ember deploy production`
## Further Reading / Useful Links
* [ember.js](https://emberjs.com/)
* [ember-cli](https://ember-cli.com/)
* Development Browser Extensions
* [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi)
* [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/)
......@@ -20,13 +20,14 @@ serde = { version = "1.0", features = [ "derive" ] }
ed25519-dalek = "1.0.0-pre.3"
base64 = "0.10"
blake2 = "0.8.0"
blake2 = "0.8"
mime = "0.3"
rand = "0.7"
hex = "0.4"
[dev-dependencies]
serde_json = "1.0"
bincode = "1.0"
[features]
default = ["generate-message"]
......
......@@ -19,9 +19,9 @@ impl HttpServer {
let mut app = tide::with_state(Arc::new(rpc));
app.at("/api").post(|mut r: Request<Arc<Responder>>| {
async move {
let json: String = r.body_json().await.unwrap();
let hopefully_json: String = dbg!(r.body_string().await).unwrap();
let req_env: RequestEnv =
serde_json::from_str(json.as_str()).expect("Malformed json envelope");
serde_json::from_str(hopefully_json.as_str()).expect("Malformed json envelope");
let Envelope { id, data } = req_env.clone().into();
let req = match data {
......@@ -45,9 +45,9 @@ impl HttpServer {
});
// static file handler for the webui, assumes the webui exists
app.at("/").strip_prefix().get(StaticEp {
root: path.into(),
});
app.at("/")
.strip_prefix()
.get(StaticEp { root: path.into() });
task::block_on(async move { app.listen(addr).await }).unwrap();
}
}
......@@ -4,8 +4,8 @@
//! wraps the transactions of libqaul-rpc in json envelopes that are
//! nicer to work with for web tools.
mod parser;
mod generator;
mod parser;
use libqaul::{users::UserAuth, Identity};
use serde::{Deserialize, Serialize};
......@@ -16,7 +16,7 @@ pub(crate) type JsonMap = BTreeMap<String, JsonValue>;
/// A struct wrapper for UserAuth
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct JsonAuth {
pub struct JsonAuth {
id: Identity,
token: String,
}
......@@ -29,7 +29,7 @@ impl From<JsonAuth> for UserAuth {
/// A json specific request envelope
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RequestEnv {
pub struct RequestEnv {
/// The request ID
pub id: String,
/// Auth data for the request
......@@ -50,6 +50,41 @@ pub struct RequestEnv {
pub data: JsonMap,
}
/// Just print an example request envelope for reference. You can see
/// this by running `cargo test json::print_sample_req -- --nocapture`
#[test]
fn print_sample_req() {
use libqaul::{api::Tag, messages::MsgQuery};
println!(
"{}",
serde_json::to_string_pretty(&RequestEnv {
id: "1".into(),
auth: Some(JsonAuth {
id: Identity::random(),
token: Identity::random().to_string()
}),
page: Some("1".into()),
method: "query".into(),
kind: "messages".into(),
data: {
let mut map = JsonMap::new();
map.insert("service".into(), JsonValue::String("net.qaul.chat".into()));
map.insert(
"query".into(),
serde_json::to_value(MsgQuery::Tag(Tag {
key: "room-id".into(),
val: Identity::random().iter().cloned().collect(),
}))
.unwrap(),
);
map
},
})
.unwrap()
);
}
/// A json specific repsonse envelope
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ResponseEnv {
......
......@@ -68,31 +68,31 @@ impl From<RequestEnv> for Envelope {
data: match (kind.as_str(), method.as_str()) {
// chat service message functions
#[cfg(features = "chat")]
("chat-message", "next") => req(Request::ChatMsgNext(de_json(data, auth))),
("chat-messages", "next") => req(Request::ChatMsgNext(de_json(data, auth))),
// #[cfg(features = "chat")]
// ("chat-message", "subscribe") => req(Request::ChatMsgSub(de_json(data, auth))),
// ("chat-messages", "subscribe") => req(Request::ChatMsgSub(de_json(data, auth))),
#[cfg(features = "chat")]
("chat-message", "send") => req(Request::ChatMsgSend(de_json(data, auth))),
("chat-messages", "create") => req(Request::ChatMsgSend(de_json(data, auth))),
#[cfg(features = "chat")]
("chat-message", "query") => req(Request::ChatMsgQuery(de_json(data, auth))),
("chat-messages", "query") => req(Request::ChatMsgQuery(de_json(data, auth))),
// chat service room management
#[cfg(features = "chat")]
("chat-room", "list") => req(Request::ChatRoomList(de_json(data, auth))),
("chat-rooms", "list") => req(Request::ChatRoomList(de_json(data, auth))),
#[cfg(features = "chat")]
("chat-room", "get") => req(Request::ChatRoomGet(de_json(data, auth))),
("chat-rooms", "get") => req(Request::ChatRoomGet(de_json(data, auth))),
#[cfg(features = "chat")]
("chat-room", "create") => req(Request::ChatRoomCreate(de_json(data, auth))),
("chat-rooms", "create") => req(Request::ChatRoomCreate(de_json(data, auth))),
#[cfg(features = "chat")]
("chat-room", "modify") => req(Request::ChatRoomModify(de_json(data, auth))),
("chat-rooms", "modify") => req(Request::ChatRoomModify(de_json(data, auth))),
#[cfg(features = "chat")]
("chat-room", "delete") => req(Request::ChatRoomDelete(de_json(data, auth))),
("chat-rooms", "delete") => req(Request::ChatRoomDelete(de_json(data, auth))),
// libqaul contact functions
("contact", "list") => req(Request::ContactAll(de_json(data, auth))),
("contact", "get") => req(Request::ContactGet(de_json(data, auth))),
("contact", "query") => req(Request::ContactQuery(de_json(data, auth))),
("contact", "modify") => req(Request::ContactQuery(de_json(data, auth))),
("contacts", "list") => req(Request::ContactAll(de_json(data, auth))),
("contacts", "get") => req(Request::ContactGet(de_json(data, auth))),
("contacts", "query") => req(Request::ContactQuery(de_json(data, auth))),
("contacts", "modify") => req(Request::ContactQuery(de_json(data, auth))),
// libqaul user functions
("users", "list") => req(Request::UserList(de_json(data, auth))),
......@@ -117,8 +117,8 @@ fn envelope_chat_message_next() {
// being lazy
let json = r#"{ "id": "1",
"auth": { "id": "1C56 105D 52C3 D617 2603 D69F 9E0F 93AE", "token": "token" },
"kind": "chat-message",
"method": "poll",
"kind": "chat-messages",
"method": "next",
"data": { "room": "1C56 105D 52C3 D617 2603 D69F 9E0F 93AE" } }"#;
let je: RequestEnv = serde_json::from_str(&json).expect("JsonEnvelope failed");
......
use serde::{Deserialize, Serialize};
use serde::{
de::{Error, SeqAccess, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt;
/// A generic metadata tag
///
......@@ -10,7 +15,7 @@ use serde::{Deserialize, Serialize};
///
/// This can be used to implement things like conversation ID's,
/// In-Reply-To, and more.
#[derive(Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize)]
#[derive(Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub struct Tag {
/// A string key for a tag
pub key: String,
......@@ -31,3 +36,201 @@ impl Tag {
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, Ord, PartialOrd)]
struct HumanVec(Vec<u8>);
impl Serialize for HumanVec {
fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if dbg!(ser.is_human_readable()) {
ser.serialize_str(
&hex::encode_upper(&self.0)
.as_bytes()
.chunks(4)
.map(std::str::from_utf8)
.collect::<Result<String, _>>()
.unwrap(),
)
} else {
ser.serialize_bytes(&self.0)
}
}
}
impl<'de> Deserialize<'de> for HumanVec {
fn deserialize<D>(der: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct HumanVecVis;
impl HumanVecVis {
fn from_str<E: Error>(s: &str) -> Result<HumanVec, E> {
Self::from_bytes(&hex::decode(s).map_err(|e| E::custom(e))?)
}
fn from_bytes<E: Error, V: AsRef<[u8]>>(v: V) -> Result<HumanVec, E> {
let v = v.as_ref();
Ok(HumanVec(v.iter().cloned().collect()))
}
}
impl<'de> Visitor<'de> for HumanVecVis {
type Value = HumanVec;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "A byte array or a hex string encoded byte array",)
}
fn visit_borrowed_str<E: Error>(self, v: &'de str) -> Result<Self::Value, E> {
Self::from_str(v)
}
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
Self::from_str(&v)
}
fn visit_borrowed_bytes<E: Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
Self::from_bytes(v)
}
fn visit_byte_buf<E: Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
Self::from_bytes(v)
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut v = Vec::new();
while let Some(b) = seq.next_element::<u8>()? {
v.push(b);
}
Self::from_bytes(v)
}
}
if der.is_human_readable() {
der.deserialize_str(HumanVecVis)
} else {
der.deserialize_bytes(HumanVecVis)
}
}
}
impl Serialize for Tag {
fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
dbg!();
let mut state = ser.serialize_struct("Tag", 2)?;
state.serialize_field("key", &self.key)?;
state.serialize_field("val", &HumanVec(self.val.clone()))?;
state.end()
}
}
impl<'de> Deserialize<'de> for Tag {
fn deserialize<D>(der: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
/// Responsible for deserialising hex-encoded payloads
///
/// This visitor is called when the deserialiser is working
/// for a human readable format, such as json.
struct TagVisitor;
impl<'de> Visitor<'de> for TagVisitor {
type Value = Tag;
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let key: String = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &self))?;
let hvec: HumanVec = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &self))?;
let val: Vec<u8> = hvec.0;
Ok(Tag { key, val })
}
fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt.write_str("struct Tag { key, val }")
}
}
der.deserialize_struct("Tag", &["key", "val"], TagVisitor)
}
}
#[test]
fn serialize_tag_json() {
let t = Tag {
key: "blorp".into(),
val: vec![172, 171],
};
use serde_json;
let json = serde_json::to_string(&t).unwrap();
assert_eq!(json.as_str(), r#"{"key":"blorp","val":"ACAB"}"#);
}
#[test]
fn serialize_tag_bincode() {
let t = Tag {
key: "blorp".into(),
val: vec![172, 171],
};
use bincode;
let bc = bincode::serialize(&t).unwrap();
assert_eq!(
bc.as_slice(),
&[5, 0, 0, 0, 0, 0, 0, 0, 98, 108, 111, 114, 112, 2, 0, 0, 0, 0, 0, 0, 0, 172, 171]
);
}
#[test]
fn deserialize_tag_json() {
let json = r#"{"key":"blorp","val":"ACAB"}"#;
use serde_json;
let t: Tag = serde_json::from_str(&json).unwrap();
assert_eq!(
t,
Tag {
key: "blorp".into(),
val: vec![172, 171],
}
);
}
#[test]
fn deserialize_tag_bincode() {
let bin = [
5, 0, 0, 0, 0, 0, 0, 0, 98, 108, 111, 114, 112, 2, 0, 0, 0, 0, 0, 0, 0, 172, 171,
];
use bincode;
let t: Tag = bincode::deserialize(&bin).unwrap();
assert_eq!(
t,
Tag {
key: "blorp".into(),
val: vec![172, 171],
}
);
}
......@@ -2,21 +2,20 @@
#![allow(unused)]
use alexandria::{Address, Data, Delta, KeyAttr, Library, ScopeAttr};
use std::sync::Arc;
/// Describes that some internal state can be made persistent
///
/// This is done by implementing a simple `store` and `load` function
/// which is called by the `DataStore` to serialise data for the
/// Alexandria storage backend.
pub(crate) trait Persist {
/// Provide storage backend with storable data
fn store(&self) -> Vec<Data>;
// /// Describes that some internal state can be made persistent
// ///
// /// This is done by implementing a simple `store` and `load` function
// /// which is called by the `DataStore` to serialise data for the
// /// Alexandria storage backend.
// pub(crate) trait Persist {
// /// Provide storage backend with storable data
// fn store(&self) -> Vec<Data>;
/// Load an instance from deserialised data
fn load(&mut self, d: Vec<Data>);
}
// /// Load an instance from deserialised data
// fn load(&mut self, d: Vec<Data>);
// }
/// Provides a persistent, namespaced key-value store
///
......@@ -55,37 +54,32 @@ pub(crate) trait Persist {
/// namespaced within the Alexandria HOME dir:
/// `$ALEX_HOME/<username>/...`
pub(crate) struct DataStore {
inner: Library,
homedir: String,
states: Vec<Arc<dyn Persist>>,
}
impl DataStore {
pub(crate) fn new(homedir: String) -> Self {
let inner = Library::new();
Self {
inner,
homedir,
states: Vec::new(),
}
}
/// Register a state component with the persistence backend
pub(crate) fn register<S>(&mut self, name: S, offset: S, modl: Arc<impl Persist>) -> usize
pub(crate) fn register<S>(&mut self, name: S, offset: S) -> usize
where
S: Into<String>,
{
let id = self.states.len();
self.inner.modify_path(
Address::scope(None, &name.into()),
Delta::Insert(ScopeAttr {
ns_auth: false,
encryption: KeyAttr::Off,
offset: self.homedir.clone() + "/" + &offset.into(),
}),
);
self.inner.sync();
id
0
// let id = self.states.len();
// self.inner.modify_path(
// Address::scope(None, &name.into()),
// Delta::Insert(ScopeAttr {
// ns_auth: false,
// encryption: KeyAttr::Off,
// offset: self.homedir.clone() + "/" + &offset.into(),
// }),
// );
// self.inner.sync();
// id
}
}
# qaul.net Licenses
**qaul.net is a fully free and open source software. It is published under the
[AGPLv3], the [GNU Affero General Public License version 3 or later] with an
additional permission (see below).**
You can download the program from the project web page https://qaul.net
## AGPLv3 - GNU Affero General Public License
qaul.net is licensed under the
[GNU Affero General Public License version 3 or later](agpl-3.0.md) with the
following additional permission.
### Additional Permissions for Apple App Store
For Submission to the Apple App Store:
Provided that you are otherwise in compliance with the [AGPLv3] for each
covered work you convey (including without limitation making the
Corresponding Source available in compliance with Section 6 of the
AGPLv3), the qaul.net developers also grant you the additional
permission to convey through the Apple App Store non-source executable
versions of the Program as incorporated into each applicable covered
work as Executable Versions only under the
[Mozilla Public License version 2.0].
## Website and Documentation
The documentation & web site of qaul.net are published under the
[Creative Commons Attribution 4.0 International](cc-by.md) license.
Feel free to edit, change and redistribute the documentation to
make this project more accessible.
## Additional License
qaul.net includes many libraries that may have different, compatible
open-source and free software licenses. You can find below a
non-conclusive list of libraries and their licenses.
* The qaul.net storage library
[Alexandria](https://git.open-communication.net/qaul/alexandria)
stays under the [GPLv3](gpl-3.0.md).
[AGPLv3]: agpl-3.0.md
[GNU Affero General Public License version 3 or later]: agpl-3.0.md
[Creative Commons Attribution 4.0 International]: cc-by.md
[Mozilla Public License version 2.0]: mpl-v2.0.md
[GPLv3]: gpl-3.0.md
# qaul.net License
qaul.net is free software, licensed under the [GPL license (Version 3)](gpl-3.0.md).
You can download the program from the Internet:
https://qaul.net
The documentation & web site of qaul.net stay under the
[Creative Commons Attribution 4.0 International](cc-by.md) license.
## Licenses of Included Software & Projects
qaul.net includes and makes use of other great open-source software & projects.
That software may have different licenses:
* Font Awesome Free icons by [Font Awesome](https://github.com/FortAwesome/Font-Awesome)
[Creative Commons Attribution 4.0 International](cc-by.md) license