forked from MirrorHub/synapse
Merge branch 'develop' into event_signing
This commit is contained in:
commit
acb2d171e8
42 changed files with 458 additions and 5581 deletions
22
CHANGES.rst
22
CHANGES.rst
|
@ -1,6 +1,22 @@
|
||||||
Changes in latest
|
Changes in synapse 0.4.1 (2014-10-17)
|
||||||
=================
|
=====================================
|
||||||
This breaks federation becuase of signing
|
Webclient:
|
||||||
|
* Fix bug with display of timestamps.
|
||||||
|
|
||||||
|
Changes in synpase 0.4.0 (2014-10-17)
|
||||||
|
=====================================
|
||||||
|
This release includes changes to the federation protocol and client-server API
|
||||||
|
that is not backwards compatible.
|
||||||
|
|
||||||
|
The Matrix specification has been moved to a separate git repository:
|
||||||
|
http://github.com/matrix-org/matrix-doc
|
||||||
|
|
||||||
|
You will also need an updated syutil and config. See UPGRADES.rst.
|
||||||
|
|
||||||
|
Homeserver:
|
||||||
|
* Sign federation transactions to assert strong identity over federation.
|
||||||
|
* Rename timestamp keys in PDUs and events from 'ts' and 'hsob_ts' to 'origin_server_ts'.
|
||||||
|
|
||||||
|
|
||||||
Changes in synapse 0.3.4 (2014-09-25)
|
Changes in synapse 0.3.4 (2014-09-25)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
14
UPGRADE.rst
14
UPGRADE.rst
|
@ -1,7 +1,15 @@
|
||||||
Upgrading to latest
|
Upgrading to v0.4.0
|
||||||
===================
|
===================
|
||||||
This breaks federation between old and new servers due to signing of
|
|
||||||
transactions.
|
This release needs an updated syutil version. Run::
|
||||||
|
|
||||||
|
python setup.py develop
|
||||||
|
|
||||||
|
You will also need to upgrade your configuration as the signing key format has
|
||||||
|
changed. Run::
|
||||||
|
|
||||||
|
python -m synapse.app.homeserver --config-path <CONFIG> --generate-config
|
||||||
|
|
||||||
|
|
||||||
Upgrading to v0.3.0
|
Upgrading to v0.3.0
|
||||||
===================
|
===================
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
0.3.4
|
0.4.1
|
||||||
|
|
|
@ -14,3 +14,4 @@ fi
|
||||||
find "$DIR" -name "*.log" -delete
|
find "$DIR" -name "*.log" -delete
|
||||||
find "$DIR" -name "*.db" -delete
|
find "$DIR" -name "*.db" -delete
|
||||||
|
|
||||||
|
rm -rf $DIR/etc
|
||||||
|
|
|
@ -8,6 +8,14 @@ cd "$DIR/.."
|
||||||
|
|
||||||
mkdir -p demo/etc
|
mkdir -p demo/etc
|
||||||
|
|
||||||
|
# Check the --no-rate-limit param
|
||||||
|
PARAMS=""
|
||||||
|
if [ $# -eq 1 ]; then
|
||||||
|
if [ $1 = "--no-rate-limit" ]; then
|
||||||
|
PARAMS="--rc-messages-per-second 1000 --rc-message-burst-count 1000"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
for port in 8080 8081 8082; do
|
for port in 8080 8081 8082; do
|
||||||
echo "Starting server on port $port... "
|
echo "Starting server on port $port... "
|
||||||
|
|
||||||
|
@ -23,7 +31,8 @@ for port in 8080 8081 8082; do
|
||||||
-d "$DIR/$port.db" \
|
-d "$DIR/$port.db" \
|
||||||
-D --pid-file "$DIR/$port.pid" \
|
-D --pid-file "$DIR/$port.pid" \
|
||||||
--manhole $((port + 1000)) \
|
--manhole $((port + 1000)) \
|
||||||
--tls-dh-params-path "demo/demo.tls.dh"
|
--tls-dh-params-path "demo/demo.tls.dh" \
|
||||||
|
$PARAMS
|
||||||
|
|
||||||
python -m synapse.app.homeserver \
|
python -m synapse.app.homeserver \
|
||||||
--config-path "demo/etc/$port.config" \
|
--config-path "demo/etc/$port.config" \
|
||||||
|
|
6
docs/README.rst
Normal file
6
docs/README.rst
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
All matrix-generic documentation now lives in its own project at
|
||||||
|
|
||||||
|
github.com/matrix-org/matrix-doc.git
|
||||||
|
|
||||||
|
Only Synapse implementation-specific documentation lives here now
|
||||||
|
(together with some older stuff will be shortly migrated over to matrix-doc)
|
|
@ -1,637 +0,0 @@
|
||||||
.. TODO kegan
|
|
||||||
Room config (specifically: message history,
|
|
||||||
public rooms). /register seems super simplistic compared to /login, maybe it
|
|
||||||
would be better if /register used the same technique as /login? /register should
|
|
||||||
be "user" not "user_id".
|
|
||||||
|
|
||||||
|
|
||||||
How to use the client-server API
|
|
||||||
================================
|
|
||||||
|
|
||||||
This guide focuses on how the client-server APIs *provided by the reference
|
|
||||||
home server* can be used. Since this is specific to a home server
|
|
||||||
implementation, there may be variations in relation to registering/logging in
|
|
||||||
which are not covered in extensive detail in this guide.
|
|
||||||
|
|
||||||
If you haven't already, get a home server up and running on
|
|
||||||
``http://localhost:8008``.
|
|
||||||
|
|
||||||
|
|
||||||
Accounts
|
|
||||||
========
|
|
||||||
Before you can send and receive messages, you must **register** for an account.
|
|
||||||
If you already have an account, you must **login** into it.
|
|
||||||
|
|
||||||
`Try out the fiddle`__
|
|
||||||
|
|
||||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/register_login
|
|
||||||
|
|
||||||
Registration
|
|
||||||
------------
|
|
||||||
The aim of registration is to get a user ID and access token which you will need
|
|
||||||
when accessing other APIs::
|
|
||||||
|
|
||||||
curl -XPOST -d '{"user":"example", "password":"wordpass", "type":"m.login.password"}' "http://localhost:8008/_matrix/client/api/v1/register"
|
|
||||||
|
|
||||||
{
|
|
||||||
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.AqdSzFmFYrLrTmteXc",
|
|
||||||
"home_server": "localhost",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
NB: If a ``user`` is not specified, one will be randomly generated for you.
|
|
||||||
If you do not specify a ``password``, you will be unable to login to the account
|
|
||||||
if you forget the ``access_token``.
|
|
||||||
|
|
||||||
Implementation note: The matrix specification does not enforce how users
|
|
||||||
register with a server. It just specifies the URL path and absolute minimum
|
|
||||||
keys. The reference home server uses a username/password to authenticate user,
|
|
||||||
but other home servers may use different methods. This is why you need to
|
|
||||||
specify the ``type`` of method.
|
|
||||||
|
|
||||||
Login
|
|
||||||
-----
|
|
||||||
The aim when logging in is to get an access token for your existing user ID::
|
|
||||||
|
|
||||||
curl -XGET "http://localhost:8008/_matrix/client/api/v1/login"
|
|
||||||
|
|
||||||
{
|
|
||||||
"flows": [
|
|
||||||
{
|
|
||||||
"type": "m.login.password"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/login"
|
|
||||||
|
|
||||||
{
|
|
||||||
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
|
|
||||||
"home_server": "localhost",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
Implementation note: Different home servers may implement different methods for
|
|
||||||
logging in to an existing account. In order to check that you know how to login
|
|
||||||
to this home server, you must perform a ``GET`` first and make sure you
|
|
||||||
recognise the login type. If you do not know how to login, you can
|
|
||||||
``GET /login/fallback`` which will return a basic webpage which you can use to
|
|
||||||
login. The reference home server implementation support username/password login,
|
|
||||||
but other home servers may support different login methods (e.g. OAuth2).
|
|
||||||
|
|
||||||
|
|
||||||
Communicating
|
|
||||||
=============
|
|
||||||
|
|
||||||
In order to communicate with another user, you must **create a room** with that
|
|
||||||
user and **send a message** to that room.
|
|
||||||
|
|
||||||
`Try out the fiddle`__
|
|
||||||
|
|
||||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/create_room_send_msg
|
|
||||||
|
|
||||||
Creating a room
|
|
||||||
---------------
|
|
||||||
If you want to send a message to someone, you have to be in a room with them. To
|
|
||||||
create a room::
|
|
||||||
|
|
||||||
curl -XPOST -d '{"room_alias_name":"tutorial"}' "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
{
|
|
||||||
"room_alias": "#tutorial:localhost",
|
|
||||||
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
The "room alias" is a human-readable string which can be shared with other users
|
|
||||||
so they can join a room, rather than the room ID which is a randomly generated
|
|
||||||
string. You can have multiple room aliases per room.
|
|
||||||
|
|
||||||
.. TODO(kegan)
|
|
||||||
How to add/remove aliases from an existing room.
|
|
||||||
|
|
||||||
|
|
||||||
Sending messages
|
|
||||||
----------------
|
|
||||||
You can now send messages to this room::
|
|
||||||
|
|
||||||
curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/send/m.room.message?access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
{
|
|
||||||
"event_id": "YUwRidLecu"
|
|
||||||
}
|
|
||||||
|
|
||||||
The event ID returned is a unique ID which identifies this message.
|
|
||||||
|
|
||||||
NB: There are no limitations to the types of messages which can be exchanged.
|
|
||||||
The only requirement is that ``"msgtype"`` is specified. The Matrix
|
|
||||||
specification outlines the following standard types: ``m.text``, ``m.image``,
|
|
||||||
``m.audio``, ``m.video``, ``m.location``, ``m.emote``. See the specification for
|
|
||||||
more information on these types.
|
|
||||||
|
|
||||||
Users and rooms
|
|
||||||
===============
|
|
||||||
|
|
||||||
Each room can be configured to allow or disallow certain rules. In particular,
|
|
||||||
these rules may specify if you require an **invitation** from someone already in
|
|
||||||
the room in order to **join the room**. In addition, you may also be able to
|
|
||||||
join a room **via a room alias** if one was set up.
|
|
||||||
|
|
||||||
`Try out the fiddle`__
|
|
||||||
|
|
||||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/room_memberships
|
|
||||||
|
|
||||||
Inviting a user to a room
|
|
||||||
-------------------------
|
|
||||||
You can directly invite a user to a room like so::
|
|
||||||
|
|
||||||
curl -XPOST -d '{"user_id":"@myfriend:localhost"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/invite?access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
This informs ``@myfriend:localhost`` of the room ID
|
|
||||||
``!CvcvRuDYDzTOzfKKgh:localhost`` and allows them to join the room.
|
|
||||||
|
|
||||||
Joining a room via an invite
|
|
||||||
----------------------------
|
|
||||||
If you receive an invite, you can join the room::
|
|
||||||
|
|
||||||
curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/join?access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
NB: Only the person invited (``@myfriend:localhost``) can change the membership
|
|
||||||
state to ``"join"``. Repeatedly joining a room does nothing.
|
|
||||||
|
|
||||||
Joining a room via an alias
|
|
||||||
---------------------------
|
|
||||||
Alternatively, if you know the room alias for this room and the room config
|
|
||||||
allows it, you can directly join a room via the alias::
|
|
||||||
|
|
||||||
curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/join/%23tutorial%3Alocalhost?access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
{
|
|
||||||
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
|
|
||||||
}
|
|
||||||
|
|
||||||
You will need to use the room ID when sending messages, not the room alias.
|
|
||||||
|
|
||||||
NB: If the room is configured to be an invite-only room, you will still require
|
|
||||||
an invite in order to join the room even though you know the room alias. As a
|
|
||||||
result, it is more common to see a room alias in relation to a public room,
|
|
||||||
which do not require invitations.
|
|
||||||
|
|
||||||
Getting events
|
|
||||||
==============
|
|
||||||
An event is some interesting piece of data that a client may be interested in.
|
|
||||||
It can be a message in a room, a room invite, etc. There are many different ways
|
|
||||||
of getting events, depending on what the client already knows.
|
|
||||||
|
|
||||||
`Try out the fiddle`__
|
|
||||||
|
|
||||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/event_stream
|
|
||||||
|
|
||||||
Getting all state
|
|
||||||
-----------------
|
|
||||||
If the client doesn't know any information on the rooms the user is
|
|
||||||
invited/joined on, they can get all the user's state for all rooms::
|
|
||||||
|
|
||||||
curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
{
|
|
||||||
"end": "s39_18_0",
|
|
||||||
"presence": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"last_active_ago": 1061436,
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
"type": "m.presence"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"rooms": [
|
|
||||||
{
|
|
||||||
"membership": "join",
|
|
||||||
"messages": {
|
|
||||||
"chunk": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"@example:localhost": 10,
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"event_id": "wAumPSTsWF",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.power_levels",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"join_rule": "public"
|
|
||||||
},
|
|
||||||
"event_id": "jrLVqKHKiI",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.join_rules",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"level": 10
|
|
||||||
},
|
|
||||||
"event_id": "WpmTgsNWUZ",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.add_state_level",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"level": 0
|
|
||||||
},
|
|
||||||
"event_id": "qUMBJyKsTQ",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.send_event_level",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"ban_level": 5,
|
|
||||||
"kick_level": 5
|
|
||||||
},
|
|
||||||
"event_id": "YAaDmKvoUW",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.ops_levels",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": null,
|
|
||||||
"displayname": null,
|
|
||||||
"membership": "join"
|
|
||||||
},
|
|
||||||
"event_id": "RJbPMtCutf",
|
|
||||||
"membership": "join",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@example:localhost",
|
|
||||||
"ts": 1409665586730,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"body": "hello",
|
|
||||||
"hsob_ts": 1409665660439,
|
|
||||||
"msgtype": "m.text"
|
|
||||||
},
|
|
||||||
"event_id": "YUwRidLecu",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"ts": 1409665660439,
|
|
||||||
"type": "m.room.message",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"membership": "invite"
|
|
||||||
},
|
|
||||||
"event_id": "YjNuBKnPsb",
|
|
||||||
"membership": "invite",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@myfriend:localhost",
|
|
||||||
"ts": 1409666426819,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": null,
|
|
||||||
"displayname": null,
|
|
||||||
"membership": "join",
|
|
||||||
"prev": "join"
|
|
||||||
},
|
|
||||||
"event_id": "KWwdDjNZnm",
|
|
||||||
"membership": "join",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@example:localhost",
|
|
||||||
"ts": 1409666551582,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": null,
|
|
||||||
"displayname": null,
|
|
||||||
"membership": "join"
|
|
||||||
},
|
|
||||||
"event_id": "JFLVteSvQc",
|
|
||||||
"membership": "join",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@example:localhost",
|
|
||||||
"ts": 1409666587265,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"end": "s39_18_0",
|
|
||||||
"start": "t1-11_18_0"
|
|
||||||
},
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"creator": "@example:localhost"
|
|
||||||
},
|
|
||||||
"event_id": "dMUoqVTZca",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.create",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"@example:localhost": 10,
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"event_id": "wAumPSTsWF",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.power_levels",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"join_rule": "public"
|
|
||||||
},
|
|
||||||
"event_id": "jrLVqKHKiI",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.join_rules",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"level": 10
|
|
||||||
},
|
|
||||||
"event_id": "WpmTgsNWUZ",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.add_state_level",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"level": 0
|
|
||||||
},
|
|
||||||
"event_id": "qUMBJyKsTQ",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.send_event_level",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"ban_level": 5,
|
|
||||||
"kick_level": 5
|
|
||||||
},
|
|
||||||
"event_id": "YAaDmKvoUW",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.ops_levels",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"membership": "invite"
|
|
||||||
},
|
|
||||||
"event_id": "YjNuBKnPsb",
|
|
||||||
"membership": "invite",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@myfriend:localhost",
|
|
||||||
"ts": 1409666426819,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": null,
|
|
||||||
"displayname": null,
|
|
||||||
"membership": "join"
|
|
||||||
},
|
|
||||||
"event_id": "JFLVteSvQc",
|
|
||||||
"membership": "join",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@example:localhost",
|
|
||||||
"ts": 1409666587265,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
This returns all the room information the user is invited/joined on, as well as
|
|
||||||
all of the presences relevant for these rooms. This can be a LOT of data. You
|
|
||||||
may just want the most recent event for each room. This can be achieved by
|
|
||||||
applying query parameters to ``limit`` this request::
|
|
||||||
|
|
||||||
curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?limit=1&access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
{
|
|
||||||
"end": "s39_18_0",
|
|
||||||
"presence": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"last_active_ago": 1279484,
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
"type": "m.presence"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"rooms": [
|
|
||||||
{
|
|
||||||
"membership": "join",
|
|
||||||
"messages": {
|
|
||||||
"chunk": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": null,
|
|
||||||
"displayname": null,
|
|
||||||
"membership": "join"
|
|
||||||
},
|
|
||||||
"event_id": "JFLVteSvQc",
|
|
||||||
"membership": "join",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@example:localhost",
|
|
||||||
"ts": 1409666587265,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"end": "s39_18_0",
|
|
||||||
"start": "t10-30_18_0"
|
|
||||||
},
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state": [
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"creator": "@example:localhost"
|
|
||||||
},
|
|
||||||
"event_id": "dMUoqVTZca",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.create",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"@example:localhost": 10,
|
|
||||||
"default": 0
|
|
||||||
},
|
|
||||||
"event_id": "wAumPSTsWF",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.power_levels",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"join_rule": "public"
|
|
||||||
},
|
|
||||||
"event_id": "jrLVqKHKiI",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.join_rules",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"level": 10
|
|
||||||
},
|
|
||||||
"event_id": "WpmTgsNWUZ",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.add_state_level",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"level": 0
|
|
||||||
},
|
|
||||||
"event_id": "qUMBJyKsTQ",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.send_event_level",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"ban_level": 5,
|
|
||||||
"kick_level": 5
|
|
||||||
},
|
|
||||||
"event_id": "YAaDmKvoUW",
|
|
||||||
"required_power_level": 10,
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "",
|
|
||||||
"ts": 1409665585188,
|
|
||||||
"type": "m.room.ops_levels",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"membership": "invite"
|
|
||||||
},
|
|
||||||
"event_id": "YjNuBKnPsb",
|
|
||||||
"membership": "invite",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@myfriend:localhost",
|
|
||||||
"ts": 1409666426819,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"content": {
|
|
||||||
"avatar_url": null,
|
|
||||||
"displayname": null,
|
|
||||||
"membership": "join"
|
|
||||||
},
|
|
||||||
"event_id": "JFLVteSvQc",
|
|
||||||
"membership": "join",
|
|
||||||
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
|
|
||||||
"state_key": "@example:localhost",
|
|
||||||
"ts": 1409666587265,
|
|
||||||
"type": "m.room.member",
|
|
||||||
"user_id": "@example:localhost"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
Getting live state
|
|
||||||
------------------
|
|
||||||
Once you know which rooms the client has previously interacted with, you need to
|
|
||||||
listen for incoming events. This can be done like so::
|
|
||||||
|
|
||||||
curl -XGET "http://localhost:8008/_matrix/client/api/v1/events?access_token=YOUR_ACCESS_TOKEN"
|
|
||||||
|
|
||||||
{
|
|
||||||
"chunk": [],
|
|
||||||
"end": "s39_18_0",
|
|
||||||
"start": "s39_18_0"
|
|
||||||
}
|
|
||||||
|
|
||||||
This will block waiting for an incoming event, timing out after several seconds.
|
|
||||||
Even if there are no new events (as in the example above), there will be some
|
|
||||||
pagination stream response keys. The client should make subsequent requests
|
|
||||||
using the value of the ``"end"`` key (in this case ``s39_18_0``) as the ``from``
|
|
||||||
query parameter e.g. ``http://localhost:8008/_matrix/client/api/v1/events?access
|
|
||||||
_token=YOUR_ACCESS_TOKEN&from=s39_18_0``. This value should be stored so when the
|
|
||||||
client reopens your app after a period of inactivity, you can resume from where
|
|
||||||
you got up to in the event stream. If it has been a long period of inactivity,
|
|
||||||
there may be LOTS of events waiting for the user. In this case, you may wish to
|
|
||||||
get all state instead and then resume getting live state from a newer end token.
|
|
||||||
|
|
||||||
NB: The timeout can be changed by adding a ``timeout`` query parameter, which is
|
|
||||||
in milliseconds. A timeout of 0 will not block.
|
|
||||||
|
|
||||||
|
|
||||||
Example application
|
|
||||||
-------------------
|
|
||||||
The following example demonstrates registration and login, live event streaming,
|
|
||||||
creating and joining rooms, sending messages, getting member lists and getting
|
|
||||||
historical messages for a room. This covers most functionality of a messaging
|
|
||||||
application.
|
|
||||||
|
|
||||||
`Try out the fiddle`__
|
|
||||||
|
|
||||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/synapse/tree/master/jsfiddles/example_app
|
|
|
@ -1,46 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"swaggerVersion": "1.2",
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"path": "-login",
|
|
||||||
"description": "Login operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "-registration",
|
|
||||||
"description": "Registration operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "-rooms",
|
|
||||||
"description": "Room operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "-profile",
|
|
||||||
"description": "Profile operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "-presence",
|
|
||||||
"description": "Presence operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "-events",
|
|
||||||
"description": "Event operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "-directory",
|
|
||||||
"description": "Directory operations"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"authorizations": {
|
|
||||||
"token": {
|
|
||||||
"scopes": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"info": {
|
|
||||||
"title": "Matrix Client-Server API Reference",
|
|
||||||
"description": "This contains the client-server API for the reference implementation of the home server",
|
|
||||||
"termsOfServiceUrl": "http://matrix.org",
|
|
||||||
"license": "Apache 2.0",
|
|
||||||
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"swaggerVersion": "1.2",
|
|
||||||
"basePath": "http://localhost:8008/_matrix/client/api/v1",
|
|
||||||
"resourcePath": "/directory",
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"path": "/directory/room/{roomAlias}",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get the room ID corresponding to this room alias.",
|
|
||||||
"notes": "Volatile: This API is likely to change.",
|
|
||||||
"type": "DirectoryResponse",
|
|
||||||
"nickname": "get_room_id_for_alias",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomAlias",
|
|
||||||
"description": "The room alias.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Create a new mapping from room alias to room ID.",
|
|
||||||
"notes": "Volatile: This API is likely to change.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "add_room_alias",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomAlias",
|
|
||||||
"description": "The room alias to set.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The room ID to set.",
|
|
||||||
"required": true,
|
|
||||||
"type": "RoomAliasRequest",
|
|
||||||
"paramType": "body"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "DELETE",
|
|
||||||
"summary": "Removes a mapping of room alias to room ID.",
|
|
||||||
"notes": "Only privileged users can perform this action.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "remove_room_alias",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomAlias",
|
|
||||||
"description": "The room alias to remove.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"models": {
|
|
||||||
"DirectoryResponse": {
|
|
||||||
"id": "DirectoryResponse",
|
|
||||||
"properties": {
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The fully-qualified room ID.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"servers": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "string"
|
|
||||||
},
|
|
||||||
"description": "A list of servers that know about this room.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RoomAliasRequest": {
|
|
||||||
"id": "RoomAliasRequest",
|
|
||||||
"properties": {
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The room ID to map the alias to.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,247 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"swaggerVersion": "1.2",
|
|
||||||
"basePath": "http://localhost:8008/_matrix/client/api/v1",
|
|
||||||
"resourcePath": "/events",
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"path": "/events",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Listen on the event stream",
|
|
||||||
"notes": "This can only be done by the logged in user. This will block until an event is received, or until the timeout is reached.",
|
|
||||||
"type": "PaginationChunk",
|
|
||||||
"nickname": "get_event_stream",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "from",
|
|
||||||
"description": "The token to stream from.",
|
|
||||||
"required": false,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "timeout",
|
|
||||||
"description": "The maximum time in milliseconds to wait for an event.",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"paramType": "query"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Bad pagination token."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/events/{eventId}",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get information about a single event.",
|
|
||||||
"notes": "Get information about a single event.",
|
|
||||||
"type": "Event",
|
|
||||||
"nickname": "get_event",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "eventId",
|
|
||||||
"description": "The event ID to get.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 404,
|
|
||||||
"message": "Event not found."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/initialSync",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get this user's current state.",
|
|
||||||
"notes": "Get this user's current state.",
|
|
||||||
"type": "InitialSyncResponse",
|
|
||||||
"nickname": "initial_sync",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "limit",
|
|
||||||
"description": "The maximum number of messages to return for each room.",
|
|
||||||
"type": "integer",
|
|
||||||
"paramType": "query",
|
|
||||||
"required": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/publicRooms",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get a list of publicly visible rooms.",
|
|
||||||
"type": "PublicRoomsPaginationChunk",
|
|
||||||
"nickname": "get_public_room_list"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"models": {
|
|
||||||
"PaginationChunk": {
|
|
||||||
"id": "PaginationChunk",
|
|
||||||
"properties": {
|
|
||||||
"start": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"chunk": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "An array of events.",
|
|
||||||
"required": true,
|
|
||||||
"items": {
|
|
||||||
"$ref": "Event"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Event": {
|
|
||||||
"id": "Event",
|
|
||||||
"properties": {
|
|
||||||
"event_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "An ID which uniquely identifies this event.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The room in which this event occurred.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PublicRoomInfo": {
|
|
||||||
"id": "PublicRoomInfo",
|
|
||||||
"properties": {
|
|
||||||
"aliases": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of room aliases for this room.",
|
|
||||||
"items": {
|
|
||||||
"$ref": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The name of the room, as given by the m.room.name state event."
|
|
||||||
},
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The room ID for this public room.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"topic": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The topic of this room, as given by the m.room.topic state event."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PublicRoomsPaginationChunk": {
|
|
||||||
"id": "PublicRoomsPaginationChunk",
|
|
||||||
"properties": {
|
|
||||||
"start": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"chunk": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of public room data.",
|
|
||||||
"required": true,
|
|
||||||
"items": {
|
|
||||||
"$ref": "PublicRoomInfo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"InitialSyncResponse": {
|
|
||||||
"id": "InitialSyncResponse",
|
|
||||||
"properties": {
|
|
||||||
"end": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A streaming token which can be used with /events to continue from this snapshot of data.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"presence": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of presence events.",
|
|
||||||
"items": {
|
|
||||||
"$ref": "Event"
|
|
||||||
},
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"rooms": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of initial sync room data.",
|
|
||||||
"required": false,
|
|
||||||
"items": {
|
|
||||||
"$ref": "InitialSyncRoomData"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"InitialSyncRoomData": {
|
|
||||||
"id": "InitialSyncRoomData",
|
|
||||||
"properties": {
|
|
||||||
"membership": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "This user's membership state in this room.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ID of this room.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"messages": {
|
|
||||||
"type": "PaginationChunk",
|
|
||||||
"description": "The most recent messages for this room, governed by the limit parameter.",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of state events representing the current state of the room.",
|
|
||||||
"required": false,
|
|
||||||
"items": {
|
|
||||||
"$ref": "Event"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"nickname": "get_login_info",
|
|
||||||
"notes": "All login stages MUST be mentioned if there is >1 login type.",
|
|
||||||
"summary": "Get the login mechanism to use when logging in.",
|
|
||||||
"type": "LoginFlows"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"nickname": "submit_login",
|
|
||||||
"notes": "If this is part of a multi-stage login, there MUST be a 'session' key.",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "A login submission",
|
|
||||||
"name": "body",
|
|
||||||
"paramType": "body",
|
|
||||||
"required": true,
|
|
||||||
"type": "LoginSubmission"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Bad login type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Missing JSON keys"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"summary": "Submit a login action.",
|
|
||||||
"type": "LoginResult"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"path": "/login"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"basePath": "http://localhost:8008/_matrix/client/api/v1",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"models": {
|
|
||||||
"LoginFlows": {
|
|
||||||
"id": "LoginFlows",
|
|
||||||
"properties": {
|
|
||||||
"flows": {
|
|
||||||
"description": "A list of valid login flows.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "LoginInfo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LoginInfo": {
|
|
||||||
"id": "LoginInfo",
|
|
||||||
"properties": {
|
|
||||||
"stages": {
|
|
||||||
"description": "Multi-stage login only: An array of all the login types required to login.",
|
|
||||||
"items": {
|
|
||||||
"$ref": "string"
|
|
||||||
},
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"description": "The login type that must be used when logging in.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LoginResult": {
|
|
||||||
"id": "LoginResult",
|
|
||||||
"properties": {
|
|
||||||
"access_token": {
|
|
||||||
"description": "The access token for this user's login if this is the final stage of the login process.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"description": "The user's fully-qualified user ID.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"next": {
|
|
||||||
"description": "Multi-stage login only: The next login type to submit.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"session": {
|
|
||||||
"description": "Multi-stage login only: The session token to send when submitting the next login type.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"LoginSubmission": {
|
|
||||||
"id": "LoginSubmission",
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"description": "The type of login being submitted.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"session": {
|
|
||||||
"description": "Multi-stage login only: The session token from an earlier login stage.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"_login_type_defined_keys_": {
|
|
||||||
"description": "Keys as defined by the specified login type, e.g. \"user\", \"password\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"resourcePath": "/login",
|
|
||||||
"swaggerVersion": "1.2"
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,164 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"swaggerVersion": "1.2",
|
|
||||||
"basePath": "http://localhost:8008/_matrix/client/api/v1",
|
|
||||||
"resourcePath": "/presence",
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"path": "/presence/{userId}/status",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Update this user's presence state.",
|
|
||||||
"notes": "This can only be done by the logged in user.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "update_presence",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The new presence state",
|
|
||||||
"required": true,
|
|
||||||
"type": "PresenceUpdate",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose presence to set.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get this user's presence state.",
|
|
||||||
"notes": "Get this user's presence state.",
|
|
||||||
"type": "PresenceUpdate",
|
|
||||||
"nickname": "get_presence",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose presence to get.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/presence/list/{userId}",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Retrieve a list of presences for all of this user's friends.",
|
|
||||||
"notes": "",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "Presence"
|
|
||||||
},
|
|
||||||
"nickname": "get_presence_list",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose presence list to get.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Add or remove users from this presence list.",
|
|
||||||
"notes": "Add or remove users from this presence list.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "modify_presence_list",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose presence list is being modified.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The modifications to make to this presence list.",
|
|
||||||
"required": true,
|
|
||||||
"type": "PresenceListModifications",
|
|
||||||
"paramType": "body"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"models": {
|
|
||||||
"PresenceUpdate": {
|
|
||||||
"id": "PresenceUpdate",
|
|
||||||
"properties": {
|
|
||||||
"presence": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Enum: The presence state.",
|
|
||||||
"enum": [
|
|
||||||
"offline",
|
|
||||||
"unavailable",
|
|
||||||
"online",
|
|
||||||
"free_for_chat"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"status_msg": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The user-defined message associated with this presence state."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subTypes": [
|
|
||||||
"Presence"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Presence": {
|
|
||||||
"id": "Presence",
|
|
||||||
"properties": {
|
|
||||||
"last_active_ago": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64",
|
|
||||||
"description": "The last time this user performed an action on their home server."
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The fully qualified user ID"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PresenceListModifications": {
|
|
||||||
"id": "PresenceListModifications",
|
|
||||||
"properties": {
|
|
||||||
"invite": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of user IDs to add to the list.",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A fully qualified user ID."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"drop": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of user IDs to remove from the list.",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A fully qualified user ID."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"swaggerVersion": "1.2",
|
|
||||||
"basePath": "http://localhost:8008/_matrix/client/api/v1",
|
|
||||||
"resourcePath": "/profile",
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"path": "/profile/{userId}/displayname",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Set a display name.",
|
|
||||||
"notes": "This can only be done by the logged in user.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "set_display_name",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The new display name for this user.",
|
|
||||||
"required": true,
|
|
||||||
"type": "DisplayName",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose display name to set.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get a display name.",
|
|
||||||
"notes": "This can be done by anyone.",
|
|
||||||
"type": "DisplayName",
|
|
||||||
"nickname": "get_display_name",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose display name to get.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/profile/{userId}/avatar_url",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Set an avatar URL.",
|
|
||||||
"notes": "This can only be done by the logged in user.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "set_avatar_url",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The new avatar url for this user.",
|
|
||||||
"required": true,
|
|
||||||
"type": "AvatarUrl",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose avatar url to set.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get an avatar url.",
|
|
||||||
"notes": "This can be done by anyone.",
|
|
||||||
"type": "AvatarUrl",
|
|
||||||
"nickname": "get_avatar_url",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose avatar url to get.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"models": {
|
|
||||||
"DisplayName": {
|
|
||||||
"id": "DisplayName",
|
|
||||||
"properties": {
|
|
||||||
"displayname": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The textual display name"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AvatarUrl": {
|
|
||||||
"id": "AvatarUrl",
|
|
||||||
"properties": {
|
|
||||||
"avatar_url": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A url to an image representing an avatar."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"nickname": "get_registration_info",
|
|
||||||
"notes": "All login stages MUST be mentioned if there is >1 login type.",
|
|
||||||
"summary": "Get the login mechanism to use when registering.",
|
|
||||||
"type": "RegistrationFlows"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"nickname": "submit_registration",
|
|
||||||
"notes": "If this is part of a multi-stage registration, there MUST be a 'session' key.",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "A registration submission",
|
|
||||||
"name": "body",
|
|
||||||
"paramType": "body",
|
|
||||||
"required": true,
|
|
||||||
"type": "RegistrationSubmission"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Bad login type"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Missing JSON keys"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"summary": "Submit a registration action.",
|
|
||||||
"type": "RegistrationResult"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"path": "/register"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"basePath": "http://localhost:8008/_matrix/client/api/v1",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"models": {
|
|
||||||
"RegistrationFlows": {
|
|
||||||
"id": "RegistrationFlows",
|
|
||||||
"properties": {
|
|
||||||
"flows": {
|
|
||||||
"description": "A list of valid registration flows.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "RegistrationInfo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RegistrationInfo": {
|
|
||||||
"id": "RegistrationInfo",
|
|
||||||
"properties": {
|
|
||||||
"stages": {
|
|
||||||
"description": "Multi-stage registration only: An array of all the login types required to registration.",
|
|
||||||
"items": {
|
|
||||||
"$ref": "string"
|
|
||||||
},
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"description": "The first login type that must be used when logging in.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RegistrationResult": {
|
|
||||||
"id": "RegistrationResult",
|
|
||||||
"properties": {
|
|
||||||
"access_token": {
|
|
||||||
"description": "The access token for this user's registration if this is the final stage of the registration process.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"user_id": {
|
|
||||||
"description": "The user's fully-qualified user ID.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"next": {
|
|
||||||
"description": "Multi-stage registration only: The next registration type to submit.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"session": {
|
|
||||||
"description": "Multi-stage registration only: The session token to send when submitting the next registration type.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RegistrationSubmission": {
|
|
||||||
"id": "RegistrationSubmission",
|
|
||||||
"properties": {
|
|
||||||
"type": {
|
|
||||||
"description": "The type of registration being submitted.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"session": {
|
|
||||||
"description": "Multi-stage registration only: The session token from an earlier registration stage.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"_registration_type_defined_keys_": {
|
|
||||||
"description": "Keys as defined by the specified registration type, e.g. \"user\", \"password\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"resourcePath": "/register",
|
|
||||||
"swaggerVersion": "1.2"
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,977 +0,0 @@
|
||||||
{
|
|
||||||
"apiVersion": "1.0.0",
|
|
||||||
"swaggerVersion": "1.2",
|
|
||||||
"basePath": "http://localhost:8008/_matrix/client/api/v1",
|
|
||||||
"resourcePath": "/rooms",
|
|
||||||
"produces": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"authorizations": {
|
|
||||||
"token": []
|
|
||||||
},
|
|
||||||
"apis": [
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/send/{eventType}",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Send a generic non-state event to this room.",
|
|
||||||
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
|
|
||||||
"type": "EventId",
|
|
||||||
"nickname": "send_non_state_event",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The event contents",
|
|
||||||
"required": true,
|
|
||||||
"type": "EventContent",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to send the message in.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "eventType",
|
|
||||||
"description": "The type of event to send.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/state/{eventType}/{stateKey}",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Send a generic state event to this room.",
|
|
||||||
"notes": "The state key can be omitted, such that you can PUT to /rooms/{roomId}/state/{eventType}. The state key defaults to a 0 length string in this case.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "send_state_event",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The event contents",
|
|
||||||
"required": true,
|
|
||||||
"type": "EventContent",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to send the message in.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "eventType",
|
|
||||||
"description": "The type of event to send.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "stateKey",
|
|
||||||
"description": "An identifier used to specify clobbering semantics. State events with the same (roomId, eventType, stateKey) will be replaced.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/send/m.room.message",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Send a message in this room.",
|
|
||||||
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
|
|
||||||
"type": "EventId",
|
|
||||||
"nickname": "send_message",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The message contents",
|
|
||||||
"required": true,
|
|
||||||
"type": "Message",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to send the message in.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/state/m.room.topic",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Set the topic for this room.",
|
|
||||||
"notes": "Set the topic for this room.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "set_topic",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The topic contents",
|
|
||||||
"required": true,
|
|
||||||
"type": "Topic",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to set the topic in.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get the topic for this room.",
|
|
||||||
"notes": "Get the topic for this room.",
|
|
||||||
"type": "Topic",
|
|
||||||
"nickname": "get_topic",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to get topic in.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 404,
|
|
||||||
"message": "Topic not found."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/state/m.room.name",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Set the name of this room.",
|
|
||||||
"notes": "Set the name of this room.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "set_room_name",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The name contents",
|
|
||||||
"required": true,
|
|
||||||
"type": "RoomName",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to set the name of.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get the room's name.",
|
|
||||||
"notes": "",
|
|
||||||
"type": "RoomName",
|
|
||||||
"nickname": "get_room_name",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to get the name of.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 404,
|
|
||||||
"message": "Name not found."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/send/m.room.message.feedback",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Send feedback to a message.",
|
|
||||||
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
|
|
||||||
"type": "EventId",
|
|
||||||
"nickname": "send_feedback",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The feedback contents",
|
|
||||||
"required": true,
|
|
||||||
"type": "Feedback",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to send the feedback in.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Bad feedback type."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/invite",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Invite a user to this room.",
|
|
||||||
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "invite",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room which has this user.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The user to invite.",
|
|
||||||
"required": true,
|
|
||||||
"type": "InviteRequest",
|
|
||||||
"paramType": "body"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/join",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Join this room.",
|
|
||||||
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "join_room",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to join.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"required": true,
|
|
||||||
"type": "JoinRequest",
|
|
||||||
"paramType": "body"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/leave",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Leave this room.",
|
|
||||||
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "leave",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to leave.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"required": true,
|
|
||||||
"type": "LeaveRequest",
|
|
||||||
"paramType": "body"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/ban",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Ban a user in the room.",
|
|
||||||
"notes": "This operation can also be done as a PUT by suffixing /{txnId}. The caller must have the required power level to do this operation.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "ban",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room which has the user to ban.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The user to ban.",
|
|
||||||
"required": true,
|
|
||||||
"type": "BanRequest",
|
|
||||||
"paramType": "body"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/state/m.room.member/{userId}",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "PUT",
|
|
||||||
"summary": "Change the membership state for a user in a room.",
|
|
||||||
"notes": "Change the membership state for a user in a room.",
|
|
||||||
"type": "void",
|
|
||||||
"nickname": "set_membership",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The new membership state",
|
|
||||||
"required": true,
|
|
||||||
"type": "Member",
|
|
||||||
"paramType": "body"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose membership is being changed.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room which has this user.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "No membership key."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Bad membership value."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 403,
|
|
||||||
"message": "When inviting: You are not in the room."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 403,
|
|
||||||
"message": "When inviting: <target> is already in the room."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 403,
|
|
||||||
"message": "When joining: Cannot force another user to join."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 403,
|
|
||||||
"message": "When joining: You are not invited to this room."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get the membership state of a user in a room.",
|
|
||||||
"notes": "Get the membership state of a user in a room.",
|
|
||||||
"type": "Member",
|
|
||||||
"nickname": "get_membership",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "userId",
|
|
||||||
"description": "The user whose membership state you want to get.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room which has this user.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 404,
|
|
||||||
"message": "Member not found."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/join/{roomAliasOrId}",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Join a room via a room alias or room ID.",
|
|
||||||
"notes": "Join a room via a room alias or room ID.",
|
|
||||||
"type": "JoinRoomInfo",
|
|
||||||
"nickname": "join",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomAliasOrId",
|
|
||||||
"description": "The room alias or room ID to join.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Bad room alias."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/createRoom",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "POST",
|
|
||||||
"summary": "Create a room.",
|
|
||||||
"notes": "Create a room.",
|
|
||||||
"type": "RoomInfo",
|
|
||||||
"nickname": "create_room",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "body",
|
|
||||||
"description": "The desired configuration for the room. This operation can also be done as a PUT by suffixing /{txnId}.",
|
|
||||||
"required": true,
|
|
||||||
"type": "RoomConfig",
|
|
||||||
"paramType": "body"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responseMessages": [
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Body must be JSON."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"code": 400,
|
|
||||||
"message": "Room alias already taken."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/messages",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get a list of messages for this room.",
|
|
||||||
"notes": "Get a list of messages for this room.",
|
|
||||||
"type": "MessagePaginationChunk",
|
|
||||||
"nickname": "get_messages",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to get messages in.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "from",
|
|
||||||
"description": "The token to start getting results from.",
|
|
||||||
"required": false,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "to",
|
|
||||||
"description": "The token to stop getting results at.",
|
|
||||||
"required": false,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "limit",
|
|
||||||
"description": "The maximum number of messages to return.",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"paramType": "query"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/members",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get a list of members for this room.",
|
|
||||||
"notes": "Get a list of members for this room.",
|
|
||||||
"type": "MemberPaginationChunk",
|
|
||||||
"nickname": "get_members",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to get a list of members from.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "from",
|
|
||||||
"description": "The token to start getting results from.",
|
|
||||||
"required": false,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "to",
|
|
||||||
"description": "The token to stop getting results at.",
|
|
||||||
"required": false,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "query"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "limit",
|
|
||||||
"description": "The maximum number of members to return.",
|
|
||||||
"required": false,
|
|
||||||
"type": "integer",
|
|
||||||
"paramType": "query"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/state",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get a list of all the current state events for this room.",
|
|
||||||
"notes": "This is equivalent to the events returned under the 'state' key for this room in /initialSync.",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "Event"
|
|
||||||
},
|
|
||||||
"nickname": "get_state_events",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to get a list of current state events from.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "/rooms/{roomId}/initialSync",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"method": "GET",
|
|
||||||
"summary": "Get all the current information for this room, including messages and state events.",
|
|
||||||
"notes": "NOT YET IMPLEMENTED.",
|
|
||||||
"type": "InitialSyncRoomData",
|
|
||||||
"nickname": "get_room_sync_data",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"name": "roomId",
|
|
||||||
"description": "The room to get information for.",
|
|
||||||
"required": true,
|
|
||||||
"type": "string",
|
|
||||||
"paramType": "path"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"models": {
|
|
||||||
"Topic": {
|
|
||||||
"id": "Topic",
|
|
||||||
"properties": {
|
|
||||||
"topic": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The topic text"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RoomName": {
|
|
||||||
"id": "RoomName",
|
|
||||||
"properties": {
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The human-readable name for the room. Can contain spaces."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Message": {
|
|
||||||
"id": "Message",
|
|
||||||
"properties": {
|
|
||||||
"msgtype": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The type of message being sent, e.g. \"m.text\"",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"_msgtype_defined_keys_": {
|
|
||||||
"description": "Additional keys as defined by the msgtype, e.g. \"body\""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Feedback": {
|
|
||||||
"id": "Feedback",
|
|
||||||
"properties": {
|
|
||||||
"target_event_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The event ID being acknowledged.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The type of feedback. Either 'delivered' or 'read'.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Member": {
|
|
||||||
"id": "Member",
|
|
||||||
"properties": {
|
|
||||||
"membership": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Enum: The membership state of this member.",
|
|
||||||
"enum": [
|
|
||||||
"invite",
|
|
||||||
"join",
|
|
||||||
"leave",
|
|
||||||
"ban"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RoomInfo": {
|
|
||||||
"id": "RoomInfo",
|
|
||||||
"properties": {
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The allocated room ID.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"room_alias": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The alias for the room.",
|
|
||||||
"required": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"JoinRoomInfo": {
|
|
||||||
"id": "JoinRoomInfo",
|
|
||||||
"properties": {
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The room ID joined, if joined via a room alias only.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RoomConfig": {
|
|
||||||
"id": "RoomConfig",
|
|
||||||
"properties": {
|
|
||||||
"visibility": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Enum: The room visibility.",
|
|
||||||
"required": false,
|
|
||||||
"enum": [
|
|
||||||
"public",
|
|
||||||
"private"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"room_alias_name": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The alias to give the new room.",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Sets the name of the room. Send a m.room.name event after creating the room with the 'name' key specified.",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"topic": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Sets the topic for the room. Send a m.room.topic event after creating the room with the 'topic' key specified.",
|
|
||||||
"required": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PaginationRequest": {
|
|
||||||
"id": "PaginationRequest",
|
|
||||||
"properties": {
|
|
||||||
"from": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The token to start getting results from."
|
|
||||||
},
|
|
||||||
"to": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The token to stop getting results at."
|
|
||||||
},
|
|
||||||
"limit": {
|
|
||||||
"type": "integer",
|
|
||||||
"description": "The maximum number of entries to return."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"PaginationChunk": {
|
|
||||||
"id": "PaginationChunk",
|
|
||||||
"properties": {
|
|
||||||
"start": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subTypes": [
|
|
||||||
"MessagePaginationChunk"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"MessagePaginationChunk": {
|
|
||||||
"id": "MessagePaginationChunk",
|
|
||||||
"properties": {
|
|
||||||
"chunk": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of message events.",
|
|
||||||
"items": {
|
|
||||||
"$ref": "MessageEvent"
|
|
||||||
},
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MemberPaginationChunk": {
|
|
||||||
"id": "MemberPaginationChunk",
|
|
||||||
"properties": {
|
|
||||||
"chunk": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of member events.",
|
|
||||||
"items": {
|
|
||||||
"$ref": "MemberEvent"
|
|
||||||
},
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Event": {
|
|
||||||
"id": "Event",
|
|
||||||
"properties": {
|
|
||||||
"event_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "An ID which uniquely identifies this event. This is automatically set by the server.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The room in which this event occurred. This is automatically set by the server.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The event type.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"subTypes": [
|
|
||||||
"MessageEvent"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"EventId": {
|
|
||||||
"id": "EventId",
|
|
||||||
"properties": {
|
|
||||||
"event_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The allocated event ID for this event.",
|
|
||||||
"required": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"EventContent": {
|
|
||||||
"id": "EventContent",
|
|
||||||
"properties": {
|
|
||||||
"__event_content_keys__": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Event-specific content keys and values.",
|
|
||||||
"required": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MessageEvent": {
|
|
||||||
"id": "MessageEvent",
|
|
||||||
"properties": {
|
|
||||||
"content": {
|
|
||||||
"type": "Message"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MemberEvent": {
|
|
||||||
"id": "MemberEvent",
|
|
||||||
"properties": {
|
|
||||||
"content": {
|
|
||||||
"type": "Member"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"InviteRequest": {
|
|
||||||
"id": "InviteRequest",
|
|
||||||
"properties": {
|
|
||||||
"user_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The fully-qualified user ID."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"JoinRequest": {
|
|
||||||
"id": "JoinRequest",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"LeaveRequest": {
|
|
||||||
"id": "LeaveRequest",
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"BanRequest": {
|
|
||||||
"id": "BanRequest",
|
|
||||||
"properties": {
|
|
||||||
"user_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The fully-qualified user ID."
|
|
||||||
},
|
|
||||||
"reason": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The reason for the ban."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"InitialSyncRoomData": {
|
|
||||||
"id": "InitialSyncRoomData",
|
|
||||||
"properties": {
|
|
||||||
"membership": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "This user's membership state in this room.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"room_id": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "The ID of this room.",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
"messages": {
|
|
||||||
"type": "MessagePaginationChunk",
|
|
||||||
"description": "The most recent messages for this room, governed by the limit parameter.",
|
|
||||||
"required": false
|
|
||||||
},
|
|
||||||
"state": {
|
|
||||||
"type": "array",
|
|
||||||
"description": "A list of state events representing the current state of the room.",
|
|
||||||
"required": false,
|
|
||||||
"items": {
|
|
||||||
"$ref": "Event"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
Definitions
|
|
||||||
===========
|
|
||||||
|
|
||||||
# *Event* -- A JSON object that represents a piece of information to be
|
|
||||||
distributed to the the room. The object includes a payload and metadata,
|
|
||||||
including a `type` used to indicate what the payload is for and how to process
|
|
||||||
them. It also includes one or more references to previous events.
|
|
||||||
|
|
||||||
# *Event graph* -- Events and their references to previous events form a
|
|
||||||
directed acyclic graph. All events must be a descendant of the first event in a
|
|
||||||
room, except for a few special circumstances.
|
|
||||||
|
|
||||||
# *State event* -- A state event is an event that has a non-null string valued
|
|
||||||
`state_key` field. It may also include a `prev_state` key referencing exactly
|
|
||||||
one state event with the same type and state key, in the same event graph.
|
|
||||||
|
|
||||||
# *State tree* -- A state tree is a tree formed by a collection of state events
|
|
||||||
that have the same type and state key (all in the same event graph.
|
|
||||||
|
|
||||||
# *State resolution algorithm* -- An algorithm that takes a state tree as input
|
|
||||||
and selects a single leaf node.
|
|
||||||
|
|
||||||
# *Current state event* -- The leaf node of a given state tree that has been
|
|
||||||
selected by the state resolution algorithm.
|
|
||||||
|
|
||||||
# *Room state* / *state dictionary* / *current state* -- A mapping of the pair
|
|
||||||
(event type, state key) to the current state event for that pair.
|
|
||||||
|
|
||||||
# *Room* -- An event graph and its associated state dictionary. An event is in
|
|
||||||
the room if it is part of the event graph.
|
|
||||||
|
|
||||||
# *Topological ordering* -- The partial ordering that can be extracted from the
|
|
||||||
event graph due to it being a DAG.
|
|
||||||
|
|
||||||
(The state definitions are purposely slightly ill-defined, since if we allow
|
|
||||||
deleting events we might end up with multiple state trees for a given event
|
|
||||||
type and state key pair.)
|
|
||||||
|
|
||||||
Federation specific
|
|
||||||
-------------------
|
|
||||||
# *(Persistent data unit) PDU* -- An encoding of an event for distribution of
|
|
||||||
the server to server protocol.
|
|
||||||
|
|
||||||
# *(Ephemeral data unit) EDU* -- A piece of information that is sent between
|
|
||||||
servers and doesn't encode an event.
|
|
||||||
|
|
||||||
Client specific
|
|
||||||
---------------
|
|
||||||
# *Child events* -- Events that reference a single event in the same room
|
|
||||||
independently of the event graph.
|
|
||||||
|
|
||||||
# *Collapsed events* -- Events that have all child events that reference it
|
|
||||||
included in the JSON object.
|
|
|
@ -1,79 +0,0 @@
|
||||||
This document outlines the format for human-readable IDs within matrix.
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
UTF-8 is quickly becoming the standard character encoding set on the web. As
|
|
||||||
such, Matrix requires that all strings MUST be encoded as UTF-8. However,
|
|
||||||
using Unicode as the character set for human-readable IDs is troublesome. There
|
|
||||||
are many different characters which appear identical to each other, but would
|
|
||||||
identify different users. In addition, there are non-printable characters which
|
|
||||||
cannot be rendered by the end-user. This opens up a security vulnerability with
|
|
||||||
phishing/spoofing of IDs, commonly known as a homograph attack.
|
|
||||||
|
|
||||||
Web browers encountered this problem when International Domain Names were
|
|
||||||
introduced. A variety of checks were put in place in order to protect users. If
|
|
||||||
an address failed the check, the raw punycode would be displayed to disambiguate
|
|
||||||
the address. Similar checks are performed by home servers in Matrix. However,
|
|
||||||
Matrix does not use punycode representations, and so does not show raw punycode
|
|
||||||
on a failed check. Instead, home servers must outright reject these misleading
|
|
||||||
IDs.
|
|
||||||
|
|
||||||
Types of human-readable IDs
|
|
||||||
---------------------------
|
|
||||||
There are two main human-readable IDs in question:
|
|
||||||
|
|
||||||
- Room aliases
|
|
||||||
- User IDs
|
|
||||||
|
|
||||||
Room aliases look like ``#localpart:domain``. These aliases point to opaque
|
|
||||||
non human-readable room IDs. These pointers can change, so there is already an
|
|
||||||
issue present with the same ID pointing to a different destination at a later
|
|
||||||
date.
|
|
||||||
|
|
||||||
User IDs look like ``@localpart:domain``. These represent actual end-users, and
|
|
||||||
unlike room aliases, there is no layer of indirection. This presents a much
|
|
||||||
greater concern with homograph attacks.
|
|
||||||
|
|
||||||
Checks
|
|
||||||
------
|
|
||||||
- Similar to web browsers.
|
|
||||||
- blacklisted chars (e.g. non-printable characters)
|
|
||||||
- mix of language sets from 'preferred' language not allowed.
|
|
||||||
- Language sets from CLDR dataset.
|
|
||||||
- Treated in segments (localpart, domain)
|
|
||||||
- Additional restrictions for ease of processing IDs.
|
|
||||||
- Room alias localparts MUST NOT have ``#`` or ``:``.
|
|
||||||
- User ID localparts MUST NOT have ``@`` or ``:``.
|
|
||||||
|
|
||||||
Rejecting
|
|
||||||
---------
|
|
||||||
- Home servers MUST reject room aliases which do not pass the check, both on
|
|
||||||
GETs and PUTs.
|
|
||||||
- Home servers MUST reject user ID localparts which do not pass the check, both
|
|
||||||
on creation and on events.
|
|
||||||
- Any home server whose domain does not pass this check, MUST use their punycode
|
|
||||||
domain name instead of the IDN, to prevent other home servers rejecting you.
|
|
||||||
- Error code is ``M_FAILED_HUMAN_ID_CHECK``. (generic enough for both failing
|
|
||||||
due to homograph attacks, and failing due to including ``:`` s, etc)
|
|
||||||
- Error message MAY go into further information about which characters were
|
|
||||||
rejected and why.
|
|
||||||
- Error message SHOULD contain a ``failed_keys`` key which contains an array
|
|
||||||
of strings which represent the keys which failed the check e.g::
|
|
||||||
|
|
||||||
failed_keys: [ user_id, room_alias ]
|
|
||||||
|
|
||||||
Other considerations
|
|
||||||
--------------------
|
|
||||||
- Basic security: Informational key on the event attached by HS to say "unsafe
|
|
||||||
ID". Problem: clients can just ignore it, and since it will appear only very
|
|
||||||
rarely, easy to forget when implementing clients.
|
|
||||||
- Moderate security: Requires client handshake. Forces clients to implement
|
|
||||||
a check, else they cannot communicate with the misleading ID. However, this is
|
|
||||||
extra overhead in both client implementations and round-trips.
|
|
||||||
- High security: Outright rejection of the ID at the point of creation /
|
|
||||||
receiving event. Point of creation rejection is preferable to avoid the ID
|
|
||||||
entering the system in the first place. However, malicious HSes can just allow
|
|
||||||
the ID. Hence, other home servers must reject them if they see them in events.
|
|
||||||
Client never sees the problem ID, provided the HS is correctly implemented.
|
|
||||||
- High security decided; client doesn't need to worry about it, no additional
|
|
||||||
protocol complexity aside from rejection of an event.
|
|
|
@ -1,43 +0,0 @@
|
||||||
===================
|
|
||||||
Documentation Style
|
|
||||||
===================
|
|
||||||
|
|
||||||
A brief single sentence to describe what this file contains; in this case a
|
|
||||||
description of the style to write documentation in.
|
|
||||||
|
|
||||||
|
|
||||||
Sections
|
|
||||||
========
|
|
||||||
|
|
||||||
Each section should be separated from the others by two blank lines. Headings
|
|
||||||
should be underlined using a row of equals signs (===). Paragraphs should be
|
|
||||||
separated by a single blank line, and wrap to no further than 80 columns.
|
|
||||||
|
|
||||||
[[TODO(username): if you want to leave some unanswered questions, notes for
|
|
||||||
further consideration, or other kinds of comment, use a TODO section. Make sure
|
|
||||||
to notate it with your name so we know who to ask about it!]]
|
|
||||||
|
|
||||||
Subsections
|
|
||||||
-----------
|
|
||||||
|
|
||||||
If required, subsections can use a row of dashes to underline their header. A
|
|
||||||
single blank line between subsections of a single section.
|
|
||||||
|
|
||||||
|
|
||||||
Bullet Lists
|
|
||||||
============
|
|
||||||
|
|
||||||
* Bullet lists can use asterisks with a single space either side.
|
|
||||||
|
|
||||||
* Another blank line between list elements.
|
|
||||||
|
|
||||||
|
|
||||||
Definition Lists
|
|
||||||
================
|
|
||||||
|
|
||||||
Terms:
|
|
||||||
Start in the first column, ending with a colon
|
|
||||||
|
|
||||||
Definitions:
|
|
||||||
Take a two space indent, following immediately from the term without a blank
|
|
||||||
line before it, but having a blank line afterwards.
|
|
|
@ -1,30 +0,0 @@
|
||||||
Matrix Specification NOTHAVEs
|
|
||||||
=============================
|
|
||||||
|
|
||||||
This document contains sections of the main specification that have been
|
|
||||||
temporarily removed, because they specify intentions or aspirations that have
|
|
||||||
in no way yet been implemented. Rather than outright-deleting them, they have
|
|
||||||
been moved here so as to stand as an initial version for such time as they
|
|
||||||
become extant.
|
|
||||||
|
|
||||||
|
|
||||||
Presence
|
|
||||||
========
|
|
||||||
|
|
||||||
Idle Time
|
|
||||||
---------
|
|
||||||
As well as the basic ``presence`` field, the presence information can also show
|
|
||||||
a sense of an "idle timer". This should be maintained individually by the
|
|
||||||
user's clients, and the home server can take the highest reported time as that
|
|
||||||
to report. When a user is offline, the home server can still report when the
|
|
||||||
user was last seen online.
|
|
||||||
|
|
||||||
Device Type
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Client devices that may limit the user experience somewhat (such as "mobile"
|
|
||||||
devices with limited ability to type on a real keyboard or read large amounts of
|
|
||||||
text) should report this to the home server, as this is also useful information
|
|
||||||
to report as "presence" if the user cannot be expected to provide a good typed
|
|
||||||
response to messages.
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,51 +0,0 @@
|
||||||
State Resolution
|
|
||||||
================
|
|
||||||
This section describes why we need state resolution and how it works.
|
|
||||||
|
|
||||||
|
|
||||||
Motivation
|
|
||||||
-----------
|
|
||||||
We want to be able to associate some shared state with rooms, e.g. a room name
|
|
||||||
or members list. This is done by having a current state dictionary that maps
|
|
||||||
from the pair event type and state key to an event.
|
|
||||||
|
|
||||||
However, since the servers involved in the room are distributed we need to be
|
|
||||||
able to handle the case when two (or more) servers try and update the state at
|
|
||||||
the same time. This is done via the state resolution algorithm.
|
|
||||||
|
|
||||||
|
|
||||||
State Tree
|
|
||||||
------------
|
|
||||||
State events contain a reference to the state it is trying to replace. These
|
|
||||||
relations form a tree where the current state is one of the leaf nodes.
|
|
||||||
|
|
||||||
Note that state events are events, and so are part of the PDU graph. Thus we
|
|
||||||
can be sure that (modulo the internet being particularly broken) we will see
|
|
||||||
all state events eventually.
|
|
||||||
|
|
||||||
|
|
||||||
Algorithm requirements
|
|
||||||
----------------------
|
|
||||||
We want the algorithm to have the following properties:
|
|
||||||
- Since we aren't guaranteed what order we receive state events in, except that
|
|
||||||
we see parents before children, the state resolution algorithm must not depend
|
|
||||||
on the order and must always come to the same result.
|
|
||||||
- If we receive a state event whose parent is the current state, then the
|
|
||||||
algorithm will select it.
|
|
||||||
- The algorithm does not depend on internal state, ensuring all servers should
|
|
||||||
come to the same decision.
|
|
||||||
|
|
||||||
These three properties mean it is enough to keep track of the current state and
|
|
||||||
compare it with any new proposed state, rather than having to keep track of all
|
|
||||||
the leafs of the tree and recomputing across the entire state tree.
|
|
||||||
|
|
||||||
|
|
||||||
Current Implementation
|
|
||||||
----------------------
|
|
||||||
The current implementation works as follows: Upon receipt of a newly proposed
|
|
||||||
state change we first find the common ancestor. Then we take the maximum
|
|
||||||
across each branch of the users' power levels, if one is higher then it is
|
|
||||||
selected as the current state. Otherwise, we check if one chain is longer than
|
|
||||||
the other, if so we choose that one. If that also fails, then we concatenate
|
|
||||||
all the pdu ids and take a SHA1 hash and compare them to select a common
|
|
||||||
ancestor.
|
|
280
pylint.cfg
Normal file
280
pylint.cfg
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
[MASTER]
|
||||||
|
|
||||||
|
# Specify a configuration file.
|
||||||
|
#rcfile=
|
||||||
|
|
||||||
|
# Python code to execute, usually for sys.path manipulation such as
|
||||||
|
# pygtk.require().
|
||||||
|
#init-hook=
|
||||||
|
|
||||||
|
# Profiled execution.
|
||||||
|
profile=no
|
||||||
|
|
||||||
|
# Add files or directories to the blacklist. They should be base names, not
|
||||||
|
# paths.
|
||||||
|
ignore=CVS
|
||||||
|
|
||||||
|
# Pickle collected data for later comparisons.
|
||||||
|
persistent=yes
|
||||||
|
|
||||||
|
# List of plugins (as comma separated values of python modules names) to load,
|
||||||
|
# usually to register additional checkers.
|
||||||
|
load-plugins=
|
||||||
|
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Enable the message, report, category or checker with the given id(s). You can
|
||||||
|
# either give multiple identifier separated by comma (,) or put this option
|
||||||
|
# multiple time. See also the "--disable" option for examples.
|
||||||
|
#enable=
|
||||||
|
|
||||||
|
# Disable the message, report, category or checker with the given id(s). You
|
||||||
|
# can either give multiple identifiers separated by comma (,) or put this
|
||||||
|
# option multiple times (only on the command line, not in the configuration
|
||||||
|
# file where it should appear only once).You can also use "--disable=all" to
|
||||||
|
# disable everything first and then reenable specific checks. For example, if
|
||||||
|
# you want to run only the similarities checker, you can use "--disable=all
|
||||||
|
# --enable=similarities". If you want to run only the classes checker, but have
|
||||||
|
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||||
|
# --disable=W"
|
||||||
|
disable=missing-docstring
|
||||||
|
|
||||||
|
|
||||||
|
[REPORTS]
|
||||||
|
|
||||||
|
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||||
|
# (visual studio) and html. You can also give a reporter class, eg
|
||||||
|
# mypackage.mymodule.MyReporterClass.
|
||||||
|
output-format=text
|
||||||
|
|
||||||
|
# Put messages in a separate file for each module / package specified on the
|
||||||
|
# command line instead of printing them on stdout. Reports (if any) will be
|
||||||
|
# written in a file name "pylint_global.[txt|html]".
|
||||||
|
files-output=no
|
||||||
|
|
||||||
|
# Tells whether to display a full report or only the messages
|
||||||
|
reports=yes
|
||||||
|
|
||||||
|
# Python expression which should return a note less than 10 (10 is the highest
|
||||||
|
# note). You have access to the variables errors warning, statement which
|
||||||
|
# respectively contain the number of errors / warnings messages and the total
|
||||||
|
# number of statements analyzed. This is used by the global evaluation report
|
||||||
|
# (RP0004).
|
||||||
|
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||||
|
|
||||||
|
# Add a comment according to your evaluation note. This is used by the global
|
||||||
|
# evaluation report (RP0004).
|
||||||
|
comment=no
|
||||||
|
|
||||||
|
# Template used to display messages. This is a python new-style format string
|
||||||
|
# used to format the message information. See doc for all details
|
||||||
|
#msg-template=
|
||||||
|
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
|
||||||
|
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||||
|
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||||
|
ignore-mixin-members=yes
|
||||||
|
|
||||||
|
# List of classes names for which member attributes should not be checked
|
||||||
|
# (useful for classes with attributes dynamically set).
|
||||||
|
ignored-classes=SQLObject
|
||||||
|
|
||||||
|
# When zope mode is activated, add a predefined set of Zope acquired attributes
|
||||||
|
# to generated-members.
|
||||||
|
zope=no
|
||||||
|
|
||||||
|
# List of members which are set dynamically and missed by pylint inference
|
||||||
|
# system, and so shouldn't trigger E0201 when accessed. Python regular
|
||||||
|
# expressions are accepted.
|
||||||
|
generated-members=REQUEST,acl_users,aq_parent
|
||||||
|
|
||||||
|
|
||||||
|
[MISCELLANEOUS]
|
||||||
|
|
||||||
|
# List of note tags to take in consideration, separated by a comma.
|
||||||
|
notes=FIXME,XXX,TODO
|
||||||
|
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Minimum lines number of a similarity.
|
||||||
|
min-similarity-lines=4
|
||||||
|
|
||||||
|
# Ignore comments when computing similarities.
|
||||||
|
ignore-comments=yes
|
||||||
|
|
||||||
|
# Ignore docstrings when computing similarities.
|
||||||
|
ignore-docstrings=yes
|
||||||
|
|
||||||
|
# Ignore imports when computing similarities.
|
||||||
|
ignore-imports=no
|
||||||
|
|
||||||
|
|
||||||
|
[VARIABLES]
|
||||||
|
|
||||||
|
# Tells whether we should check for unused import in __init__ files.
|
||||||
|
init-import=no
|
||||||
|
|
||||||
|
# A regular expression matching the beginning of the name of dummy variables
|
||||||
|
# (i.e. not used).
|
||||||
|
dummy-variables-rgx=_$|dummy
|
||||||
|
|
||||||
|
# List of additional names supposed to be defined in builtins. Remember that
|
||||||
|
# you should avoid to define new builtins when possible.
|
||||||
|
additional-builtins=
|
||||||
|
|
||||||
|
|
||||||
|
[BASIC]
|
||||||
|
|
||||||
|
# Required attributes for module, separated by a comma
|
||||||
|
required-attributes=
|
||||||
|
|
||||||
|
# List of builtins function names that should not be used, separated by a comma
|
||||||
|
bad-functions=map,filter,apply,input
|
||||||
|
|
||||||
|
# Regular expression which should only match correct module names
|
||||||
|
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct module level names
|
||||||
|
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct class names
|
||||||
|
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct function names
|
||||||
|
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct method names
|
||||||
|
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct instance attribute names
|
||||||
|
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct argument names
|
||||||
|
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct variable names
|
||||||
|
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct attribute names in class
|
||||||
|
# bodies
|
||||||
|
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||||
|
|
||||||
|
# Regular expression which should only match correct list comprehension /
|
||||||
|
# generator expression variable names
|
||||||
|
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||||
|
|
||||||
|
# Good variable names which should always be accepted, separated by a comma
|
||||||
|
good-names=i,j,k,ex,Run,_
|
||||||
|
|
||||||
|
# Bad variable names which should always be refused, separated by a comma
|
||||||
|
bad-names=foo,bar,baz,toto,tutu,tata
|
||||||
|
|
||||||
|
# Regular expression which should only match function or class names that do
|
||||||
|
# not require a docstring.
|
||||||
|
no-docstring-rgx=__.*__
|
||||||
|
|
||||||
|
# Minimum line length for functions/classes that require docstrings, shorter
|
||||||
|
# ones are exempt.
|
||||||
|
docstring-min-length=-1
|
||||||
|
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
|
||||||
|
# Maximum number of characters on a single line.
|
||||||
|
max-line-length=80
|
||||||
|
|
||||||
|
# Regexp for a line that is allowed to be longer than the limit.
|
||||||
|
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||||
|
|
||||||
|
# Allow the body of an if to be on the same line as the test if there is no
|
||||||
|
# else.
|
||||||
|
single-line-if-stmt=no
|
||||||
|
|
||||||
|
# List of optional constructs for which whitespace checking is disabled
|
||||||
|
no-space-check=trailing-comma,dict-separator
|
||||||
|
|
||||||
|
# Maximum number of lines in a module
|
||||||
|
max-module-lines=1000
|
||||||
|
|
||||||
|
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||||
|
# tab).
|
||||||
|
indent-string=' '
|
||||||
|
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
|
||||||
|
# Maximum number of arguments for function / method
|
||||||
|
max-args=5
|
||||||
|
|
||||||
|
# Argument names that match this expression will be ignored. Default to name
|
||||||
|
# with leading underscore
|
||||||
|
ignored-argument-names=_.*
|
||||||
|
|
||||||
|
# Maximum number of locals for function / method body
|
||||||
|
max-locals=15
|
||||||
|
|
||||||
|
# Maximum number of return / yield for function / method body
|
||||||
|
max-returns=6
|
||||||
|
|
||||||
|
# Maximum number of branch for function / method body
|
||||||
|
max-branches=12
|
||||||
|
|
||||||
|
# Maximum number of statements in function / method body
|
||||||
|
max-statements=50
|
||||||
|
|
||||||
|
# Maximum number of parents for a class (see R0901).
|
||||||
|
max-parents=7
|
||||||
|
|
||||||
|
# Maximum number of attributes for a class (see R0902).
|
||||||
|
max-attributes=7
|
||||||
|
|
||||||
|
# Minimum number of public methods for a class (see R0903).
|
||||||
|
min-public-methods=2
|
||||||
|
|
||||||
|
# Maximum number of public methods for a class (see R0904).
|
||||||
|
max-public-methods=20
|
||||||
|
|
||||||
|
|
||||||
|
[IMPORTS]
|
||||||
|
|
||||||
|
# Deprecated modules which should not be used, separated by a comma
|
||||||
|
deprecated-modules=regsub,TERMIOS,Bastion,rexec
|
||||||
|
|
||||||
|
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||||
|
# given file (report RP0402 must not be disabled)
|
||||||
|
import-graph=
|
||||||
|
|
||||||
|
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||||
|
# not be disabled)
|
||||||
|
ext-import-graph=
|
||||||
|
|
||||||
|
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||||
|
# not be disabled)
|
||||||
|
int-import-graph=
|
||||||
|
|
||||||
|
|
||||||
|
[CLASSES]
|
||||||
|
|
||||||
|
# List of interface methods to ignore, separated by a comma. This is used for
|
||||||
|
# instance to not check methods defines in Zope's Interface base class.
|
||||||
|
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||||
|
|
||||||
|
# List of method names used to declare (i.e. assign) instance attributes.
|
||||||
|
defining-attr-methods=__init__,__new__,setUp
|
||||||
|
|
||||||
|
# List of valid names for the first argument in a class method.
|
||||||
|
valid-classmethod-first-arg=cls
|
||||||
|
|
||||||
|
# List of valid names for the first argument in a metaclass class method.
|
||||||
|
valid-metaclass-classmethod-first-arg=mcs
|
||||||
|
|
||||||
|
|
||||||
|
[EXCEPTIONS]
|
||||||
|
|
||||||
|
# Exceptions that will emit a warning when being caught. Defaults to
|
||||||
|
# "Exception"
|
||||||
|
overgeneral-exceptions=Exception
|
8
setup.py
8
setup.py
|
@ -31,9 +31,10 @@ setup(
|
||||||
packages=find_packages(exclude=["tests"]),
|
packages=find_packages(exclude=["tests"]),
|
||||||
description="Reference Synapse Home Server",
|
description="Reference Synapse Home Server",
|
||||||
install_requires=[
|
install_requires=[
|
||||||
"syutil==0.0.1",
|
"syutil==0.0.2",
|
||||||
"Twisted>=14.0.0",
|
"Twisted>=14.0.0",
|
||||||
"service_identity>=1.0.0",
|
"service_identity>=1.0.0",
|
||||||
|
"pyopenssl>=0.14",
|
||||||
"pyyaml",
|
"pyyaml",
|
||||||
"pyasn1",
|
"pyasn1",
|
||||||
"pynacl",
|
"pynacl",
|
||||||
|
@ -41,11 +42,12 @@ setup(
|
||||||
"py-bcrypt",
|
"py-bcrypt",
|
||||||
],
|
],
|
||||||
dependency_links=[
|
dependency_links=[
|
||||||
"git+ssh://git@github.com/matrix-org/syutil.git#egg=syutil-0.0.1",
|
"https://github.com/matrix-org/syutil/tarball/v0.0.2#egg=syutil-0.0.2",
|
||||||
],
|
],
|
||||||
setup_requires=[
|
setup_requires=[
|
||||||
"setuptools_trial",
|
"setuptools_trial",
|
||||||
"setuptools>=1.0.0", # Needs setuptools that supports git+ssh. It's not obvious when support for this was introduced.
|
"setuptools>=1.0.0", # Needs setuptools that supports git+ssh.
|
||||||
|
# TODO: Do we need this now? we don't use git+ssh.
|
||||||
"mock"
|
"mock"
|
||||||
],
|
],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
|
|
|
@ -16,4 +16,4 @@
|
||||||
""" This is a reference implementation of a synapse home server.
|
""" This is a reference implementation of a synapse home server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "0.3.4"
|
__version__ = "0.4.1"
|
||||||
|
|
|
@ -74,7 +74,7 @@ class ServerConfig(Config):
|
||||||
return syutil.crypto.signing_key.read_signing_keys(
|
return syutil.crypto.signing_key.read_signing_keys(
|
||||||
signing_keys.splitlines(True)
|
signing_keys.splitlines(True)
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
"Error reading signing_key."
|
"Error reading signing_key."
|
||||||
" Try running again with --generate-config"
|
" Try running again with --generate-config"
|
||||||
|
@ -94,7 +94,7 @@ class ServerConfig(Config):
|
||||||
with open(args.signing_key_path, "w") as signing_key_file:
|
with open(args.signing_key_path, "w") as signing_key_file:
|
||||||
syutil.crypto.signing_key.write_signing_keys(
|
syutil.crypto.signing_key.write_signing_keys(
|
||||||
signing_key_file,
|
signing_key_file,
|
||||||
(syutil.crypto.SigningKey.generate("auto"),),
|
(syutil.crypto.signing_key.generate_singing_key("auto"),),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
signing_keys = cls.read_file(args.signing_key_path, "signing_key")
|
signing_keys = cls.read_file(args.signing_key_path, "signing_key")
|
||||||
|
|
|
@ -16,6 +16,9 @@ from twisted.internet import ssl
|
||||||
from OpenSSL import SSL
|
from OpenSSL import SSL
|
||||||
from twisted.internet._sslverify import _OpenSSLECCurve, _defaultCurveName
|
from twisted.internet._sslverify import _OpenSSLECCurve, _defaultCurveName
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ServerContextFactory(ssl.ContextFactory):
|
class ServerContextFactory(ssl.ContextFactory):
|
||||||
"""Factory for PyOpenSSL SSL contexts that are used to handle incoming
|
"""Factory for PyOpenSSL SSL contexts that are used to handle incoming
|
||||||
|
@ -31,7 +34,7 @@ class ServerContextFactory(ssl.ContextFactory):
|
||||||
_ecCurve = _OpenSSLECCurve(_defaultCurveName)
|
_ecCurve = _OpenSSLECCurve(_defaultCurveName)
|
||||||
_ecCurve.addECKeyToContext(context)
|
_ecCurve.addECKeyToContext(context)
|
||||||
except:
|
except:
|
||||||
pass
|
logger.exception("Failed to enable eliptic curve for TLS")
|
||||||
context.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
|
context.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
|
||||||
context.use_certificate(config.tls_certificate)
|
context.use_certificate(config.tls_certificate)
|
||||||
context.use_privatekey(config.tls_private_key)
|
context.use_privatekey(config.tls_private_key)
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
from twisted.web.http import HTTPClient
|
from twisted.web.http import HTTPClient
|
||||||
from twisted.internet.protocol import Factory
|
from twisted.internet.protocol import Factory
|
||||||
from twisted.internet import defer, reactor
|
from twisted.internet import defer, reactor
|
||||||
from twisted.internet.endpoints import connectProtocol
|
|
||||||
from synapse.http.endpoint import matrix_endpoint
|
from synapse.http.endpoint import matrix_endpoint
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
|
|
@ -38,6 +38,7 @@ class Keyring(object):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def verify_json_for_server(self, server_name, json_object):
|
def verify_json_for_server(self, server_name, json_object):
|
||||||
|
logger.debug("Verifying for %s", server_name)
|
||||||
key_ids = signature_ids(json_object, server_name)
|
key_ids = signature_ids(json_object, server_name)
|
||||||
if not key_ids:
|
if not key_ids:
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
|
|
|
@ -238,6 +238,11 @@ class TransportLayer(object):
|
||||||
|
|
||||||
auth_headers = request.requestHeaders.getRawHeaders(b"Authorization")
|
auth_headers = request.requestHeaders.getRawHeaders(b"Authorization")
|
||||||
|
|
||||||
|
if not auth_headers:
|
||||||
|
raise SynapseError(
|
||||||
|
401, "Missing Authorization headers", Codes.UNAUTHORIZED,
|
||||||
|
)
|
||||||
|
|
||||||
for auth in auth_headers:
|
for auth in auth_headers:
|
||||||
if auth.startswith("X-Matrix"):
|
if auth.startswith("X-Matrix"):
|
||||||
(origin, key, sig) = parse_auth_header(auth)
|
(origin, key, sig) = parse_auth_header(auth)
|
||||||
|
@ -256,10 +261,14 @@ class TransportLayer(object):
|
||||||
def _with_authentication(self, handler):
|
def _with_authentication(self, handler):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def new_handler(request, *args, **kwargs):
|
def new_handler(request, *args, **kwargs):
|
||||||
|
try:
|
||||||
(origin, content) = yield self._authenticate_request(request)
|
(origin, content) = yield self._authenticate_request(request)
|
||||||
response = yield handler(
|
response = yield handler(
|
||||||
origin, content, request.args, *args, **kwargs
|
origin, content, request.args, *args, **kwargs
|
||||||
)
|
)
|
||||||
|
except:
|
||||||
|
logger.exception("_authenticate_request failed")
|
||||||
|
raise
|
||||||
defer.returnValue(response)
|
defer.returnValue(response)
|
||||||
return new_handler
|
return new_handler
|
||||||
|
|
||||||
|
@ -392,9 +401,13 @@ class TransportLayer(object):
|
||||||
defer.returnValue((400, {"error": "Invalid transaction"}))
|
defer.returnValue((400, {"error": "Invalid transaction"}))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
code, response = yield self.received_handler.on_incoming_transaction(
|
code, response = yield self.received_handler.on_incoming_transaction(
|
||||||
transaction_data
|
transaction_data
|
||||||
)
|
)
|
||||||
|
except:
|
||||||
|
logger.exception("on_incoming_transaction failed")
|
||||||
|
raise
|
||||||
|
|
||||||
defer.returnValue((code, response))
|
defer.returnValue((code, response))
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
"""Contains functions for registering clients."""
|
"""Contains functions for registering clients."""
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
from twisted.python import log
|
||||||
|
|
||||||
from synapse.types import UserID
|
from synapse.types import UserID
|
||||||
from synapse.api.errors import (
|
from synapse.api.errors import (
|
||||||
|
@ -126,7 +127,7 @@ class RegistrationHandler(BaseHandler):
|
||||||
try:
|
try:
|
||||||
threepid = yield self._threepid_from_creds(c)
|
threepid = yield self._threepid_from_creds(c)
|
||||||
except:
|
except:
|
||||||
logger.err()
|
log.err()
|
||||||
raise RegistrationError(400, "Couldn't validate 3pid")
|
raise RegistrationError(400, "Couldn't validate 3pid")
|
||||||
|
|
||||||
if not threepid:
|
if not threepid:
|
||||||
|
|
|
@ -101,6 +101,8 @@ class BaseHttpClient(object):
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
|
producer = None
|
||||||
|
if body_callback:
|
||||||
producer = body_callback(method, url_bytes, headers_dict)
|
producer = body_callback(method, url_bytes, headers_dict)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -312,6 +314,42 @@ class IdentityServerHttpClient(BaseHttpClient):
|
||||||
|
|
||||||
defer.returnValue(json.loads(body))
|
defer.returnValue(json.loads(body))
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def get_json(self, destination, path, args={}, retry_on_dns_fail=True):
|
||||||
|
""" Get's some json from the given host homeserver and path
|
||||||
|
|
||||||
|
Args:
|
||||||
|
destination (str): The remote server to send the HTTP request
|
||||||
|
to.
|
||||||
|
path (str): The HTTP path.
|
||||||
|
args (dict): A dictionary used to create query strings, defaults to
|
||||||
|
None.
|
||||||
|
**Note**: The value of each key is assumed to be an iterable
|
||||||
|
and *not* a string.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred: Succeeds when we get *any* HTTP response.
|
||||||
|
|
||||||
|
The result of the deferred is a tuple of `(code, response)`,
|
||||||
|
where `response` is a dict representing the decoded JSON body.
|
||||||
|
"""
|
||||||
|
logger.debug("get_json args: %s", args)
|
||||||
|
|
||||||
|
query_bytes = urllib.urlencode(args, True)
|
||||||
|
logger.debug("Query bytes: %s Retry DNS: %s", args, retry_on_dns_fail)
|
||||||
|
|
||||||
|
response = yield self._create_request(
|
||||||
|
destination.encode("ascii"),
|
||||||
|
"GET",
|
||||||
|
path.encode("ascii"),
|
||||||
|
query_bytes=query_bytes,
|
||||||
|
retry_on_dns_fail=retry_on_dns_fail,
|
||||||
|
body_callback=None
|
||||||
|
)
|
||||||
|
|
||||||
|
body = yield readBody(response)
|
||||||
|
|
||||||
|
defer.returnValue(json.loads(body))
|
||||||
|
|
||||||
class CaptchaServerHttpClient(MatrixHttpClient):
|
class CaptchaServerHttpClient(MatrixHttpClient):
|
||||||
"""Separate HTTP client for talking to google's captcha servers"""
|
"""Separate HTTP client for talking to google's captcha servers"""
|
||||||
|
|
|
@ -344,7 +344,7 @@ class RoomInitialSyncRestServlet(RestServlet):
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_GET(self, request, room_id):
|
def on_GET(self, request, room_id):
|
||||||
user = yield self.auth.get_user_by_req(request)
|
yield self.auth.get_user_by_req(request)
|
||||||
# TODO: Get all the initial sync data for this room and return in the
|
# TODO: Get all the initial sync data for this room and return in the
|
||||||
# same format as initial sync, that is:
|
# same format as initial sync, that is:
|
||||||
# {
|
# {
|
||||||
|
|
|
@ -36,7 +36,7 @@ class VoipRestServlet(RestServlet):
|
||||||
if not turnUris or not turnSecret or not userLifetime:
|
if not turnUris or not turnSecret or not userLifetime:
|
||||||
defer.returnValue( (200, {}) )
|
defer.returnValue( (200, {}) )
|
||||||
|
|
||||||
expiry = self.hs.get_clock().time_msec() + userLifetime
|
expiry = (self.hs.get_clock().time_msec() + userLifetime) / 1000
|
||||||
username = "%d:%s" % (expiry, auth_user.to_string())
|
username = "%d:%s" % (expiry, auth_user.to_string())
|
||||||
|
|
||||||
mac = hmac.new(turnSecret, msg=username, digestmod=hashlib.sha1)
|
mac = hmac.new(turnSecret, msg=username, digestmod=hashlib.sha1)
|
||||||
|
|
|
@ -70,7 +70,7 @@ SCHEMAS = [
|
||||||
|
|
||||||
# Remember to update this number every time an incompatible change is made to
|
# Remember to update this number every time an incompatible change is made to
|
||||||
# database schema files, so the users will be informed on server restarts.
|
# database schema files, so the users will be informed on server restarts.
|
||||||
SCHEMA_VERSION = 5
|
SCHEMA_VERSION = 6
|
||||||
|
|
||||||
|
|
||||||
class _RollbackButIsFineException(Exception):
|
class _RollbackButIsFineException(Exception):
|
||||||
|
@ -487,9 +487,10 @@ def prepare_database(db_conn):
|
||||||
db_conn.commit()
|
db_conn.commit()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
sql_script = "BEGIN TRANSACTION;"
|
||||||
for sql_loc in SCHEMAS:
|
for sql_loc in SCHEMAS:
|
||||||
sql_script = read_schema(sql_loc)
|
sql_script += read_schema(sql_loc)
|
||||||
|
sql_script += "COMMIT TRANSACTION;"
|
||||||
c.executescript(sql_script)
|
c.executescript(sql_script)
|
||||||
db_conn.commit()
|
db_conn.commit()
|
||||||
c.execute("PRAGMA user_version = %d" % SCHEMA_VERSION)
|
c.execute("PRAGMA user_version = %d" % SCHEMA_VERSION)
|
||||||
|
|
|
@ -104,7 +104,6 @@ class KeyStore(SQLBaseStore):
|
||||||
ts_now_ms (int): The time now in milliseconds
|
ts_now_ms (int): The time now in milliseconds
|
||||||
verification_key (VerifyKey): The NACL verify key.
|
verification_key (VerifyKey): The NACL verify key.
|
||||||
"""
|
"""
|
||||||
verify_key_bytes = verify_key.encode()
|
|
||||||
return self._simple_insert(
|
return self._simple_insert(
|
||||||
table="server_signature_keys",
|
table="server_signature_keys",
|
||||||
values={
|
values={
|
||||||
|
|
31
synapse/storage/schema/delta/v6.sql
Normal file
31
synapse/storage/schema/delta/v6.sql
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/* Copyright 2014 OpenMarket Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS server_tls_certificates(
|
||||||
|
server_name TEXT, -- Server name.
|
||||||
|
fingerprint TEXT, -- Certificate fingerprint.
|
||||||
|
from_server TEXT, -- Which key server the certificate was fetched from.
|
||||||
|
ts_added_ms INTEGER, -- When the certifcate was added.
|
||||||
|
tls_certificate BLOB, -- DER encoded x509 certificate.
|
||||||
|
CONSTRAINT uniqueness UNIQUE (server_name, fingerprint)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS server_signature_keys(
|
||||||
|
server_name TEXT, -- Server name.
|
||||||
|
key_id TEXT, -- Key version.
|
||||||
|
from_server TEXT, -- Which key server the key was fetched form.
|
||||||
|
ts_added_ms INTEGER, -- When the key was added.
|
||||||
|
verify_key BLOB, -- NACL verification key.
|
||||||
|
CONSTRAINT uniqueness UNIQUE (server_name, key_id)
|
||||||
|
);
|
1
synapse/test_pyflakes.py
Normal file
1
synapse/test_pyflakes.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import an_unused_module
|
|
@ -148,10 +148,10 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) {
|
||||||
// ts is later.
|
// ts is later.
|
||||||
var latestData = true;
|
var latestData = true;
|
||||||
if (!isLiveEvent) {
|
if (!isLiveEvent) {
|
||||||
var eventTs = event.ts;
|
var eventTs = event.origin_server_ts;
|
||||||
var storedEvent = $rootScope.events.rooms[event.room_id][event.type];
|
var storedEvent = $rootScope.events.rooms[event.room_id][event.type];
|
||||||
if (storedEvent) {
|
if (storedEvent) {
|
||||||
if (storedEvent.ts > eventTs) {
|
if (storedEvent.origin_server_ts > eventTs) {
|
||||||
// ignore it, we have a newer one already.
|
// ignore it, we have a newer one already.
|
||||||
latestData = false;
|
latestData = false;
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ function(matrixService, $rootScope, $q, $timeout, mPresence) {
|
||||||
// could be a membership change, display name change, etc.
|
// could be a membership change, display name change, etc.
|
||||||
// Find out which one.
|
// Find out which one.
|
||||||
var memberChanges = undefined;
|
var memberChanges = undefined;
|
||||||
if (event.content.prev !== event.content.membership) {
|
if (event.prev_content && (event.prev_content.membership !== event.content.membership)) {
|
||||||
memberChanges = "membership";
|
memberChanges = "membership";
|
||||||
}
|
}
|
||||||
else if (event.prev_content && (event.prev_content.displayname !== event.content.displayname)) {
|
else if (event.prev_content && (event.prev_content.displayname !== event.content.displayname)) {
|
||||||
|
|
|
@ -59,7 +59,7 @@ angular.module('RecentsController')
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return lastMsgRoomB.ts - lastMsgRoomA.ts;
|
return lastMsgRoomB.origin_server_ts - lastMsgRoomA.origin_server_ts;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return filtered;
|
return filtered;
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
Declaring it in this way ensures the data-binding -->
|
Declaring it in this way ensures the data-binding -->
|
||||||
{{ lastMsg = eventHandlerService.getLastMessage(room.room_id, true);"" }}
|
{{ lastMsg = eventHandlerService.getLastMessage(room.room_id, true);"" }}
|
||||||
|
|
||||||
{{ (lastMsg.ts) | date:'MMM d HH:mm' }}
|
{{ (lastMsg.origin_server_ts) | date:'MMM d HH:mm' }}
|
||||||
|
|
||||||
<img ng-click="leave(room.room_id); $event.stopPropagation();" src="img/close.png" width="10" height="10" style="margin-bottom: -1px; margin-left: 2px;" alt="close"/>
|
<img ng-click="leave(room.room_id); $event.stopPropagation();" src="img/close.png" width="10" height="10" style="margin-bottom: -1px; margin-left: 2px;" alt="close"/>
|
||||||
</td>
|
</td>
|
||||||
|
@ -42,12 +42,12 @@
|
||||||
<span ng-if="lastMsg.user_id === lastMsg.state_key">
|
<span ng-if="lastMsg.user_id === lastMsg.state_key">
|
||||||
{{lastMsg.state_key | mUserDisplayName: room.room_id }} left
|
{{lastMsg.state_key | mUserDisplayName: room.room_id }} left
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="lastMsg.user_id !== lastMsg.state_key">
|
<span ng-if="lastMsg.user_id !== lastMsg.state_key && lastMsg.prev_content">
|
||||||
{{ lastMsg.user_id | mUserDisplayName: room.room_id }}
|
{{ lastMsg.user_id | mUserDisplayName: room.room_id }}
|
||||||
{{ {"join": "kicked", "ban": "unbanned"}[lastMsg.content.prev] }}
|
{{ {"invite": "kicked", "join": "kicked", "ban": "unbanned"}[lastMsg.prev_content.membership] }}
|
||||||
{{ lastMsg.state_key | mUserDisplayName: room.room_id }}
|
{{ lastMsg.state_key | mUserDisplayName: room.room_id }}
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="'join' === lastMsg.content.prev && lastMsg.content.reason">
|
<span ng-if="lastMsg.prev_content && 'join' === lastMsg.prev_content.membership && lastMsg.content.reason">
|
||||||
: {{ lastMsg.content.reason }}
|
: {{ lastMsg.content.reason }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
{{ lastMsg.user_id | mUserDisplayName: room.room_id }}
|
{{ lastMsg.user_id | mUserDisplayName: room.room_id }}
|
||||||
{{ {"invite": "invited", "ban": "banned"}[lastMsg.content.membership] }}
|
{{ {"invite": "invited", "ban": "banned"}[lastMsg.content.membership] }}
|
||||||
{{ lastMsg.state_key | mUserDisplayName: room.room_id }}
|
{{ lastMsg.state_key | mUserDisplayName: room.room_id }}
|
||||||
<span ng-if="'ban' === lastMsg.content.prev && lastMsg.content.reason">
|
<span ng-if="lastMsg.prev_content && 'ban' === lastMsg.prev_content.membership && lastMsg.content.reason">
|
||||||
: {{ lastMsg.content.reason }}
|
: {{ lastMsg.content.reason }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -603,9 +603,9 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
||||||
var echoMessage = {
|
var echoMessage = {
|
||||||
content: {
|
content: {
|
||||||
body: (cmd === "/me" ? args : input),
|
body: (cmd === "/me" ? args : input),
|
||||||
hsob_ts: new Date().getTime(), // fake a timestamp
|
|
||||||
msgtype: (cmd === "/me" ? "m.emote" : "m.text"),
|
msgtype: (cmd === "/me" ? "m.emote" : "m.text"),
|
||||||
},
|
},
|
||||||
|
origin_server_ts: new Date().getTime(), // fake a timestamp
|
||||||
room_id: $scope.room_id,
|
room_id: $scope.room_id,
|
||||||
type: "m.room.message",
|
type: "m.room.message",
|
||||||
user_id: $scope.state.user_id,
|
user_id: $scope.state.user_id,
|
||||||
|
@ -640,7 +640,7 @@ angular.module('RoomController', ['ngSanitize', 'matrixFilter', 'mFileInput'])
|
||||||
|
|
||||||
if (echoMessage) {
|
if (echoMessage) {
|
||||||
// Mark the message as unsent for the rest of the page life
|
// Mark the message as unsent for the rest of the page life
|
||||||
echoMessage.content.hsob_ts = "Unsent";
|
echoMessage.origin_server_ts = "Unsent";
|
||||||
echoMessage.echo_msg_state = "messageUnSent";
|
echoMessage.echo_msg_state = "messageUnSent";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -68,7 +68,6 @@
|
||||||
ng-hide="state.permission_denied"
|
ng-hide="state.permission_denied"
|
||||||
ng-style="{ 'visibility': state.messages_visibility }"
|
ng-style="{ 'visibility': state.messages_visibility }"
|
||||||
keep-scroll>
|
keep-scroll>
|
||||||
<!-- FIXME: need to have better timestamp semantics than the (msg.content.hsob_ts || msg.ts) hack below -->
|
|
||||||
<table id="messageTable" infinite-scroll="paginateMore()">
|
<table id="messageTable" infinite-scroll="paginateMore()">
|
||||||
<tr ng-repeat="msg in events.rooms[room_id].messages"
|
<tr ng-repeat="msg in events.rooms[room_id].messages"
|
||||||
ng-class="(events.rooms[room_id].messages[$index + 1].user_id !== msg.user_id ? 'differentUser' : '') + (msg.user_id === state.user_id ? ' mine' : '')" scroll-item>
|
ng-class="(events.rooms[room_id].messages[$index + 1].user_id !== msg.user_id ? 'differentUser' : '') + (msg.user_id === state.user_id ? ' mine' : '')" scroll-item>
|
||||||
|
@ -76,7 +75,7 @@
|
||||||
<div class="sender" ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id"> {{ msg.user_id | mUserDisplayName: room_id }}</div>
|
<div class="sender" ng-hide="events.rooms[room_id].messages[$index - 1].user_id === msg.user_id"> {{ msg.user_id | mUserDisplayName: room_id }}</div>
|
||||||
<div class="timestamp"
|
<div class="timestamp"
|
||||||
ng-class="msg.echo_msg_state">
|
ng-class="msg.echo_msg_state">
|
||||||
{{ (msg.content.hsob_ts || msg.ts) | date:'MMM d HH:mm' }}
|
{{ (msg.origin_server_ts) | date:'MMM d HH:mm' }}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="avatar">
|
<td class="avatar">
|
||||||
|
@ -92,11 +91,11 @@
|
||||||
<span ng-if="msg.user_id === msg.state_key">
|
<span ng-if="msg.user_id === msg.state_key">
|
||||||
{{ members[msg.state_key].displayname || msg.state_key }} left
|
{{ members[msg.state_key].displayname || msg.state_key }} left
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="msg.user_id !== msg.state_key">
|
<span ng-if="msg.user_id !== msg.state_key && msg.prev_content">
|
||||||
{{ members[msg.user_id].displayname || msg.user_id }}
|
{{ members[msg.user_id].displayname || msg.user_id }}
|
||||||
{{ {"join": "kicked", "ban": "unbanned"}[msg.content.prev] }}
|
{{ {"invite": "kicked", "join": "kicked", "ban": "unbanned"}[msg.prev_content.membership] }}
|
||||||
{{ members[msg.state_key].displayname || msg.state_key }}
|
{{ members[msg.state_key].displayname || msg.state_key }}
|
||||||
<span ng-if="'join' === msg.content.prev && msg.content.reason">
|
<span ng-if="'join' === msg.prev_content.membership && msg.content.reason">
|
||||||
: {{ msg.content.reason }}
|
: {{ msg.content.reason }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -106,7 +105,7 @@
|
||||||
{{ members[msg.user_id].displayname || msg.user_id }}
|
{{ members[msg.user_id].displayname || msg.user_id }}
|
||||||
{{ {"invite": "invited", "ban": "banned"}[msg.content.membership] }}
|
{{ {"invite": "invited", "ban": "banned"}[msg.content.membership] }}
|
||||||
{{ members[msg.state_key].displayname || msg.state_key }}
|
{{ members[msg.state_key].displayname || msg.state_key }}
|
||||||
<span ng-if="'ban' === msg.content.prev && msg.content.reason">
|
<span ng-if="msg.prev_content && 'ban' === msg.prev_content.membership && msg.content.reason">
|
||||||
: {{ msg.content.reason }}
|
: {{ msg.content.reason }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
Loading…
Reference in a new issue