diff options
Diffstat (limited to 'synapse')
-rw-r--r-- | synapse/__init__.py | 2 | ||||
-rwxr-xr-x | synapse/app/homeserver.py | 8 | ||||
-rw-r--r-- | synapse/config/logger.py | 5 | ||||
-rw-r--r-- | synapse/config/server.py | 8 | ||||
-rw-r--r-- | synapse/events/utils.py | 10 | ||||
-rw-r--r-- | synapse/handlers/events.py | 7 | ||||
-rw-r--r-- | synapse/handlers/federation.py | 6 | ||||
-rw-r--r-- | synapse/handlers/message.py | 15 | ||||
-rw-r--r-- | synapse/handlers/room.py | 17 | ||||
-rw-r--r-- | synapse/handlers/typing.py | 8 | ||||
-rw-r--r-- | synapse/media/v1/__init__.py | 6 | ||||
-rw-r--r-- | synapse/media/v1/thumbnailer.py | 2 | ||||
-rw-r--r-- | synapse/python_dependencies.py | 80 | ||||
-rw-r--r-- | synapse/rest/events.py | 5 | ||||
-rw-r--r-- | synapse/rest/initial_sync.py | 5 | ||||
-rw-r--r-- | synapse/rest/room.py | 5 | ||||
-rw-r--r-- | synapse/server.py | 4 | ||||
-rw-r--r-- | synapse/storage/stream.py | 8 |
18 files changed, 174 insertions, 27 deletions
diff --git a/synapse/__init__.py b/synapse/__init__.py index e7e27b06ec..895a0766d2 100644 --- a/synapse/__init__.py +++ b/synapse/__init__.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -""" This is a reference implementation of a synapse home server. +""" This is a reference implementation of a Matrix home server. """ __version__ = "0.6.1b" diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py index 5fec8da7ca..43b5c26144 100755 --- a/synapse/app/homeserver.py +++ b/synapse/app/homeserver.py @@ -18,6 +18,8 @@ from synapse.storage import prepare_database, UpgradeDatabaseException from synapse.server import HomeServer +from synapse.python_dependencies import check_requirements + from twisted.internet import reactor from twisted.enterprise import adbapi from twisted.web.resource import Resource @@ -39,6 +41,8 @@ from synapse.util.logcontext import LoggingContext from daemonize import Daemonize import twisted.manhole.telnet +import synapse + import logging import os import re @@ -198,7 +202,10 @@ def setup(): config.setup_logging() + check_requirements() + logger.info("Server hostname: %s", config.server_name) + logger.info("Server version: %s", synapse.__version__) if re.search(":[0-9]+$", config.server_name): domain_with_port = config.server_name @@ -277,6 +284,7 @@ def run(): def main(): with LoggingContext("main"): + check_requirements() setup() diff --git a/synapse/config/logger.py b/synapse/config/logger.py index 15383b3184..f9568ebd21 100644 --- a/synapse/config/logger.py +++ b/synapse/config/logger.py @@ -66,7 +66,10 @@ class LoggingConfig(Config): formatter = logging.Formatter(log_format) if self.log_file: - handler = logging.FileHandler(self.log_file) + # TODO: Customisable file size / backup count + handler = logging.handlers.RotatingFileHandler( + self.log_file, maxBytes=(1000 * 1000 * 100), backupCount=3 + ) else: handler = logging.StreamHandler() handler.setFormatter(formatter) diff --git a/synapse/config/server.py b/synapse/config/server.py index 4f73c85466..31e44cc857 100644 --- a/synapse/config/server.py +++ b/synapse/config/server.py @@ -47,8 +47,12 @@ class ServerConfig(Config): def add_arguments(cls, parser): super(ServerConfig, cls).add_arguments(parser) server_group = parser.add_argument_group("server") - server_group.add_argument("-H", "--server-name", default="localhost", - help="The name of the server") + server_group.add_argument( + "-H", "--server-name", default="localhost", + help="The domain name of the server, with optional explicit port. " + "This is used by remote servers to connect to this server, " + "e.g. matrix.org, localhost:8080, etc." + ) server_group.add_argument("--signing-key-path", help="The signing key to sign messages with") server_group.add_argument("-p", "--bind-port", metavar="PORT", diff --git a/synapse/events/utils.py b/synapse/events/utils.py index 4ad37188ba..bcb5457278 100644 --- a/synapse/events/utils.py +++ b/synapse/events/utils.py @@ -89,13 +89,21 @@ def prune_event(event): return type(event)(allowed_fields) -def serialize_event(hs, e): +def serialize_event(hs, e, client_event=True): # FIXME(erikj): To handle the case of presence events and the like if not isinstance(e, EventBase): return e # Should this strip out None's? d = {k: v for k, v in e.get_dict().items()} + + if not client_event: + # set the age and keep all other keys + if "age_ts" in d["unsigned"]: + now = int(hs.get_clock().time_msec()) + d["unsigned"]["age"] = now - d["unsigned"]["age_ts"] + return d + if "age_ts" in d["unsigned"]: now = int(hs.get_clock().time_msec()) d["age"] = now - d["unsigned"]["age_ts"] diff --git a/synapse/handlers/events.py b/synapse/handlers/events.py index 808219bd10..c9ade253dd 100644 --- a/synapse/handlers/events.py +++ b/synapse/handlers/events.py @@ -46,7 +46,8 @@ class EventStreamHandler(BaseHandler): @defer.inlineCallbacks @log_function - def get_stream(self, auth_user_id, pagin_config, timeout=0): + def get_stream(self, auth_user_id, pagin_config, timeout=0, + as_client_event=True): auth_user = self.hs.parse_userid(auth_user_id) try: @@ -78,7 +79,9 @@ class EventStreamHandler(BaseHandler): auth_user, room_ids, pagin_config, timeout ) - chunks = [self.hs.serialize_event(e) for e in events] + chunks = [ + self.hs.serialize_event(e, as_client_event) for e in events + ] chunk = { "chunk": chunks, diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py index d26975a88a..195f7c618a 100644 --- a/synapse/handlers/federation.py +++ b/synapse/handlers/federation.py @@ -617,13 +617,13 @@ class FederationHandler(BaseHandler): @defer.inlineCallbacks @log_function - def on_backfill_request(self, origin, context, pdu_list, limit): - in_room = yield self.auth.check_host_in_room(context, origin) + def on_backfill_request(self, origin, room_id, pdu_list, limit): + in_room = yield self.auth.check_host_in_room(room_id, origin) if not in_room: raise AuthError(403, "Host not in room.") events = yield self.store.get_backfill_events( - context, + room_id, pdu_list, limit ) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 7195de98b5..f2a2f16933 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -67,7 +67,7 @@ class MessageHandler(BaseHandler): @defer.inlineCallbacks def get_messages(self, user_id=None, room_id=None, pagin_config=None, - feedback=False): + feedback=False, as_client_event=True): """Get messages in a room. Args: @@ -76,6 +76,7 @@ class MessageHandler(BaseHandler): pagin_config (synapse.api.streams.PaginationConfig): The pagination config rules to apply, if any. feedback (bool): True to get compressed feedback with the messages + as_client_event (bool): True to get events in client-server format. Returns: dict: Pagination API results """ @@ -99,7 +100,9 @@ class MessageHandler(BaseHandler): ) chunk = { - "chunk": [self.hs.serialize_event(e) for e in events], + "chunk": [ + self.hs.serialize_event(e, as_client_event) for e in events + ], "start": pagin_config.from_token.to_string(), "end": next_token.to_string(), } @@ -211,7 +214,7 @@ class MessageHandler(BaseHandler): @defer.inlineCallbacks def snapshot_all_rooms(self, user_id=None, pagin_config=None, - feedback=False): + feedback=False, as_client_event=True): """Retrieve a snapshot of all rooms the user is invited or has joined. This snapshot may include messages for all rooms where the user is @@ -222,6 +225,7 @@ class MessageHandler(BaseHandler): pagin_config (synapse.api.streams.PaginationConfig): The pagination config used to determine how many messages *PER ROOM* to return. feedback (bool): True to get feedback along with these messages. + as_client_event (bool): True to get events in client-server format. Returns: A list of dicts with "room_id" and "membership" keys for all rooms the user is currently invited or joined in on. Rooms where the user @@ -280,7 +284,10 @@ class MessageHandler(BaseHandler): end_token = now_token.copy_and_replace("room_key", token[1]) d["messages"] = { - "chunk": [self.hs.serialize_event(m) for m in messages], + "chunk": [ + self.hs.serialize_event(m, as_client_event) + for m in messages + ], "start": start_token.to_string(), "end": end_token.to_string(), } diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py index 59719a1fae..6d0db18e51 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py @@ -425,10 +425,22 @@ class RoomMemberHandler(BaseHandler): event.room_id, self.hs.hostname ) + if not is_host_in_room: + # is *anyone* in the room? + room_member_keys = [ + v for (k, v) in context.current_state.keys() if ( + k == "m.room.member" + ) + ] + if len(room_member_keys) == 0: + # has the room been created so we can join it? + create_event = context.current_state.get(("m.room.create", "")) + if create_event: + is_host_in_room = True if is_host_in_room: should_do_dance = False - elif room_host: + elif room_host: # TODO: Shouldn't this be remote_room_host? should_do_dance = True else: # TODO(markjh): get prev_state from snapshot @@ -442,7 +454,8 @@ class RoomMemberHandler(BaseHandler): should_do_dance = not self.hs.is_mine(inviter) room_host = inviter.domain else: - should_do_dance = False + # return the same error as join_room_alias does + raise SynapseError(404, "No known servers") if should_do_dance: handler = self.hs.get_handlers().federation_handler diff --git a/synapse/handlers/typing.py b/synapse/handlers/typing.py index ab698b36e1..22ce7873d0 100644 --- a/synapse/handlers/typing.py +++ b/synapse/handlers/typing.py @@ -83,9 +83,15 @@ class TypingNotificationHandler(BaseHandler): if member in self._member_typing_timer: self.clock.cancel_call_later(self._member_typing_timer[member]) + def _cb(): + logger.debug( + "%s has timed out in %s", target_user.to_string(), room_id + ) + self._stopped_typing(member) + self._member_typing_until[member] = until self._member_typing_timer[member] = self.clock.call_later( - timeout / 1000, lambda: self._stopped_typing(member) + timeout / 1000.0, _cb ) if was_present: diff --git a/synapse/media/v1/__init__.py b/synapse/media/v1/__init__.py index 619999d268..d6c6690577 100644 --- a/synapse/media/v1/__init__.py +++ b/synapse/media/v1/__init__.py @@ -22,7 +22,8 @@ except IOError as e: if str(e).startswith("decoder jpeg not available"): raise Exception( "FATAL: jpeg codec not supported. Install pillow correctly! " - " 'sudo apt-get install libjpeg-dev' then 'pip install -I pillow'" + " 'sudo apt-get install libjpeg-dev' then 'pip uninstall pillow &&" + " pip install pillow --user'" ) except Exception: # any other exception is fine @@ -36,7 +37,8 @@ except IOError as e: if str(e).startswith("decoder zip not available"): raise Exception( "FATAL: zip codec not supported. Install pillow correctly! " - " 'sudo apt-get install libjpeg-dev' then 'pip install -I pillow'" + " 'sudo apt-get install libjpeg-dev' then 'pip uninstall pillow &&" + " pip install pillow --user'" ) except Exception: # any other exception is fine diff --git a/synapse/media/v1/thumbnailer.py b/synapse/media/v1/thumbnailer.py index bc86efea8f..28404f2b7b 100644 --- a/synapse/media/v1/thumbnailer.py +++ b/synapse/media/v1/thumbnailer.py @@ -82,7 +82,7 @@ class Thumbnailer(object): def save_image(self, output_image, output_type, output_path): output_bytes_io = BytesIO() - output_image.save(output_bytes_io, self.FORMATS[output_type]) + output_image.save(output_bytes_io, self.FORMATS[output_type], quality=70) output_bytes = output_bytes_io.getvalue() with open(output_path, "wb") as output_file: output_file.write(output_bytes) diff --git a/synapse/python_dependencies.py b/synapse/python_dependencies.py new file mode 100644 index 0000000000..b1fae991e0 --- /dev/null +++ b/synapse/python_dependencies.py @@ -0,0 +1,80 @@ +import logging +from distutils.version import LooseVersion + +logger = logging.getLogger(__name__) + +REQUIREMENTS = { + "syutil==0.0.2": ["syutil"], + "matrix_angular_sdk==0.6.0": ["syweb==0.6.0"], + "Twisted>=14.0.0": ["twisted>=14.0.0"], + "service_identity>=1.0.0": ["service_identity>=1.0.0"], + "pyopenssl>=0.14": ["OpenSSL>=0.14"], + "pyyaml": ["yaml"], + "pyasn1": ["pyasn1"], + "pynacl": ["nacl"], + "daemonize": ["daemonize"], + "py-bcrypt": ["bcrypt"], + "frozendict>=0.4": ["frozendict"], + "pillow": ["PIL"], +} + + +class MissingRequirementError(Exception): + pass + + +def check_requirements(): + """Checks that all the modules needed by synapse have been correctly + installed and are at the correct version""" + for dependency, module_requirements in REQUIREMENTS.items(): + for module_requirement in module_requirements: + if ">=" in module_requirement: + module_name, required_version = module_requirement.split(">=") + version_test = ">=" + elif "==" in module_requirement: + module_name, required_version = module_requirement.split("==") + version_test = "==" + else: + module_name = module_requirement + version_test = None + + try: + module = __import__(module_name) + except ImportError: + logging.exception( + "Can't import %r which is part of %r", + module_name, dependency + ) + raise MissingRequirementError( + "Can't import %r which is part of %r" + % (module_name, dependency) + ) + version = getattr(module, "__version__", None) + file_path = getattr(module, "__file__", None) + logger.info( + "Using %r version %r from %r to satisfy %r", + module_name, version, file_path, dependency + ) + + if version_test == ">=": + if version is None: + raise MissingRequirementError( + "Version of %r isn't set as __version__ of module %r" + % (dependency, module_name) + ) + if LooseVersion(version) < LooseVersion(required_version): + raise MissingRequirementError( + "Version of %r in %r is too old. %r < %r" + % (dependency, file_path, version, required_version) + ) + elif version_test == "==": + if version is None: + raise MissingRequirementError( + "Version of %r isn't set as __version__ of module %r" + % (dependency, module_name) + ) + if LooseVersion(version) != LooseVersion(required_version): + raise MissingRequirementError( + "Unexpected version of %r in %r. %r != %r" + % (dependency, file_path, version, required_version) + ) diff --git a/synapse/rest/events.py b/synapse/rest/events.py index cf6d13f817..bedcb2bcc6 100644 --- a/synapse/rest/events.py +++ b/synapse/rest/events.py @@ -44,8 +44,11 @@ class EventStreamRestServlet(RestServlet): except ValueError: raise SynapseError(400, "timeout must be in milliseconds.") + as_client_event = "raw" not in request.args + chunk = yield handler.get_stream( - auth_user.to_string(), pagin_config, timeout=timeout + auth_user.to_string(), pagin_config, timeout=timeout, + as_client_event=as_client_event ) except: logger.exception("Event stream failed") diff --git a/synapse/rest/initial_sync.py b/synapse/rest/initial_sync.py index a571589581..b13d56b286 100644 --- a/synapse/rest/initial_sync.py +++ b/synapse/rest/initial_sync.py @@ -27,12 +27,15 @@ class InitialSyncRestServlet(RestServlet): def on_GET(self, request): user = yield self.auth.get_user_by_req(request) with_feedback = "feedback" in request.args + as_client_event = "raw" not in request.args pagination_config = PaginationConfig.from_request(request) handler = self.handlers.message_handler content = yield handler.snapshot_all_rooms( user_id=user.to_string(), pagin_config=pagination_config, - feedback=with_feedback) + feedback=with_feedback, + as_client_event=as_client_event + ) defer.returnValue((200, content)) diff --git a/synapse/rest/room.py b/synapse/rest/room.py index e40773758a..caafa959e6 100644 --- a/synapse/rest/room.py +++ b/synapse/rest/room.py @@ -314,12 +314,15 @@ class RoomMessageListRestServlet(RestServlet): request, default_limit=10, ) with_feedback = "feedback" in request.args + as_client_event = "raw" not in request.args handler = self.handlers.message_handler msgs = yield handler.get_messages( room_id=room_id, user_id=user.to_string(), pagin_config=pagination_config, - feedback=with_feedback) + feedback=with_feedback, + as_client_event=as_client_event + ) defer.returnValue((200, msgs)) diff --git a/synapse/server.py b/synapse/server.py index c3bf46abbf..d861efd2fd 100644 --- a/synapse/server.py +++ b/synapse/server.py @@ -149,8 +149,8 @@ class BaseHomeServer(object): object.""" return EventID.from_string(s) - def serialize_event(self, e): - return serialize_event(self, e) + def serialize_event(self, e, as_client_event=True): + return serialize_event(self, e, as_client_event) def get_ip_from_request(self, request): # May be an X-Forwarding-For header depending on config diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py index bedc3c6c52..744c821dfe 100644 --- a/synapse/storage/stream.py +++ b/synapse/storage/stream.py @@ -284,8 +284,12 @@ class StreamStore(SQLBaseStore): rows.reverse() # As we selected with reverse ordering if rows: - topo = rows[0]["topological_ordering"] - toke = rows[0]["stream_ordering"] + # XXX: Always subtract 1 since the start token always goes + # backwards (parity with paginate_room_events). It isn't + # obvious that this is correct; we should clarify the algorithm + # used here. + topo = rows[0]["topological_ordering"] - 1 + toke = rows[0]["stream_ordering"] - 1 start_token = "t%s-%s" % (topo, toke) token = (start_token, end_token) |