From 8166ebd91a3c2932e7991bff0b54da9ef5479df2 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 09:53:09 +0100 Subject: [PATCH 01/62] cmdclient: Now works correctly with user localpart login. Default to 8008 not 8080. --- cmdclient/console.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmdclient/console.py b/cmdclient/console.py index f6a7731eab..a83140710b 100755 --- a/cmdclient/console.py +++ b/cmdclient/console.py @@ -88,6 +88,8 @@ class SynapseCmd(cmd.Cmd): return False def _domain(self): + if "user" not in self.config or not self.config["user"]: + return None return self.config["user"].split(":")[1] def do_config(self, line): @@ -191,8 +193,10 @@ class SynapseCmd(cmd.Cmd): p = getpass.getpass("Enter your password: ") user = args["user_id"] if self._is_on("complete_usernames") and not user.startswith("@"): - user = "@" + user + ":" + self._domain() - + domain = self._domain() + if domain: + user = "@" + user + ":" + domain + reactor.callFromThread(self._do_login, user, p) #print " got %s " % p except Exception as e: @@ -700,7 +704,7 @@ def main(server_url, identity_server_url, username, token, config_path): if __name__ == '__main__': parser = argparse.ArgumentParser("Starts a synapse client.") parser.add_argument( - "-s", "--server", dest="server", default="http://localhost:8080", + "-s", "--server", dest="server", default="http://localhost:8008", help="The URL of the home server to talk to.") parser.add_argument( "-i", "--identity-server", dest="identityserver", default="http://localhost:8090", From 65c7f78e9f864aff4d273f59f7f9bf43b81db9bd Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 11:07:44 +0200 Subject: [PATCH 02/62] Made IRC style command easier to handle specifically for possible arguments --- webclient/room/room-controller.js | 32 +++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 72c290ad73..da2a9b0e77 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -250,28 +250,40 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) $scope.state.sending = true; - // Send the text message var promise; - // FIXME: handle other commands too - if ($scope.textInput.indexOf("/me") === 0) { - promise = matrixService.sendEmoteMessage($scope.room_id, $scope.textInput.substr(4)); - } - else if ($scope.textInput.indexOf("/nick ") === 0) { - // Change user display name - promise = matrixService.setDisplayName($scope.textInput.substr(6)); + + // Check for IRC style commands first + if ($scope.textInput.indexOf("/") === 0) { + var args = $scope.textInput.split(' '); + var cmd = args[0]; + + switch (cmd) { + case "/me": + var emoteMsg = args.slice(1).join(' '); + promise = matrixService.sendEmoteMessage($scope.room_id, emoteMsg); + break; + + case "/nick": + // Change user display name + if (2 === args.length) { + promise = matrixService.setDisplayName(args[1]); + } + break; + } } else { + // Send the text message promise = matrixService.sendTextMessage($scope.room_id, $scope.textInput); } promise.then( function() { - console.log("Sent message"); + console.log("Request successfully sent"); $scope.textInput = ""; $scope.state.sending = false; }, function(error) { - $scope.feedback = "Failed to send: " + error.data.error; + $scope.feedback = "Request failed: " + error.data.error; $scope.state.sending = false; }); }; From 56427b8057db532ca0887e14e71cb0071dcc6903 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 11:17:58 +0200 Subject: [PATCH 03/62] Added /ban command --- webclient/components/matrix/matrix-service.js | 14 +++++++++++++- webclient/room/room-controller.js | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 2ae55bea9f..869c1f731d 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -165,6 +165,17 @@ angular.module('matrixService', []) // TODO: Use PUT with transaction IDs return doRequest("POST", path, undefined, data); }, + + // Bans a user from from a room + ban: function(room_id, user_id, reason) { + var path = "/rooms/$room_id/ban/"; + path = path.replace("$room_id", encodeURIComponent(room_id)); + + return doRequest("PUT", path, undefined, { + user_id: user_id, + reason: reason + }); + }, // Retrieves the room ID corresponding to a room alias resolveRoomAlias:function(room_alias) { @@ -408,7 +419,8 @@ angular.module('matrixService', []) state: presence }); }, - + + /****** Permanent storage of user information ******/ // Returns the current config diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index da2a9b0e77..fcd24c2749 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -269,6 +269,22 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) promise = matrixService.setDisplayName(args[1]); } break; + + case "/ban": + // Ban the user id from the room + if (2 <= args.length) { + + // TODO: The user may have entered the display name + // Need display name -> user_id resolution. Pb: how to manage user with same display names? + var user_id = args[1]; + + // Does the user provide a reason? + if (3 <= args.length) { + var reason = args.slice(2).join(' '); + } + promise = matrixService.ban($scope.room_id, user_id, reason); + } + break; } } else { From e4eb5cb443b0d61e7c201666059178e37d0a4fc7 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 10:27:04 +0100 Subject: [PATCH 04/62] cmdclient: Fixed /join to work with the updated c-s API. --- cmdclient/console.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmdclient/console.py b/cmdclient/console.py index a83140710b..6d77d64f23 100755 --- a/cmdclient/console.py +++ b/cmdclient/console.py @@ -316,7 +316,7 @@ class SynapseCmd(cmd.Cmd): try: args = self._parse(line, ["roomname"], force_keys=True) path = "/join/%s" % urllib.quote(args["roomname"]) - reactor.callFromThread(self._run_and_pprint, "PUT", path, {}) + reactor.callFromThread(self._run_and_pprint, "POST", path, {}) except Exception as e: print e From 8ad056b2079880145163f6e562ec99f480e87a7f Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 10:32:51 +0100 Subject: [PATCH 05/62] Prefix API links with /docs/api/client-server so they should link through correctly on matrix.org --- docs/specification.rst | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index fdd5f07917..bae3f3a7b4 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -1672,45 +1672,45 @@ User ID: .. ============================================= .. |createRoom| replace:: ``/createRoom`` -.. _createRoom: /-rooms/create_room +.. _createRoom: /docs/api/client-server/#!/-rooms/create_room .. |initialSync| replace:: ``/initialSync`` -.. _initialSync: /-events/initial_sync +.. _initialSync: /docs/api/client-server/#!/-events/initial_sync .. |/rooms//initialSync| replace:: ``/rooms//initialSync`` -.. _/rooms//initialSync: /-rooms/get_room_sync_data +.. _/rooms//initialSync: /docs/api/client-server/#!/-rooms/get_room_sync_data .. |login| replace:: ``/login`` -.. _login: /-login +.. _login: /docs/api/client-server/#!/-login .. |/rooms//messages| replace:: ``/rooms//messages`` -.. _/rooms//messages: /-rooms/get_messages +.. _/rooms//messages: /docs/api/client-server/#!/-rooms/get_messages .. |/rooms//members| replace:: ``/rooms//members`` -.. _/rooms//members: /-rooms/get_members +.. _/rooms//members: /docs/api/client-server/#!/-rooms/get_members .. |/rooms//state| replace:: ``/rooms//state`` -.. _/rooms//state: /-rooms/get_state_events +.. _/rooms//state: /docs/api/client-server/#!/-rooms/get_state_events .. |/rooms//send/| replace:: ``/rooms//send/`` -.. _/rooms//send/: /-rooms/send_non_state_event +.. _/rooms//send/: /docs/api/client-server/#!/-rooms/send_non_state_event .. |/rooms//state//| replace:: ``/rooms//state//`` -.. _/rooms//state//: /-rooms/send_state_event +.. _/rooms//state//: /docs/api/client-server/#!/-rooms/send_state_event .. |/rooms//invite| replace:: ``/rooms//invite`` -.. _/rooms//invite: /-rooms/invite +.. _/rooms//invite: /docs/api/client-server/#!/-rooms/invite .. |/rooms//join| replace:: ``/rooms//join`` -.. _/rooms//join: /-rooms/join_room +.. _/rooms//join: /docs/api/client-server/#!/-rooms/join_room .. |/rooms//leave| replace:: ``/rooms//leave`` -.. _/rooms//leave: /-rooms/leave +.. _/rooms//leave: /docs/api/client-server/#!/-rooms/leave .. |/rooms//ban| replace:: ``/rooms//ban`` -.. _/rooms//ban: /-rooms/ban +.. _/rooms//ban: /docs/api/client-server/#!/-rooms/ban .. |/join/| replace:: ``/join/`` -.. _/join/: /-rooms/join +.. _/join/: /docs/api/client-server/#!/-rooms/join -.. _`Event Stream`: /-events/get_event_stream +.. _`Event Stream`: /docs/api/client-server/#!/-events/get_event_stream From 4ba5b4b55d67b4188d551916e0707f456a6af787 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 11:38:24 +0200 Subject: [PATCH 06/62] Do not show banned people in member list. Format ban events strings in room and recent flows --- webclient/recents/recents.html | 4 ++-- webclient/room/room-controller.js | 5 +++++ webclient/room/room.html | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/webclient/recents/recents.html b/webclient/recents/recents.html index db3b0fb32f..9978e08b13 100644 --- a/webclient/recents/recents.html +++ b/webclient/recents/recents.html @@ -23,8 +23,8 @@
{{ room.lastMsg.user_id }} - {{ {"join": "joined", "leave": "left", "invite": "invited"}[room.lastMsg.content.membership] }} - {{ room.lastMsg.content.membership === "invite" ? (room.lastMsg.state_key || '') : '' }} + {{ {"join": "joined", "leave": "left", "invite": "invited", "ban": "banned"}[msg.content.membership] }} + {{ (msg.content.membership === "invite" || msg.content.membership === "ban") ? (msg.state_key || '') : '' }}
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index fcd24c2749..48ddda62cc 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -161,6 +161,11 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) var updateMemberList = function(chunk) { if (chunk.room_id != $scope.room_id) return; + // Ignore banned people + if ("ban" === chunk.membership) { + return; + } + // set target_user_id to keep things clear var target_user_id = chunk.state_key; diff --git a/webclient/room/room.html b/webclient/room/room.html index e672b1d7e2..05e87387a5 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -50,8 +50,9 @@
{{ members[msg.user_id].displayname || msg.user_id }} - {{ {"join": "joined", "leave": "left", "invite": "invited"}[msg.content.membership] }} - {{ msg.content.membership === "invite" ? (msg.state_key || '') : '' }} + {{ {"join": "joined", "leave": "left", "invite": "invited", "ban": "banned"}[msg.content.membership] }} + {{ (msg.content.membership === "invite" || msg.content.membership === "ban") ? (msg.state_key || '') : '' }} + From ddf3ca7ab365fdc80817a1b43ea34e4e8afe5299 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Tue, 2 Sep 2014 13:30:36 +0100 Subject: [PATCH 07/62] Neater is_presence_visible() code --- synapse/handlers/presence.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 9bfceda88a..8c1c2bf850 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -155,19 +155,18 @@ class PresenceHandler(BaseHandler): if observer_user == observed_user: defer.returnValue(True) - allowed_by_subscription = yield self.store.is_presence_visible( - observed_localpart=observed_user.localpart, - observer_userid=observer_user.to_string(), - ) - - if allowed_by_subscription: + if (yield self.store.do_users_share_a_room( + [observer_user, observed_user] + )): defer.returnValue(True) - share_room = yield self.store.do_users_share_a_room( - [observer_user, observed_user] - ) + if (yield self.store.is_presence_visible( + observed_localpart=observed_user.localpart, + observer_userid=observer_user.to_string(), + )): + defer.returnValue(True) - defer.returnValue(share_room) + defer.returnValue(False) @defer.inlineCallbacks def get_state(self, target_user, auth_user): From 967b45bc1a07ae0e2b3e9522b4857e773deacbd9 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Tue, 2 Sep 2014 15:26:09 +0100 Subject: [PATCH 08/62] Allow optional non-suppression of exceptions through the Distributor --- synapse/util/distributor.py | 17 +++++++++++++---- tests/test_distributor.py | 24 +++++++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/synapse/util/distributor.py b/synapse/util/distributor.py index 9605d7d1b9..9cffaec8f2 100644 --- a/synapse/util/distributor.py +++ b/synapse/util/distributor.py @@ -32,7 +32,9 @@ class Distributor(object): model will do for today. """ - def __init__(self): + def __init__(self, suppress_failures=True): + self.suppress_failures = suppress_failures + self.signals = {} self.pre_registration = {} @@ -40,7 +42,9 @@ class Distributor(object): if name in self.signals: raise KeyError("%r already has a signal named %s" % (self, name)) - self.signals[name] = Signal(name) + self.signals[name] = Signal(name, + suppress_failures=self.suppress_failures, + ) if name in self.pre_registration: signal = self.signals[name] @@ -74,8 +78,9 @@ class Signal(object): method into all of the observers. """ - def __init__(self, name): + def __init__(self, name, suppress_failures): self.name = name + self.suppress_failures = suppress_failures self.observers = [] def observe(self, observer): @@ -104,6 +109,10 @@ class Signal(object): failure.type, failure.value, failure.getTracebackObject())) + if not self.suppress_failures: + raise failure deferreds.append(d.addErrback(eb)) - return defer.DeferredList(deferreds) + return defer.DeferredList( + deferreds, fireOnOneErrback=not self.suppress_failures + ) diff --git a/tests/test_distributor.py b/tests/test_distributor.py index 21c91f335b..2869fdfd76 100644 --- a/tests/test_distributor.py +++ b/tests/test_distributor.py @@ -13,9 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unittest - from twisted.internet import defer +from twisted.trial import unittest from mock import Mock, patch @@ -75,6 +74,24 @@ class DistributorTestCase(unittest.TestCase): self.assertIsInstance(mock_logger.warning.call_args[0][0], str) + @defer.inlineCallbacks + def test_signal_catch_no_suppress(self): + # Gut-wrenching + self.dist.suppress_failures = False + + self.dist.declare("whail") + + observer = Mock() + observer.return_value = defer.fail( + Exception("Oopsie") + ) + + self.dist.observe("whail", observer) + + d = self.dist.fire("whail") + + yield self.assertFailure(d, Exception) + def test_signal_prereg(self): observer = Mock() self.dist.observe("flare", observer) @@ -85,5 +102,6 @@ class DistributorTestCase(unittest.TestCase): observer.assert_called_with(4, 5) def test_signal_undeclared(self): - with self.assertRaises(KeyError): + def code(): self.dist.fire("notification") + self.assertRaises(KeyError, code) From da3e04df8bcbb42f383abf406f4d71cf54b77ae2 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Tue, 2 Sep 2014 16:16:51 +0100 Subject: [PATCH 09/62] Rename the 'do_users_share_a_room' to something slightly less verb-sounding --- synapse/handlers/presence.py | 2 +- synapse/storage/roommember.py | 2 +- tests/handlers/test_presence.py | 4 ++-- tests/handlers/test_presencelike.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 8c1c2bf850..48549b03e5 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -155,7 +155,7 @@ class PresenceHandler(BaseHandler): if observer_user == observed_user: defer.returnValue(True) - if (yield self.store.do_users_share_a_room( + if (yield self.store.user_rooms_intersect( [observer_user, observed_user] )): defer.returnValue(True) diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py index 2746126e85..c380ec0ebd 100644 --- a/synapse/storage/roommember.py +++ b/synapse/storage/roommember.py @@ -165,7 +165,7 @@ class RoomMemberStore(SQLBaseStore): defer.returnValue(results) @defer.inlineCallbacks - def do_users_share_a_room(self, user_list): + def user_rooms_intersect(self, user_list): """ Checks whether a list of users share a room. """ user_list_clause = " OR ".join(["m.user_id = ?"] * len(user_list)) diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index b8309bc063..618fb7f270 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -117,10 +117,10 @@ class PresenceStateTestCase(unittest.TestCase): return defer.succeed([]) room_member_handler.get_room_members = get_room_members - def do_users_share_a_room(userlist): + def user_rooms_intersect(userlist): shared = all(map(lambda u: u in self.room_members, userlist)) return defer.succeed(shared) - self.datastore.do_users_share_a_room = do_users_share_a_room + self.datastore.user_rooms_intersect = user_rooms_intersect self.mock_start = Mock() self.mock_stop = Mock() diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py index 38cc34350b..638e506d34 100644 --- a/tests/handlers/test_presencelike.py +++ b/tests/handlers/test_presencelike.py @@ -107,9 +107,9 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): return defer.succeed(self.presence_list) self.datastore.get_presence_list = get_presence_list - def do_users_share_a_room(userlist): + def user_rooms_intersect(userlist): return defer.succeed(False) - self.datastore.do_users_share_a_room = do_users_share_a_room + self.datastore.user_rooms_intersect = user_rooms_intersect self.handlers = hs.get_handlers() From 46ac4a2f854e3338334abd990ecbb7fbb2b5f786 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 10:44:40 +0100 Subject: [PATCH 10/62] Fill out power level bits in the spec --- docs/specification.rst | 105 ++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index bae3f3a7b4..c67b4ca2b4 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -428,9 +428,10 @@ event also has a ``creator`` key which contains the user ID of the room creator. It will also generate several other events in order to manage permissions in this room. This includes: - - ``m.room.power_levels`` : Sets the authority of the room creator. + - ``m.room.power_levels`` : Sets the power levels of users. - ``m.room.join_rules`` : Whether the room is "invite-only" or not. - - ``m.room.add_state_level`` + - ``m.room.add_state_level``: The power level required in order to + add new state to the room (as opposed to updating exisiting state) - ``m.room.send_event_level`` : The power level required in order to send a message in this room. - ``m.room.ops_level`` : The power level required in order to kick or @@ -463,6 +464,27 @@ Permissions Link through to respective sections where necessary. How does this tie in with permissions, e.g. give example of creating a read-only room. +Permissions for rooms are done via the concept of power levels - to do any +action in a room a user must have a suitable power level. + +Power levels for users are defined in ``m.room.power_levels``, where both +a default and specific users' power levels can be set. By default all users +have a power level of 0. + +State events may contain a ``required_power_level`` key, which indicates the +minimum power a user must have before they can update that state key. The only +exception to this is when a user leaves a room. + +To perform certain actions there are additional power level requirements +defined in the following state events: + +- ``m.room.send_event_level`` defines the minimum level for sending non-state + events. Defaults to 5. +- ``m.room.add_state_level`` defines the minimum level for adding new state, + rather than updating existing state. Defaults to 5. +- ``m.room.ops_level`` defines the minimum levels to ban and kick other users. + This defaults to a kick and ban levels of 5 each. + Joining rooms ------------- @@ -797,89 +819,88 @@ prefixed with ``m.`` to force this state change directly will fail. See the `Rooms`_ section for how to use the membership APIs. -``m.room.config`` +``m.room.create`` Summary: - The room config. + The first event in the room. Type: State event JSON format: - TODO + ``{ "creator": "string"}`` Example: - TODO + ``{ "creator": "@user:example.com" }`` Description: - TODO : What it represents, What are the valid keys / values and what they represent, When is this event emitted and by what + This is the first event in a room and cannot be changed. It acts as the + root of all other events. -``m.room.invite_join`` - Summary: - TODO. - Type: - State event - JSON format: - TODO - Example: - TODO - Description: - TODO : What it represents, What are the valid keys / values and what they represent, When is this event emitted and by what - ``m.room.join_rules`` Summary: - TODO. + Descripes how/if people are allowed to join. Type: State event JSON format: - TODO + ``{ "join_rule": "enum [ public|knock|invite|private ]" }`` Example: - TODO + ``{ "join_rule": "public" }`` Description: - TODO : What it represents, What are the valid keys / values and what they represent, When is this event emitted and by what - + TODO : Use docs/models/rooms.rst + ``m.room.power_levels`` Summary: - TODO. + Defines the power levels of users in the room. Type: State event JSON format: - TODO + ``{ "": , ..., "default": }`` Example: - TODO + ``{ "@user:example.com": 5, "@user2:example.com": 10, "default": 0 }`` Description: - TODO : What it represents, What are the valid keys / values and what they represent, When is this event emitted and by what - + If a user is in the list, then they have the associated power level. + Otherwise they have the default level. If not ``default`` key is supplied, + it is assumed to be 0. + ``m.room.add_state_level`` Summary: - TODO. + Defines the minimum power level a user needs to add state. Type: State event JSON format: - TODO + ``{ "level": }`` Example: - TODO + ``{ "level": 5 }`` Description: - TODO : What it represents, What are the valid keys / values and what they represent, When is this event emitted and by what + To add a new piece of state to the room a user must have the given power + level. This does not apply to updating current state, which is goverened + by the ``required_power_level`` event key. ``m.room.send_event_level`` Summary: - TODO. + Defines the minimum power level a user needs to send an event. Type: State event JSON format: - TODO + ``{ "level": }`` Example: - TODO + ``{ "level": 0 }`` Description: - TODO : What it represents, What are the valid keys / values and what they represent, When is this event emitted and by what - + To send a new event into the room a user must have at least this power + level. This allows ops to make the room read only by increasing this level, + or muting individual users by lowering their power level below this + threshold. + ``m.room.ops_levels`` Summary: - TODO. + Defines the minimum power levels that a user must have before they can + kick and/or ban other users. Type: State event JSON format: - TODO + ``{ "ban_level": , "kick_level": }`` Example: - TODO + ``{ "ban_level": 5, "kick_level": 5 }`` Description: - TODO : What it represents, What are the valid keys / values and what they represent, When is this event emitted and by what + This defines who can ban and/or kick people in the room. Most of the time + ``ban_level`` will be greater than or equal to ``kick_level`` since + banning is more severe than kicking. ``m.room.message`` Summary: From d29d41322a8c99af763f36c4f05b6c19fcb2f7e1 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 11:45:30 +0200 Subject: [PATCH 11/62] Better indicate in the page feedback section that a user can't join a room (because he has been banned for ex). May be worth to be put in the middle of screen --- webclient/room/room-controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 48ddda62cc..13bddc5451 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -396,7 +396,8 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) onInit3(); }, function(reason) { - $scope.feedback = "Can't join room: " + reason; + console.log("Can't join room: " + JSON.stringify(reason)); + $scope.feedback = "You do not have permission to join this room"; }); } else { From ce5c88006eed9f59103f3aeb73d265731f3d7c4e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 10:47:00 +0100 Subject: [PATCH 12/62] Update port numbers. --- docs/client-server/swagger_matrix/api-docs-directory | 2 +- docs/client-server/swagger_matrix/api-docs-events | 2 +- docs/client-server/swagger_matrix/api-docs-login | 2 +- docs/client-server/swagger_matrix/api-docs-presence | 2 +- docs/client-server/swagger_matrix/api-docs-profile | 2 +- docs/client-server/swagger_matrix/api-docs-registration | 2 +- docs/client-server/swagger_matrix/api-docs-rooms | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/client-server/swagger_matrix/api-docs-directory b/docs/client-server/swagger_matrix/api-docs-directory index 98109a0fbc..380ec3f477 100644 --- a/docs/client-server/swagger_matrix/api-docs-directory +++ b/docs/client-server/swagger_matrix/api-docs-directory @@ -1,7 +1,7 @@ { "apiVersion": "1.0.0", "swaggerVersion": "1.2", - "basePath": "http://localhost:8080/_matrix/client/api/v1", + "basePath": "http://localhost:8008/_matrix/client/api/v1", "resourcePath": "/directory", "produces": [ "application/json" diff --git a/docs/client-server/swagger_matrix/api-docs-events b/docs/client-server/swagger_matrix/api-docs-events index e5dd3a6113..1bdb9b034a 100644 --- a/docs/client-server/swagger_matrix/api-docs-events +++ b/docs/client-server/swagger_matrix/api-docs-events @@ -1,7 +1,7 @@ { "apiVersion": "1.0.0", "swaggerVersion": "1.2", - "basePath": "http://localhost:8080/_matrix/client/api/v1", + "basePath": "http://localhost:8008/_matrix/client/api/v1", "resourcePath": "/events", "produces": [ "application/json" diff --git a/docs/client-server/swagger_matrix/api-docs-login b/docs/client-server/swagger_matrix/api-docs-login index 8cc598b3c1..77a588a68e 100644 --- a/docs/client-server/swagger_matrix/api-docs-login +++ b/docs/client-server/swagger_matrix/api-docs-login @@ -40,7 +40,7 @@ "path": "/login" } ], - "basePath": "http://localhost:8080/_matrix/client/api/v1", + "basePath": "http://localhost:8008/_matrix/client/api/v1", "consumes": [ "application/json" ], diff --git a/docs/client-server/swagger_matrix/api-docs-presence b/docs/client-server/swagger_matrix/api-docs-presence index d52ce2164a..99630d2056 100644 --- a/docs/client-server/swagger_matrix/api-docs-presence +++ b/docs/client-server/swagger_matrix/api-docs-presence @@ -1,7 +1,7 @@ { "apiVersion": "1.0.0", "swaggerVersion": "1.2", - "basePath": "http://localhost:8080/_matrix/client/api/v1", + "basePath": "http://localhost:8008/_matrix/client/api/v1", "resourcePath": "/presence", "produces": [ "application/json" diff --git a/docs/client-server/swagger_matrix/api-docs-profile b/docs/client-server/swagger_matrix/api-docs-profile index 188259fa3d..d2fccaa67d 100644 --- a/docs/client-server/swagger_matrix/api-docs-profile +++ b/docs/client-server/swagger_matrix/api-docs-profile @@ -1,7 +1,7 @@ { "apiVersion": "1.0.0", "swaggerVersion": "1.2", - "basePath": "http://localhost:8080/_matrix/client/api/v1", + "basePath": "http://localhost:8008/_matrix/client/api/v1", "resourcePath": "/profile", "produces": [ "application/json" diff --git a/docs/client-server/swagger_matrix/api-docs-registration b/docs/client-server/swagger_matrix/api-docs-registration index 2048aec1d2..a3dbc01b36 100644 --- a/docs/client-server/swagger_matrix/api-docs-registration +++ b/docs/client-server/swagger_matrix/api-docs-registration @@ -37,7 +37,7 @@ "path": "/register" } ], - "basePath": "http://localhost:8080/_matrix/client/api/v1", + "basePath": "http://localhost:8008/_matrix/client/api/v1", "consumes": [ "application/json" ], diff --git a/docs/client-server/swagger_matrix/api-docs-rooms b/docs/client-server/swagger_matrix/api-docs-rooms index 0a8bb3c2a5..7d1aeb837f 100644 --- a/docs/client-server/swagger_matrix/api-docs-rooms +++ b/docs/client-server/swagger_matrix/api-docs-rooms @@ -1,7 +1,7 @@ { "apiVersion": "1.0.0", "swaggerVersion": "1.2", - "basePath": "http://localhost:8080/_matrix/client/api/v1", + "basePath": "http://localhost:8008/_matrix/client/api/v1", "resourcePath": "/rooms", "produces": [ "application/json" From 3a3fadcece2f90171d82aee1b44956bb4b383549 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 10:58:53 +0100 Subject: [PATCH 13/62] Make API docs not lie on registration/login. --- .../swagger_matrix/api-docs-login | 22 +++++++++++++++++-- .../swagger_matrix/api-docs-registration | 4 ++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/client-server/swagger_matrix/api-docs-login b/docs/client-server/swagger_matrix/api-docs-login index 77a588a68e..d6f8d84f29 100644 --- a/docs/client-server/swagger_matrix/api-docs-login +++ b/docs/client-server/swagger_matrix/api-docs-login @@ -8,7 +8,7 @@ "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": "LoginInfo" + "type": "LoginFlows" }, { "method": "POST", @@ -45,12 +45,26 @@ "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.", - "format": "string", + "items": { + "$ref": "string" + }, "type": "array" }, "type": { @@ -65,6 +79,10 @@ "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.", diff --git a/docs/client-server/swagger_matrix/api-docs-registration b/docs/client-server/swagger_matrix/api-docs-registration index a3dbc01b36..f4669ea2f0 100644 --- a/docs/client-server/swagger_matrix/api-docs-registration +++ b/docs/client-server/swagger_matrix/api-docs-registration @@ -52,6 +52,10 @@ "user_id": { "description": "The fully-qualified user ID.", "type": "string" + }, + "home_server": { + "description": "The name of the home server.", + "type": "string" } } }, From 2385d396c303a8ba5c4f0d1ce31ee8a31393f4af Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 11:05:29 +0100 Subject: [PATCH 14/62] URL decode user IDs for /profile REST path segments. --- synapse/rest/profile.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/synapse/rest/profile.py b/synapse/rest/profile.py index 06076667c7..c4a0a8d260 100644 --- a/synapse/rest/profile.py +++ b/synapse/rest/profile.py @@ -19,6 +19,7 @@ from twisted.internet import defer from base import RestServlet, client_path_pattern import json +import urllib class ProfileDisplaynameRestServlet(RestServlet): @@ -26,6 +27,7 @@ class ProfileDisplaynameRestServlet(RestServlet): @defer.inlineCallbacks def on_GET(self, request, user_id): + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) displayname = yield self.handlers.profile_handler.get_displayname( @@ -37,6 +39,7 @@ class ProfileDisplaynameRestServlet(RestServlet): @defer.inlineCallbacks def on_PUT(self, request, user_id): auth_user = yield self.auth.get_user_by_req(request) + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) try: @@ -59,6 +62,7 @@ class ProfileAvatarURLRestServlet(RestServlet): @defer.inlineCallbacks def on_GET(self, request, user_id): + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) avatar_url = yield self.handlers.profile_handler.get_avatar_url( @@ -70,6 +74,7 @@ class ProfileAvatarURLRestServlet(RestServlet): @defer.inlineCallbacks def on_PUT(self, request, user_id): auth_user = yield self.auth.get_user_by_req(request) + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) try: @@ -92,6 +97,7 @@ class ProfileRestServlet(RestServlet): @defer.inlineCallbacks def on_GET(self, request, user_id): + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) displayname = yield self.handlers.profile_handler.get_displayname( From bf10cf5f1a16eaf741257b8b2670491c4d7caf25 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 3 Sep 2014 11:10:29 +0100 Subject: [PATCH 15/62] move contentrepo class to it's own file --- synapse/app/homeserver.py | 3 +- synapse/http/content_repository.py | 191 +++++++++++++++++++++++++++++ synapse/http/server.py | 163 +----------------------- 3 files changed, 194 insertions(+), 163 deletions(-) create mode 100644 synapse/http/content_repository.py diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 8a7cd07fec..5e2a91b1da 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -23,7 +23,8 @@ from twisted.enterprise import adbapi from twisted.web.resource import Resource from twisted.web.static import File from twisted.web.server import Site -from synapse.http.server import JsonResource, RootRedirect, ContentRepoResource +from synapse.http.server import JsonResource, RootRedirect +from synapse.http.content_repository import ContentRepoResource from synapse.http.client import TwistedHttpClient from synapse.api.urls import ( CLIENT_PREFIX, FEDERATION_PREFIX, WEB_CLIENT_PREFIX, CONTENT_REPO_PREFIX diff --git a/synapse/http/content_repository.py b/synapse/http/content_repository.py new file mode 100644 index 0000000000..9b1ab3fc3e --- /dev/null +++ b/synapse/http/content_repository.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +# Copyright 2014 matrix.org +# +# 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. + +from .server import respond_with_json_bytes + +from synapse.util.stringutils import random_string +from synapse.api.errors import ( + cs_exception, SynapseError, CodeMessageException, Codes, cs_error +) + +from twisted.protocols.basic import FileSender +from twisted.web import server, resource +from twisted.internet import defer + +import base64 +import json +import logging +import os +import re + +logger = logging.getLogger(__name__) + + +class ContentRepoResource(resource.Resource): + """Provides file uploading and downloading. + + Uploads are POSTed to wherever this Resource is linked to. This resource + returns a "content token" which can be used to GET this content again. The + token is typically a path, but it may not be. Tokens can expire, be one-time + uses, etc. + + In this case, the token is a path to the file and contains 3 interesting + sections: + - User ID base64d (for namespacing content to each user) + - random 24 char string + - Content type base64d (so we can return it when clients GET it) + + """ + isLeaf = True + + def __init__(self, hs, directory, auth): + resource.Resource.__init__(self) + self.hs = hs + self.directory = directory + self.auth = auth + + if not os.path.isdir(self.directory): + os.mkdir(self.directory) + logger.info("ContentRepoResource : Created %s directory.", + self.directory) + + @defer.inlineCallbacks + def map_request_to_name(self, request): + # auth the user + auth_user = yield self.auth.get_user_by_req(request) + + # namespace all file uploads on the user + prefix = base64.urlsafe_b64encode( + auth_user.to_string() + ).replace('=', '') + + # use a random string for the main portion + main_part = random_string(24) + + # suffix with a file extension if we can make one. This is nice to + # provide a hint to clients on the file information. We will also reuse + # this info to spit back the content type to the client. + suffix = "" + if request.requestHeaders.hasHeader("Content-Type"): + content_type = request.requestHeaders.getRawHeaders( + "Content-Type")[0] + suffix = "." + base64.urlsafe_b64encode(content_type) + if (content_type.split("/")[0].lower() in + ["image", "video", "audio"]): + file_ext = content_type.split("/")[-1] + # be a little paranoid and only allow a-z + file_ext = re.sub("[^a-z]", "", file_ext) + suffix += "." + file_ext + + file_name = prefix + main_part + suffix + file_path = os.path.join(self.directory, file_name) + logger.info("User %s is uploading a file to path %s", + auth_user.to_string(), + file_path) + + # keep trying to make a non-clashing file, with a sensible max attempts + attempts = 0 + while os.path.exists(file_path): + main_part = random_string(24) + file_name = prefix + main_part + suffix + file_path = os.path.join(self.directory, file_name) + attempts += 1 + if attempts > 25: # really? Really? + raise SynapseError(500, "Unable to create file.") + + defer.returnValue(file_path) + + def render_GET(self, request): + # no auth here on purpose, to allow anyone to view, even across home + # servers. + + # TODO: A little crude here, we could do this better. + filename = request.path.split('/')[-1] + # be paranoid + filename = re.sub("[^0-9A-z.-_]", "", filename) + + file_path = self.directory + "/" + filename + + logger.debug("Searching for %s", file_path) + + if os.path.isfile(file_path): + # filename has the content type + base64_contentype = filename.split(".")[1] + content_type = base64.urlsafe_b64decode(base64_contentype) + logger.info("Sending file %s", file_path) + f = open(file_path, 'rb') + request.setHeader('Content-Type', content_type) + d = FileSender().beginFileTransfer(f, request) + + # after the file has been sent, clean up and finish the request + def cbFinished(ignored): + f.close() + request.finish() + d.addCallback(cbFinished) + else: + respond_with_json_bytes( + request, + 404, + json.dumps(cs_error("Not found", code=Codes.NOT_FOUND)), + send_cors=True) + + return server.NOT_DONE_YET + + def render_POST(self, request): + self._async_render(request) + return server.NOT_DONE_YET + + def render_OPTIONS(self, request): + respond_with_json_bytes(request, 200, {}, send_cors=True) + return server.NOT_DONE_YET + + @defer.inlineCallbacks + def _async_render(self, request): + try: + fname = yield self.map_request_to_name(request) + + # TODO I have a suspcious feeling this is just going to block + with open(fname, "wb") as f: + f.write(request.content.read()) + + + # FIXME (erikj): These should use constants. + file_name = os.path.basename(fname) + # FIXME: we can't assume what the public mounted path of the repo is + # ...plus self-signed SSL won't work to remote clients anyway + # ...and we can't assume that it's SSL anyway, as we might want to + # server it via the non-SSL listener... + url = "https://%s/_matrix/content/%s" % ( + self.hs.domain_with_port, file_name + ) + + respond_with_json_bytes(request, 200, + json.dumps({"content_token": url}), + send_cors=True) + + except CodeMessageException as e: + logger.exception(e) + respond_with_json_bytes(request, e.code, + json.dumps(cs_exception(e))) + except Exception as e: + logger.error("Failed to store file: %s" % e) + respond_with_json_bytes( + request, + 500, + json.dumps({"error": "Internal server error"}), + send_cors=True) + + + diff --git a/synapse/http/server.py b/synapse/http/server.py index 74c220e869..5440a7fdda 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -18,22 +18,16 @@ from syutil.jsonutil import ( encode_canonical_json, encode_pretty_printed_json ) from synapse.api.errors import ( - cs_exception, SynapseError, CodeMessageException, Codes, cs_error + cs_exception, SynapseError, CodeMessageException ) -from synapse.util.stringutils import random_string from twisted.internet import defer, reactor -from twisted.protocols.basic import FileSender from twisted.web import server, resource from twisted.web.server import NOT_DONE_YET from twisted.web.util import redirectTo -import base64 import collections -import json import logging -import os -import re logger = logging.getLogger(__name__) @@ -198,161 +192,6 @@ class RootRedirect(resource.Resource): return resource.Resource.getChild(self, name, request) -class ContentRepoResource(resource.Resource): - """Provides file uploading and downloading. - - Uploads are POSTed to wherever this Resource is linked to. This resource - returns a "content token" which can be used to GET this content again. The - token is typically a path, but it may not be. Tokens can expire, be one-time - uses, etc. - - In this case, the token is a path to the file and contains 3 interesting - sections: - - User ID base64d (for namespacing content to each user) - - random 24 char string - - Content type base64d (so we can return it when clients GET it) - - """ - isLeaf = True - - def __init__(self, hs, directory, auth): - resource.Resource.__init__(self) - self.hs = hs - self.directory = directory - self.auth = auth - - if not os.path.isdir(self.directory): - os.mkdir(self.directory) - logger.info("ContentRepoResource : Created %s directory.", - self.directory) - - @defer.inlineCallbacks - def map_request_to_name(self, request): - # auth the user - auth_user = yield self.auth.get_user_by_req(request) - - # namespace all file uploads on the user - prefix = base64.urlsafe_b64encode( - auth_user.to_string() - ).replace('=', '') - - # use a random string for the main portion - main_part = random_string(24) - - # suffix with a file extension if we can make one. This is nice to - # provide a hint to clients on the file information. We will also reuse - # this info to spit back the content type to the client. - suffix = "" - if request.requestHeaders.hasHeader("Content-Type"): - content_type = request.requestHeaders.getRawHeaders( - "Content-Type")[0] - suffix = "." + base64.urlsafe_b64encode(content_type) - if (content_type.split("/")[0].lower() in - ["image", "video", "audio"]): - file_ext = content_type.split("/")[-1] - # be a little paranoid and only allow a-z - file_ext = re.sub("[^a-z]", "", file_ext) - suffix += "." + file_ext - - file_name = prefix + main_part + suffix - file_path = os.path.join(self.directory, file_name) - logger.info("User %s is uploading a file to path %s", - auth_user.to_string(), - file_path) - - # keep trying to make a non-clashing file, with a sensible max attempts - attempts = 0 - while os.path.exists(file_path): - main_part = random_string(24) - file_name = prefix + main_part + suffix - file_path = os.path.join(self.directory, file_name) - attempts += 1 - if attempts > 25: # really? Really? - raise SynapseError(500, "Unable to create file.") - - defer.returnValue(file_path) - - def render_GET(self, request): - # no auth here on purpose, to allow anyone to view, even across home - # servers. - - # TODO: A little crude here, we could do this better. - filename = request.path.split('/')[-1] - # be paranoid - filename = re.sub("[^0-9A-z.-_]", "", filename) - - file_path = self.directory + "/" + filename - - logger.debug("Searching for %s", file_path) - - if os.path.isfile(file_path): - # filename has the content type - base64_contentype = filename.split(".")[1] - content_type = base64.urlsafe_b64decode(base64_contentype) - logger.info("Sending file %s", file_path) - f = open(file_path, 'rb') - request.setHeader('Content-Type', content_type) - d = FileSender().beginFileTransfer(f, request) - - # after the file has been sent, clean up and finish the request - def cbFinished(ignored): - f.close() - request.finish() - d.addCallback(cbFinished) - else: - respond_with_json_bytes( - request, - 404, - json.dumps(cs_error("Not found", code=Codes.NOT_FOUND)), - send_cors=True) - - return server.NOT_DONE_YET - - def render_POST(self, request): - self._async_render(request) - return server.NOT_DONE_YET - - def render_OPTIONS(self, request): - respond_with_json_bytes(request, 200, {}, send_cors=True) - return server.NOT_DONE_YET - - @defer.inlineCallbacks - def _async_render(self, request): - try: - fname = yield self.map_request_to_name(request) - - # TODO I have a suspcious feeling this is just going to block - with open(fname, "wb") as f: - f.write(request.content.read()) - - - # FIXME (erikj): These should use constants. - file_name = os.path.basename(fname) - # FIXME: we can't assume what the public mounted path of the repo is - # ...plus self-signed SSL won't work to remote clients anyway - # ...and we can't assume that it's SSL anyway, as we might want to - # server it via the non-SSL listener... - url = "https://%s/_matrix/content/%s" % ( - self.hs.domain_with_port, file_name - ) - - respond_with_json_bytes(request, 200, - json.dumps({"content_token": url}), - send_cors=True) - - except CodeMessageException as e: - logger.exception(e) - respond_with_json_bytes(request, e.code, - json.dumps(cs_exception(e))) - except Exception as e: - logger.error("Failed to store file: %s" % e) - respond_with_json_bytes( - request, - 500, - json.dumps({"error": "Internal server error"}), - send_cors=True) - - def respond_with_json_bytes(request, code, json_bytes, send_cors=False, response_code_message=None): """Sends encoded JSON in response to the given request. From 65693e9e150341ac0df236817e1afc54ceb5acb5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 11:24:37 +0100 Subject: [PATCH 16/62] Fixed GET /events/$id to be not broken. --- synapse/handlers/events.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/synapse/handlers/events.py b/synapse/handlers/events.py index 980a169b25..3e9efabd2c 100644 --- a/synapse/handlers/events.py +++ b/synapse/handlers/events.py @@ -126,5 +126,7 @@ class EventHandler(BaseHandler): defer.returnValue(None) return - yield self.auth.check(event, raises=True) + if hasattr(event, "room_id"): + yield self.auth.check_joined_room(event.room_id, user.to_string()) + defer.returnValue(event) From acfabfff9c7cda10b5be085732e80bb7a2af50a6 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 11:31:48 +0100 Subject: [PATCH 17/62] Fixed /presence APIs to urldecode user IDs. --- synapse/rest/presence.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py index bce3943542..967bb7d34f 100644 --- a/synapse/rest/presence.py +++ b/synapse/rest/presence.py @@ -21,7 +21,7 @@ from base import RestServlet, client_path_pattern import json import logging - +import urllib logger = logging.getLogger(__name__) @@ -32,6 +32,7 @@ class PresenceStatusRestServlet(RestServlet): @defer.inlineCallbacks def on_GET(self, request, user_id): auth_user = yield self.auth.get_user_by_req(request) + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) state = yield self.handlers.presence_handler.get_state( @@ -42,6 +43,7 @@ class PresenceStatusRestServlet(RestServlet): @defer.inlineCallbacks def on_PUT(self, request, user_id): auth_user = yield self.auth.get_user_by_req(request) + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) state = {} @@ -77,6 +79,7 @@ class PresenceListRestServlet(RestServlet): @defer.inlineCallbacks def on_GET(self, request, user_id): auth_user = yield self.auth.get_user_by_req(request) + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) if not user.is_mine: @@ -97,6 +100,7 @@ class PresenceListRestServlet(RestServlet): @defer.inlineCallbacks def on_POST(self, request, user_id): auth_user = yield self.auth.get_user_by_req(request) + user_id = urllib.unquote(user_id) user = self.hs.parse_userid(user_id) if not user.is_mine: From c54d8df5040140c95b8ca31ce2ac023379f8cfdc Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 11:45:30 +0100 Subject: [PATCH 18/62] Update API docs to use 'presence' key not 'state'. Fixed error messages when setting presence. --- docs/client-server/swagger_matrix/api-docs-presence | 2 +- synapse/handlers/presence.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/client-server/swagger_matrix/api-docs-presence b/docs/client-server/swagger_matrix/api-docs-presence index 99630d2056..5c9f160508 100644 --- a/docs/client-server/swagger_matrix/api-docs-presence +++ b/docs/client-server/swagger_matrix/api-docs-presence @@ -106,7 +106,7 @@ "PresenceUpdate": { "id": "PresenceUpdate", "properties": { - "state": { + "presence": { "type": "string", "description": "Enum: The presence state.", "enum": [ diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index 48549b03e5..beb5aa3a6a 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -207,7 +207,7 @@ class PresenceHandler(BaseHandler): raise SynapseError(400, "User is not hosted on this Home Server") if target_user != auth_user: - raise AuthError(400, "Cannot set another user's displayname") + raise AuthError(400, "Cannot set another user's presence") if "status_msg" not in state: state["status_msg"] = None From 10afd895c4318240d2afca19f34cd93440f43dd0 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 11:56:47 +0100 Subject: [PATCH 19/62] Edited /presence REST servlet to raise SynapseErrors to return a standard error response, rather than a string. --- synapse/rest/presence.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py index 967bb7d34f..69be6fe989 100644 --- a/synapse/rest/presence.py +++ b/synapse/rest/presence.py @@ -17,6 +17,7 @@ """ from twisted.internet import defer +from synapse.api.errors import SynapseError from base import RestServlet, client_path_pattern import json @@ -58,11 +59,15 @@ class PresenceStatusRestServlet(RestServlet): if "status_msg" in content: state["status_msg"] = content.pop("status_msg") + if not isinstance(state["status_msg"], basestring): + raise SynapseError(400, "status_msg must be a string.") if content: raise KeyError() + except SynapseError as e: + raise e except: - defer.returnValue((400, "Unable to parse state")) + raise SynapseError(400, "Unable to parse state") yield self.handlers.presence_handler.set_state( target_user=user, auth_user=auth_user, state=state) @@ -83,10 +88,10 @@ class PresenceListRestServlet(RestServlet): user = self.hs.parse_userid(user_id) if not user.is_mine: - defer.returnValue((400, "User not hosted on this Home Server")) + raise SynapseError(400, "User not hosted on this Home Server") if auth_user != user: - defer.returnValue((400, "Cannot get another user's presence list")) + raise SynapseError(400, "Cannot get another user's presence list") presence = yield self.handlers.presence_handler.get_presence_list( observer_user=user, accepted=True) @@ -104,17 +109,17 @@ class PresenceListRestServlet(RestServlet): user = self.hs.parse_userid(user_id) if not user.is_mine: - defer.returnValue((400, "User not hosted on this Home Server")) + raise SynapseError(400, "User not hosted on this Home Server") if auth_user != user: - defer.returnValue(( - 400, "Cannot modify another user's presence list")) + raise SynapseError( + 400, "Cannot modify another user's presence list") try: content = json.loads(request.content.read()) except: logger.exception("JSON parse error") - defer.returnValue((400, "Unable to parse content")) + raise SynapseError(400, "Unable to parse content") deferreds = [] From bc21350298ff87b5f4cebfbba669435eb4ff4e75 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 11:57:23 +0100 Subject: [PATCH 20/62] Add option to change content repo location --- synapse/app/homeserver.py | 5 ++++- synapse/config/server.py | 12 ++++++++++++ synapse/http/server.py | 7 ++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 8a7cd07fec..fced1aa243 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -74,7 +74,9 @@ class SynapseHomeServer(HomeServer): return File("webclient") # TODO configurable? def build_resource_for_content_repo(self): - return ContentRepoResource(self, self.upload_dir, self.auth) + return ContentRepoResource( + self, self.upload_dir, self.auth, self.content_addr + ) def build_db_pool(self): """ Set up all the dbs. Since all the *.sql have IF NOT EXISTS, so we @@ -248,6 +250,7 @@ def setup(): db_name=config.database_path, tls_context_factory=tls_context_factory, config=config, + content_addr=config.content_addr, ) hs.register_servlets() diff --git a/synapse/config/server.py b/synapse/config/server.py index 36143e3c9c..cb178435ea 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -32,6 +32,14 @@ class ServerConfig(Config): self.webclient = True self.manhole = args.manhole + if not args.content_addr: + host = args.server_name + if ':' not in host: + host = "%s:%d" % (host, args.bind_port) + args.content_addr = "https://%s" % (host,) + + self.content_addr = args.content_addr + @classmethod def add_arguments(cls, parser): super(ServerConfig, cls).add_arguments(parser) @@ -57,6 +65,9 @@ class ServerConfig(Config): type=int, help="Turn on the twisted telnet manhole" " service on the given port.") + server_group.add_argument("--content-addr", default=None, + help="The host and scheme to use for the " + "content repository") def read_signing_key(self, signing_key_path): signing_key_base64 = self.read_file(signing_key_path, "signing_key") @@ -77,3 +88,4 @@ class ServerConfig(Config): with open(args.signing_key_path, "w") as signing_key_file: key = nacl.signing.SigningKey.generate() signing_key_file.write(encode_base64(key.encode())) + diff --git a/synapse/http/server.py b/synapse/http/server.py index 74c220e869..8b887006bb 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -215,11 +215,12 @@ class ContentRepoResource(resource.Resource): """ isLeaf = True - def __init__(self, hs, directory, auth): + def __init__(self, hs, directory, auth, external_addr): resource.Resource.__init__(self) self.hs = hs self.directory = directory self.auth = auth + self.external_addr = external_addr.rstrip('/') if not os.path.isdir(self.directory): os.mkdir(self.directory) @@ -332,8 +333,8 @@ class ContentRepoResource(resource.Resource): # ...plus self-signed SSL won't work to remote clients anyway # ...and we can't assume that it's SSL anyway, as we might want to # server it via the non-SSL listener... - url = "https://%s/_matrix/content/%s" % ( - self.hs.domain_with_port, file_name + url = "%s/_matrix/content/%s" % ( + self.external_addr, file_name ) respond_with_json_bytes(request, 200, From dfea1730dcb7f75189ede3f8ee71c5421d2aa5ed Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 12:09:11 +0100 Subject: [PATCH 21/62] apidocs: mtime_age > last_active_ago. Presence REST: Sanity check values in invite/drop arrays. --- docs/client-server/swagger_matrix/api-docs-presence | 4 ++-- synapse/rest/presence.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/client-server/swagger_matrix/api-docs-presence b/docs/client-server/swagger_matrix/api-docs-presence index 5c9f160508..6b22446024 100644 --- a/docs/client-server/swagger_matrix/api-docs-presence +++ b/docs/client-server/swagger_matrix/api-docs-presence @@ -128,10 +128,10 @@ "Presence": { "id": "Presence", "properties": { - "mtime_age": { + "last_active_ago": { "type": "integer", "format": "int64", - "description": "The last time this user's presence state changed, in milliseconds." + "description": "The last time this user performed an action on their home server." }, "user_id": { "type": "string", diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py index 69be6fe989..5c5adb4236 100644 --- a/synapse/rest/presence.py +++ b/synapse/rest/presence.py @@ -125,12 +125,20 @@ class PresenceListRestServlet(RestServlet): if "invite" in content: for u in content["invite"]: + if not isinstance(u, basestring): + raise SynapseError(400, "Bad invite value.") + if len(u) == 0: + continue invited_user = self.hs.parse_userid(u) deferreds.append(self.handlers.presence_handler.send_invite( observer_user=user, observed_user=invited_user)) if "drop" in content: for u in content["drop"]: + if not isinstance(u, basestring): + raise SynapseError(400, "Bad drop value.") + if len(u) == 0: + continue dropped_user = self.hs.parse_userid(u) deferreds.append(self.handlers.presence_handler.drop( observer_user=user, observed_user=dropped_user)) From cd0afb85c42aa7d550a61661547a6e7131c83f3d Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 13:08:17 +0100 Subject: [PATCH 22/62] Updated feedback api docs and fixed feedback content template bug --- docs/client-server/swagger_matrix/api-docs-rooms | 10 ++++++++++ synapse/api/events/room.py | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/client-server/swagger_matrix/api-docs-rooms b/docs/client-server/swagger_matrix/api-docs-rooms index 7d1aeb837f..3638e44e8c 100644 --- a/docs/client-server/swagger_matrix/api-docs-rooms +++ b/docs/client-server/swagger_matrix/api-docs-rooms @@ -640,6 +640,16 @@ "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": { diff --git a/synapse/api/events/room.py b/synapse/api/events/room.py index f6d3c59a9a..e4a99ff216 100644 --- a/synapse/api/events/room.py +++ b/synapse/api/events/room.py @@ -103,8 +103,7 @@ class FeedbackEvent(SynapseEvent): def get_content_template(self): return { "type": u"string", - "target_event_id": u"string", - "msg_sender_id": u"string" + "target_event_id": u"string" } From 4b85c5f52ca5a87fa998d70db81d9f158aa90ada Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 14:12:56 +0200 Subject: [PATCH 23/62] Added /op $user_id $powerLevel --- .../matrix/event-handler-service.js | 8 ++-- webclient/components/matrix/matrix-service.js | 40 ++++++++++++++++++- webclient/room/room-controller.js | 18 ++++++++- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index d6a0600132..908a9fe2b5 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -32,6 +32,7 @@ angular.module('eventHandlerService', []) var MSG_EVENT = "MSG_EVENT"; var MEMBER_EVENT = "MEMBER_EVENT"; var PRESENCE_EVENT = "PRESENCE_EVENT"; + var POWERLEVEL_EVENT = "POWERLEVEL_EVENT"; var CALL_EVENT = "CALL_EVENT"; var InitialSyncDeferred = $q.defer(); @@ -107,10 +108,8 @@ angular.module('eventHandlerService', []) var handlePowerLevels = function(event, isLiveEvent) { initRoom(event.room_id); - $rootScope.events.rooms[event.room_id][event.type] = event; - - //TODO - //$rootScope.$broadcast(PRESENCE_EVENT, event, isLiveEvent); + $rootScope.events.rooms[event.room_id][event.type] = event; + $rootScope.$broadcast(POWERLEVEL_EVENT, event, isLiveEvent); }; var handleCallEvent = function(event, isLiveEvent) { @@ -122,6 +121,7 @@ angular.module('eventHandlerService', []) MSG_EVENT: MSG_EVENT, MEMBER_EVENT: MEMBER_EVENT, PRESENCE_EVENT: PRESENCE_EVENT, + POWERLEVEL_EVENT: POWERLEVEL_EVENT, CALL_EVENT: CALL_EVENT, diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 869c1f731d..bedabe6286 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -263,7 +263,7 @@ angular.module('matrixService', []) // get a list of public rooms on your home server publicRooms: function() { - var path = "/publicRooms" + var path = "/publicRooms"; return doRequest("GET", path); }, @@ -319,7 +319,7 @@ angular.module('matrixService', []) // hit the Identity Server for a 3PID request. linkEmail: function(email, clientSecret, sendAttempt) { - var path = "/_matrix/identity/api/v1/validate/email/requestToken" + var path = "/_matrix/identity/api/v1/validate/email/requestToken"; var data = "clientSecret="+clientSecret+"&email=" + encodeURIComponent(email)+"&sendAttempt="+sendAttempt; var headers = {}; headers["Content-Type"] = "application/x-www-form-urlencoded"; @@ -520,6 +520,42 @@ angular.module('matrixService', []) } } return powerLevel; + }, + + // + /** + * Change the power level of a user + * @param {String} room_id the room id + * @param {String} user_id the user id + * @param {Number} powerLevel a value between 0 and 10 + * @returns {promise} an $http promise + */ + setUserPowerLevel: function(room_id, user_id, powerLevel) { + // Sanity check + if (powerLevel < 0 || 10 < powerLevel) { + // Format the error as is it was sent by the server + var deferred = $q.defer(); + deferred.reject({data:{error: "Invalid powerLevel: " + powerLevel}}); + return deferred.promise; + } + + // Hack: currently, there is no home server API so do it by hand by updating + // the current m.room.power_levels of the room and send it to the server + var room = $rootScope.events.rooms[room_id]; + if (room && room["m.room.power_levels"]) { + var content = angular.copy(room["m.room.power_levels"].content); + content[user_id] = powerLevel; + + var path = "/rooms/$room_id/state/m.room.power_levels"; + path = path.replace("$room_id", encodeURIComponent(room_id)); + + return doRequest("PUT", path, undefined, content); + } + + // The room does not exist or does not contain power_levels data + var deferred = $q.defer(); + deferred.reject({data:{error: "Invalied room: " + room_id}}); + return deferred.promise; } }; diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 13bddc5451..d96241b863 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -85,6 +85,14 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) updatePresence(event); } }); + + $scope.$on(eventHandlerService.POWERLEVEL_EVENT, function(ngEvent, event, isLive) { + if (isLive && event.room_id === $scope.room_id) { + for (var user_id in event.content) { + updateUserPowerLevel(user_id); + } + } + }); $scope.memberCount = function() { return Object.keys($scope.members).length; @@ -278,7 +286,6 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) case "/ban": // Ban the user id from the room if (2 <= args.length) { - // TODO: The user may have entered the display name // Need display name -> user_id resolution. Pb: how to manage user with same display names? var user_id = args[1]; @@ -290,6 +297,15 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) promise = matrixService.ban($scope.room_id, user_id, reason); } break; + + case "/op": + if (3 === args.length) { + var user_id = args[1]; + var powerLevel = parseInt(args[2]); + + promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel); + } + break; } } else { From 27cdbf7b94d495e501309e42402d3a656b42f26b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 13:32:25 +0100 Subject: [PATCH 24/62] apidocs: Tweak join response format. Explicitly state empty JSON objects where they are required by the spec. Mark unimplemented room GET APIs clearly. --- .../swagger_matrix/api-docs-rooms | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/docs/client-server/swagger_matrix/api-docs-rooms b/docs/client-server/swagger_matrix/api-docs-rooms index 3638e44e8c..40bcc45a78 100644 --- a/docs/client-server/swagger_matrix/api-docs-rooms +++ b/docs/client-server/swagger_matrix/api-docs-rooms @@ -267,6 +267,12 @@ "required": true, "type": "string", "paramType": "path" + }, + { + "name": "body", + "required": true, + "type": "JoinRequest", + "paramType": "body" } ] } @@ -291,6 +297,12 @@ "required": true, "type": "string", "paramType": "path" + }, + { + "name": "body", + "required": true, + "type": "LeaveRequest", + "paramType": "body" } ] } @@ -424,10 +436,10 @@ "path": "/join/{roomAliasOrId}", "operations": [ { - "method": "PUT", + "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": "RoomInfo", + "type": "JoinRoomInfo", "nickname": "join", "consumes": [ "application/json" @@ -574,7 +586,7 @@ { "method": "GET", "summary": "Get a list of all the current state events for this room.", - "notes": "Get a list of all the current state events for this room.", + "notes": "NOT YET IMPLEMENTED.", "type": "array", "items": { "$ref": "Event" @@ -598,7 +610,7 @@ { "method": "GET", "summary": "Get all the current information for this room, including messages and state events.", - "notes": "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": [ @@ -662,7 +674,7 @@ "invite", "join", "leave", - "knock" + "ban" ] } } @@ -682,6 +694,16 @@ } } }, + "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": { @@ -840,6 +862,14 @@ } } }, + "JoinRequest": { + "id": "JoinRequest", + "properties": {} + }, + "LeaveRequest": { + "id": "LeaveRequest", + "properties": {} + }, "BanRequest": { "id": "BanRequest", "properties": { From 56bc57cf5095559cb77f69bffbedafdaf5d7b689 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 13:42:56 +0100 Subject: [PATCH 25/62] apidocs: Added m.room.name --- .../swagger_matrix/api-docs-rooms | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/docs/client-server/swagger_matrix/api-docs-rooms b/docs/client-server/swagger_matrix/api-docs-rooms index 40bcc45a78..0e1fa452a2 100644 --- a/docs/client-server/swagger_matrix/api-docs-rooms +++ b/docs/client-server/swagger_matrix/api-docs-rooms @@ -180,6 +180,59 @@ } ] }, + { + "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": [ @@ -636,6 +689,15 @@ } } }, + "RoomName": { + "id": "RoomName", + "properties": { + "name": { + "type": "string", + "description": "The human-readable name for the room. Can contain spaces." + } + } + }, "Message": { "id": "Message", "properties": { From 061e81419551d9640ebf5ace88f2c254c6077e1d Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 3 Sep 2014 13:40:28 +0100 Subject: [PATCH 26/62] Make sure to print exceptions properly from notifier failures --- synapse/notifier.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/synapse/notifier.py b/synapse/notifier.py index 3260aa744f..157925d102 100644 --- a/synapse/notifier.py +++ b/synapse/notifier.py @@ -167,7 +167,12 @@ class Notifier(object): ) def eb(failure): - logger.exception("Failed to notify listener", failure) + logger.error("Failed to notify listener", + exc_info=( + failure.type, + failure.value, + failure.getTracebackObject()) + ) yield defer.DeferredList( [notify(l).addErrback(eb) for l in listeners] From 86d62322366a74f7fcf6f05b1c13f0e3acd3dc8b Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Tue, 2 Sep 2014 15:38:48 +0100 Subject: [PATCH 27/62] Don't eat federation transmit errors during unit tests; fix remote presence EDU-sending test because of this --- synapse/federation/replication.py | 1 - tests/handlers/test_presence.py | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/synapse/federation/replication.py b/synapse/federation/replication.py index cadf574b3b..564f629594 100644 --- a/synapse/federation/replication.py +++ b/synapse/federation/replication.py @@ -628,7 +628,6 @@ class _TransactionQueue(object): for deferred in deferreds: deferred.errback(e) - yield deferred finally: # We want to be *very* sure we delete this after we stop processing diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index 618fb7f270..4583ff8bc6 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -719,6 +719,22 @@ class PresencePushTestCase(unittest.TestCase): ), defer.succeed((200, "OK")) ) + put_json.expect_call_and_return( + call("remote", + path=ANY, # Can't guarantee which txn ID will be which + data=_expect_edu("remote", "m.presence", + content={ + "push": [ + {"user_id": "@apple:test", + "presence": u"online", + "state": u"online", + "last_active_ago": 0}, + ], + } + ) + ), + defer.succeed((200, "OK")) + ) self.room_members = [self.u_apple, self.u_onion] From da31b96b55c7ae54c0ec7708c40c7637287521b1 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Tue, 2 Sep 2014 16:29:04 +0100 Subject: [PATCH 28/62] Implement presence state visibilty limiting when polling eventsource for stream --- synapse/handlers/presence.py | 66 +++++++++++++++++++++++++++----- tests/handlers/test_presence.py | 68 ++++++++++++++++++++++++++++++++- tests/rest/test_presence.py | 28 +++++++++++--- 3 files changed, 147 insertions(+), 15 deletions(-) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index beb5aa3a6a..ae4735898e 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -156,7 +156,7 @@ class PresenceHandler(BaseHandler): defer.returnValue(True) if (yield self.store.user_rooms_intersect( - [observer_user, observed_user] + [u.to_string() for u in observer_user, observed_user] )): defer.returnValue(True) @@ -772,15 +772,52 @@ class PresenceEventSource(object): self.hs = hs self.clock = hs.get_clock() + @defer.inlineCallbacks + def is_visible(self, observer_user, observed_user): + if observer_user == observed_user: + defer.returnValue(True) + + presence = self.hs.get_handlers().presence_handler + + if (yield presence.store.user_rooms_intersect( + [u.to_string() for u in observer_user, observed_user] + )): + defer.returnValue(True) + + if observed_user.is_mine: + pushmap = presence._local_pushmap + + defer.returnValue( + observed_user.localpart in pushmap and + observer_user in pushmap[observed_user.localpart] + ) + else: + recvmap = presence._remote_recvmap + + defer.returnValue( + observed_user in recvmap and + observer_user in recvmap[observed_user] + ) + + @defer.inlineCallbacks def get_new_events_for_user(self, user, from_key, limit): from_key = int(from_key) + observer_user = user + presence = self.hs.get_handlers().presence_handler cachemap = presence._user_cachemap - # TODO(paul): limit, and filter by visibility - updates = [(k, cachemap[k]) for k in cachemap - if from_key < cachemap[k].serial] + updates = [] + # TODO(paul): use a DeferredList ? How to limit concurrency. + for observed_user in cachemap.keys(): + if not (from_key < cachemap[observed_user].serial): + continue + + if (yield self.is_visible(observer_user, observed_user)): + updates.append((observed_user, cachemap[observed_user])) + + # TODO(paul): limit if updates: clock = self.clock @@ -788,14 +825,15 @@ class PresenceEventSource(object): latest_serial = max([x[1].serial for x in updates]) data = [x[1].make_event(user=x[0], clock=clock) for x in updates] - return ((data, latest_serial)) + defer.returnValue((data, latest_serial)) else: - return (([], presence._user_cachemap_latest_serial)) + defer.returnValue(([], presence._user_cachemap_latest_serial)) def get_current_key(self): presence = self.hs.get_handlers().presence_handler return presence._user_cachemap_latest_serial + @defer.inlineCallbacks def get_pagination_rows(self, user, pagination_config, key): # TODO (erikj): Does this make sense? Ordering? @@ -812,7 +850,17 @@ class PresenceEventSource(object): presence = self.hs.get_handlers().presence_handler cachemap = presence._user_cachemap - # TODO(paul): limit, and filter by visibility + updates = [] + # TODO(paul): use a DeferredList ? How to limit concurrency. + for observed_user in cachemap.keys(): + if not (to_key < cachemap[observed_user].serial < from_key): + continue + + if (yield self.is_visible(observer_user, observed_user)): + updates.append((observed_user, cachemap[observed_user])) + + # TODO(paul): limit + updates = [(k, cachemap[k]) for k in cachemap if to_key < cachemap[k].serial < from_key] @@ -830,13 +878,13 @@ class PresenceEventSource(object): next_token = next_token.copy_and_replace( "presence_key", earliest_serial ) - return ((data, next_token)) + defer.returnValue((data, next_token)) else: if not to_token: to_token = from_token.copy_and_replace( "presence_key", 0 ) - return (([], to_token)) + defer.returnValue(([], to_token)) class UserPresenceCache(object): diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index 4583ff8bc6..8506961d2d 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -118,7 +118,9 @@ class PresenceStateTestCase(unittest.TestCase): room_member_handler.get_room_members = get_room_members def user_rooms_intersect(userlist): - shared = all(map(lambda u: u in self.room_members, userlist)) + room_member_ids = map(lambda u: u.to_string(), self.room_members) + + shared = all(map(lambda i: i in room_member_ids, userlist)) return defer.succeed(shared) self.datastore.user_rooms_intersect = user_rooms_intersect @@ -562,6 +564,13 @@ class PresencePushTestCase(unittest.TestCase): return defer.succeed([]) self.datastore.get_joined_hosts_for_room = get_room_hosts + def user_rooms_intersect(userlist): + room_member_ids = map(lambda u: u.to_string(), self.room_members) + + shared = all(map(lambda i: i in room_member_ids, userlist)) + return defer.succeed(shared) + self.datastore.user_rooms_intersect = user_rooms_intersect + @defer.inlineCallbacks def fetch_room_distributions_into(room_id, localusers=None, remotedomains=None, ignore_user=None): @@ -604,6 +613,7 @@ class PresencePushTestCase(unittest.TestCase): self.u_apple = hs.parse_userid("@apple:test") self.u_banana = hs.parse_userid("@banana:test") self.u_clementine = hs.parse_userid("@clementine:test") + self.u_durian = hs.parse_userid("@durian:test") self.u_elderberry = hs.parse_userid("@elderberry:test") # Remote user @@ -632,6 +642,7 @@ class PresencePushTestCase(unittest.TestCase): {"presence": ONLINE} ) + # Apple sees self-reflection (events, _) = yield self.event_source.get_new_events_for_user( self.u_apple, 0, None ) @@ -647,6 +658,55 @@ class PresencePushTestCase(unittest.TestCase): "last_active_ago": 0, }}, ], + msg="Presence event should be visible to self-reflection" + ) + + # Banana sees it because of presence subscription + (events, _) = yield self.event_source.get_new_events_for_user( + self.u_banana, 0, None + ) + + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals(events, + [ + {"type": "m.presence", + "content": { + "user_id": "@apple:test", + "presence": ONLINE, + "state": ONLINE, + "last_active_ago": 0, + }}, + ], + msg="Presence event should be visible to explicit subscribers" + ) + + # Elderberry sees it because of same room + (events, _) = yield self.event_source.get_new_events_for_user( + self.u_elderberry, 0, None + ) + + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals(events, + [ + {"type": "m.presence", + "content": { + "user_id": "@apple:test", + "presence": ONLINE, + "state": ONLINE, + "last_active_ago": 0, + }}, + ], + msg="Presence event should be visible to other room members" + ) + + # Durian is not in the room, should not see this event + (events, _) = yield self.event_source.get_new_events_for_user( + self.u_durian, 0, None + ) + + self.assertEquals(self.event_source.get_current_key(), 1) + self.assertEquals(events, [], + msg="Presence event should not be visible to others" ) presence = yield self.handler.get_presence_list( @@ -664,6 +724,10 @@ class PresencePushTestCase(unittest.TestCase): presence ) + # TODO(paul): Gut-wrenching + banana_set = self.handler._local_pushmap.setdefault("banana", set()) + banana_set.add(self.u_apple) + yield self.handler.set_state(self.u_banana, self.u_banana, {"presence": ONLINE} ) @@ -825,6 +889,8 @@ class PresencePushTestCase(unittest.TestCase): "a-room" ) + self.room_members.append(self.u_clementine) + (events, _) = yield self.event_source.get_new_events_for_user( self.u_apple, 0, None ) diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py index e2cdd80e07..df8dd74151 100644 --- a/tests/rest/test_presence.py +++ b/tests/rest/test_presence.py @@ -269,11 +269,16 @@ class PresenceEventStreamTestCase(unittest.TestCase): hs.register_servlets() - hs.handlers.room_member_handler = Mock(spec=[ - "get_rooms_for_user", - ]) - hs.handlers.room_member_handler.get_rooms_for_user = ( - lambda u: defer.succeed([])) + hs.handlers.room_member_handler = Mock(spec=[]) + + self.room_members = [] + + def get_rooms_for_user(user): + if user in self.room_members: + return ["a-room"] + else: + return [] + hs.handlers.room_member_handler.get_rooms_for_user = get_rooms_for_user self.mock_datastore = hs.get_datastore() @@ -285,6 +290,17 @@ class PresenceEventStreamTestCase(unittest.TestCase): return defer.succeed(None) self.mock_datastore.get_profile_avatar_url = get_profile_avatar_url + def user_rooms_intersect(user_list): + room_member_ids = map(lambda u: u.to_string(), self.room_members) + + shared = all(map(lambda i: i in room_member_ids, user_list)) + return defer.succeed(shared) + self.mock_datastore.user_rooms_intersect = user_rooms_intersect + + def get_joined_hosts_for_room(room_id): + return [] + self.mock_datastore.get_joined_hosts_for_room = get_joined_hosts_for_room + self.presence = hs.get_handlers().presence_handler self.u_apple = hs.parse_userid("@apple:test") @@ -292,6 +308,8 @@ class PresenceEventStreamTestCase(unittest.TestCase): @defer.inlineCallbacks def test_shortpoll(self): + self.room_members = [self.u_apple, self.u_banana] + self.mock_datastore.set_presence_state.return_value = defer.succeed( {"state": ONLINE}) self.mock_datastore.get_presence_list.return_value = defer.succeed( From b1195c125fe6ff7e14743c6984623cbdcfad780a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 13:51:10 +0100 Subject: [PATCH 29/62] hs: Updated synapse.http.client to handle DNSLookupErrors and bail immediately. --- synapse/http/client.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/synapse/http/client.py b/synapse/http/client.py index 093bdf0e3f..709c952226 100644 --- a/synapse/http/client.py +++ b/synapse/http/client.py @@ -15,6 +15,7 @@ from twisted.internet import defer, reactor +from twisted.internet.error import DNSLookupError from twisted.web.client import _AgentBase, _URI, readBody from twisted.web.http_headers import Headers @@ -23,7 +24,7 @@ from synapse.util.async import sleep from syutil.jsonutil import encode_canonical_json -from synapse.api.errors import CodeMessageException +from synapse.api.errors import CodeMessageException, SynapseError import json import logging @@ -198,6 +199,10 @@ class TwistedHttpClient(HttpClient): logger.debug("Got response to %s", method) break + except DNSLookupError as dns: + logger.warn("DNS Lookup failed to %s with %s", destination, + dns) + raise SynapseError(400, "Domain specified not found.") except Exception as e: logger.exception("Got error in _create_request") _print_ex(e) From 4777c1cd5bfb33c4aa4bcc7d6364339f0c40ef5a Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 15:00:04 +0200 Subject: [PATCH 30/62] BF: Do not be polluted by ops events that come when paginating back --- webclient/components/matrix/event-handler-service.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 908a9fe2b5..7c62cd1b5d 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -108,8 +108,11 @@ angular.module('eventHandlerService', []) var handlePowerLevels = function(event, isLiveEvent) { initRoom(event.room_id); - $rootScope.events.rooms[event.room_id][event.type] = event; - $rootScope.$broadcast(POWERLEVEL_EVENT, event, isLiveEvent); + // Keep the latest data. Do not care of events that come when paginating back + if (!$rootScope.events.rooms[event.room_id][event.type] || isLiveEvent) { + $rootScope.events.rooms[event.room_id][event.type] = event; + $rootScope.$broadcast(POWERLEVEL_EVENT, event, isLiveEvent); + } }; var handleCallEvent = function(event, isLiveEvent) { From 756e171ad0715daef6b0bdaccb61b0278a786ef3 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 3 Sep 2014 14:14:04 +0100 Subject: [PATCH 31/62] Store SQL DDL deltas as well; attempt to upgrade the database on startup if it's too old --- UPGRADE.rst | 5 - database-prepare-for-0.2.0.sh | 10 -- synapse/app/homeserver.py | 22 ++-- synapse/storage/schema/delta/v2.sql | 168 ++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 22 deletions(-) delete mode 100755 database-prepare-for-0.2.0.sh create mode 100644 synapse/storage/schema/delta/v2.sql diff --git a/UPGRADE.rst b/UPGRADE.rst index 99d58ab64f..da2a7a0a21 100644 --- a/UPGRADE.rst +++ b/UPGRADE.rst @@ -1,11 +1,6 @@ Upgrading to v0.2.0 =================== -To upgrade the database schema, run:: - - ./database-prepare-for-0.2.0.sh ".db" - - The home server now requires setting up of SSL config before it can run. To automatically generate default config use:: diff --git a/database-prepare-for-0.2.0.sh b/database-prepare-for-0.2.0.sh deleted file mode 100755 index e90171010b..0000000000 --- a/database-prepare-for-0.2.0.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# This is will prepare a synapse database for running with v0.2.0 of synapse. - -set -e - -cp "$1" "$1.bak" - -sqlite3 "$1" < "synapse/storage/schema/im.sql" -sqlite3 "$1" <<< "PRAGMA user_version = 2;" diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 0fa4b40c94..db2e9ebb0c 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -93,20 +93,28 @@ class SynapseHomeServer(HomeServer): if row and row[0]: user_version = row[0] - if user_version < SCHEMA_VERSION: - # TODO(paul): add some kind of intelligent fixup here - raise ValueError("Cannot use this database as the " + - "schema version (%d) does not match (%d)" % - (user_version, SCHEMA_VERSION) + if user_version > SCHEMA_VERSION: + raise ValueError("Cannot use this database as it is too " + + "new for the server to understand" ) + elif user_version < SCHEMA_VERSION: + logging.info("Upgrading database from version %d", + user_version + ) + + # Run every version since after the current version. + for v in range(user_version + 1, SCHEMA_VERSION + 1): + sql_script = read_schema("delta/v%d" % (v)) + c.executescript(sql_script) + + db_conn.commit() else: for sql_loc in SCHEMAS: sql_script = read_schema(sql_loc) c.executescript(sql_script) - db_conn.commit() - + db_conn.commit() c.execute("PRAGMA user_version = %d" % SCHEMA_VERSION) c.close() diff --git a/synapse/storage/schema/delta/v2.sql b/synapse/storage/schema/delta/v2.sql new file mode 100644 index 0000000000..ad929b4370 --- /dev/null +++ b/synapse/storage/schema/delta/v2.sql @@ -0,0 +1,168 @@ +/* Copyright 2014 matrix.org + * + * 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 events( + stream_ordering INTEGER PRIMARY KEY AUTOINCREMENT, + topological_ordering INTEGER NOT NULL, + event_id TEXT NOT NULL, + type TEXT NOT NULL, + room_id TEXT NOT NULL, + content TEXT NOT NULL, + unrecognized_keys TEXT, + processed BOOL NOT NULL, + outlier BOOL NOT NULL, + CONSTRAINT ev_uniq UNIQUE (event_id) +); + +CREATE INDEX IF NOT EXISTS events_event_id ON events (event_id); +CREATE INDEX IF NOT EXISTS events_stream_ordering ON events (stream_ordering); +CREATE INDEX IF NOT EXISTS events_topological_ordering ON events (topological_ordering); +CREATE INDEX IF NOT EXISTS events_room_id ON events (room_id); + +CREATE TABLE IF NOT EXISTS state_events( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + type TEXT NOT NULL, + state_key TEXT NOT NULL, + prev_state TEXT +); + +CREATE UNIQUE INDEX IF NOT EXISTS state_events_event_id ON state_events (event_id); +CREATE INDEX IF NOT EXISTS state_events_room_id ON state_events (room_id); +CREATE INDEX IF NOT EXISTS state_events_type ON state_events (type); +CREATE INDEX IF NOT EXISTS state_events_state_key ON state_events (state_key); + + +CREATE TABLE IF NOT EXISTS current_state_events( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + type TEXT NOT NULL, + state_key TEXT NOT NULL, + CONSTRAINT curr_uniq UNIQUE (room_id, type, state_key) ON CONFLICT REPLACE +); + +CREATE INDEX IF NOT EXISTS curr_events_event_id ON current_state_events (event_id); +CREATE INDEX IF NOT EXISTS current_state_events_room_id ON current_state_events (room_id); +CREATE INDEX IF NOT EXISTS current_state_events_type ON current_state_events (type); +CREATE INDEX IF NOT EXISTS current_state_events_state_key ON current_state_events (state_key); + +CREATE TABLE IF NOT EXISTS room_memberships( + event_id TEXT NOT NULL, + user_id TEXT NOT NULL, + sender TEXT NOT NULL, + room_id TEXT NOT NULL, + membership TEXT NOT NULL +); + +CREATE INDEX IF NOT EXISTS room_memberships_event_id ON room_memberships (event_id); +CREATE INDEX IF NOT EXISTS room_memberships_room_id ON room_memberships (room_id); +CREATE INDEX IF NOT EXISTS room_memberships_user_id ON room_memberships (user_id); + +CREATE TABLE IF NOT EXISTS feedback( + event_id TEXT NOT NULL, + feedback_type TEXT, + target_event_id TEXT, + sender TEXT, + room_id TEXT +); + +CREATE TABLE IF NOT EXISTS topics( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + topic TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS room_names( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + name TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS rooms( + room_id TEXT PRIMARY KEY NOT NULL, + is_public INTEGER, + creator TEXT +); + +CREATE TABLE IF NOT EXISTS room_join_rules( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + join_rule TEXT NOT NULL +); +CREATE INDEX IF NOT EXISTS room_join_rules_event_id ON room_join_rules(event_id); +CREATE INDEX IF NOT EXISTS room_join_rules_room_id ON room_join_rules(room_id); + + +CREATE TABLE IF NOT EXISTS room_power_levels( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + user_id TEXT NOT NULL, + level INTEGER NOT NULL +); +CREATE INDEX IF NOT EXISTS room_power_levels_event_id ON room_power_levels(event_id); +CREATE INDEX IF NOT EXISTS room_power_levels_room_id ON room_power_levels(room_id); +CREATE INDEX IF NOT EXISTS room_power_levels_room_user ON room_power_levels(room_id, user_id); + + +CREATE TABLE IF NOT EXISTS room_default_levels( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + level INTEGER NOT NULL +); + +CREATE INDEX IF NOT EXISTS room_default_levels_event_id ON room_default_levels(event_id); +CREATE INDEX IF NOT EXISTS room_default_levels_room_id ON room_default_levels(room_id); + + +CREATE TABLE IF NOT EXISTS room_add_state_levels( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + level INTEGER NOT NULL +); + +CREATE INDEX IF NOT EXISTS room_add_state_levels_event_id ON room_add_state_levels(event_id); +CREATE INDEX IF NOT EXISTS room_add_state_levels_room_id ON room_add_state_levels(room_id); + + +CREATE TABLE IF NOT EXISTS room_send_event_levels( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + level INTEGER NOT NULL +); + +CREATE INDEX IF NOT EXISTS room_send_event_levels_event_id ON room_send_event_levels(event_id); +CREATE INDEX IF NOT EXISTS room_send_event_levels_room_id ON room_send_event_levels(room_id); + + +CREATE TABLE IF NOT EXISTS room_ops_levels( + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + ban_level INTEGER, + kick_level INTEGER +); + +CREATE INDEX IF NOT EXISTS room_ops_levels_event_id ON room_ops_levels(event_id); +CREATE INDEX IF NOT EXISTS room_ops_levels_room_id ON room_ops_levels(room_id); + + +CREATE TABLE IF NOT EXISTS room_hosts( + room_id TEXT NOT NULL, + host TEXT NOT NULL, + CONSTRAINT room_hosts_uniq UNIQUE (room_id, host) ON CONFLICT IGNORE +); + +CREATE INDEX IF NOT EXISTS room_hosts_room_id ON room_hosts (room_id); + +PRAGMA user_version = 2; From 932f35a7f0bb37c6f2279634f74dc6eb8d81b3af Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 15:14:13 +0200 Subject: [PATCH 32/62] Added /deop $user_id --- webclient/components/matrix/matrix-service.js | 29 +++++++++++++++++-- webclient/room/room-controller.js | 10 ++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index bedabe6286..eadbb03d9e 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -522,7 +522,6 @@ angular.module('matrixService', []) return powerLevel; }, - // /** * Change the power level of a user * @param {String} room_id the room id @@ -554,7 +553,33 @@ angular.module('matrixService', []) // The room does not exist or does not contain power_levels data var deferred = $q.defer(); - deferred.reject({data:{error: "Invalied room: " + room_id}}); + deferred.reject({data:{error: "Invalid room: " + room_id}}); + return deferred.promise; + }, + + /** + * reset the power level of a user so that he will use the room default power level + * @param {String} room_id the room id + * @param {String} user_id the user id + * @returns {promise} an $http promise + */ + resetUserPowerLevel: function(room_id, user_id) { + // Hack: currently, there is no home server API so do it by hand by updating + // the current m.room.power_levels of the room and send it to the server + var room = $rootScope.events.rooms[room_id]; + if (room && room["m.room.power_levels"]) { + var content = angular.copy(room["m.room.power_levels"].content); + delete content[user_id]; + + var path = "/rooms/$room_id/state/m.room.power_levels"; + path = path.replace("$room_id", encodeURIComponent(room_id)); + + return doRequest("PUT", path, undefined, content); + } + + // The room does not exist or does not contain power_levels data + var deferred = $q.defer(); + deferred.reject({data:{error: "Invalid room: " + room_id}}); return deferred.promise; } diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index d96241b863..ab8b35961c 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -299,13 +299,21 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) break; case "/op": + // Define the power level of an user if (3 === args.length) { var user_id = args[1]; var powerLevel = parseInt(args[2]); - promise = matrixService.setUserPowerLevel($scope.room_id, user_id, powerLevel); } break; + + case "/deop": + // Reset the power level of an user + if (2 === args.length) { + var user_id = args[1]; + promise = matrixService.resetUserPowerLevel($scope.room_id, user_id); + } + break; } } else { From 4c7da89219894ed565992a4209a23ddcbfa42a33 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 15:18:57 +0200 Subject: [PATCH 33/62] Do not define power level limits in setUserPowerLevel. Update the function to be used as a resetUserPowerLevel --- webclient/components/matrix/matrix-service.js | 36 ++----------------- webclient/room/room-controller.js | 2 +- 2 files changed, 3 insertions(+), 35 deletions(-) diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index eadbb03d9e..6c7f6d366f 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -523,20 +523,14 @@ angular.module('matrixService', []) }, /** - * Change the power level of a user + * Change or reset the power level of a user * @param {String} room_id the room id * @param {String} user_id the user id * @param {Number} powerLevel a value between 0 and 10 + * If undefined, the user power level will be reset, ie he will use the default room user power level * @returns {promise} an $http promise */ setUserPowerLevel: function(room_id, user_id, powerLevel) { - // Sanity check - if (powerLevel < 0 || 10 < powerLevel) { - // Format the error as is it was sent by the server - var deferred = $q.defer(); - deferred.reject({data:{error: "Invalid powerLevel: " + powerLevel}}); - return deferred.promise; - } // Hack: currently, there is no home server API so do it by hand by updating // the current m.room.power_levels of the room and send it to the server @@ -551,32 +545,6 @@ angular.module('matrixService', []) return doRequest("PUT", path, undefined, content); } - // The room does not exist or does not contain power_levels data - var deferred = $q.defer(); - deferred.reject({data:{error: "Invalid room: " + room_id}}); - return deferred.promise; - }, - - /** - * reset the power level of a user so that he will use the room default power level - * @param {String} room_id the room id - * @param {String} user_id the user id - * @returns {promise} an $http promise - */ - resetUserPowerLevel: function(room_id, user_id) { - // Hack: currently, there is no home server API so do it by hand by updating - // the current m.room.power_levels of the room and send it to the server - var room = $rootScope.events.rooms[room_id]; - if (room && room["m.room.power_levels"]) { - var content = angular.copy(room["m.room.power_levels"].content); - delete content[user_id]; - - var path = "/rooms/$room_id/state/m.room.power_levels"; - path = path.replace("$room_id", encodeURIComponent(room_id)); - - return doRequest("PUT", path, undefined, content); - } - // The room does not exist or does not contain power_levels data var deferred = $q.defer(); deferred.reject({data:{error: "Invalid room: " + room_id}}); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index ab8b35961c..77e93e8930 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -311,7 +311,7 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) // Reset the power level of an user if (2 === args.length) { var user_id = args[1]; - promise = matrixService.resetUserPowerLevel($scope.room_id, user_id); + promise = matrixService.setUserPowerLevel($scope.room_id, user_id, undefined); } break; } From c06d07a2769e49cef8619bea6112bd2f938f0f2f Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 15:25:59 +0200 Subject: [PATCH 34/62] Send unrecognized commands as text message (as before) --- webclient/room/room-controller.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 77e93e8930..f07d086e4a 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -316,7 +316,8 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) break; } } - else { + + if (!promise) { // Send the text message promise = matrixService.sendTextMessage($scope.room_id, $scope.textInput); } From 7fc84c7019801f74514cfaeadc2e86508ff55ba3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 14:26:35 +0100 Subject: [PATCH 35/62] Make retrying requests on DNS failures configurable, and turn off retrying only in directory.get_association --- synapse/handlers/directory.py | 6 +++++- synapse/http/client.py | 25 +++++++++++++++++++------ tests/handlers/test_directory.py | 6 +++++- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py index 7c89150d99..2e9c6b688b 100644 --- a/synapse/handlers/directory.py +++ b/synapse/handlers/directory.py @@ -18,6 +18,7 @@ from twisted.internet import defer from ._base import BaseHandler from synapse.api.errors import SynapseError +from synapse.http.client import HttpClient import logging @@ -68,7 +69,10 @@ class DirectoryHandler(BaseHandler): result = yield self.federation.make_query( destination=room_alias.domain, query_type="directory", - args={"room_alias": room_alias.to_string()}, + args={ + "room_alias": room_alias.to_string(), + HttpClient.RETRY_DNS_LOOKUP_FAILURES: False + } ) if result and "room_id" in result and "servers" in result: diff --git a/synapse/http/client.py b/synapse/http/client.py index 709c952226..f74e3eca73 100644 --- a/synapse/http/client.py +++ b/synapse/http/client.py @@ -44,6 +44,7 @@ _destination_mappings = { class HttpClient(object): """ Interface for talking json over http """ + RETRY_DNS_LOOKUP_FAILURES = "__retry_dns" def put_json(self, destination, path, data): """ Sends the specifed json data using PUT @@ -143,13 +144,23 @@ class TwistedHttpClient(HttpClient): destination = _destination_mappings[destination] logger.debug("get_json args: %s", args) + + retry_on_dns_fail = True + if HttpClient.RETRY_DNS_LOOKUP_FAILURES in args: + # FIXME: This isn't ideal, but the interface exposed in get_json + # isn't comprehensive enough to give caller's any control over + # their connection mechanics. + retry_on_dns_fail = args.pop(HttpClient.RETRY_DNS_LOOKUP_FAILURES) + 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 + query_bytes=query_bytes, + retry_on_dns_fail=retry_on_dns_fail ) body = yield readBody(response) @@ -158,7 +169,8 @@ class TwistedHttpClient(HttpClient): @defer.inlineCallbacks def _create_request(self, destination, method, path_bytes, param_bytes=b"", - query_bytes=b"", producer=None, headers_dict={}): + query_bytes=b"", producer=None, headers_dict={}, + retry_on_dns_fail=True): """ Creates and sends a request to the given url """ headers_dict[b"User-Agent"] = [b"Synapse"] @@ -199,11 +211,12 @@ class TwistedHttpClient(HttpClient): logger.debug("Got response to %s", method) break - except DNSLookupError as dns: - logger.warn("DNS Lookup failed to %s with %s", destination, - dns) - raise SynapseError(400, "Domain specified not found.") except Exception as e: + if not retry_on_dns_fail and isinstance(e, DNSLookupError): + logger.warn("DNS Lookup failed to %s with %s", destination, + e) + raise SynapseError(400, "Domain specified not found.") + logger.exception("Got error in _create_request") _print_ex(e) diff --git a/tests/handlers/test_directory.py b/tests/handlers/test_directory.py index 88ac8933f8..e509c1b629 100644 --- a/tests/handlers/test_directory.py +++ b/tests/handlers/test_directory.py @@ -21,6 +21,7 @@ from mock import Mock import logging from synapse.server import HomeServer +from synapse.http.client import HttpClient from synapse.handlers.directory import DirectoryHandler from synapse.storage.directory import RoomAliasMapping @@ -92,7 +93,10 @@ class DirectoryTestCase(unittest.TestCase): self.mock_federation.make_query.assert_called_with( destination="remote", query_type="directory", - args={"room_alias": "#another:remote"} + args={ + "room_alias": "#another:remote", + HttpClient.RETRY_DNS_LOOKUP_FAILURES: False + } ) @defer.inlineCallbacks From 073a42cc9528ae1df07e81ce8fcd301b457230c5 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 14:40:54 +0100 Subject: [PATCH 36/62] Add support for room names --- webclient/app-filter.js | 7 ++++++- .../components/matrix/event-handler-service.js | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/webclient/app-filter.js b/webclient/app-filter.js index e0e8130e45..dccb526a4a 100644 --- a/webclient/app-filter.js +++ b/webclient/app-filter.js @@ -97,7 +97,12 @@ angular.module('matrixWebClient') // Else, build the name from its users var room = $rootScope.events.rooms[room_id]; if (room) { - if (room.members) { + var room_name_event = room["m.room.name"]; + + if (room_name_event) { + roomName = room_name_event.content.name; + } + else if (room.members) { // Limit the room renaming to 1:1 room if (2 === Object.keys(room.members).length) { for (var i in room.members) { diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index 7c62cd1b5d..bf6103d50d 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -34,6 +34,7 @@ angular.module('eventHandlerService', []) var PRESENCE_EVENT = "PRESENCE_EVENT"; var POWERLEVEL_EVENT = "POWERLEVEL_EVENT"; var CALL_EVENT = "CALL_EVENT"; + var NAME_EVENT = "NAME_EVENT"; var InitialSyncDeferred = $q.defer(); @@ -115,6 +116,15 @@ angular.module('eventHandlerService', []) } }; + var handleRoomName = function(event, isLiveEvent) { + console.log("handleRoomName " + isLiveEvent); + + initRoom(event.room_id); + + $rootScope.events.rooms[event.room_id][event.type] = event; + $rootScope.$broadcast(NAME_EVENT, event, isLiveEvent); + }; + var handleCallEvent = function(event, isLiveEvent) { $rootScope.$broadcast(CALL_EVENT, event, isLiveEvent); }; @@ -126,6 +136,7 @@ angular.module('eventHandlerService', []) PRESENCE_EVENT: PRESENCE_EVENT, POWERLEVEL_EVENT: POWERLEVEL_EVENT, CALL_EVENT: CALL_EVENT, + NAME_EVENT: NAME_EVENT, handleEvent: function(event, isLiveEvent) { @@ -149,7 +160,9 @@ angular.module('eventHandlerService', []) case 'm.room.power_levels': handlePowerLevels(event, isLiveEvent); break; - + case 'm.room.name': + handleRoomName(event, isLiveEvent); + break; default: console.log("Unable to handle event type " + event.type); console.log(JSON.stringify(event, undefined, 4)); From 277fd2250ac82d8471090f66f45cb61153bcfc1a Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 15:58:28 +0200 Subject: [PATCH 37/62] Added /unban $user_id --- webclient/room/room-controller.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index f07d086e4a..69305c7fa9 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -298,6 +298,16 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) } break; + case "/unban": + // Unban the user id from the room + if (2 === args.length) { + var user_id = args[1]; + + // Reset the user membership to leave to unban him + promise = matrixService.membershipChange($scope.room_id, user_id, "leave"); + } + break; + case "/op": // Define the power level of an user if (3 === args.length) { From dada11dc5f4e318e13da89f33e5a4dcdf165c08f Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 3 Sep 2014 15:27:58 +0100 Subject: [PATCH 38/62] Bugfix for back-pagination of presence --- synapse/handlers/presence.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index ae4735898e..a88ce61f79 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -840,6 +840,8 @@ class PresenceEventSource(object): from_token = pagination_config.from_token to_token = pagination_config.to_token + observer_user = user + from_key = int(from_token.presence_key) if to_token: From cda31fb7553ba3d880de09a464ae3b62ea6632fc Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 3 Sep 2014 15:37:10 +0100 Subject: [PATCH 39/62] Kill the state ... key from all the Presence messages --- synapse/handlers/presence.py | 31 +++--------------- synapse/rest/presence.py | 6 +--- tests/handlers/test_presence.py | 50 ++++++++++------------------- tests/handlers/test_presencelike.py | 47 ++++++++++++++------------- tests/rest/test_presence.py | 20 +++++++----- 5 files changed, 60 insertions(+), 94 deletions(-) diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index a88ce61f79..e911e18511 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -180,7 +180,7 @@ class PresenceHandler(BaseHandler): state = yield self.store.get_presence_state(target_user.localpart) if "mtime" in state: del state["mtime"] - state["presence"] = state["state"] + state["presence"] = state.pop("state") if target_user in self._user_cachemap: state["last_active"] = ( @@ -213,15 +213,11 @@ class PresenceHandler(BaseHandler): state["status_msg"] = None for k in state.keys(): - if k not in ("presence", "state", "status_msg"): + if k not in ("presence", "status_msg"): raise SynapseError( 400, "Unexpected presence state key '%s'" % (k,) ) - # Handle legacy "state" key for now - if "state" in state: - state["presence"] = state.pop("state") - if state["presence"] not in self.STATE_LEVELS: raise SynapseError(400, "'%s' is not a valid presence state" % state["presence"] @@ -600,7 +596,7 @@ class PresenceHandler(BaseHandler): if state is None: state = yield self.store.get_presence_state(user.localpart) del state["mtime"] - state["presence"] = state["state"] + state["presence"] = state.pop("state") if user in self._user_cachemap: state["last_active"] = ( @@ -621,8 +617,6 @@ class PresenceHandler(BaseHandler): "user_id": user.to_string(), } user_state.update(**state) - if "state" in user_state and "presence" not in user_state: - user_state["presence"] = user_state["state"] yield self.federation.send_edu( destination=destination, @@ -654,21 +648,12 @@ class PresenceHandler(BaseHandler): state = dict(push) del state["user_id"] - if "presence" in state: - # all is OK - pass - elif "state" in state: - # Legacy handling - state["presence"] = state["state"] - else: + if "presence" not in state: logger.warning("Received a presence 'push' EDU from %s without" - + " either a 'presence' or 'state' key", origin + + " a 'presence' key", origin ) continue - if "state" in state: - del state["state"] - if "last_active_ago" in state: state["last_active"] = int( self.clock.time_msec() - state.pop("last_active_ago") @@ -900,7 +885,6 @@ class UserPresenceCache(object): def update(self, state, serial): assert("mtime_age" not in state) - assert("state" not in state) self.state.update(state) # Delete keys that are now 'None' @@ -918,11 +902,6 @@ class UserPresenceCache(object): def get_state(self): # clone it so caller can't break our cache state = dict(self.state) - - # Legacy handling - if "presence" in state: - state["state"] = state["presence"] - return state def make_event(self, user, clock): diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py index 5c5adb4236..9410fcae56 100644 --- a/synapse/rest/presence.py +++ b/synapse/rest/presence.py @@ -51,11 +51,7 @@ class PresenceStatusRestServlet(RestServlet): try: content = json.loads(request.content.read()) - # Legacy handling - if "state" in content: - state["presence"] = content.pop("state") - else: - state["presence"] = content.pop("presence") + state["presence"] = content.pop("presence") if "status_msg" in content: state["status_msg"] = content.pop("status_msg") diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index 8506961d2d..74ac3ea8f5 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -142,7 +142,7 @@ class PresenceStateTestCase(unittest.TestCase): ) self.assertEquals( - {"state": ONLINE, "presence": ONLINE, "status_msg": "Online"}, + {"presence": ONLINE, "status_msg": "Online"}, state ) mocked_get.assert_called_with("apple") @@ -159,7 +159,7 @@ class PresenceStateTestCase(unittest.TestCase): ) self.assertEquals( - {"state": ONLINE, "presence": ONLINE, "status_msg": "Online"}, + {"presence": ONLINE, "status_msg": "Online"}, state ) mocked_get.assert_called_with("apple") @@ -178,7 +178,7 @@ class PresenceStateTestCase(unittest.TestCase): ) self.assertEquals( - {"state": ONLINE, "presence": ONLINE, "status_msg": "Online"}, + {"presence": ONLINE, "status_msg": "Online"}, state ) @@ -208,7 +208,8 @@ class PresenceStateTestCase(unittest.TestCase): state={"presence": UNAVAILABLE, "status_msg": "Away"}) mocked_set.assert_called_with("apple", - {"state": UNAVAILABLE, "status_msg": "Away"}) + {"state": UNAVAILABLE, "status_msg": "Away"} + ) self.mock_start.assert_called_with(self.u_apple, state={ "presence": UNAVAILABLE, @@ -460,8 +461,7 @@ class PresenceInvitesTestCase(unittest.TestCase): self.assertEquals([ {"observed_user": self.u_banana, - "presence": OFFLINE, - "state": OFFLINE}, + "presence": OFFLINE}, ], presence) self.datastore.get_presence_list.assert_called_with("apple", @@ -478,8 +478,7 @@ class PresenceInvitesTestCase(unittest.TestCase): self.assertEquals([ {"observed_user": self.u_banana, - "presence": OFFLINE, - "state": OFFLINE}, + "presence": OFFLINE}, ], presence) self.datastore.get_presence_list.assert_called_with("apple", @@ -625,7 +624,8 @@ class PresencePushTestCase(unittest.TestCase): self.room_members = [self.u_apple, self.u_elderberry] self.datastore.set_presence_state.return_value = defer.succeed( - {"state": ONLINE}) + {"state": ONLINE} + ) # TODO(paul): Gut-wrenching self.handler._user_cachemap[self.u_apple] = UserPresenceCache() @@ -654,7 +654,6 @@ class PresencePushTestCase(unittest.TestCase): "content": { "user_id": "@apple:test", "presence": ONLINE, - "state": ONLINE, "last_active_ago": 0, }}, ], @@ -673,7 +672,6 @@ class PresencePushTestCase(unittest.TestCase): "content": { "user_id": "@apple:test", "presence": ONLINE, - "state": ONLINE, "last_active_ago": 0, }}, ], @@ -692,7 +690,6 @@ class PresencePushTestCase(unittest.TestCase): "content": { "user_id": "@apple:test", "presence": ONLINE, - "state": ONLINE, "last_active_ago": 0, }}, ], @@ -715,11 +712,9 @@ class PresencePushTestCase(unittest.TestCase): self.assertEquals( [ {"observed_user": self.u_banana, - "presence": OFFLINE, - "state": OFFLINE}, + "presence": OFFLINE}, {"observed_user": self.u_clementine, - "presence": OFFLINE, - "state": OFFLINE}, + "presence": OFFLINE}, ], presence ) @@ -740,11 +735,9 @@ class PresencePushTestCase(unittest.TestCase): self.assertEquals([ {"observed_user": self.u_banana, "presence": ONLINE, - "state": ONLINE, "last_active_ago": 2000}, {"observed_user": self.u_clementine, - "presence": OFFLINE, - "state": OFFLINE}, + "presence": OFFLINE}, ], presence) (events, _) = yield self.event_source.get_new_events_for_user( @@ -758,7 +751,6 @@ class PresencePushTestCase(unittest.TestCase): "content": { "user_id": "@banana:test", "presence": ONLINE, - "state": ONLINE, "last_active_ago": 2000 }}, ] @@ -775,7 +767,6 @@ class PresencePushTestCase(unittest.TestCase): "push": [ {"user_id": "@apple:test", "presence": u"online", - "state": u"online", "last_active_ago": 0}, ], } @@ -791,7 +782,6 @@ class PresencePushTestCase(unittest.TestCase): "push": [ {"user_id": "@apple:test", "presence": u"online", - "state": u"online", "last_active_ago": 0}, ], } @@ -837,7 +827,7 @@ class PresencePushTestCase(unittest.TestCase): content={ "push": [ {"user_id": "@potato:remote", - "state": "online", + "presence": "online", "last_active_ago": 1000}, ], } @@ -855,7 +845,6 @@ class PresencePushTestCase(unittest.TestCase): "content": { "user_id": "@potato:remote", "presence": ONLINE, - "state": ONLINE, "last_active_ago": 1000, }} ] @@ -866,7 +855,7 @@ class PresencePushTestCase(unittest.TestCase): state = yield self.handler.get_state(self.u_potato, self.u_apple) self.assertEquals( - {"state": ONLINE, "presence": ONLINE, "last_active_ago": 3000}, + {"presence": ONLINE, "last_active_ago": 3000}, state ) @@ -902,7 +891,6 @@ class PresencePushTestCase(unittest.TestCase): "content": { "user_id": "@clementine:test", "presence": ONLINE, - "state": ONLINE, "last_active_ago": 0, }} ] @@ -919,8 +907,7 @@ class PresencePushTestCase(unittest.TestCase): content={ "push": [ {"user_id": "@apple:test", - "presence": "online", - "state": "online"}, + "presence": "online"}, ], } ), @@ -934,8 +921,7 @@ class PresencePushTestCase(unittest.TestCase): content={ "push": [ {"user_id": "@banana:test", - "presence": "offline", - "state": "offline"}, + "presence": "offline"}, ], } ), @@ -964,8 +950,7 @@ class PresencePushTestCase(unittest.TestCase): content={ "push": [ {"user_id": "@clementine:test", - "presence": "online", - "state": "online"}, + "presence": "online"}, ], } ), @@ -1264,7 +1249,6 @@ class PresencePollingTestCase(unittest.TestCase): "push": [ {"user_id": "@banana:test", "presence": "offline", - "state": "offline", "status_msg": None}, ], }, diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py index 638e506d34..fc0ef8c705 100644 --- a/tests/handlers/test_presencelike.py +++ b/tests/handlers/test_presencelike.py @@ -148,10 +148,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): yield self.handlers.presence_handler.set_state( target_user=self.u_apple, auth_user=self.u_apple, - state={"state": UNAVAILABLE, "status_msg": "Away"}) + state={"presence": UNAVAILABLE, "status_msg": "Away"}) mocked_set.assert_called_with("apple", - {"state": UNAVAILABLE, "status_msg": "Away"}) + {"state": UNAVAILABLE, "status_msg": "Away"} + ) @defer.inlineCallbacks def test_push_local(self): @@ -161,7 +162,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): ] self.datastore.set_presence_state.return_value = defer.succeed( - {"state": ONLINE}) + {"state": ONLINE} + ) # TODO(paul): Gut-wrenching from synapse.handlers.presence import UserPresenceCache @@ -177,9 +179,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): apple_set.add(self.u_clementine) yield self.handlers.presence_handler.set_state(self.u_apple, - self.u_apple, {"state": ONLINE}) + self.u_apple, {"presence": ONLINE} + ) yield self.handlers.presence_handler.set_state(self.u_banana, - self.u_banana, {"state": ONLINE}) + self.u_banana, {"presence": ONLINE} + ) presence = yield self.handlers.presence_handler.get_presence_list( observer_user=self.u_apple, accepted=True) @@ -187,14 +191,12 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): self.assertEquals([ {"observed_user": self.u_banana, "presence": ONLINE, - "state": ONLINE, "last_active_ago": 0, "displayname": "Frank", "avatar_url": "http://foo"}, {"observed_user": self.u_clementine, - "presence": OFFLINE, - "state": OFFLINE}], - presence) + "presence": OFFLINE} + ], presence) self.mock_update_client.assert_has_calls([ call(users_to_push=set([self.u_apple, self.u_banana, self.u_clementine]), @@ -242,7 +244,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): ] self.datastore.set_presence_state.return_value = defer.succeed( - {"state": ONLINE}) + {"state": ONLINE} + ) # TODO(paul): Gut-wrenching from synapse.handlers.presence import UserPresenceCache @@ -257,7 +260,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): apple_set.add(self.u_potato.domain) yield self.handlers.presence_handler.set_state(self.u_apple, - self.u_apple, {"state": ONLINE}) + self.u_apple, {"presence": ONLINE} + ) self.replication.send_edu.assert_called_with( destination="remote", @@ -266,7 +270,6 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): "push": [ {"user_id": "@apple:test", "presence": "online", - "state": "online", "last_active_ago": 0, "displayname": "Frank", "avatar_url": "http://foo"}, @@ -283,18 +286,19 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): # TODO(paul): Gut-wrenching potato_set = self.handlers.presence_handler._remote_recvmap.setdefault( - self.u_potato, set()) + self.u_potato, set() + ) potato_set.add(self.u_apple) yield self.replication.received_edu( - "remote", "m.presence", { - "push": [ - {"user_id": "@potato:remote", - "state": "online", - "displayname": "Frank", - "avatar_url": "http://foo"}, - ], - } + "remote", "m.presence", { + "push": [ + {"user_id": "@potato:remote", + "presence": "online", + "displayname": "Frank", + "avatar_url": "http://foo"}, + ], + } ) self.mock_update_client.assert_called_with( @@ -313,7 +317,6 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase): self.assertEquals( {"presence": ONLINE, - "state": ONLINE, "displayname": "Frank", "avatar_url": "http://foo"}, state) diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py index df8dd74151..f2751f56d3 100644 --- a/tests/rest/test_presence.py +++ b/tests/rest/test_presence.py @@ -99,7 +99,7 @@ class PresenceStateTestCase(unittest.TestCase): self.assertEquals(200, code) self.assertEquals( - {"presence": ONLINE, "state": ONLINE, "status_msg": "Available"}, + {"presence": ONLINE, "status_msg": "Available"}, response ) mocked_get.assert_called_with("apple") @@ -115,7 +115,8 @@ class PresenceStateTestCase(unittest.TestCase): self.assertEquals(200, code) mocked_set.assert_called_with("apple", - {"state": UNAVAILABLE, "status_msg": "Away"}) + {"state": UNAVAILABLE, "status_msg": "Away"} + ) class PresenceListTestCase(unittest.TestCase): @@ -176,7 +177,7 @@ class PresenceListTestCase(unittest.TestCase): self.assertEquals(200, code) self.assertEquals([ - {"user_id": "@banana:test", "presence": OFFLINE, "state": OFFLINE}, + {"user_id": "@banana:test", "presence": OFFLINE}, ], response) self.datastore.get_presence_list.assert_called_with( @@ -311,9 +312,11 @@ class PresenceEventStreamTestCase(unittest.TestCase): self.room_members = [self.u_apple, self.u_banana] self.mock_datastore.set_presence_state.return_value = defer.succeed( - {"state": ONLINE}) + {"state": ONLINE} + ) self.mock_datastore.get_presence_list.return_value = defer.succeed( - []) + [] + ) (code, response) = yield self.mock_resource.trigger("GET", "/events?timeout=0", None) @@ -329,9 +332,11 @@ class PresenceEventStreamTestCase(unittest.TestCase): ) self.mock_datastore.set_presence_state.return_value = defer.succeed( - {"state": ONLINE}) + {"state": ONLINE} + ) self.mock_datastore.get_presence_list.return_value = defer.succeed( - []) + [] + ) yield self.presence.set_state(self.u_banana, self.u_banana, state={"presence": ONLINE} @@ -346,7 +351,6 @@ class PresenceEventStreamTestCase(unittest.TestCase): "content": { "user_id": "@banana:test", "presence": ONLINE, - "state": ONLINE, "displayname": "Frank", "last_active_ago": 0, }}, From e4c40158c5a8c95d6d2727247a3af183dd9a9e8d Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 15:48:51 +0100 Subject: [PATCH 40/62] Snapshot prev_state for generic events --- synapse/handlers/message.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index c9e3c4e451..528e77b438 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -142,7 +142,12 @@ class MessageHandler(BaseRoomHandler): SynapseError if something went wrong. """ - snapshot = yield self.store.snapshot_room(event.room_id, event.user_id) + snapshot = yield self.store.snapshot_room( + event.room_id, + event.user_id, + state_type=event.type, + state_key=event.state_key, + ) yield self.auth.check(event, snapshot, raises=True) From b5f9d47c89a52d46ccbe4cc364d57e93c72fd22c Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 15:50:05 +0100 Subject: [PATCH 41/62] Handle new state events which don't have a common ancestor --- synapse/state.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/synapse/state.py b/synapse/state.py index e1a1a159bb..bb208cafc7 100644 --- a/synapse/state.py +++ b/synapse/state.py @@ -179,6 +179,18 @@ class StateHandler(object): key=lambda x: x.depth ) + if not hasattr(missing_prev, "prev_state_id"): + # FIXME Hmm + # temporary fallback + for algo in conflict_res: + new_res, curr_res = algo(new_branch, current_branch) + + if new_res < curr_res: + defer.returnValue(False) + elif new_res > curr_res: + defer.returnValue(True) + return + pdu_id = missing_prev.prev_state_id origin = missing_prev.prev_state_origin From ede161d2965fa41789c0ef799f154d7a27f60d1c Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 16:59:09 +0200 Subject: [PATCH 42/62] Added /kick $user_id --- webclient/room/room-controller.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 69305c7fa9..00f585b5e2 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -283,8 +283,18 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) } break; + case "/kick": + // Kick a user from the room + if (2 === args.length) { + var user_id = args[1]; + + // Set his state in the room as leave + promise = matrixService.membershipChange($scope.room_id, user_id, "leave"); + } + break; + case "/ban": - // Ban the user id from the room + // Ban a user from the room if (2 <= args.length) { // TODO: The user may have entered the display name // Need display name -> user_id resolution. Pb: how to manage user with same display names? @@ -299,7 +309,7 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) break; case "/unban": - // Unban the user id from the room + // Unban a user from the room if (2 === args.length) { var user_id = args[1]; @@ -309,7 +319,7 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) break; case "/op": - // Define the power level of an user + // Define the power level of a user if (3 === args.length) { var user_id = args[1]; var powerLevel = parseInt(args[2]); @@ -318,7 +328,7 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) break; case "/deop": - // Reset the power level of an user + // Reset the power level of a user if (2 === args.length) { var user_id = args[1]; promise = matrixService.setUserPowerLevel($scope.room_id, user_id, undefined); From 5f7cdbe0b8c6f62ea4092a859f16af1b88078d4a Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 17:01:12 +0200 Subject: [PATCH 43/62] List commands and their usage in the settings page --- webclient/settings/settings.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/webclient/settings/settings.html b/webclient/settings/settings.html index 03927838d2..49dc603540 100644 --- a/webclient/settings/settings.html +++ b/webclient/settings/settings.html @@ -74,6 +74,21 @@
Access token: {{ config.access_token }}

+ +

Commands

+
+ The following commands are available in the room chat: +
    +
  • /nick <display_name>: change your display name
  • +
  • /me <action>: send the action you are doing. /me will be replaced by your display name
  • +
  • /kick <user_id>: kick the user
  • +
  • /ban <user_id> [<reason>]: ban the user
  • +
  • /unban <user_id>: unban the user
  • +
  • /op <user_id> <power_level>: set user power level
  • +
  • /deop <user_id>: reset user power level to the room default value
  • +
+
+
{{ feedback }} From 30bcbc433a4d59ddd0e8c3a268c9d3ce668c74c2 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 16:04:21 +0100 Subject: [PATCH 44/62] Fix up directory server to not require uploading room hosts. Update the room hosts table with the current room hosts (if we have them) on GET. --- synapse/handlers/directory.py | 11 ++++++++++- synapse/rest/directory.py | 2 +- tests/handlers/test_directory.py | 5 +++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py index 2e9c6b688b..22aab9b0a8 100644 --- a/synapse/handlers/directory.py +++ b/synapse/handlers/directory.py @@ -37,7 +37,7 @@ class DirectoryHandler(BaseHandler): ) @defer.inlineCallbacks - def create_association(self, room_alias, room_id, servers): + def create_association(self, room_alias, room_id, servers=None): # TODO(erikj): Do auth. if not room_alias.is_mine: @@ -48,6 +48,12 @@ class DirectoryHandler(BaseHandler): # TODO(erikj): Check if there is a current association. + if not servers: + servers = yield self.store.get_joined_hosts_for_room(room_id) + + if not servers: + raise SynapseError(400, "Failed to get server list") + yield self.store.create_room_alias_association( room_alias, room_id, @@ -83,6 +89,9 @@ class DirectoryHandler(BaseHandler): defer.returnValue({}) return + extra_servers = yield self.store.get_joined_hosts_for_room(room_id) + servers = list(set(extra_servers) | set(servers)) + defer.returnValue({ "room_id": room_id, "servers": servers, diff --git a/synapse/rest/directory.py b/synapse/rest/directory.py index dc347652a0..29ace4f1d3 100644 --- a/synapse/rest/directory.py +++ b/synapse/rest/directory.py @@ -54,7 +54,7 @@ class ClientDirectoryServer(RestServlet): logger.debug("Got room name: %s", room_alias.to_string()) room_id = content["room_id"] - servers = content["servers"] + servers = content["servers"] if "servers" in content else None logger.debug("Got room_id: %s", room_id) logger.debug("Got servers: %s", servers) diff --git a/tests/handlers/test_directory.py b/tests/handlers/test_directory.py index e509c1b629..2361c6d031 100644 --- a/tests/handlers/test_directory.py +++ b/tests/handlers/test_directory.py @@ -50,6 +50,7 @@ class DirectoryTestCase(unittest.TestCase): hs = HomeServer("test", datastore=Mock(spec=[ "get_association_from_room_alias", + "get_joined_hosts_for_room", ]), http_client=None, resource_for_federation=Mock(), @@ -61,6 +62,10 @@ class DirectoryTestCase(unittest.TestCase): self.datastore = hs.get_datastore() + def hosts(room_id): + return defer.succeed([]) + self.datastore.get_joined_hosts_for_room.side_effect = hosts + self.my_room = hs.parse_roomalias("#my-room:test") self.remote_room = hs.parse_roomalias("#another:remote") From 581c54bebef865039352f4ec141c98413cb45f53 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 16:27:01 +0100 Subject: [PATCH 45/62] Add exception handling to directory servlet, so we don't 500. Mark directory API as volatile in the api docs. --- .../swagger_matrix/api-docs-directory | 2 ++ synapse/rest/directory.py | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/client-server/swagger_matrix/api-docs-directory b/docs/client-server/swagger_matrix/api-docs-directory index 380ec3f477..ce12be8c96 100644 --- a/docs/client-server/swagger_matrix/api-docs-directory +++ b/docs/client-server/swagger_matrix/api-docs-directory @@ -13,6 +13,7 @@ { "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": [ @@ -28,6 +29,7 @@ { "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": [ diff --git a/synapse/rest/directory.py b/synapse/rest/directory.py index 29ace4f1d3..d02b15395b 100644 --- a/synapse/rest/directory.py +++ b/synapse/rest/directory.py @@ -16,6 +16,7 @@ from twisted.internet import defer +from synapse.api.errors import SynapseError, Codes from base import RestServlet, client_path_pattern import json @@ -44,8 +45,10 @@ class ClientDirectoryServer(RestServlet): @defer.inlineCallbacks def on_PUT(self, request, room_alias): - # TODO(erikj): Exceptions - content = json.loads(request.content.read()) + content = _parse_json(request) + if not "room_id" in content: + raise SynapseError(400, "Missing room_id key", + errcode=Codes.BAD_JSON) logger.debug("Got content: %s", content) @@ -72,3 +75,14 @@ class ClientDirectoryServer(RestServlet): logger.exception("Failed to create association") defer.returnValue((200, {})) + + +def _parse_json(request): + try: + content = json.loads(request.content.read()) + if type(content) != dict: + raise SynapseError(400, "Content must be a JSON object.", + errcode=Codes.NOT_JSON) + return content + except ValueError: + raise SynapseError(400, "Content not JSON.", errcode=Codes.NOT_JSON) From beaf50f5c603546111b5b25b9c0f22f18f38480b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 16:31:01 +0100 Subject: [PATCH 46/62] Bubble up SynapseErrors so expected failures aren't masked. --- synapse/rest/directory.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/synapse/rest/directory.py b/synapse/rest/directory.py index d02b15395b..65aa2196c2 100644 --- a/synapse/rest/directory.py +++ b/synapse/rest/directory.py @@ -71,6 +71,8 @@ class ClientDirectoryServer(RestServlet): yield dir_handler.create_association( room_alias, room_id, servers ) + except SynapseError as e: + raise e except: logger.exception("Failed to create association") From ee2bcdec653edfc5316164f2a58bda64ed8b761f Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 3 Sep 2014 17:04:00 +0100 Subject: [PATCH 47/62] Limit the size of uploads --- synapse/config/homeserver.py | 3 ++- synapse/config/repository.py | 39 ++++++++++++++++++++++++++++++ synapse/http/content_repository.py | 14 +++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 synapse/config/repository.py diff --git a/synapse/config/homeserver.py b/synapse/config/homeserver.py index a9aa4c735c..1318a0eead 100644 --- a/synapse/config/homeserver.py +++ b/synapse/config/homeserver.py @@ -18,9 +18,10 @@ from .server import ServerConfig from .logger import LoggingConfig from .database import DatabaseConfig from .ratelimiting import RatelimitConfig +from .repository import ContentRepositoryConfig class HomeServerConfig(TlsConfig, ServerConfig, DatabaseConfig, LoggingConfig, - RatelimitConfig): + RatelimitConfig, ContentRepositoryConfig): pass if __name__=='__main__': diff --git a/synapse/config/repository.py b/synapse/config/repository.py new file mode 100644 index 0000000000..407c8d6c24 --- /dev/null +++ b/synapse/config/repository.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright 2014 matrix.org +# +# 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. + +from ._base import Config +import os + +class ContentRepositoryConfig(Config): + def __init__(self, args): + super(ContentRepositoryConfig, self).__init__(args) + self.max_upload_size = self.parse_size(args.max_upload_size) + + def parse_size(self, string): + sizes = {"K": 1024, "M": 1024 * 1024} + size = 1 + suffix = string[-1] + if suffix in sizes: + string = string[:-1] + size = sizes[suffix] + return int(string) * size + + @classmethod + def add_arguments(cls, parser): + super(ContentRepositoryConfig, cls).add_arguments(parser) + db_group = parser.add_argument_group("content_repository") + db_group.add_argument( + "--max-upload-size", default="1M" + ) diff --git a/synapse/http/content_repository.py b/synapse/http/content_repository.py index 5f5cd9b9e0..6a80c5f2c1 100644 --- a/synapse/http/content_repository.py +++ b/synapse/http/content_repository.py @@ -56,6 +56,7 @@ class ContentRepoResource(resource.Resource): self.directory = directory self.auth = auth self.external_addr = external_addr.rstrip('/') + self.max_upload_size = hs.config.max_upload_size if not os.path.isdir(self.directory): os.mkdir(self.directory) @@ -155,6 +156,19 @@ class ContentRepoResource(resource.Resource): @defer.inlineCallbacks def _async_render(self, request): try: + # TODO: The checks here are a bit late. The content will have + # already been uploaded to a tmp file at this point + content_length = request.getHeader("Content-Length") + if content_length is None: + raise SynapseError( + msg="Request must specify a Content-Length", code=400 + ) + if int(content_length) > self.max_upload_size: + raise SynapseError( + msg="Upload request body is too large", + code=413, + ) + fname = yield self.map_request_to_name(request) # TODO I have a suspcious feeling this is just going to block From e3b261b0b7f3e3097270f1a55bf9651fc120ec5b Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 3 Sep 2014 17:26:16 +0100 Subject: [PATCH 48/62] Move securitY threat model docs into specification --- docs/server-server/security-threat-model.rst | 141 ------------------- docs/specification.rst | 124 ++++++++++++++++ 2 files changed, 124 insertions(+), 141 deletions(-) delete mode 100644 docs/server-server/security-threat-model.rst diff --git a/docs/server-server/security-threat-model.rst b/docs/server-server/security-threat-model.rst deleted file mode 100644 index cf0430e43d..0000000000 --- a/docs/server-server/security-threat-model.rst +++ /dev/null @@ -1,141 +0,0 @@ -Overview -======== - -Scope ------ - -This document considers threats specific to the server to server federation -synapse protocol. - - -Attacker --------- - -It is assumed that the attacker can see and manipulate all network traffic -between any of the servers and may be in control of one or more homeservers -participating in the federation protocol. - -Threat Model -============ - -Denial of Service ------------------ - -The attacker could attempt to prevent delivery of messages to or from the -victim in order to: - - * Disrupt service or marketing campaign of a commercial competitor. - * Censor a discussion or censor a participant in a discussion. - * Perform general vandalism. - -Threat: Resource Exhaustion -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could cause the victims server to exhaust a particular resource -(e.g. open TCP connections, CPU, memory, disk storage) - -Threat: Unrecoverable Consistency Violations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could send messages which created an unrecoverable "split-brain" -state in the cluster such that the victim's servers could no longer dervive a -consistent view of the chatroom state. - -Threat: Bad History -~~~~~~~~~~~~~~~~~~~ - -An attacker could convince the victim to accept invalid messages which the -victim would then include in their view of the chatroom history. Other servers -in the chatroom would reject the invalid messages and potentially reject the -victims messages as well since they depended on the invalid messages. - -Threat: Block Network Traffic -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could try to firewall traffic between the victim's server and some -or all of the other servers in the chatroom. - -Threat: High Volume of Messages -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could send large volumes of messages to a chatroom with the victim -making the chatroom unusable. - -Threat: Banning users without necessary authorisation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could attempt to ban a user from a chatroom with the necessary -authorisation. - -Spoofing --------- - -An attacker could try to send a message claiming to be from the victim without -the victim having sent the message in order to: - - * Impersonate the victim while performing illict activity. - * Obtain privileges of the victim. - -Threat: Altering Message Contents -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could try to alter the contents of an existing message from the -victim. - -Threat: Fake Message "origin" Field -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could try to send a new message purporting to be from the victim -with a phony "origin" field. - -Spamming --------- - -The attacker could try to send a high volume of solicicted or unsolicted -messages to the victim in order to: - - * Find victims for scams. - * Market unwanted products. - -Threat: Unsoliticted Messages -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could try to send messages to victims who do not wish to receive -them. - -Threat: Abusive Messages -~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could send abusive or threatening messages to the victim - -Spying ------- - -The attacker could try to access message contents or metadata for messages sent -by the victim or to the victim that were not intended to reach the attacker in -order to: - - * Gain sensitive personal or commercial information. - * Impersonate the victim using credentials contained in the messages. - (e.g. password reset messages) - * Discover who the victim was talking to and when. - -Threat: Disclosure during Transmission -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could try to expose the message contents or metadata during -transmission between the servers. - -Threat: Disclosure to Servers Outside Chatroom -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could try to convince servers within a chatroom to send messages to -a server it controls that was not authorised to be within the chatroom. - -Threat: Disclosure to Servers Within Chatroom -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An attacker could take control of a server within a chatroom to expose message -contents or metadata for messages in that room. - - diff --git a/docs/specification.rst b/docs/specification.rst index c67b4ca2b4..bae18147a8 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -1625,6 +1625,130 @@ SRV Records Security ======== + +Threat Model +------------ + +Denial of Service +~~~~~~~~~~~~~~~~~ + +The attacker could attempt to prevent delivery of messages to or from the +victim in order to: + + * Disrupt service or marketing campaign of a commercial competitor. + * Censor a discussion or censor a participant in a discussion. + * Perform general vandalism. + +Threat: Resource Exhaustion ++++++++++++++++++++++++++++ + +An attacker could cause the victims server to exhaust a particular resource +(e.g. open TCP connections, CPU, memory, disk storage) + +Threat: Unrecoverable Consistency Violations +++++++++++++++++++++++++++++++++++++++++++++ + +An attacker could send messages which created an unrecoverable "split-brain" +state in the cluster such that the victim's servers could no longer dervive a +consistent view of the chatroom state. + +Threat: Bad History ++++++++++++++++++++ + +An attacker could convince the victim to accept invalid messages which the +victim would then include in their view of the chatroom history. Other servers +in the chatroom would reject the invalid messages and potentially reject the +victims messages as well since they depended on the invalid messages. + +Threat: Block Network Traffic ++++++++++++++++++++++++++++++ + +An attacker could try to firewall traffic between the victim's server and some +or all of the other servers in the chatroom. + +Threat: High Volume of Messages ++++++++++++++++++++++++++++++++ + +An attacker could send large volumes of messages to a chatroom with the victim +making the chatroom unusable. + +Threat: Banning users without necessary authorisation ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +An attacker could attempt to ban a user from a chatroom with the necessary +authorisation. + +Spoofing +~~~~~~~~ + +An attacker could try to send a message claiming to be from the victim without +the victim having sent the message in order to: + +* Impersonate the victim while performing illict activity. +* Obtain privileges of the victim. + +Threat: Altering Message Contents ++++++++++++++++++++++++++++++++++ + +An attacker could try to alter the contents of an existing message from the +victim. + +Threat: Fake Message "origin" Field ++++++++++++++++++++++++++++++++++++ + +An attacker could try to send a new message purporting to be from the victim +with a phony "origin" field. + +Spamming +~~~~~~~~ + +The attacker could try to send a high volume of solicicted or unsolicted +messages to the victim in order to: + +* Find victims for scams. +* Market unwanted products. + +Threat: Unsoliticted Messages ++++++++++++++++++++++++++++++ + +An attacker could try to send messages to victims who do not wish to receive +them. + +Threat: Abusive Messages +++++++++++++++++++++++++ + +An attacker could send abusive or threatening messages to the victim + +Spying +~~~~~~ + +The attacker could try to access message contents or metadata for messages sent +by the victim or to the victim that were not intended to reach the attacker in +order to: + +* Gain sensitive personal or commercial information. +* Impersonate the victim using credentials contained in the messages. + (e.g. password reset messages) +* Discover who the victim was talking to and when. + +Threat: Disclosure during Transmission +++++++++++++++++++++++++++++++++++++++ + +An attacker could try to expose the message contents or metadata during +transmission between the servers. + +Threat: Disclosure to Servers Outside Chatroom +++++++++++++++++++++++++++++++++++++++++++++++ + +An attacker could try to convince servers within a chatroom to send messages to +a server it controls that was not authorised to be within the chatroom. + +Threat: Disclosure to Servers Within Chatroom +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +An attacker could take control of a server within a chatroom to expose message +contents or metadata for messages in that room. + .. NOTE:: This section is a work in progress. From f93aba1d66689efa89f11c5230b31b7d04220af2 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 3 Sep 2014 17:28:35 +0100 Subject: [PATCH 49/62] Fix formating for threat model --- docs/specification.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index bae18147a8..e1c83bed78 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -1626,6 +1626,9 @@ SRV Records Security ======== +.. NOTE:: + This section is a work in progress. + Threat Model ------------ @@ -1635,9 +1638,9 @@ Denial of Service The attacker could attempt to prevent delivery of messages to or from the victim in order to: - * Disrupt service or marketing campaign of a commercial competitor. - * Censor a discussion or censor a participant in a discussion. - * Perform general vandalism. +* Disrupt service or marketing campaign of a commercial competitor. +* Censor a discussion or censor a participant in a discussion. +* Perform general vandalism. Threat: Resource Exhaustion +++++++++++++++++++++++++++ @@ -1749,9 +1752,6 @@ Threat: Disclosure to Servers Within Chatroom An attacker could take control of a server within a chatroom to expose message contents or metadata for messages in that room. -.. NOTE:: - This section is a work in progress. - Rate limiting ------------- Home servers SHOULD implement rate limiting to reduce the risk of being overloaded. If a From 8a7c1d6a00db7e22f0c7678fa4647589a0dcdc31 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 3 Sep 2014 17:29:13 +0100 Subject: [PATCH 50/62] fix the copyright holder from matrix.org to OpenMarket Ltd, as matrix.org hasn't been incorporated in time for launch. --- cmdclient/console.py | 2 +- cmdclient/http.py | 2 +- experiments/cursesio.py | 2 +- experiments/test_messaging.py | 2 +- graph/graph.py | 2 +- scripts/copyrighter.pl | 4 ++-- scripts/gendoc.sh | 4 +++- setup.py | 2 +- synapse/__init__.py | 2 +- synapse/api/__init__.py | 2 +- synapse/api/auth.py | 2 +- synapse/api/constants.py | 2 +- synapse/api/errors.py | 2 +- synapse/api/events/__init__.py | 2 +- synapse/api/events/factory.py | 2 +- synapse/api/events/room.py | 2 +- synapse/api/ratelimiting.py | 2 +- synapse/api/urls.py | 2 +- synapse/app/__init__.py | 2 +- synapse/app/homeserver.py | 2 +- synapse/config/__init__.py | 2 +- synapse/config/_base.py | 2 +- synapse/config/database.py | 2 +- synapse/config/homeserver.py | 2 +- synapse/config/logger.py | 2 +- synapse/config/ratelimiting.py | 2 +- synapse/config/server.py | 2 +- synapse/config/tls.py | 2 +- synapse/crypto/__init__.py | 2 +- synapse/crypto/context_factory.py | 2 +- synapse/crypto/keyclient.py | 2 +- synapse/crypto/keyserver.py | 2 +- synapse/crypto/resource/__init__.py | 2 +- synapse/crypto/resource/key.py | 2 +- synapse/federation/__init__.py | 2 +- synapse/federation/pdu_codec.py | 2 +- synapse/federation/persistence.py | 2 +- synapse/federation/replication.py | 2 +- synapse/federation/transport.py | 2 +- synapse/federation/units.py | 2 +- synapse/handlers/__init__.py | 2 +- synapse/handlers/_base.py | 2 +- synapse/handlers/directory.py | 2 +- synapse/handlers/events.py | 2 +- synapse/handlers/federation.py | 2 +- synapse/handlers/login.py | 2 +- synapse/handlers/message.py | 2 +- synapse/handlers/presence.py | 2 +- synapse/handlers/profile.py | 2 +- synapse/handlers/register.py | 2 +- synapse/handlers/room.py | 2 +- synapse/handlers/typing.py | 2 +- synapse/http/__init__.py | 2 +- synapse/http/client.py | 2 +- synapse/http/content_repository.py | 2 +- synapse/http/endpoint.py | 2 +- synapse/http/server.py | 2 +- synapse/notifier.py | 2 +- synapse/rest/__init__.py | 2 +- synapse/rest/base.py | 2 +- synapse/rest/directory.py | 2 +- synapse/rest/events.py | 2 +- synapse/rest/initial_sync.py | 2 +- synapse/rest/login.py | 2 +- synapse/rest/presence.py | 2 +- synapse/rest/profile.py | 2 +- synapse/rest/register.py | 2 +- synapse/rest/room.py | 2 +- synapse/rest/transactions.py | 2 +- synapse/server.py | 2 +- synapse/state.py | 2 +- synapse/storage/__init__.py | 2 +- synapse/storage/_base.py | 2 +- synapse/storage/directory.py | 2 +- synapse/storage/feedback.py | 2 +- synapse/storage/keys.py | 2 +- synapse/storage/pdu.py | 2 +- synapse/storage/presence.py | 2 +- synapse/storage/profile.py | 2 +- synapse/storage/registration.py | 2 +- synapse/storage/room.py | 2 +- synapse/storage/roommember.py | 2 +- synapse/storage/schema/delta/v2.sql | 2 +- synapse/storage/schema/edge_pdus.sql | 2 +- synapse/storage/schema/im.sql | 2 +- synapse/storage/schema/keys.sql | 2 +- synapse/storage/schema/pdu.sql | 2 +- synapse/storage/schema/presence.sql | 2 +- synapse/storage/schema/profiles.sql | 2 +- synapse/storage/schema/room_aliases.sql | 2 +- synapse/storage/schema/transactions.sql | 2 +- synapse/storage/schema/users.sql | 2 +- synapse/storage/stream.py | 2 +- synapse/storage/transactions.py | 2 +- synapse/streams/__init__.py | 2 +- synapse/streams/config.py | 2 +- synapse/streams/events.py | 2 +- synapse/types.py | 2 +- synapse/util/__init__.py | 2 +- synapse/util/async.py | 2 +- synapse/util/distributor.py | 2 +- synapse/util/jsonobject.py | 2 +- synapse/util/lockutils.py | 2 +- synapse/util/logutils.py | 2 +- synapse/util/stringutils.py | 2 +- tests/__init__.py | 2 +- tests/events/__init__.py | 2 +- tests/events/test_events.py | 2 +- tests/federation/test_federation.py | 2 +- tests/federation/test_pdu_codec.py | 2 +- tests/handlers/test_directory.py | 2 +- tests/handlers/test_federation.py | 2 +- tests/handlers/test_presence.py | 2 +- tests/handlers/test_presencelike.py | 2 +- tests/handlers/test_profile.py | 2 +- tests/handlers/test_room.py | 2 +- tests/handlers/test_typing.py | 2 +- tests/rest/__init__.py | 2 +- tests/rest/test_events.py | 2 +- tests/rest/test_presence.py | 2 +- tests/rest/test_profile.py | 2 +- tests/rest/test_rooms.py | 2 +- tests/rest/utils.py | 2 +- tests/storage/test_base.py | 2 +- tests/test_distributor.py | 2 +- tests/test_state.py | 2 +- tests/test_types.py | 2 +- tests/util/__init__.py | 2 +- tests/util/test_lock.py | 2 +- tests/utils.py | 2 +- webclient/app-controller.js | 2 +- webclient/app-directive.js | 2 +- webclient/app-filter.js | 2 +- webclient/app.js | 2 +- webclient/components/fileInput/file-input-directive.js | 2 +- webclient/components/fileUpload/file-upload-service.js | 2 +- webclient/components/matrix/event-handler-service.js | 2 +- webclient/components/matrix/event-stream-service.js | 2 +- webclient/components/matrix/matrix-call.js | 2 +- webclient/components/matrix/matrix-phone-service.js | 2 +- webclient/components/matrix/matrix-service.js | 2 +- webclient/components/matrix/presence-service.js | 2 +- webclient/components/utilities/utilities-service.js | 2 +- webclient/home/home-controller.js | 2 +- webclient/login/login-controller.js | 2 +- webclient/login/register-controller.js | 2 +- webclient/recents/recents-controller.js | 2 +- webclient/recents/recents-filter.js | 2 +- webclient/room/room-controller.js | 2 +- webclient/room/room-directive.js | 2 +- webclient/settings/settings-controller.js | 2 +- webclient/user/user-controller.js | 2 +- 152 files changed, 155 insertions(+), 153 deletions(-) diff --git a/cmdclient/console.py b/cmdclient/console.py index 6d77d64f23..2e6b026762 100755 --- a/cmdclient/console.py +++ b/cmdclient/console.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright 2014 matrix.org +# 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. diff --git a/cmdclient/http.py b/cmdclient/http.py index 9de6be9b72..869f782ec1 100644 --- a/cmdclient/http.py +++ b/cmdclient/http.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/experiments/cursesio.py b/experiments/cursesio.py index 31fbda5504..95d87a1fda 100644 --- a/experiments/cursesio.py +++ b/experiments/cursesio.py @@ -1,4 +1,4 @@ -# Copyright 2014 matrix.org +# 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. diff --git a/experiments/test_messaging.py b/experiments/test_messaging.py index 3ff7ab820f..fedf786cec 100644 --- a/experiments/test_messaging.py +++ b/experiments/test_messaging.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/graph/graph.py b/graph/graph.py index ac06d979e1..b2acadcf5e 100644 --- a/graph/graph.py +++ b/graph/graph.py @@ -1,4 +1,4 @@ -# Copyright 2014 matrix.org +# 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. diff --git a/scripts/copyrighter.pl b/scripts/copyrighter.pl index e476c9cc85..7c03ef21fc 100755 --- a/scripts/copyrighter.pl +++ b/scripts/copyrighter.pl @@ -1,5 +1,5 @@ #!/usr/bin/perl -pi -# Copyright 2014 matrix.org +# 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. @@ -14,7 +14,7 @@ # limitations under the License. $copyright = <##' $MATRIXDO perl -pi -e 's##
[matrix]
#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html -perl -pi -e 's##
#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html \ No newline at end of file +perl -pi -e 's##
#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html + +scp -r $MATRIXDOTORG/docs matrix@ldc-prd-matrix-001:/sites/matrix-beta \ No newline at end of file diff --git a/setup.py b/setup.py index 0eec5c9354..a9470b4c9e 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/__init__.py b/synapse/__init__.py index b45cf47b56..564d789645 100644 --- a/synapse/__init__.py +++ b/synapse/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/__init__.py b/synapse/api/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/synapse/api/__init__.py +++ b/synapse/api/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/auth.py b/synapse/api/auth.py index 54ecbe5b3a..b4eda3df01 100644 --- a/synapse/api/auth.py +++ b/synapse/api/auth.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/constants.py b/synapse/api/constants.py index 668ffa07ca..fcef062fc9 100644 --- a/synapse/api/constants.py +++ b/synapse/api/constants.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/errors.py b/synapse/api/errors.py index ad79bc7ff9..84afe4fa37 100644 --- a/synapse/api/errors.py +++ b/synapse/api/errors.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py index 9502f5df8f..f95468fc65 100644 --- a/synapse/api/events/__init__.py +++ b/synapse/api/events/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py index 159728b2d2..a3b293e024 100644 --- a/synapse/api/events/factory.py +++ b/synapse/api/events/factory.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/events/room.py b/synapse/api/events/room.py index e4a99ff216..33f0f0cb99 100644 --- a/synapse/api/events/room.py +++ b/synapse/api/events/room.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/ratelimiting.py b/synapse/api/ratelimiting.py index b9b6a9de1a..b25358090f 100644 --- a/synapse/api/ratelimiting.py +++ b/synapse/api/ratelimiting.py @@ -1,4 +1,4 @@ -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/api/urls.py b/synapse/api/urls.py index 3d0b5de965..6314f31f7a 100644 --- a/synapse/api/urls.py +++ b/synapse/api/urls.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/app/__init__.py b/synapse/app/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/synapse/app/__init__.py +++ b/synapse/app/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index db2e9ebb0c..49cf928cc1 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/__init__.py b/synapse/config/__init__.py index fe8a073cd3..f9811bfa04 100644 --- a/synapse/config/__init__.py +++ b/synapse/config/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/_base.py b/synapse/config/_base.py index 4b445806da..7dc68230bd 100644 --- a/synapse/config/_base.py +++ b/synapse/config/_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/database.py b/synapse/config/database.py index edf2361914..460445f15d 100644 --- a/synapse/config/database.py +++ b/synapse/config/database.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/homeserver.py b/synapse/config/homeserver.py index 1318a0eead..76e2cdeddd 100644 --- a/synapse/config/homeserver.py +++ b/synapse/config/homeserver.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/logger.py b/synapse/config/logger.py index 8db6621ae8..56cd095433 100644 --- a/synapse/config/logger.py +++ b/synapse/config/logger.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/ratelimiting.py b/synapse/config/ratelimiting.py index ee572e1fbf..f126782b8d 100644 --- a/synapse/config/ratelimiting.py +++ b/synapse/config/ratelimiting.py @@ -1,4 +1,4 @@ -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/server.py b/synapse/config/server.py index cb178435ea..d7f3e9e0b0 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/config/tls.py b/synapse/config/tls.py index 16f6f3aba6..72d5518a89 100644 --- a/synapse/config/tls.py +++ b/synapse/config/tls.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/crypto/__init__.py b/synapse/crypto/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/synapse/crypto/__init__.py +++ b/synapse/crypto/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/crypto/context_factory.py b/synapse/crypto/context_factory.py index 344f2dd218..f86bd19255 100644 --- a/synapse/crypto/context_factory.py +++ b/synapse/crypto/context_factory.py @@ -1,4 +1,4 @@ -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/crypto/keyclient.py b/synapse/crypto/keyclient.py index e615866b68..c11df5c529 100644 --- a/synapse/crypto/keyclient.py +++ b/synapse/crypto/keyclient.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/crypto/keyserver.py b/synapse/crypto/keyserver.py index 3d80a0e660..a23484dbae 100644 --- a/synapse/crypto/keyserver.py +++ b/synapse/crypto/keyserver.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/crypto/resource/__init__.py b/synapse/crypto/resource/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/synapse/crypto/resource/__init__.py +++ b/synapse/crypto/resource/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/crypto/resource/key.py b/synapse/crypto/resource/key.py index 6aecd2b95f..48d14b9f4a 100644 --- a/synapse/crypto/resource/key.py +++ b/synapse/crypto/resource/key.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/federation/__init__.py b/synapse/federation/__init__.py index b15e7cf941..1351b68fd6 100644 --- a/synapse/federation/__init__.py +++ b/synapse/federation/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/federation/pdu_codec.py b/synapse/federation/pdu_codec.py index adc166c564..cef61108dd 100644 --- a/synapse/federation/pdu_codec.py +++ b/synapse/federation/pdu_codec.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/federation/persistence.py b/synapse/federation/persistence.py index 4cf72b2e42..de36a80e41 100644 --- a/synapse/federation/persistence.py +++ b/synapse/federation/persistence.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/federation/replication.py b/synapse/federation/replication.py index 564f629594..e12510017f 100644 --- a/synapse/federation/replication.py +++ b/synapse/federation/replication.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/federation/transport.py b/synapse/federation/transport.py index 50c3df4a5d..6e62ae7c74 100644 --- a/synapse/federation/transport.py +++ b/synapse/federation/transport.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/federation/units.py b/synapse/federation/units.py index b468f70546..9740431279 100644 --- a/synapse/federation/units.py +++ b/synapse/federation/units.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/__init__.py b/synapse/handlers/__init__.py index b2208b26c3..5308e2c8e1 100644 --- a/synapse/handlers/__init__.py +++ b/synapse/handlers/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py index 57429ad2ef..9989fe8670 100644 --- a/synapse/handlers/_base.py +++ b/synapse/handlers/_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py index 22aab9b0a8..1b9e831fc0 100644 --- a/synapse/handlers/directory.py +++ b/synapse/handlers/directory.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/events.py b/synapse/handlers/events.py index 3e9efabd2c..fd24a11fb8 100644 --- a/synapse/handlers/events.py +++ b/synapse/handlers/events.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index eac110419c..84059d8033 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/login.py b/synapse/handlers/login.py index 0220fa0604..6ee7ce5a2d 100644 --- a/synapse/handlers/login.py +++ b/synapse/handlers/login.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 528e77b438..dad2bbd1a4 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py index e911e18511..c79bb6ff76 100644 --- a/synapse/handlers/presence.py +++ b/synapse/handlers/presence.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py index 6799132054..023d8c0cf2 100644 --- a/synapse/handlers/profile.py +++ b/synapse/handlers/profile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py index 593c603346..432bbcdcb5 100644 --- a/synapse/handlers/register.py +++ b/synapse/handlers/register.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 58afced8f5..64956c320c 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/handlers/typing.py b/synapse/handlers/typing.py index 3268427ecd..0ca4e5c31e 100644 --- a/synapse/handlers/typing.py +++ b/synapse/handlers/typing.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/http/__init__.py b/synapse/http/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/synapse/http/__init__.py +++ b/synapse/http/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/http/client.py b/synapse/http/client.py index f74e3eca73..79e17de157 100644 --- a/synapse/http/client.py +++ b/synapse/http/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/http/content_repository.py b/synapse/http/content_repository.py index 6a80c5f2c1..7dd4a859f8 100644 --- a/synapse/http/content_repository.py +++ b/synapse/http/content_repository.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/http/endpoint.py b/synapse/http/endpoint.py index 6c1fdcb853..7018ee3458 100644 --- a/synapse/http/endpoint.py +++ b/synapse/http/endpoint.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/http/server.py b/synapse/http/server.py index 5440a7fdda..8d419c02dd 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/notifier.py b/synapse/notifier.py index 157925d102..5b02c71d1e 100644 --- a/synapse/notifier.py +++ b/synapse/notifier.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/__init__.py b/synapse/rest/__init__.py index f33024e72a..ed785cfbd5 100644 --- a/synapse/rest/__init__.py +++ b/synapse/rest/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/base.py b/synapse/rest/base.py index e855d293e5..2e8e3fa7d4 100644 --- a/synapse/rest/base.py +++ b/synapse/rest/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/directory.py b/synapse/rest/directory.py index 65aa2196c2..18df7c8d8b 100644 --- a/synapse/rest/directory.py +++ b/synapse/rest/directory.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/events.py b/synapse/rest/events.py index 2e7563d14b..7fde143200 100644 --- a/synapse/rest/events.py +++ b/synapse/rest/events.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/initial_sync.py b/synapse/rest/initial_sync.py index d18c4c0f60..a1cb442256 100644 --- a/synapse/rest/initial_sync.py +++ b/synapse/rest/initial_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/login.py b/synapse/rest/login.py index 99e4f10aac..c7bf901c8e 100644 --- a/synapse/rest/login.py +++ b/synapse/rest/login.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py index 9410fcae56..7fc8ce4404 100644 --- a/synapse/rest/presence.py +++ b/synapse/rest/presence.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/profile.py b/synapse/rest/profile.py index c4a0a8d260..2e17f87fa1 100644 --- a/synapse/rest/profile.py +++ b/synapse/rest/profile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/register.py b/synapse/rest/register.py index f17ec11cf4..965d1c452f 100644 --- a/synapse/rest/register.py +++ b/synapse/rest/register.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/room.py b/synapse/rest/room.py index d76a2f5cd4..37b6b8cbc6 100644 --- a/synapse/rest/room.py +++ b/synapse/rest/room.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/rest/transactions.py b/synapse/rest/transactions.py index b8aa1ef11c..e06dcc8c57 100644 --- a/synapse/rest/transactions.py +++ b/synapse/rest/transactions.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/server.py b/synapse/server.py index 35e311a47d..83368ea5a7 100644 --- a/synapse/server.py +++ b/synapse/server.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/state.py b/synapse/state.py index bb208cafc7..36d8210eb5 100644 --- a/synapse/state.py +++ b/synapse/state.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py index aadaab06e7..d97014f4da 100644 --- a/synapse/storage/__init__.py +++ b/synapse/storage/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py index 33d56f47ce..aaa09f47d0 100644 --- a/synapse/storage/_base.py +++ b/synapse/storage/_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/directory.py b/synapse/storage/directory.py index b22ce02f3f..bf55449253 100644 --- a/synapse/storage/directory.py +++ b/synapse/storage/directory.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/feedback.py b/synapse/storage/feedback.py index bac3dea955..8a18617188 100644 --- a/synapse/storage/feedback.py +++ b/synapse/storage/feedback.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/keys.py b/synapse/storage/keys.py index 4d19b9f641..5a38c3e8f2 100644 --- a/synapse/storage/keys.py +++ b/synapse/storage/keys.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/pdu.py b/synapse/storage/pdu.py index 9fd44f2454..0bf97e37ee 100644 --- a/synapse/storage/pdu.py +++ b/synapse/storage/pdu.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/presence.py b/synapse/storage/presence.py index a529104f4d..71b2bb084d 100644 --- a/synapse/storage/presence.py +++ b/synapse/storage/presence.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/profile.py b/synapse/storage/profile.py index 91dd565033..7e1fdd9d88 100644 --- a/synapse/storage/profile.py +++ b/synapse/storage/profile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py index b1e4196435..fd762bc643 100644 --- a/synapse/storage/registration.py +++ b/synapse/storage/registration.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/room.py b/synapse/storage/room.py index 01ae190316..017169ce00 100644 --- a/synapse/storage/room.py +++ b/synapse/storage/room.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py index c380ec0ebd..75c9a60101 100644 --- a/synapse/storage/roommember.py +++ b/synapse/storage/roommember.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/schema/delta/v2.sql b/synapse/storage/schema/delta/v2.sql index ad929b4370..73b140465e 100644 --- a/synapse/storage/schema/delta/v2.sql +++ b/synapse/storage/schema/delta/v2.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/edge_pdus.sql b/synapse/storage/schema/edge_pdus.sql index 17b3c52f0d..8a00868065 100644 --- a/synapse/storage/schema/edge_pdus.sql +++ b/synapse/storage/schema/edge_pdus.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/im.sql b/synapse/storage/schema/im.sql index dbefbbda31..6ffea51310 100644 --- a/synapse/storage/schema/im.sql +++ b/synapse/storage/schema/im.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/keys.sql b/synapse/storage/schema/keys.sql index 45cdbcecae..706a1a03ff 100644 --- a/synapse/storage/schema/keys.sql +++ b/synapse/storage/schema/keys.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/pdu.sql b/synapse/storage/schema/pdu.sql index ca3de005e9..16e111a56c 100644 --- a/synapse/storage/schema/pdu.sql +++ b/synapse/storage/schema/pdu.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/presence.sql b/synapse/storage/schema/presence.sql index b1081d3aab..595b3b5a69 100644 --- a/synapse/storage/schema/presence.sql +++ b/synapse/storage/schema/presence.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/profiles.sql b/synapse/storage/schema/profiles.sql index 1092d7672c..58209f1af0 100644 --- a/synapse/storage/schema/profiles.sql +++ b/synapse/storage/schema/profiles.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/room_aliases.sql b/synapse/storage/schema/room_aliases.sql index d72b79e6ed..9191016814 100644 --- a/synapse/storage/schema/room_aliases.sql +++ b/synapse/storage/schema/room_aliases.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/transactions.sql b/synapse/storage/schema/transactions.sql index 4b1a2368f6..88e3e4e04d 100644 --- a/synapse/storage/schema/transactions.sql +++ b/synapse/storage/schema/transactions.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/schema/users.sql b/synapse/storage/schema/users.sql index 46b60297cb..2519702971 100644 --- a/synapse/storage/schema/users.sql +++ b/synapse/storage/schema/users.sql @@ -1,4 +1,4 @@ -/* Copyright 2014 matrix.org +/* 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. diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py index 0b78222827..2cb0067a67 100644 --- a/synapse/storage/stream.py +++ b/synapse/storage/stream.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/storage/transactions.py b/synapse/storage/transactions.py index a277e4971a..7467e1035b 100644 --- a/synapse/storage/transactions.py +++ b/synapse/storage/transactions.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/streams/__init__.py b/synapse/streams/__init__.py index fe8a073cd3..f9811bfa04 100644 --- a/synapse/streams/__init__.py +++ b/synapse/streams/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/streams/config.py b/synapse/streams/config.py index 01bab568ff..6483ce2e25 100644 --- a/synapse/streams/config.py +++ b/synapse/streams/config.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/streams/events.py b/synapse/streams/events.py index 08d6e6f733..41715436b0 100644 --- a/synapse/streams/events.py +++ b/synapse/streams/events.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/types.py b/synapse/types.py index 1a9dceabf5..c51bc8e4f2 100644 --- a/synapse/types.py +++ b/synapse/types.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/util/__init__.py b/synapse/util/__init__.py index 3ea431a7f9..c9a73b0413 100644 --- a/synapse/util/__init__.py +++ b/synapse/util/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/util/async.py b/synapse/util/async.py index ebbdc00ae1..647ea6142c 100644 --- a/synapse/util/async.py +++ b/synapse/util/async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/util/distributor.py b/synapse/util/distributor.py index 9cffaec8f2..1de50e049f 100644 --- a/synapse/util/distributor.py +++ b/synapse/util/distributor.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/util/jsonobject.py b/synapse/util/jsonobject.py index e2840b59f9..6c99705747 100644 --- a/synapse/util/jsonobject.py +++ b/synapse/util/jsonobject.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/util/lockutils.py b/synapse/util/lockutils.py index d0bb50d035..3a84c09db4 100644 --- a/synapse/util/lockutils.py +++ b/synapse/util/lockutils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/util/logutils.py b/synapse/util/logutils.py index 3d7c46ae44..fadf0bd510 100644 --- a/synapse/util/logutils.py +++ b/synapse/util/logutils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/synapse/util/stringutils.py b/synapse/util/stringutils.py index e1b0796e56..8767e437dd 100644 --- a/synapse/util/stringutils.py +++ b/synapse/util/stringutils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/__init__.py b/tests/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/events/__init__.py b/tests/events/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/tests/events/__init__.py +++ b/tests/events/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/events/test_events.py b/tests/events/test_events.py index 35e9c68f5c..93d5c15c6f 100644 --- a/tests/events/test_events.py +++ b/tests/events/test_events.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/federation/test_federation.py b/tests/federation/test_federation.py index 51308ca358..0b105fe723 100644 --- a/tests/federation/test_federation.py +++ b/tests/federation/test_federation.py @@ -1,4 +1,4 @@ -# Copyright 2014 matrix.org +# 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. diff --git a/tests/federation/test_pdu_codec.py b/tests/federation/test_pdu_codec.py index 2c546040b8..9f74ba119f 100644 --- a/tests/federation/test_pdu_codec.py +++ b/tests/federation/test_pdu_codec.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/handlers/test_directory.py b/tests/handlers/test_directory.py index 2361c6d031..72a2b1443a 100644 --- a/tests/handlers/test_directory.py +++ b/tests/handlers/test_directory.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py index fd19442645..7c4921e226 100644 --- a/tests/handlers/test_federation.py +++ b/tests/handlers/test_federation.py @@ -1,4 +1,4 @@ -# Copyright 2014 matrix.org +# 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. diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py index 74ac3ea8f5..9eb8b6909f 100644 --- a/tests/handlers/test_presence.py +++ b/tests/handlers/test_presence.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py index fc0ef8c705..b35980d948 100644 --- a/tests/handlers/test_presencelike.py +++ b/tests/handlers/test_presencelike.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/handlers/test_profile.py b/tests/handlers/test_profile.py index 87a8139920..8e7a89b479 100644 --- a/tests/handlers/test_profile.py +++ b/tests/handlers/test_profile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/handlers/test_room.py b/tests/handlers/test_room.py index 4591a5ea58..5687bbea0b 100644 --- a/tests/handlers/test_room.py +++ b/tests/handlers/test_room.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py index c3c98074cc..6532ac94a3 100644 --- a/tests/handlers/test_typing.py +++ b/tests/handlers/test_typing.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/rest/__init__.py b/tests/rest/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/tests/rest/__init__.py +++ b/tests/rest/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/rest/test_events.py b/tests/rest/test_events.py index c8527f3517..1dccf4c503 100644 --- a/tests/rest/test_events.py +++ b/tests/rest/test_events.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py index f2751f56d3..a1db0fbcf3 100644 --- a/tests/rest/test_presence.py +++ b/tests/rest/test_presence.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/rest/test_profile.py b/tests/rest/test_profile.py index 24456769c7..f41810df1f 100644 --- a/tests/rest/test_profile.py +++ b/tests/rest/test_profile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/rest/test_rooms.py b/tests/rest/test_rooms.py index 23c50824c7..4ea5828d4f 100644 --- a/tests/rest/test_rooms.py +++ b/tests/rest/test_rooms.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/rest/utils.py b/tests/rest/utils.py index ef9a6071e2..77f5ecf0df 100644 --- a/tests/rest/utils.py +++ b/tests/rest/utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/storage/test_base.py b/tests/storage/test_base.py index 5567480921..330311448d 100644 --- a/tests/storage/test_base.py +++ b/tests/storage/test_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/test_distributor.py b/tests/test_distributor.py index 2869fdfd76..04933f0ecf 100644 --- a/tests/test_distributor.py +++ b/tests/test_distributor.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/test_state.py b/tests/test_state.py index 58fd0bf3be..a1f5ee869b 100644 --- a/tests/test_state.py +++ b/tests/test_state.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/test_types.py b/tests/test_types.py index d2ccbcfa55..571938356c 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/util/__init__.py b/tests/util/__init__.py index 2216c0f1ca..9bff9ec169 100644 --- a/tests/util/__init__.py +++ b/tests/util/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/util/test_lock.py b/tests/util/test_lock.py index dd83d204d9..5623d78423 100644 --- a/tests/util/test_lock.py +++ b/tests/util/test_lock.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/tests/utils.py b/tests/utils.py index aa7e499e15..d90214e418 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2014 matrix.org +# 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. diff --git a/webclient/app-controller.js b/webclient/app-controller.js index 42c45f7c31..36b56aa032 100644 --- a/webclient/app-controller.js +++ b/webclient/app-controller.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/app-directive.js b/webclient/app-directive.js index eee0d3842f..75283598ab 100644 --- a/webclient/app-directive.js +++ b/webclient/app-directive.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/app-filter.js b/webclient/app-filter.js index dccb526a4a..31d4ac4421 100644 --- a/webclient/app-filter.js +++ b/webclient/app-filter.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/app.js b/webclient/app.js index dac4f048cd..d25e2a6234 100644 --- a/webclient/app.js +++ b/webclient/app.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/components/fileInput/file-input-directive.js b/webclient/components/fileInput/file-input-directive.js index c5e4ae07a8..14e2f772f7 100644 --- a/webclient/components/fileInput/file-input-directive.js +++ b/webclient/components/fileInput/file-input-directive.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/components/fileUpload/file-upload-service.js b/webclient/components/fileUpload/file-upload-service.js index 699a3cbffc..e0f67b2c6c 100644 --- a/webclient/components/fileUpload/file-upload-service.js +++ b/webclient/components/fileUpload/file-upload-service.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index bf6103d50d..a2fbad796d 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/components/matrix/event-stream-service.js b/webclient/components/matrix/event-stream-service.js index 441148670e..1c0f7712b4 100644 --- a/webclient/components/matrix/event-stream-service.js +++ b/webclient/components/matrix/event-stream-service.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/components/matrix/matrix-call.js b/webclient/components/matrix/matrix-call.js index 47b63d7f2f..3e13e4e81f 100644 --- a/webclient/components/matrix/matrix-call.js +++ b/webclient/components/matrix/matrix-call.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/components/matrix/matrix-phone-service.js b/webclient/components/matrix/matrix-phone-service.js index d9e2e8baa3..ca86b473e7 100644 --- a/webclient/components/matrix/matrix-phone-service.js +++ b/webclient/components/matrix/matrix-phone-service.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 6c7f6d366f..a0674d06c9 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/components/matrix/presence-service.js b/webclient/components/matrix/presence-service.js index 555118133b..952c8ec8a9 100644 --- a/webclient/components/matrix/presence-service.js +++ b/webclient/components/matrix/presence-service.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/components/utilities/utilities-service.js b/webclient/components/utilities/utilities-service.js index 3df2f04458..b417cc5b39 100644 --- a/webclient/components/utilities/utilities-service.js +++ b/webclient/components/utilities/utilities-service.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/home/home-controller.js b/webclient/home/home-controller.js index f4ce3053ea..11b3682d34 100644 --- a/webclient/home/home-controller.js +++ b/webclient/home/home-controller.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/login/login-controller.js b/webclient/login/login-controller.js index 7369a28ef0..8f8414af2a 100644 --- a/webclient/login/login-controller.js +++ b/webclient/login/login-controller.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/login/register-controller.js b/webclient/login/register-controller.js index 0ece57502b..42b14a3d40 100644 --- a/webclient/login/register-controller.js +++ b/webclient/login/register-controller.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/recents/recents-controller.js b/webclient/recents/recents-controller.js index d7d3bf4053..3209f2cbdf 100644 --- a/webclient/recents/recents-controller.js +++ b/webclient/recents/recents-controller.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/recents/recents-filter.js b/webclient/recents/recents-filter.js index 45653fca96..d80de6fbeb 100644 --- a/webclient/recents/recents-filter.js +++ b/webclient/recents/recents-filter.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 00f585b5e2..a26d1c72db 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/room/room-directive.js b/webclient/room/room-directive.js index 1a99a37abb..659bcbc60f 100644 --- a/webclient/room/room-directive.js +++ b/webclient/room/room-directive.js @@ -1,5 +1,5 @@ /* - Copyright 2014 matrix.org + 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. diff --git a/webclient/settings/settings-controller.js b/webclient/settings/settings-controller.js index dc680ef075..7a26367a1b 100644 --- a/webclient/settings/settings-controller.js +++ b/webclient/settings/settings-controller.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. diff --git a/webclient/user/user-controller.js b/webclient/user/user-controller.js index b5b2d439a2..3940db6683 100644 --- a/webclient/user/user-controller.js +++ b/webclient/user/user-controller.js @@ -1,5 +1,5 @@ /* -Copyright 2014 matrix.org +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. From 5dd1a738f8dcd18176e606249fc461d4197d0d71 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 18:33:36 +0200 Subject: [PATCH 51/62] Use /rooms/$room_id/state/m.room.member/$user_id to change the membership of another user --- webclient/components/matrix/matrix-service.js | 14 +++++++++++++- webclient/room/room-controller.js | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index a0674d06c9..165930fbc1 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -165,13 +165,25 @@ angular.module('matrixService', []) // TODO: Use PUT with transaction IDs return doRequest("POST", path, undefined, data); }, + + // Change the membership of an another user + setMembership: function(room_id, user_id, membershipValue) { + // The REST path spec + var path = "/rooms/$room_id/state/m.room.member/$user_id"; + path = path.replace("$room_id", encodeURIComponent(room_id)); + path = path.replace("$user_id", user_id); + + return doRequest("PUT", path, undefined, { + membership: membershipValue + }); + }, // Bans a user from from a room ban: function(room_id, user_id, reason) { var path = "/rooms/$room_id/ban/"; path = path.replace("$room_id", encodeURIComponent(room_id)); - return doRequest("PUT", path, undefined, { + return doRequest("POST", path, undefined, { user_id: user_id, reason: reason }); diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index a26d1c72db..0ffbe8761f 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -289,7 +289,7 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) var user_id = args[1]; // Set his state in the room as leave - promise = matrixService.membershipChange($scope.room_id, user_id, "leave"); + promise = matrixService.setMembership($scope.room_id, user_id, "leave"); } break; @@ -314,7 +314,7 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) var user_id = args[1]; // Reset the user membership to leave to unban him - promise = matrixService.membershipChange($scope.room_id, user_id, "leave"); + promise = matrixService.setMembership($scope.room_id, user_id, "leave"); } break; From 40814138765a4f1273b50013e3547035c75f8d68 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 3 Sep 2014 17:32:31 +0100 Subject: [PATCH 52/62] Default PID file should be 'homeserver.pid' to match the other 'homeserver.*' naming convention --- synapse/config/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/config/server.py b/synapse/config/server.py index d7f3e9e0b0..516e4cf882 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -58,7 +58,7 @@ class ServerConfig(Config): help="Local interface to listen on") server_group.add_argument("-D", "--daemonize", action='store_true', help="Daemonize the home server") - server_group.add_argument('--pid-file', default="hs.pid", + server_group.add_argument('--pid-file', default="homeserver.pid", help="When running as a daemon, the file to" " store the pid in") server_group.add_argument("--manhole", metavar="PORT", dest="manhole", From 7c4ce957c76a187a5ef8fd79646ce77457137138 Mon Sep 17 00:00:00 2001 From: Mark Haines Date: Wed, 3 Sep 2014 17:37:08 +0100 Subject: [PATCH 53/62] Unindent list in specification to remove blockquote --- docs/specification.rst | 66 ++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index e1c83bed78..c5b6fe29c8 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -30,48 +30,52 @@ ecosystem to communicate with one another. The principles that Matrix attempts to follow are: - - Pragmatic Web-friendly APIs (i.e. JSON over REST) - - Keep It Simple & Stupid +- Pragmatic Web-friendly APIs (i.e. JSON over REST) +- Keep It Simple & Stupid - + provide a simple architecture with minimal third-party dependencies. + + provide a simple architecture with minimal third-party dependencies. - - Fully open: +- Fully open: - + Fully open federation - anyone should be able to participate in the global Matrix network - + Fully open standard - publicly documented standard with no IP or patent licensing encumbrances - + Fully open source reference implementation - liberally-licensed example implementations with no - IP or patent licensing encumbrances + + Fully open federation - anyone should be able to participate in the global + Matrix network + + Fully open standard - publicly documented standard with no IP or patent + licensing encumbrances + + Fully open source reference implementation - liberally-licensed example + implementations with no IP or patent licensing encumbrances - - Empowering the end-user +- Empowering the end-user - + The user should be able to choose the server and clients they use - + The user should be control how private their communication is - + The user should know precisely where their data is stored + + The user should be able to choose the server and clients they use + + The user should be control how private their communication is + + The user should know precisely where their data is stored - - Fully decentralised - no single points of control over conversations or the network as a whole - - Learning from history to avoid repeating it +- Fully decentralised - no single points of control over conversations or the + network as a whole +- Learning from history to avoid repeating it - + Trying to take the best aspects of XMPP, SIP, IRC, SMTP, IMAP and NNTP whilst trying to avoid their failings + + Trying to take the best aspects of XMPP, SIP, IRC, SMTP, IMAP and NNTP + whilst trying to avoid their failings The functionality that Matrix provides includes: - - Creation and management of fully distributed chat rooms with no - single points of control or failure - - Eventually-consistent cryptographically secure synchronisation of room - state across a global open network of federated servers and services - - Sending and receiving extensible messages in a room with (optional) - end-to-end encryption - - Extensible user management (inviting, joining, leaving, kicking, banning) - mediated by a power-level based user privilege system. - - Extensible room state management (room naming, aliasing, topics, bans) - - Extensible user profile management (avatars, displaynames, etc) - - Managing user accounts (registration, login, logout) - - Use of 3rd Party IDs (3PIDs) such as email addresses, phone numbers, - Facebook accounts to authenticate, identify and discover users on Matrix. - - Trusted federation of Identity servers for: +- Creation and management of fully distributed chat rooms with no + single points of control or failure +- Eventually-consistent cryptographically secure synchronisation of room + state across a global open network of federated servers and services +- Sending and receiving extensible messages in a room with (optional) + end-to-end encryption +- Extensible user management (inviting, joining, leaving, kicking, banning) + mediated by a power-level based user privilege system. +- Extensible room state management (room naming, aliasing, topics, bans) +- Extensible user profile management (avatars, displaynames, etc) +- Managing user accounts (registration, login, logout) +- Use of 3rd Party IDs (3PIDs) such as email addresses, phone numbers, + Facebook accounts to authenticate, identify and discover users on Matrix. +- Trusted federation of Identity servers for: - + Publishing user public keys for PKI - + Mapping of 3PIDs to Matrix IDs + + Publishing user public keys for PKI + + Mapping of 3PIDs to Matrix IDs The end goal of Matrix is to be a ubiquitous messaging layer for synchronising arbitrary data between sets of people, devices and services - be that for instant From 1b491e50c9ea7bd389d558c89b30540b386dcea5 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 17:46:37 +0100 Subject: [PATCH 54/62] Implement a kick api --- synapse/rest/room.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/synapse/rest/room.py b/synapse/rest/room.py index 37b6b8cbc6..308b447090 100644 --- a/synapse/rest/room.py +++ b/synapse/rest/room.py @@ -388,7 +388,7 @@ class RoomMembershipRestServlet(RestServlet): def register(self, http_server): # /rooms/$roomid/[invite|join|leave] PATTERN = ("/rooms/(?P[^/]*)/" + - "(?Pjoin|invite|leave|ban)") + "(?Pjoin|invite|leave|ban|kick)") register_txn_path(self, PATTERN, http_server) @defer.inlineCallbacks @@ -399,11 +399,14 @@ class RoomMembershipRestServlet(RestServlet): # target user is you unless it is an invite state_key = user.to_string() - if membership_action in ["invite", "ban"]: + if membership_action in ["invite", "ban", "kick"]: if "user_id" not in content: raise SynapseError(400, "Missing user_id key.") state_key = content["user_id"] + if membership_action == "kick": + membership_action = "leave" + event = self.event_factory.create_event( etype=RoomMemberEvent.TYPE, content={"membership": unicode(membership_action)}, From 79bf9d25db8758a0368c0de568918cc72074965c Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 17:48:01 +0100 Subject: [PATCH 55/62] Added more terms. --- docs/specification.rst | 72 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index c5b6fe29c8..d331f36b4b 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -1809,12 +1809,76 @@ Glossary .. NOTE:: This section is a work in progress. -.. TODO - - domain specific words/acronyms with definitions +Backfilling: + The process of synchronising historic state from one home server to another, + to backfill the event storage so that scrollback can be presented to the + client(s). Not to be confused with pagination. + +Context: + A single human-level entity of interest (currently, a chat room) + +EDU (Ephemeral Data Unit): + A message that relates directly to a given pair of home servers that are + exchanging it. EDUs are short-lived messages that related only to one single + pair of servers; they are not persisted for a long time and are not forwarded + on to other servers. Because of this, they have no internal ID nor previous + EDUs reference chain. + +Event: + A record of activity that records a single thing that happened on to a context + (currently, a chat room). These are the "chat messages" that Synapse makes + available. + +PDU (Persistent Data Unit): + A message that relates to a single context, irrespective of the server that + is communicating it. PDUs either encode a single Event, or a single State + change. A PDU is referred to by its PDU ID; the pair of its origin server + and local reference from that server. + +PDU ID: + The pair of PDU Origin and PDU Reference, that together globally uniquely + refers to a specific PDU. + +PDU Origin: + The name of the origin server that generated a given PDU. This may not be the + server from which it has been received, due to the way they are copied around + from server to server. The origin always records the original server that + created it. + +PDU Reference: + A local ID used to refer to a specific PDU from a given origin server. These + references are opaque at the protocol level, but may optionally have some + structured meaning within a given origin server or implementation. + +Presence: + The concept of whether a user is currently online, how available they declare + they are, and so on. See also: doc/model/presence + +Profile: + A set of metadata about a user, such as a display name, provided for the + benefit of other users. See also: doc/model/profiles + +Room ID: + An opaque string (of as-yet undecided format) that identifies a particular + room and used in PDUs referring to it. + +Room Alias: + A human-readable string of the form #name:some.domain that users can use as a + pointer to identify a room; a Directory Server will map this to its Room ID + +State: + A set of metadata maintained about a Context, which is replicated among the + servers in addition to the history of Events. User ID: - An opaque ID which identifies an end-user, which consists of some opaque - localpart combined with the domain name of their home server. + A string of the form @localpart:domain.name that identifies a user for + wire-protocol purposes. The localpart is meaningless outside of a particular + home server. This takes a human-readable form that end-users can use directly + if they so wish, avoiding the 3PIDs. + +Transaction: + A message which relates to the communication between a given pair of servers. + A transaction contains possibly-empty lists of PDUs and EDUs. .. Links through the external API docs are below From 1a32b1f002bcc436070bacd931e6abbd40a793d1 Mon Sep 17 00:00:00 2001 From: Emmanuel ROHEE Date: Wed, 3 Sep 2014 18:55:27 +0200 Subject: [PATCH 56/62] Normalise users power levels so that the user with the higher power level will have a bar covering 100% of the width of his avatar --- webclient/room/room-controller.js | 23 +++++++++++++++++++++++ webclient/room/room.html | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js index 0ffbe8761f..52c57856ee 100644 --- a/webclient/room/room-controller.js +++ b/webclient/room/room-controller.js @@ -253,6 +253,29 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput']) var member = $scope.members[user_id]; if (member) { member.powerLevel = matrixService.getUserPowerLevel($scope.room_id, user_id); + + normaliseMembersPowerLevels(); + } + } + + // Normalise users power levels so that the user with the higher power level + // will have a bar covering 100% of the width of his avatar + var normaliseMembersPowerLevels = function() { + // Find the max power level + var maxPowerLevel = 0; + for (var i in $scope.members) { + var member = $scope.members[i]; + if (member.powerLevel) { + maxPowerLevel = Math.max(maxPowerLevel, member.powerLevel); + } + } + + // Normalized them on a 0..100% scale to be use in css width + if (maxPowerLevel) { + for (var i in $scope.members) { + var member = $scope.members[i]; + member.powerLevelNorm = (member.powerLevel * 100) / maxPowerLevel; + } } } diff --git a/webclient/room/room.html b/webclient/room/room.html index 05e87387a5..e29f511ecf 100644 --- a/webclient/room/room.html +++ b/webclient/room/room.html @@ -24,7 +24,7 @@ title="{{ member.id }}" width="80" height="80"/> -
+
{{ member.displayname || member.id.substr(0, member.id.indexOf(':')) }}
{{ member.displayname ? "" : member.id.substr(member.id.indexOf(':')) }}
From f68dbbd3dad4fd589197d3a3f55136eef2b43acd Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 17:59:54 +0100 Subject: [PATCH 57/62] More explanation of federation keys. --- docs/specification.rst | 132 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 124 insertions(+), 8 deletions(-) diff --git a/docs/specification.rst b/docs/specification.rst index d331f36b4b..44eddcaf87 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -1510,6 +1510,31 @@ Each transaction has: - An origin and destination server name. - A list of "previous IDs". - A list of PDUs and EDUs - the actual message payload that the Transaction carries. + +``origin`` + Type: + String + Description: + DNS name of homeserver making this transaction. + +``ts`` + Type: + Integer + Description: + Timestamp in milliseconds on originating homeserver when this transaction + started. + +``previous_ids`` + Type: + List of strings + Description: + List of transactions that were sent immediately prior to this transaction. + +``pdus`` + Type: + List of Objects. + Description: + List of updates contained in this transaction. :: @@ -1551,8 +1576,98 @@ All PDUs have: - A list of other PDU IDs that have been seen recently on that context (regardless of which origin sent them) -[[TODO(paul): Update this structure so that 'pdu_id' is a two-element -[origin,ref] pair like the prev_pdus are]] +``context`` + Type: + String + Description: + Event context identifier + +``origin`` + Type: + String + Description: + DNS name of homeserver that created this PDU. + +``pdu_id`` + Type: + String + Description: + Unique identifier for PDU within the context for the originating homeserver + +``ts`` + Type: + Integer + Description: + Timestamp in milliseconds on originating homeserver when this PDU was created. + +``pdu_type`` + Type: + String + Description: + PDU event type. + +``prev_pdus`` + Type: + List of pairs of strings + Description: + The originating homeserver and PDU ids of the most recent PDUs the + homeserver was aware of for this context when it made this PDU. + +``depth`` + Type: + Integer + Description: + The maximum depth of the previous PDUs plus one. + + +.. TODO paul + [[TODO(paul): Update this structure so that 'pdu_id' is a two-element + [origin,ref] pair like the prev_pdus are]] + + +For state updates: + +``is_state`` + Type: + Boolean + Description: + True if this PDU is updating state. + +``state_key`` + Type: + String + Description: + Optional key identifying the updated state within the context. + +``power_level`` + Type: + Integer + Description: + The asserted power level of the user performing the update. + +``min_update`` + Type: + Integer + Description: + The required power level needed to replace this update. + +``prev_state_id`` + Type: + String + Description: + PDU event type. + +``prev_state_origin`` + Type: + String + Description: + The PDU id of the update this replaces. + +``user`` + Type: + String + Description: + The user updating the state. :: @@ -1593,12 +1708,13 @@ keys exist to support this: "prev_state_id":TODO "prev_state_origin":TODO} -[[TODO(paul): At this point we should probably have a long description of how -State management works, with descriptions of clobbering rules, power levels, etc -etc... But some of that detail is rather up-in-the-air, on the whiteboard, and -so on. This part needs refining. And writing in its own document as the details -relate to the server/system as a whole, not specifically to server-server -federation.]] +.. TODO paul + [[TODO(paul): At this point we should probably have a long description of how + State management works, with descriptions of clobbering rules, power levels, etc + etc... But some of that detail is rather up-in-the-air, on the whiteboard, and + so on. This part needs refining. And writing in its own document as the details + relate to the server/system as a whole, not specifically to server-server + federation.]] EDUs, by comparison to PDUs, do not have an ID, a context, or a list of "previous" IDs. The only mandatory fields for these are the type, origin and From 5391ccdfe67fa21493f5402830e7e494a9bc4787 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 18:02:33 +0100 Subject: [PATCH 58/62] Marked docs/client-server/specification.rst as old. --- .../{specification.rst => OLD_specification.rst} | 14 ++++++++++++++ 1 file changed, 14 insertions(+) rename docs/client-server/{specification.rst => OLD_specification.rst} (99%) diff --git a/docs/client-server/specification.rst b/docs/client-server/OLD_specification.rst similarity index 99% rename from docs/client-server/specification.rst rename to docs/client-server/OLD_specification.rst index 2f6645ceb9..47fba5eeac 100644 --- a/docs/client-server/specification.rst +++ b/docs/client-server/OLD_specification.rst @@ -2,6 +2,20 @@ Matrix Client-Server API ======================== + +.. WARNING:: + This specification is old. Please see /docs/specification.rst instead. + + + + + + + + + + + The following specification outlines how a client can send and receive data from a home server. From 0d75b9fa96250cb6ee0759f7d23806dde0c50302 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 18:02:51 +0100 Subject: [PATCH 59/62] Fix a few cases where we used user_id instead of state_key --- webclient/app-filter.js | 10 +++++----- webclient/components/matrix/event-handler-service.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/webclient/app-filter.js b/webclient/app-filter.js index 31d4ac4421..27f435674f 100644 --- a/webclient/app-filter.js +++ b/webclient/app-filter.js @@ -107,18 +107,18 @@ angular.module('matrixWebClient') if (2 === Object.keys(room.members).length) { for (var i in room.members) { var member = room.members[i]; - if (member.user_id !== matrixService.config().user_id) { + if (member.state_key !== matrixService.config().user_id) { - if (member.user_id in $rootScope.presence) { + if (member.state_key in $rootScope.presence) { // If the user is available in presence, use the displayname there // as it is the most uptodate - roomName = $rootScope.presence[member.user_id].content.displayname; + roomName = $rootScope.presence[member.state_key].content.displayname; } else if (member.content.displayname) { roomName = member.content.displayname; } else { - roomName = member.user_id; + roomName = member.state_key; } } } @@ -145,7 +145,7 @@ angular.module('matrixWebClient') roomName = $rootScope.presence[userID].content.displayname; } else { - roomName = member.user_id; + roomName = userID; } } } diff --git a/webclient/components/matrix/event-handler-service.js b/webclient/components/matrix/event-handler-service.js index a2fbad796d..ee478d2eb0 100644 --- a/webclient/components/matrix/event-handler-service.js +++ b/webclient/components/matrix/event-handler-service.js @@ -97,7 +97,7 @@ angular.module('eventHandlerService', []) } } - $rootScope.events.rooms[event.room_id].members[event.user_id] = event; + $rootScope.events.rooms[event.room_id].members[event.state_key] = event; $rootScope.$broadcast(MEMBER_EVENT, event, isLiveEvent); }; From f196d77f6602fc61fa6df3790d217aa2c3b9c5f3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 3 Sep 2014 18:07:36 +0100 Subject: [PATCH 60/62] Added federation protocol urls section from other docs. --- docs/specification.rst | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/docs/specification.rst b/docs/specification.rst index 44eddcaf87..239e51b4f3 100644 --- a/docs/specification.rst +++ b/docs/specification.rst @@ -1726,6 +1726,79 @@ destination home server names, and the actual nested content. "origin":"blue", "destination":"orange", "content":...} + + +Protocol URLs +============= +.. WARNING:: + This section may be misleading or inaccurate. + +All these URLs are namespaced within a prefix of:: + + /_matrix/federation/v1/... + +For active pushing of messages representing live activity "as it happens":: + + PUT .../send/:transaction_id/ + Body: JSON encoding of a single Transaction + Response: TODO + +The transaction_id path argument will override any ID given in the JSON body. +The destination name will be set to that of the receiving server itself. Each +embedded PDU in the transaction body will be processed. + + +To fetch a particular PDU:: + + GET .../pdu/:origin/:pdu_id/ + Response: JSON encoding of a single Transaction containing one PDU + +Retrieves a given PDU from the server. The response will contain a single new +Transaction, inside which will be the requested PDU. + + +To fetch all the state of a given context:: + + GET .../state/:context/ + Response: JSON encoding of a single Transaction containing multiple PDUs + +Retrieves a snapshot of the entire current state of the given context. The +response will contain a single Transaction, inside which will be a list of +PDUs that encode the state. + +To backfill events on a given context:: + + GET .../backfill/:context/ + Query args: v, limit + Response: JSON encoding of a single Transaction containing multiple PDUs + +Retrieves a sliding-window history of previous PDUs that occurred on the +given context. Starting from the PDU ID(s) given in the "v" argument, the +PDUs that preceeded it are retrieved, up to a total number given by the +"limit" argument. These are then returned in a new Transaction containing all +off the PDUs. + + +To stream events all the events:: + + GET .../pull/ + Query args: origin, v + Response: JSON encoding of a single Transaction consisting of multiple PDUs + +Retrieves all of the transactions later than any version given by the "v" +arguments. + + +To make a query:: + + GET .../query/:query_type + Query args: as specified by the individual query types + Response: JSON encoding of a response object + +Performs a single query request on the receiving home server. The Query Type +part of the path specifies the kind of query being made, and its query +arguments have a meaning specific to that kind of query. The response is a +JSON-encoded object whose meaning also depends on the kind of query. Backfilling ----------- From d703e712f7b6f6741010360617d88dfb067b349d Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Wed, 3 Sep 2014 18:18:35 +0100 Subject: [PATCH 61/62] Add support to _simple_insert() to do INSERT OR REPLACE --- synapse/storage/_base.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py index aaa09f47d0..bae50e7d1f 100644 --- a/synapse/storage/_base.py +++ b/synapse/storage/_base.py @@ -79,19 +79,21 @@ class SQLBaseStore(object): # "Simple" SQL API methods that operate on a single table with no JOINs, # no complex WHERE clauses, just a dict of values for columns. - def _simple_insert(self, table, values): + def _simple_insert(self, table, values, or_replace=False): """Executes an INSERT query on the named table. Args: table : string giving the table name values : dict of new column names and values for them + or_replace : bool; if True performs an INSERT OR REPLACE """ return self._db_pool.runInteraction( - self._simple_insert_txn, table, values, + self._simple_insert_txn, table, values, or_replace=or_replace ) - def _simple_insert_txn(self, txn, table, values): - sql = "INSERT INTO %s (%s) VALUES(%s)" % ( + def _simple_insert_txn(self, txn, table, values, or_replace=False): + sql = "%s INTO %s (%s) VALUES(%s)" % ( + ("INSERT OR REPLACE" if or_replace else "INSERT"), table, ", ".join(k for k in values), ", ".join("?" for k in values) From 66a5bc4fad9c60846b467389e2ae83067b6bb23c Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Wed, 3 Sep 2014 18:19:26 +0100 Subject: [PATCH 62/62] Fix ban path --- webclient/components/matrix/matrix-service.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js index 165930fbc1..9ca4135f7f 100644 --- a/webclient/components/matrix/matrix-service.js +++ b/webclient/components/matrix/matrix-service.js @@ -180,7 +180,7 @@ angular.module('matrixService', []) // Bans a user from from a room ban: function(room_id, user_id, reason) { - var path = "/rooms/$room_id/ban/"; + var path = "/rooms/$room_id/ban"; path = path.replace("$room_id", encodeURIComponent(room_id)); return doRequest("POST", path, undefined, {