summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthew Hodgson <matthew.hodgson@openmarket.com>2014-08-13 16:14:30 +0100
committerMatthew Hodgson <matthew.hodgson@openmarket.com>2014-08-13 16:14:30 +0100
commit2ec5f6c2f2bccca6fe3004fd7d4c2632428a4ba5 (patch)
tree93449489f2a1244b4f807f79fed5e23446d60e56
parenttimestamps (diff)
parentDon't pass host_web_client flag to register_servlets, it needs to be a Resour... (diff)
downloadsynapse-2ec5f6c2f2bccca6fe3004fd7d4c2632428a4ba5.tar.xz
Merge branch 'master' of git+ssh://github.com/matrix-org/synapse
-rw-r--r--MAP.rst35
-rw-r--r--README.rst184
-rwxr-xr-xcmdclient/console.py17
-rw-r--r--cmdclient/http.py14
-rw-r--r--docs/server-server/specification.rst14
-rw-r--r--experiments/cursesio.py14
-rw-r--r--experiments/test_messaging.py14
-rw-r--r--graph/graph.py14
-rwxr-xr-xscripts/copyrighter.pl33
-rw-r--r--setup.py14
-rw-r--r--synapse/__init__.py1
-rw-r--r--synapse/api/__init__.py1
-rw-r--r--synapse/api/auth.py1
-rw-r--r--synapse/api/constants.py9
-rw-r--r--synapse/api/errors.py1
-rw-r--r--synapse/api/events/__init__.py1
-rw-r--r--synapse/api/events/factory.py1
-rw-r--r--synapse/api/events/room.py1
-rw-r--r--synapse/api/notifier.py1
-rw-r--r--synapse/api/streams/__init__.py1
-rw-r--r--synapse/api/streams/event.py1
-rw-r--r--synapse/app/__init__.py1
-rw-r--r--synapse/app/homeserver.py13
-rw-r--r--synapse/crypto/__init__.py1
-rw-r--r--synapse/crypto/config.py1
-rw-r--r--synapse/crypto/keyclient.py1
-rw-r--r--synapse/crypto/keyserver.py1
-rw-r--r--synapse/crypto/resource/__init__.py1
-rw-r--r--synapse/crypto/resource/key.py1
-rw-r--r--synapse/federation/__init__.py1
-rw-r--r--synapse/federation/handler.py1
-rw-r--r--synapse/federation/pdu_codec.py1
-rw-r--r--synapse/federation/persistence.py1
-rw-r--r--synapse/federation/replication.py1
-rw-r--r--synapse/federation/transport.py34
-rw-r--r--synapse/federation/units.py1
-rw-r--r--synapse/handlers/__init__.py1
-rw-r--r--synapse/handlers/_base.py1
-rw-r--r--synapse/handlers/directory.py1
-rw-r--r--synapse/handlers/events.py1
-rw-r--r--synapse/handlers/federation.py1
-rw-r--r--synapse/handlers/login.py1
-rw-r--r--synapse/handlers/presence.py1
-rw-r--r--synapse/handlers/profile.py1
-rw-r--r--synapse/handlers/register.py1
-rw-r--r--synapse/handlers/room.py1
-rw-r--r--synapse/http/__init__.py1
-rw-r--r--synapse/http/client.py1
-rw-r--r--synapse/http/endpoint.py1
-rw-r--r--synapse/http/server.py1
-rw-r--r--synapse/rest/__init__.py10
-rw-r--r--synapse/rest/base.py43
-rw-r--r--synapse/rest/directory.py1
-rw-r--r--synapse/rest/events.py1
-rw-r--r--synapse/rest/im.py1
-rw-r--r--synapse/rest/login.py1
-rw-r--r--synapse/rest/presence.py1
-rw-r--r--synapse/rest/profile.py1
-rw-r--r--synapse/rest/public.py1
-rw-r--r--synapse/rest/register.py1
-rw-r--r--synapse/rest/room.py1
-rw-r--r--synapse/rest/webclient.py45
-rw-r--r--synapse/server.py11
-rw-r--r--synapse/state.py1
-rw-r--r--synapse/storage/__init__.py1
-rw-r--r--synapse/storage/_base.py1
-rw-r--r--synapse/storage/directory.py1
-rw-r--r--synapse/storage/feedback.py1
-rw-r--r--synapse/storage/message.py1
-rw-r--r--synapse/storage/pdu.py1
-rw-r--r--synapse/storage/presence.py1
-rw-r--r--synapse/storage/profile.py1
-rw-r--r--synapse/storage/registration.py1
-rw-r--r--synapse/storage/room.py8
-rw-r--r--synapse/storage/roomdata.py1
-rw-r--r--synapse/storage/roommember.py1
-rw-r--r--synapse/storage/stream.py1
-rw-r--r--synapse/storage/transactions.py1
-rw-r--r--synapse/types.py1
-rw-r--r--synapse/util/__init__.py1
-rw-r--r--synapse/util/async.py1
-rw-r--r--synapse/util/distributor.py1
-rw-r--r--synapse/util/jsonobject.py1
-rw-r--r--synapse/util/lockutils.py1
-rw-r--r--synapse/util/logutils.py1
-rw-r--r--synapse/util/stringutils.py1
-rw-r--r--tests/__init__.py14
-rw-r--r--tests/events/__init__.py14
-rw-r--r--tests/events/test_events.py14
-rw-r--r--tests/federation/test_federation.py29
-rw-r--r--tests/federation/test_pdu_codec.py14
-rw-r--r--tests/handlers/test_federation.py14
-rw-r--r--tests/handlers/test_presence.py38
-rw-r--r--tests/handlers/test_presencelike.py26
-rw-r--r--tests/handlers/test_profile.py14
-rw-r--r--tests/handlers/test_room.py14
-rw-r--r--tests/rest/__init__.py14
-rw-r--r--tests/rest/test_events.py14
-rw-r--r--tests/rest/test_presence.py26
-rw-r--r--tests/rest/test_profile.py14
-rw-r--r--tests/rest/test_rooms.py14
-rw-r--r--tests/rest/utils.py14
-rw-r--r--tests/storage/test_base.py14
-rw-r--r--tests/test_distributor.py15
-rw-r--r--tests/test_state.py14
-rw-r--r--tests/test_types.py15
-rw-r--r--tests/util/__init__.py14
-rw-r--r--tests/util/test_lock.py14
-rw-r--r--tests/utils.py15
-rw-r--r--webclient/app-controller.js16
-rw-r--r--webclient/app.css14
-rw-r--r--webclient/app.js16
-rw-r--r--webclient/components/matrix/matrix-service.js27
-rw-r--r--webclient/room/room-controller.js40
-rw-r--r--webclient/room/room.html17
-rw-r--r--webclient/rooms/rooms-controller.js16
116 files changed, 940 insertions, 188 deletions
diff --git a/MAP.rst b/MAP.rst
new file mode 100644
index 0000000000..0f8e9818a8
--- /dev/null
+++ b/MAP.rst
@@ -0,0 +1,35 @@
+Directory Structure
+===================
+
+Warning: this may be a bit stale...
+
+::
+
+    .
+    ├── cmdclient           Basic CLI python Matrix client
+    ├── demo                Scripts for running standalone Matrix demos
+    ├── docs                All doc, including the draft Matrix API spec
+    │   ├── client-server       The client-server Matrix API spec
+    │   ├── model               Domain-specific elements of the Matrix API spec
+    │   ├── server-server       The server-server model of the Matrix API spec
+    │   └── sphinx              The internal API doc of the Synapse homeserver
+    ├── experiments         Early experiments of using Synapse's internal APIs
+    ├── graph               Visualisation of Matrix's distributed message store 
+    ├── synapse             The reference Matrix homeserver implementation
+    │   ├── api                 Common building blocks for the APIs
+    │   │   ├── events              Definition of state representation Events 
+    │   │   └── streams             Definition of streamable Event objects
+    │   ├── app                 The __main__ entry point for the homeserver
+    │   ├── crypto              The PKI client/server used for secure federation
+    │   │   └── resource            PKI helper objects (e.g. keys)
+    │   ├── federation          Server-server state replication logic
+    │   ├── handlers            The main business logic of the homeserver
+    │   ├── http                Wrappers around Twisted's HTTP server & client
+    │   ├── rest                Servlet-style RESTful API
+    │   ├── storage             Persistence subsystem (currently only sqlite3)
+    │   │   └── schema              sqlite persistence schema
+    │   └── util                Synapse-specific utilities
+    ├── tests               Unit tests for the Synapse homeserver
+    └── webclient           Basic AngularJS Matrix web client
+
+
diff --git a/README.rst b/README.rst
index c58ebf0dbe..319bbb6e4e 100644
--- a/README.rst
+++ b/README.rst
@@ -1,16 +1,54 @@
-About
-=====
-
-Matrix is an ambitious new ecosystem for open federated Instant Messaging and VoIP[1].
+Quick Start
+===========
+
+Matrix is an ambitious new ecosystem for open federated Instant Messaging and
+VoIP[1].  The basics you need to know to get up and running are:
+
+    - Chatrooms are distributed and do not exist on any single server.  Rooms 
+      can be found using names like ``#matrix:matrix.org`` or 
+      ``#test:localhost:8080`` or they can be ephemeral.
+    
+    - Matrix user IDs look like ``@matthew:matrix.org`` (although in the future
+      you will normally refer to yourself and others using a 3PID: email
+      address, phone number, etc rather than manipulating matrix user IDs)
+
+The overall architecture is::
+
+      client <----> homeserver <=================> homeserver  <-----> client
+                e.g. matrix.org:8080        e.g. mydomain.net:8080
+
+To get up and running:
+      
+    - To simply play with an **existing** homeserver you can
+      just go straight to http://matrix.org/alpha.
+    
+    - To run your own **private** homeserver on localhost:8080, install synapse 
+      with ``python setup.py develop --user`` and then run one with
+      ``python synapse/app/homeserver.py``
+      
+    - To run your own webclient:
+      ``cd webclient; python -m SimpleHTTPServer`` and hit http://localhost:8000
+      in your web browser (a recent Chrome, Safari or Firefox for now,
+      please...)
+             
+    - To make the homeserver **public** and let it exchange messages with 
+      other homeservers and participate in the overall Matrix federation, open 
+      up port 8080 and run ``python synapse/app/homeserver.py --host 
+      machine.my.domain.name``.  Then come join ``#matrix:matrix.org`` and
+      say hi! :)
+    
+About Matrix
+============
 
-Matrix specifies a set of pragmatic RESTful HTTP JSON APIs as an open standard, providing:
+Matrix specifies a set of pragmatic RESTful HTTP JSON APIs for VoIP and IM as an
+open standard, providing:
 
     - Creating and managing 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
+    - Eventually-consistent cryptographically secure[2] 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[2]
+      end-to-end encryption[3]
     - Inviting, joining, leaving, kicking, banning room members
     - Managing user accounts (registration, login, logout)
     - Using 3rd Party IDs (3PIDs) such as email addresses, phone numbers,
@@ -18,11 +56,11 @@ Matrix specifies a set of pragmatic RESTful HTTP JSON APIs as an open standard,
     - Placing 1:1 VoIP and Video calls (in development)
 
 These APIs are intended to be implemented on a wide range of servers, services
-and clients which then form the Matrix ecosystem, and allow developers to build
-messaging and VoIP functionality on top of the open Matrix community rather than
-using closed or proprietary solutions.  The hope is for Matrix to act as the
-building blocks for a new generation of fully open and interoperable messaging
-and VoIP apps for the internet.
+and clients, letting developers build messaging and VoIP functionality on top of
+the entirely open Matrix ecosystem rather than using closed or proprietary
+solutions. The hope is for Matrix to act as the building blocks for a new
+generation of fully open and interoperable messaging and VoIP apps for the
+internet.
 
 Synapse is a reference "homeserver" implementation of Matrix from the core
 development team at matrix.org, written in Python/Twisted for clarity and
@@ -38,57 +76,30 @@ control and own your own communications and history or use one hosted by someone
 else (e.g. matrix.org) - there is no single point of control or mandatory
 service provider in Matrix, unlike WhatsApp, Facebook, Hangouts, etc.
 
-Synapse ships with two basic demo Matrix clients: webclient (a basic group chat web client demo implemented in AngularJS) and cmdclient (a basic Python commandline utility which lets you easily see what the JSON APIs are up to).
+Synapse ships with two basic demo Matrix clients: webclient (a basic group chat
+web client demo implemented in AngularJS) and cmdclient (a basic Python
+commandline utility which lets you easily see what the JSON APIs are up to).
 
-We'd like to invite you to take a look at the Matrix spec, try to run a homeserver, and join the existing Matrix chatrooms already out there, experiment with the APIs and the demo clients, and let us know your thoughts at https://github.com/matrix-org/synapse/issues or at matrix@matrix.org.
+We'd like to invite you to take a look at the Matrix spec, try to run a
+homeserver, and join the existing Matrix chatrooms already out there, experiment
+with the APIs and the demo clients, and let us know your thoughts at
+https://github.com/matrix-org/synapse/issues or at matrix@matrix.org.
 
 Thanks for trying Matrix!
 
 [1] VoIP currently in development
 
-[2] End-to-end encryption is currently in development
-
-
-Directory Structure
-===================
-
-::
-
-    .
-    ├── cmdclient           Basic CLI python Matrix client
-    ├── demo                Scripts for running standalone Matrix demos
-    ├── docs                All doc, including the draft Matrix API spec
-    │   ├── client-server   The client-server Matrix API spec
-    │   ├── model           Domain-specific elements of the Matrix API spec
-    │   ├── server-server   The server-server model of the Matrix API spec
-    │   └── sphinx          The internal API doc of the Synapse homeserver
-    ├── experiments         Early experiments of using Synapse's internal APIs
-    ├── graph               Visualisation of Matrix's distributed message store 
-    ├── synapse             The reference Matrix homeserver implementation
-    │   ├── api                 Common building blocks for the APIs
-    │   │   ├── events              Definition of state representation Events 
-    │   │   └── streams             Definition of streamable Event objects
-    │   ├── app                 The __main__ entry point for the homeserver
-    │   ├── crypto              The PKI client/server used for secure federation
-    │   │   └── resource            PKI helper objects (e.g. keys)
-    │   ├── federation          Server-server state replication logic
-    │   ├── handlers            The main business logic of the homeserver
-    │   ├── http                Wrappers around Twisted's HTTP server & client
-    │   ├── rest                Servlet-style RESTful API
-    │   ├── storage             Persistence subsystem (currently only sqlite3)
-    │   │   └── schema              sqlite persistence schema
-    │   └── util                Synapse-specific utilities
-    ├── tests               Unit tests for the Synapse homeserver
-    └── webclient           Basic AngularJS Matrix web client
-
-
-Installation
-============
+[2] Cryptographic signing of messages isn't turned on yet
+
+[3] End-to-end encryption is currently in development
+
+
+Homeserver Installation
+=======================
 
 First, the dependencies need to be installed.  Start by installing 
 'python2.7-dev' and the various tools of the compiler toolchain.
-
-N.B. that python 2.x where x >= 7 is required.
+N.B. synapse requires python 2.x where x >= 7
 
   Installing prerequisites on ubuntu::
 
@@ -102,6 +113,18 @@ The homeserver has a number of external dependencies, that are easiest
 to install by making setup.py do so, in --user mode::
 
     $ python setup.py develop --user
+    
+You'll need a version of setuptools new enough to know about git, so you
+may need to also run:
+
+    $ sudo apt-get install python-pip
+    $ sudo pip install --upgrade setuptools
+    
+If you get errors about ``sodium.h`` being missing, you may also need to
+manually install a newer PyNaCl via pip as setuptools installs an old one. Or
+you can check PyNaCl out of git directly (https://github.com/pyca/pynacl) and
+installing it. Installing PyNaCl using pip may also work (remember to remove any
+other versions installed by setuputils in, for example, ~/.local/lib).
 
 This will run a process of downloading and installing into your
 user's .local/lib directory all of the required dependencies that are
@@ -119,8 +142,8 @@ This should end with a 'PASSED' result::
     PASSED (successes=143)
 
 
-Running The Synapse Homeserver
-==============================
+Setting up Federation
+=====================
 
 In order for other homeservers to send messages to your server, it will need to
 be publicly visible on the internet, and they will need to know its host name.
@@ -161,6 +184,14 @@ For the initial alpha release, the homeserver is not speaking TLS for
 either client-server or server-server traffic for ease of debugging. We have
 also not spent any time yet getting the homeserver to run behind loadbalancers.
 
+Running a Demo Federation of Homeservers
+----------------------------------------
+
+If you want to get up and running quickly with a trio of homeservers in a
+private federation (``localhost:8080``, ``localhost:8081`` and
+``localhost:8082``) which you can then access through the webclient running at http://localhost:8080.  Simply run::
+
+    $ demo/start.sh
 
 Running The Demo Web Client
 ===========================
@@ -200,19 +231,44 @@ synapse sandbox running on localhost)
 Logging In To An Existing Account
 ---------------------------------
 
-[[TODO(paul): It seems the current web client still requests an access_token -
-  I suspect this part will need updating before we can point people at how to
-  perform e.g. user+password or 3PID authenticated login]]
+Just enter the ``@localpart:my.domain.here`` matrix user ID and password into the form and click the Login button.
 
 
-Building Documentation
-======================
+Identity Servers
+================
 
-Before building documentation install spinx and sphinxcontrib-napoleon::
+The job of authenticating 3PIDs and tracking which 3PIDs are associated with a
+given matrix user is very security-sensitive, as there is obvious risk of spam
+if it is too easy to sign up for Matrix accounts or harvest 3PID data. Meanwhile
+the job of publishing the end-to-end encryption public keys for Matrix users is
+also very security-sensitive for similar reasons.
+
+Therefore the role of managing trusted identity in the Matrix ecosystem is
+farmed out to a cluster of known trusted ecosystem partners, who run 'Matrix
+Identity Servers' such as ``sydent``, whose role is purely to authenticate and
+track 3PID logins and publish end-user public keys.
+
+It's currently early days for identity servers as Matrix is not yet using 3PIDs
+as the primary means of identity and E2E encryption is not complete. As such,
+we're not yet running an identity server in public.
+
+
+Where's the spec?!
+==================
+
+For now, please go spelunking in the ``docs/`` directory to find out.
+
+
+Building Internal API Documentation
+===================================
+
+Before building internal API documentation install spinx and
+sphinxcontrib-napoleon::
 
     $ pip install sphinx
     $ pip install sphinxcontrib-napoleon
 
-Building documentation::
+Building internal API documentation::
 
     $ python setup.py build_sphinx
+
diff --git a/cmdclient/console.py b/cmdclient/console.py
index 595ce429cd..6c6e2085b4 100755
--- a/cmdclient/console.py
+++ b/cmdclient/console.py
@@ -1,4 +1,19 @@
-#! /usr/bin/env python
+#!/usr/bin/env python
+
+# 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.
+
 """ Starts a synapse client console. """
 
 from twisted.internet import reactor, defer, threads
diff --git a/cmdclient/http.py b/cmdclient/http.py
index e71593837a..9de6be9b72 100644
--- a/cmdclient/http.py
+++ b/cmdclient/http.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.web.client import Agent, readBody
 from twisted.web.http_headers import Headers
 from twisted.internet import defer, reactor
diff --git a/docs/server-server/specification.rst b/docs/server-server/specification.rst
index e1e49cc069..a386bd3e7d 100644
--- a/docs/server-server/specification.rst
+++ b/docs/server-server/specification.rst
@@ -116,9 +116,13 @@ federation.]]
 Protocol URLs
 =============
 
+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/
+  PUT .../send/:transaction_id/
     Body: JSON encoding of a single Transaction
 
     Response: [[TODO(paul): I don't actually understand what
@@ -132,7 +136,7 @@ For active pushing of messages representing live activity "as it happens":
 
 To fetch a particular PDU:
 
-  GET /pdu/:origin/:pdu_id/
+  GET .../pdu/:origin/:pdu_id/
 
     Response: JSON encoding of a single Transaction containing one PDU
 
@@ -142,7 +146,7 @@ To fetch a particular PDU:
 
 To fetch all the state of a given context:
 
-  GET /state/:context/
+  GET .../state/:context/
 
     Response: JSON encoding of a single Transaction containing multiple PDUs
 
@@ -153,7 +157,7 @@ To fetch all the state of a given context:
 
 To paginate events on a given context:
 
-  GET /paginate/:context/
+  GET .../paginate/:context/
     Query args: v, limit
 
     Response: JSON encoding of a single Transaction containing multiple PDUs
@@ -167,7 +171,7 @@ To paginate events on a given context:
 
 To stream events all the events:
 
-  GET /pull/
+  GET .../pull/
     Query args: origin, v
 
   Response: JSON encoding of a single Transaction consisting of multiple PDUs
diff --git a/experiments/cursesio.py b/experiments/cursesio.py
index 83b4a9f186..31fbda5504 100644
--- a/experiments/cursesio.py
+++ b/experiments/cursesio.py
@@ -1,3 +1,17 @@
+# 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.
+
 import curses
 import curses.wrapper
 from curses.ascii import isprint
diff --git a/experiments/test_messaging.py b/experiments/test_messaging.py
index 9a45425173..f4ae71bfc4 100644
--- a/experiments/test_messaging.py
+++ b/experiments/test_messaging.py
@@ -1,4 +1,18 @@
 # -*- 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.
+
 
 """ This is an example of using the server to server implementation to do a
 basic chat style thing. It accepts commands from stdin and outputs to stdout.
diff --git a/graph/graph.py b/graph/graph.py
index c600773412..03af12cbbd 100644
--- a/graph/graph.py
+++ b/graph/graph.py
@@ -1,3 +1,17 @@
+# 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.
+
 
 import sqlite3
 import pydot
diff --git a/scripts/copyrighter.pl b/scripts/copyrighter.pl
new file mode 100755
index 0000000000..e476c9cc85
--- /dev/null
+++ b/scripts/copyrighter.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl -pi
+# 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.
+
+$copyright = <<EOT;
+# 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.
+
+EOT
+
+s/^(# -\*- coding: utf-8 -\*-\n)?/$1$copyright/ if ($. == 1);
diff --git a/setup.py b/setup.py
index 41e2433a7c..fca3c77700 100644
--- a/setup.py
+++ b/setup.py
@@ -1,3 +1,17 @@
+# 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.
+
 import os
 from setuptools import setup, find_packages
 
diff --git a/synapse/__init__.py b/synapse/__init__.py
index aa760fb341..1e7b2ab272 100644
--- a/synapse/__init__.py
+++ b/synapse/__init__.py
@@ -12,5 +12,6 @@
 # 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.
+
 """ This is a reference implementation of a synapse home server.
 """
diff --git a/synapse/api/__init__.py b/synapse/api/__init__.py
index fe8a073cd3..2216c0f1ca 100644
--- a/synapse/api/__init__.py
+++ b/synapse/api/__init__.py
@@ -12,3 +12,4 @@
 # 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.
+
diff --git a/synapse/api/auth.py b/synapse/api/auth.py
index 5c66a7261f..84bc0398fd 100644
--- a/synapse/api/auth.py
+++ b/synapse/api/auth.py
@@ -12,6 +12,7 @@
 # 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.
+
 """This module contains classes for authenticating the user."""
 from twisted.internet import defer
 
diff --git a/synapse/api/constants.py b/synapse/api/constants.py
index 37bf41bfb3..1ff1af76ec 100644
--- a/synapse/api/constants.py
+++ b/synapse/api/constants.py
@@ -12,6 +12,7 @@
 # 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.
+
 """Contains constants from the specification."""
 
 
@@ -36,7 +37,7 @@ class Feedback(object):
 
 class PresenceState(object):
     """Represents the presence state of a user."""
-    OFFLINE = 0
-    BUSY = 1
-    ONLINE = 2
-    FREE_FOR_CHAT = 3
+    OFFLINE = u"offline"
+    UNAVAILABLE = u"unavailable"
+    ONLINE = u"online"
+    FREE_FOR_CHAT = u"free_for_chat"
diff --git a/synapse/api/errors.py b/synapse/api/errors.py
index 7ad4d636c2..8b9766fab7 100644
--- a/synapse/api/errors.py
+++ b/synapse/api/errors.py
@@ -12,6 +12,7 @@
 # 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.
+
 """Contains exceptions and error codes."""
 
 import logging
diff --git a/synapse/api/events/__init__.py b/synapse/api/events/__init__.py
index bc2daf3361..921fd08832 100644
--- a/synapse/api/events/__init__.py
+++ b/synapse/api/events/__init__.py
@@ -12,6 +12,7 @@
 # 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 synapse.api.errors import SynapseError, Codes
 from synapse.util.jsonobject import JsonEncodedObject
 
diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py
index ea7afa234e..12aa04fc6e 100644
--- a/synapse/api/events/factory.py
+++ b/synapse/api/events/factory.py
@@ -12,6 +12,7 @@
 # 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 synapse.api.events.room import (
     RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
     InviteJoinEvent, RoomConfigEvent
diff --git a/synapse/api/events/room.py b/synapse/api/events/room.py
index b31cd19f4b..f3df849af2 100644
--- a/synapse/api/events/room.py
+++ b/synapse/api/events/room.py
@@ -12,6 +12,7 @@
 # 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 . import SynapseEvent
 
 
diff --git a/synapse/api/notifier.py b/synapse/api/notifier.py
index 974f7f0ba0..22d2914d38 100644
--- a/synapse/api/notifier.py
+++ b/synapse/api/notifier.py
@@ -12,6 +12,7 @@
 # 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 synapse.api.constants import Membership
 from synapse.api.events.room import RoomMemberEvent
 
diff --git a/synapse/api/streams/__init__.py b/synapse/api/streams/__init__.py
index 08137c1e79..989e63f9ec 100644
--- a/synapse/api/streams/__init__.py
+++ b/synapse/api/streams/__init__.py
@@ -12,6 +12,7 @@
 # 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 synapse.api.errors import SynapseError
 
 
diff --git a/synapse/api/streams/event.py b/synapse/api/streams/event.py
index 0cc1a3e36a..4b6d739e54 100644
--- a/synapse/api/streams/event.py
+++ b/synapse/api/streams/event.py
@@ -12,6 +12,7 @@
 # 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.
+
 """This module contains classes for streaming from the event stream: /events.
 """
 from twisted.internet import defer
diff --git a/synapse/app/__init__.py b/synapse/app/__init__.py
index fe8a073cd3..2216c0f1ca 100644
--- a/synapse/app/__init__.py
+++ b/synapse/app/__init__.py
@@ -12,3 +12,4 @@
 # 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.
+
diff --git a/synapse/app/homeserver.py b/synapse/app/homeserver.py
index 5708b3ad95..82afb04c7d 100644
--- a/synapse/app/homeserver.py
+++ b/synapse/app/homeserver.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # Copyright 2014 matrix.org
 #
@@ -12,7 +13,6 @@
 # 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.
-#!/usr/bin/env python
 
 from synapse.storage import read_schema
 
@@ -88,13 +88,12 @@ def setup_logging(verbosity=0, filename=None, config_path=None):
             '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(message)s'
         )
 
-        if not verbosity or verbosity == 0:
-            level = logging.WARNING
-        elif verbosity == 1:
-            level = logging.INFO
-        else:
+        level = logging.INFO
+        if verbosity:
             level = logging.DEBUG
 
+        # FIXME: we need a logging.WARN for a -q quiet option
+
         logging.basicConfig(level=level, filename=filename, format=log_format)
     else:
         logging.config.fileConfig(config_path)
@@ -126,6 +125,8 @@ def setup():
     parser.add_argument('--pid-file', dest="pid", help="When running as a "
                         "daemon, the file to store the pid in",
                         default="hs.pid")
+    parser.add_argument("-w", "--webclient", dest="webclient",
+                        action="store_true", help="Host the web client.")
     args = parser.parse_args()
 
     verbosity = int(args.verbose) if args.verbose else None
diff --git a/synapse/crypto/__init__.py b/synapse/crypto/__init__.py
index fe8a073cd3..2216c0f1ca 100644
--- a/synapse/crypto/__init__.py
+++ b/synapse/crypto/__init__.py
@@ -12,3 +12,4 @@
 # 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.
+
diff --git a/synapse/crypto/config.py b/synapse/crypto/config.py
index 801dfd8656..2330133e71 100644
--- a/synapse/crypto/config.py
+++ b/synapse/crypto/config.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 import ConfigParser as configparser
 import argparse
 import socket
diff --git a/synapse/crypto/keyclient.py b/synapse/crypto/keyclient.py
index b53d1c572b..e615866b68 100644
--- a/synapse/crypto/keyclient.py
+++ b/synapse/crypto/keyclient.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.web.http import HTTPClient
 from twisted.internet import defer, reactor
 from twisted.internet.protocol import ClientFactory
diff --git a/synapse/crypto/keyserver.py b/synapse/crypto/keyserver.py
index 48bd380781..3d80a0e660 100644
--- a/synapse/crypto/keyserver.py
+++ b/synapse/crypto/keyserver.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import reactor, ssl
 from twisted.web import server
 from twisted.web.resource import Resource
diff --git a/synapse/crypto/resource/__init__.py b/synapse/crypto/resource/__init__.py
index fe8a073cd3..2216c0f1ca 100644
--- a/synapse/crypto/resource/__init__.py
+++ b/synapse/crypto/resource/__init__.py
@@ -12,3 +12,4 @@
 # 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.
+
diff --git a/synapse/crypto/resource/key.py b/synapse/crypto/resource/key.py
index 6ce6e0b034..6aecd2b95f 100644
--- a/synapse/crypto/resource/key.py
+++ b/synapse/crypto/resource/key.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.web.resource import Resource
 from twisted.web.server import NOT_DONE_YET
 from twisted.internet import defer
diff --git a/synapse/federation/__init__.py b/synapse/federation/__init__.py
index b4d95ed5ac..ac0c10dc33 100644
--- a/synapse/federation/__init__.py
+++ b/synapse/federation/__init__.py
@@ -12,6 +12,7 @@
 # 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.
+
 """ This package includes all the federation specific logic.
 """
 
diff --git a/synapse/federation/handler.py b/synapse/federation/handler.py
index 31e8470b33..d361f0aaf7 100644
--- a/synapse/federation/handler.py
+++ b/synapse/federation/handler.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import defer
 
 from .pdu_codec import PduCodec
diff --git a/synapse/federation/pdu_codec.py b/synapse/federation/pdu_codec.py
index 9155930e47..adc166c564 100644
--- a/synapse/federation/pdu_codec.py
+++ b/synapse/federation/pdu_codec.py
@@ -12,6 +12,7 @@
 # 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 .units import Pdu
 
 import copy
diff --git a/synapse/federation/persistence.py b/synapse/federation/persistence.py
index ad4111c683..372245712a 100644
--- a/synapse/federation/persistence.py
+++ b/synapse/federation/persistence.py
@@ -12,6 +12,7 @@
 # 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.
+
 """ This module contains all the persistence actions done by the federation
 package.
 
diff --git a/synapse/federation/replication.py b/synapse/federation/replication.py
index 0f5b974291..bea5335f89 100644
--- a/synapse/federation/replication.py
+++ b/synapse/federation/replication.py
@@ -12,6 +12,7 @@
 # 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.
+
 """This layer is responsible for replicating with remote home servers using
 a given transport.
 """
diff --git a/synapse/federation/transport.py b/synapse/federation/transport.py
index 2136adf8d7..ff3fc34419 100644
--- a/synapse/federation/transport.py
+++ b/synapse/federation/transport.py
@@ -12,6 +12,7 @@
 # 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.
+
 """The transport layer is responsible for both sending transactions to remote
 home servers and receiving a variety of requests from other home servers.
 
@@ -32,6 +33,9 @@ import re
 logger = logging.getLogger(__name__)
 
 
+PREFIX = "/matrix/federation/v1"
+
+
 class TransportLayer(object):
     """This is a basic implementation of the transport layer that translates
     transactions and other requests to/from HTTP.
@@ -83,9 +87,9 @@ class TransportLayer(object):
         logger.debug("get_context_state dest=%s, context=%s",
                      destination, context)
 
-        path = "/state/%s/" % context
+        subpath = "/state/%s/" % context
 
-        return self._do_request_for_transaction(destination, path)
+        return self._do_request_for_transaction(destination, subpath)
 
     @log_function
     def get_pdu(self, destination, pdu_origin, pdu_id):
@@ -103,9 +107,9 @@ class TransportLayer(object):
         logger.debug("get_pdu dest=%s, pdu_origin=%s, pdu_id=%s",
                      destination, pdu_origin, pdu_id)
 
-        path = "/pdu/%s/%s/" % (pdu_origin, pdu_id)
+        subpath = "/pdu/%s/%s/" % (pdu_origin, pdu_id)
 
-        return self._do_request_for_transaction(destination, path)
+        return self._do_request_for_transaction(destination, subpath)
 
     @log_function
     def paginate(self, dest, context, pdu_tuples, limit):
@@ -129,14 +133,14 @@ class TransportLayer(object):
         if not pdu_tuples:
             return
 
-        path = "/paginate/%s/" % context
+        subpath = "/paginate/%s/" % context
 
         args = {"v": ["%s,%s" % (i, o) for i, o in pdu_tuples]}
         args["limit"] = limit
 
         return self._do_request_for_transaction(
             dest,
-            path,
+            subpath,
             args=args,
         )
 
@@ -165,7 +169,7 @@ class TransportLayer(object):
 
         code, response = yield self.client.put_json(
             transaction.destination,
-            path="/send/%s/" % transaction.transaction_id,
+            path=PREFIX + "/send/%s/" % transaction.transaction_id,
             data=data
         )
 
@@ -188,7 +192,7 @@ class TransportLayer(object):
         # This is when someone is trying to send us a bunch of data.
         self.server.register_path(
             "PUT",
-            re.compile("^/send/([^/]*)/$"),
+            re.compile("^" + PREFIX + "/send/([^/]*)/$"),
             self._on_send_request
         )
 
@@ -206,7 +210,7 @@ class TransportLayer(object):
         # This is for when someone asks us for everything since version X
         self.server.register_path(
             "GET",
-            re.compile("^/pull/$"),
+            re.compile("^" + PREFIX + "/pull/$"),
             lambda request: handler.on_pull_request(
                 request.args["origin"][0],
                 request.args["v"]
@@ -217,7 +221,7 @@ class TransportLayer(object):
         # data_id pair.
         self.server.register_path(
             "GET",
-            re.compile("^/pdu/([^/]*)/([^/]*)/$"),
+            re.compile("^" + PREFIX + "/pdu/([^/]*)/([^/]*)/$"),
             lambda request, pdu_origin, pdu_id: handler.on_pdu_request(
                 pdu_origin, pdu_id
             )
@@ -226,7 +230,7 @@ class TransportLayer(object):
         # This is when someone asks for all data for a given context.
         self.server.register_path(
             "GET",
-            re.compile("^/state/([^/]*)/$"),
+            re.compile("^" + PREFIX + "/state/([^/]*)/$"),
             lambda request, context: handler.on_context_state_request(
                 context
             )
@@ -234,7 +238,7 @@ class TransportLayer(object):
 
         self.server.register_path(
             "GET",
-            re.compile("^/paginate/([^/]*)/$"),
+            re.compile("^" + PREFIX + "/paginate/([^/]*)/$"),
             lambda request, context: self._on_paginate_request(
                 context, request.args["v"],
                 request.args["limit"]
@@ -243,7 +247,7 @@ class TransportLayer(object):
 
         self.server.register_path(
             "GET",
-            re.compile("^/context/([^/]*)/$"),
+            re.compile("^" + PREFIX + "/context/([^/]*)/$"),
             lambda request, context: handler.on_context_pdus_request(context)
         )
 
@@ -299,7 +303,7 @@ class TransportLayer(object):
 
     @defer.inlineCallbacks
     @log_function
-    def _do_request_for_transaction(self, destination, path, args={}):
+    def _do_request_for_transaction(self, destination, subpath, args={}):
         """
         Args:
             destination (str)
@@ -312,7 +316,7 @@ class TransportLayer(object):
 
         data = yield self.client.get_json(
             destination,
-            path=path,
+            path=PREFIX + subpath,
             args=args,
         )
 
diff --git a/synapse/federation/units.py b/synapse/federation/units.py
index 0efea7b768..2b2f11f36a 100644
--- a/synapse/federation/units.py
+++ b/synapse/federation/units.py
@@ -12,6 +12,7 @@
 # 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.
+
 """ Defines the JSON structure of the protocol units used by the server to
 server protocol.
 """
diff --git a/synapse/handlers/__init__.py b/synapse/handlers/__init__.py
index 5688b68e49..8a4aa6e5d6 100644
--- a/synapse/handlers/__init__.py
+++ b/synapse/handlers/__init__.py
@@ -12,6 +12,7 @@
 # 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 .register import RegistrationHandler
 from .room import (
     MessageHandler, RoomCreationHandler, RoomMemberHandler, RoomListHandler
diff --git a/synapse/handlers/_base.py b/synapse/handlers/_base.py
index 87a392dd77..c2f4685c92 100644
--- a/synapse/handlers/_base.py
+++ b/synapse/handlers/_base.py
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 
+
 class BaseHandler(object):
 
     def __init__(self, hs):
diff --git a/synapse/handlers/directory.py b/synapse/handlers/directory.py
index 456007c71d..3cc6348906 100644
--- a/synapse/handlers/directory.py
+++ b/synapse/handlers/directory.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import defer
 from ._base import BaseHandler
 
diff --git a/synapse/handlers/events.py b/synapse/handlers/events.py
index 79742a4e1c..3af7d824a2 100644
--- a/synapse/handlers/events.py
+++ b/synapse/handlers/events.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from ._base import BaseHandler
diff --git a/synapse/handlers/federation.py b/synapse/handlers/federation.py
index 12e7afca4c..7026df90a2 100644
--- a/synapse/handlers/federation.py
+++ b/synapse/handlers/federation.py
@@ -12,6 +12,7 @@
 # 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.
+
 """Contains handlers for federation events."""
 
 from ._base import BaseHandler
diff --git a/synapse/handlers/login.py b/synapse/handlers/login.py
index 5a1acd7102..ca69829d77 100644
--- a/synapse/handlers/login.py
+++ b/synapse/handlers/login.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from ._base import BaseHandler
diff --git a/synapse/handlers/presence.py b/synapse/handlers/presence.py
index 38db4b1d67..1c24efd454 100644
--- a/synapse/handlers/presence.py
+++ b/synapse/handlers/presence.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from synapse.api.errors import SynapseError, AuthError
diff --git a/synapse/handlers/profile.py b/synapse/handlers/profile.py
index a27206b002..976b8cfcdc 100644
--- a/synapse/handlers/profile.py
+++ b/synapse/handlers/profile.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from synapse.api.errors import SynapseError, AuthError
diff --git a/synapse/handlers/register.py b/synapse/handlers/register.py
index 246c1f6530..593c603346 100644
--- a/synapse/handlers/register.py
+++ b/synapse/handlers/register.py
@@ -12,6 +12,7 @@
 # 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.
+
 """Contains functions for registering clients."""
 from twisted.internet import defer
 
diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 4d82b33993..eae40765b3 100644
--- a/synapse/handlers/room.py
+++ b/synapse/handlers/room.py
@@ -12,6 +12,7 @@
 # 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.
+
 """Contains functions for performing events on rooms."""
 from twisted.internet import defer
 
diff --git a/synapse/http/__init__.py b/synapse/http/__init__.py
index fe8a073cd3..2216c0f1ca 100644
--- a/synapse/http/__init__.py
+++ b/synapse/http/__init__.py
@@ -12,3 +12,4 @@
 # 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.
+
diff --git a/synapse/http/client.py b/synapse/http/client.py
index bb22b0ee9a..d0facbdc6c 100644
--- a/synapse/http/client.py
+++ b/synapse/http/client.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import defer, reactor
 from twisted.web.client import _AgentBase, _URI, readBody
 from twisted.web.http_headers import Headers
diff --git a/synapse/http/endpoint.py b/synapse/http/endpoint.py
index c4e6e63a80..d91500b07d 100644
--- a/synapse/http/endpoint.py
+++ b/synapse/http/endpoint.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet.endpoints import SSL4ClientEndpoint, TCP4ClientEndpoint
 from twisted.internet import defer
 from twisted.internet.error import ConnectError
diff --git a/synapse/http/server.py b/synapse/http/server.py
index 9fb45971d5..d7f4b691bc 100644
--- a/synapse/http/server.py
+++ b/synapse/http/server.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from syutil.jsonutil import (
     encode_canonical_json, encode_pretty_printed_json
 )
diff --git a/synapse/rest/__init__.py b/synapse/rest/__init__.py
index 5598295793..74a372e2ff 100644
--- a/synapse/rest/__init__.py
+++ b/synapse/rest/__init__.py
@@ -13,10 +13,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from . import (
-    room, events, register, profile, public, presence, im, directory
+    room, events, register, login, profile, public, presence, im, directory,
+    webclient
 )
 
+
 class RestServletFactory(object):
 
     """ A factory for creating REST servlets.
@@ -35,10 +38,13 @@ class RestServletFactory(object):
         room.register_servlets(hs, http_server)
         events.register_servlets(hs, http_server)
         register.register_servlets(hs, http_server)
+        login.register_servlets(hs, http_server)
         profile.register_servlets(hs, http_server)
         public.register_servlets(hs, http_server)
         presence.register_servlets(hs, http_server)
         im.register_servlets(hs, http_server)
         directory.register_servlets(hs, http_server)
 
-
+    def register_web_client(self, hs):
+        http_server = hs.get_http_server()
+        webclient.register_servlets(hs, http_server)
diff --git a/synapse/rest/base.py b/synapse/rest/base.py
index d90ac611fe..65d417f757 100644
--- a/synapse/rest/base.py
+++ b/synapse/rest/base.py
@@ -12,6 +12,7 @@
 # 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.
+
 """ This module contains base REST classes for constructing REST servlets. """
 import re
 
@@ -29,48 +30,6 @@ def client_path_pattern(path_regex):
     return re.compile("^/matrix/client/api/v1" + path_regex)
 
 
-class RestServletFactory(object):
-
-    """ A factory for creating REST servlets.
-
-    These REST servlets represent the entire client-server REST API. Generally
-    speaking, they serve as wrappers around events and the handlers that
-    process them.
-
-    See synapse.api.events for information on synapse events.
-    """
-
-    def __init__(self, hs):
-        http_server = hs.get_http_server()
-
-        # You get import errors if you try to import before the classes in this
-        # file are defined, hence importing here instead.
-
-        import room
-        room.register_servlets(hs, http_server)
-
-        import events
-        events.register_servlets(hs, http_server)
-
-        import register
-        register.register_servlets(hs, http_server)
-
-        import profile
-        profile.register_servlets(hs, http_server)
-
-        import public
-        public.register_servlets(hs, http_server)
-
-        import presence
-        presence.register_servlets(hs, http_server)
-
-        import im
-        im.register_servlets(hs, http_server)
-
-        import login
-        login.register_servlets(hs, http_server)
-
-
 class RestServlet(object):
 
     """ A Synapse REST Servlet.
diff --git a/synapse/rest/directory.py b/synapse/rest/directory.py
index a426003a38..31fd26e848 100644
--- a/synapse/rest/directory.py
+++ b/synapse/rest/directory.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import defer
 
 from synapse.types import RoomAlias, RoomID
diff --git a/synapse/rest/events.py b/synapse/rest/events.py
index 147257a940..dc811b813a 100644
--- a/synapse/rest/events.py
+++ b/synapse/rest/events.py
@@ -12,6 +12,7 @@
 # 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.
+
 """This module contains REST servlets to do with event streaming, /events."""
 from twisted.internet import defer
 
diff --git a/synapse/rest/im.py b/synapse/rest/im.py
index 39f2dbd749..63a77716a0 100644
--- a/synapse/rest/im.py
+++ b/synapse/rest/im.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from synapse.api.streams import PaginationConfig
diff --git a/synapse/rest/login.py b/synapse/rest/login.py
index 0284e125b4..88a3218332 100644
--- a/synapse/rest/login.py
+++ b/synapse/rest/login.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from synapse.api.errors import SynapseError
diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py
index e4925c20a5..6043848595 100644
--- a/synapse/rest/presence.py
+++ b/synapse/rest/presence.py
@@ -12,6 +12,7 @@
 # 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.
+
 """ This module contains REST servlets to do with presence: /presence/<paths>
 """
 from twisted.internet import defer
diff --git a/synapse/rest/profile.py b/synapse/rest/profile.py
index f384227c29..3d0427bf72 100644
--- a/synapse/rest/profile.py
+++ b/synapse/rest/profile.py
@@ -12,6 +12,7 @@
 # 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.
+
 """ This module contains REST servlets to do with profile: /profile/<paths> """
 from twisted.internet import defer
 
diff --git a/synapse/rest/public.py b/synapse/rest/public.py
index 6fd1731a61..3430c8049f 100644
--- a/synapse/rest/public.py
+++ b/synapse/rest/public.py
@@ -12,6 +12,7 @@
 # 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.
+
 """This module contains REST servlets to do with public paths: /public"""
 from twisted.internet import defer
 
diff --git a/synapse/rest/register.py b/synapse/rest/register.py
index f1cbce5c67..eb457562b9 100644
--- a/synapse/rest/register.py
+++ b/synapse/rest/register.py
@@ -12,6 +12,7 @@
 # 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.
+
 """This module contains REST servlets to do with registration: /register"""
 from twisted.internet import defer
 
diff --git a/synapse/rest/room.py b/synapse/rest/room.py
index c96de5e65d..228bc9623d 100644
--- a/synapse/rest/room.py
+++ b/synapse/rest/room.py
@@ -12,6 +12,7 @@
 # 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.
+
 """ This module contains REST servlets to do with rooms: /rooms/<paths> """
 from twisted.internet import defer
 
diff --git a/synapse/rest/webclient.py b/synapse/rest/webclient.py
new file mode 100644
index 0000000000..75a425c14c
--- /dev/null
+++ b/synapse/rest/webclient.py
@@ -0,0 +1,45 @@
+# -*- 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 synapse.rest.base import RestServlet
+
+import logging
+import re
+
+logger = logging.getLogger(__name__)
+
+
+class WebClientRestServlet(RestServlet):
+    # No PATTERN; we have custom dispatch rules here
+
+    def register(self, http_server):
+        http_server.register_path("GET",
+                                  re.compile("^/$"),
+                                  self.on_GET_redirect)
+        http_server.register_path("GET",
+                                  re.compile("^/matrix/client$"),
+                                  self.on_GET)
+
+    def on_GET(self, request):
+        return (200, "not implemented")
+
+    def on_GET_redirect(self, request):
+        request.setHeader("Location", request.uri + "matrix/client")
+        return (302, None)
+
+
+def register_servlets(hs, http_server):
+    logger.info("Registering web client.")
+    WebClientRestServlet(hs).register(http_server)
\ No newline at end of file
diff --git a/synapse/server.py b/synapse/server.py
index 0aff75f399..0211972d05 100644
--- a/synapse/server.py
+++ b/synapse/server.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 # This file provides some classes for setting up (partially-populated)
 # homeservers; either as a full homeserver as a real application, or a small
 # partial one for unit test mocking.
@@ -171,6 +172,10 @@ class HomeServer(BaseHomeServer):
         return Distributor()
 
     def register_servlets(self):
-        """Simply building the ServletFactory is sufficient to have it
-        register."""
-        self.get_rest_servlet_factory()
+        """ Register all servlets associated with this HomeServer.
+
+        Args:
+            host_web_client (bool): True to host the web client as well.
+        """
+        # Simply building the ServletFactory is sufficient to have it register
+        factory = self.get_rest_servlet_factory()
diff --git a/synapse/state.py b/synapse/state.py
index 439c0b519a..b081de8f4f 100644
--- a/synapse/state.py
+++ b/synapse/state.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import defer
 
 from synapse.federation.pdu_codec import encode_event_id
diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py
index ec93f9f8a7..3c27428c08 100644
--- a/synapse/storage/__init__.py
+++ b/synapse/storage/__init__.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from synapse.api.events.room import (
     RoomMemberEvent, MessageEvent, RoomTopicEvent, FeedbackEvent,
     RoomConfigEvent
diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py
index 4d98a6fd0d..65f691ead4 100644
--- a/synapse/storage/_base.py
+++ b/synapse/storage/_base.py
@@ -12,6 +12,7 @@
 # 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.
+
 import logging
 
 from twisted.internet import defer
diff --git a/synapse/storage/directory.py b/synapse/storage/directory.py
index 71fa9d9c9c..b22ce02f3f 100644
--- a/synapse/storage/directory.py
+++ b/synapse/storage/directory.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore
 from twisted.internet import defer
 
diff --git a/synapse/storage/feedback.py b/synapse/storage/feedback.py
index 2b421e3342..9bd562c762 100644
--- a/synapse/storage/feedback.py
+++ b/synapse/storage/feedback.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore, Table
 from synapse.api.events.room import FeedbackEvent
 
diff --git a/synapse/storage/message.py b/synapse/storage/message.py
index 4822fa709d..7bb69c1384 100644
--- a/synapse/storage/message.py
+++ b/synapse/storage/message.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore, Table
 from synapse.api.events.room import MessageEvent
 
diff --git a/synapse/storage/pdu.py b/synapse/storage/pdu.py
index a1cdde0a3b..202d7f6cb6 100644
--- a/synapse/storage/pdu.py
+++ b/synapse/storage/pdu.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore, Table, JoinHelper
 
 from synapse.util.logutils import log_function
diff --git a/synapse/storage/presence.py b/synapse/storage/presence.py
index e57ddaf149..6f5b042c25 100644
--- a/synapse/storage/presence.py
+++ b/synapse/storage/presence.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore
 
 
diff --git a/synapse/storage/profile.py b/synapse/storage/profile.py
index d2f24930c1..91dd565033 100644
--- a/synapse/storage/profile.py
+++ b/synapse/storage/profile.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore
 
 
diff --git a/synapse/storage/registration.py b/synapse/storage/registration.py
index 4a970dd546..68cdfbb4ca 100644
--- a/synapse/storage/registration.py
+++ b/synapse/storage/registration.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from sqlite3 import IntegrityError
diff --git a/synapse/storage/room.py b/synapse/storage/room.py
index 174cbcf3d8..a97162831b 100644
--- a/synapse/storage/room.py
+++ b/synapse/storage/room.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from sqlite3 import IntegrityError
@@ -92,7 +93,10 @@ class RoomStore(SQLBaseStore):
         latest_topic = ("SELECT max(room_data.id) FROM room_data WHERE "
                         + "room_data.type = ? GROUP BY room_id")
 
-        query = ("SELECT rooms.*, room_data.content FROM rooms LEFT JOIN "
+        query = ("SELECT rooms.*, room_data.content, room_alias FROM rooms "
+                 + "LEFT JOIN "
+                 + "room_aliases ON room_aliases.room_id = rooms.room_id "
+                 + "LEFT JOIN "
                  + "room_data ON rooms.room_id = room_data.room_id WHERE "
                  + "(room_data.id IN (" + latest_topic + ") "
                  + "OR room_data.id IS NULL) AND rooms.is_public = ?")
@@ -102,7 +106,7 @@ class RoomStore(SQLBaseStore):
         )
 
         # return only the keys the specification expects
-        ret_keys = ["room_id", "topic"]
+        ret_keys = ["room_id", "topic", "room_alias"]
 
         # extract topic from the json (icky) FIXME
         for i, room_row in enumerate(res):
diff --git a/synapse/storage/roomdata.py b/synapse/storage/roomdata.py
index 781d477931..cc04d1ba14 100644
--- a/synapse/storage/roomdata.py
+++ b/synapse/storage/roomdata.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore, Table
 
 import collections
diff --git a/synapse/storage/roommember.py b/synapse/storage/roommember.py
index e6e7617797..ef73be4af4 100644
--- a/synapse/storage/roommember.py
+++ b/synapse/storage/roommember.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 from synapse.types import UserID
diff --git a/synapse/storage/stream.py b/synapse/storage/stream.py
index c3b1bfeb32..1dedffac49 100644
--- a/synapse/storage/stream.py
+++ b/synapse/storage/stream.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from ._base import SQLBaseStore
 from .message import MessagesTable
 from .feedback import FeedbackTable
diff --git a/synapse/storage/transactions.py b/synapse/storage/transactions.py
index aa41e2ad7f..a277e4971a 100644
--- a/synapse/storage/transactions.py
+++ b/synapse/storage/transactions.py
@@ -12,6 +12,7 @@
 # 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 SQLBaseStore, Table
 from .pdu import PdusTable
 
diff --git a/synapse/types.py b/synapse/types.py
index 1adc95bbb0..054b1e713c 100644
--- a/synapse/types.py
+++ b/synapse/types.py
@@ -12,6 +12,7 @@
 # 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 synapse.api.errors import SynapseError
 
 from collections import namedtuple
diff --git a/synapse/util/__init__.py b/synapse/util/__init__.py
index 5361cb7ec2..3ea431a7f9 100644
--- a/synapse/util/__init__.py
+++ b/synapse/util/__init__.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import reactor
 
 import time
diff --git a/synapse/util/async.py b/synapse/util/async.py
index e04db8e285..ebbdc00ae1 100644
--- a/synapse/util/async.py
+++ b/synapse/util/async.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import defer, reactor
 
 
diff --git a/synapse/util/distributor.py b/synapse/util/distributor.py
index 32d19402b4..9605d7d1b9 100644
--- a/synapse/util/distributor.py
+++ b/synapse/util/distributor.py
@@ -12,6 +12,7 @@
 # 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 twisted.internet import defer
 
 import logging
diff --git a/synapse/util/jsonobject.py b/synapse/util/jsonobject.py
index 190a80a322..e2840b59f9 100644
--- a/synapse/util/jsonobject.py
+++ b/synapse/util/jsonobject.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 import copy
 
 class JsonEncodedObject(object):
diff --git a/synapse/util/lockutils.py b/synapse/util/lockutils.py
index e4d609d84e..758be0b901 100644
--- a/synapse/util/lockutils.py
+++ b/synapse/util/lockutils.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from twisted.internet import defer
 
 import logging
diff --git a/synapse/util/logutils.py b/synapse/util/logutils.py
index 08d5aafca4..9270a1790b 100644
--- a/synapse/util/logutils.py
+++ b/synapse/util/logutils.py
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+
 from inspect import getcallargs
 
 import logging
diff --git a/synapse/util/stringutils.py b/synapse/util/stringutils.py
index 91550583a4..e1b0796e56 100644
--- a/synapse/util/stringutils.py
+++ b/synapse/util/stringutils.py
@@ -12,6 +12,7 @@
 # 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.
+
 import random
 import string
 
diff --git a/tests/__init__.py b/tests/__init__.py
index 40a96afc6f..2216c0f1ca 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1 +1,15 @@
 # -*- 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.
+
diff --git a/tests/events/__init__.py b/tests/events/__init__.py
index 40a96afc6f..2216c0f1ca 100644
--- a/tests/events/__init__.py
+++ b/tests/events/__init__.py
@@ -1 +1,15 @@
 # -*- 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.
+
diff --git a/tests/events/test_events.py b/tests/events/test_events.py
index 11d3d09c96..35e9c68f5c 100644
--- a/tests/events/test_events.py
+++ b/tests/events/test_events.py
@@ -1,4 +1,18 @@
 # -*- 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 synapse.api.events import SynapseEvent
 
 import unittest
diff --git a/tests/federation/test_federation.py b/tests/federation/test_federation.py
index 1792f9de56..f493ee253e 100644
--- a/tests/federation/test_federation.py
+++ b/tests/federation/test_federation.py
@@ -1,3 +1,17 @@
+# 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.
+
 # trial imports
 from twisted.internet import defer
 from twisted.trial import unittest
@@ -82,7 +96,7 @@ class FederationTestCase(unittest.TestCase):
 
         # Empty context initially
         (code, response) = yield self.mock_http_server.trigger("GET",
-                "/state/my-context/", None)
+                "/matrix/federation/v1/state/my-context/", None)
         self.assertEquals(200, code)
         self.assertFalse(response["pdus"])
 
@@ -107,7 +121,7 @@ class FederationTestCase(unittest.TestCase):
         )
 
         (code, response) = yield self.mock_http_server.trigger("GET",
-                "/state/my-context/", None)
+                "/matrix/federation/v1/state/my-context/", None)
         self.assertEquals(200, code)
         self.assertEquals(1, len(response["pdus"]))
 
@@ -118,7 +132,7 @@ class FederationTestCase(unittest.TestCase):
         )
 
         (code, response) = yield self.mock_http_server.trigger("GET",
-                "/pdu/red/abc123def456/", None)
+                "/matrix/federation/v1/pdu/red/abc123def456/", None)
         self.assertEquals(404, code)
 
         # Now insert such a PDU
@@ -137,7 +151,7 @@ class FederationTestCase(unittest.TestCase):
         )
 
         (code, response) = yield self.mock_http_server.trigger("GET",
-                "/pdu/red/abc123def456/", None)
+                "/matrix/federation/v1/pdu/red/abc123def456/", None)
         self.assertEquals(200, code)
         self.assertEquals(1, len(response["pdus"]))
         self.assertEquals("m.text", response["pdus"][0]["pdu_type"])
@@ -163,7 +177,7 @@ class FederationTestCase(unittest.TestCase):
 
         self.mock_http_client.put_json.assert_called_with(
                 "remote",
-                path="/send/1000000/",
+                path="/matrix/federation/v1/send/1000000/",
                 data={
                     "ts": 1000000,
                     "origin": "test",
@@ -198,7 +212,7 @@ class FederationTestCase(unittest.TestCase):
         # MockClock ensures we can guess these timestamps
         self.mock_http_client.put_json.assert_called_with(
                 "remote",
-                path="/send/1000000/",
+                path="/matrix/federation/v1/send/1000000/",
                 data={
                     "origin": "test",
                     "ts": 1000000,
@@ -220,7 +234,8 @@ class FederationTestCase(unittest.TestCase):
 
         self.federation.register_edu_handler("m.test", recv_observer)
 
-        yield self.mock_http_server.trigger("PUT", "/send/1001000/",
+        yield self.mock_http_server.trigger("PUT",
+                "/matrix/federation/v1/send/1001000/",
                 """{
                     "origin": "remote",
                     "ts": 1001000,
diff --git a/tests/federation/test_pdu_codec.py b/tests/federation/test_pdu_codec.py
index 688182fa5b..2c546040b8 100644
--- a/tests/federation/test_pdu_codec.py
+++ b/tests/federation/test_pdu_codec.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.trial import unittest
 
 from synapse.federation.pdu_codec import (
diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py
index 880cfb47b8..bdee7cfad4 100644
--- a/tests/handlers/test_federation.py
+++ b/tests/handlers/test_federation.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.internet import defer
 from twisted.trial import unittest
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index e814357520..2299a2a7ba 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.trial import unittest
 from twisted.internet import defer
@@ -13,7 +27,7 @@ from synapse.handlers.presence import PresenceHandler, UserPresenceCache
 
 
 OFFLINE = PresenceState.OFFLINE
-BUSY = PresenceState.BUSY
+UNAVAILABLE = PresenceState.UNAVAILABLE
 ONLINE = PresenceState.ONLINE
 
 
@@ -135,12 +149,12 @@ class PresenceStateTestCase(unittest.TestCase):
 
         yield self.handler.set_state(
                 target_user=self.u_apple, auth_user=self.u_apple,
-                state={"state": BUSY, "status_msg": "Away"})
+                state={"state": UNAVAILABLE, "status_msg": "Away"})
 
         mocked_set.assert_called_with("apple",
-                {"state": 1, "status_msg": "Away"})
+                {"state": UNAVAILABLE, "status_msg": "Away"})
         self.mock_start.assert_called_with(self.u_apple,
-                state={"state": 1, "status_msg": "Away"})
+                state={"state": UNAVAILABLE, "status_msg": "Away"})
 
         yield self.handler.set_state(
                 target_user=self.u_apple, auth_user=self.u_apple,
@@ -541,7 +555,7 @@ class PresencePushTestCase(unittest.TestCase):
                     content={
                         "push": [
                             {"user_id": "@apple:test",
-                            "state": 2},
+                            "state": "online"},
                         ],
                     }),
                 call(
@@ -550,7 +564,7 @@ class PresencePushTestCase(unittest.TestCase):
                     content={
                         "push": [
                             {"user_id": "@apple:test",
-                             "state": 2},
+                             "state": "online"},
                         ],
                     })
         ], any_order=True)
@@ -568,7 +582,7 @@ class PresencePushTestCase(unittest.TestCase):
                 "remote", "m.presence", {
                     "push": [
                         {"user_id": "@potato:remote",
-                         "state": 2},
+                         "state": "online"},
                     ],
                 }
         )
@@ -632,7 +646,7 @@ class PresencePushTestCase(unittest.TestCase):
                     content={
                         "push": [
                             {"user_id": "@apple:test",
-                            "state": 2},
+                            "state": "online"},
                         ],
                     }),
                 call(
@@ -641,7 +655,7 @@ class PresencePushTestCase(unittest.TestCase):
                     content={
                         "push": [
                             {"user_id": "@banana:test",
-                            "state": 0},
+                            "state": "offline"},
                         ],
                     }),
         ], any_order=True)
@@ -652,7 +666,7 @@ class PresencePushTestCase(unittest.TestCase):
 
         self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
         self.handler._user_cachemap[self.u_clementine].update(
-                {"state": PresenceState.ONLINE}, self.u_clementine)
+                {"state": ONLINE}, self.u_clementine)
         self.room_members.append(self.u_potato)
 
         yield self.distributor.fire("user_joined_room", self.u_clementine,
@@ -666,7 +680,7 @@ class PresencePushTestCase(unittest.TestCase):
                     content={
                         "push": [
                             {"user_id": "@clementine:test",
-                            "state": 2},
+                            "state": "online"},
                         ],
                     }),
         )
@@ -868,7 +882,7 @@ class PresencePollingTestCase(unittest.TestCase):
                 content={
                     "push": [
                         {"user_id": "@banana:test",
-                         "state": 0,
+                         "state": "offline",
                          "status_msg": None},
                     ],
                 },
diff --git a/tests/handlers/test_presencelike.py b/tests/handlers/test_presencelike.py
index c194e4dd72..224cf646f4 100644
--- a/tests/handlers/test_presencelike.py
+++ b/tests/handlers/test_presencelike.py
@@ -1,4 +1,18 @@
 # -*- 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.
+
 """This file contains tests of the "presence-like" data that is shared between
 presence and profiles; namely, the displayname and avatar_url."""
 
@@ -15,7 +29,7 @@ from synapse.handlers.profile import ProfileHandler
 
 
 OFFLINE = PresenceState.OFFLINE
-BUSY = PresenceState.BUSY
+UNAVAILABLE = PresenceState.UNAVAILABLE
 ONLINE = PresenceState.ONLINE
 
 
@@ -111,12 +125,12 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
 
         yield self.handlers.presence_handler.set_state(
                 target_user=self.u_apple, auth_user=self.u_apple,
-                state={"state": BUSY, "status_msg": "Away"})
+                state={"state": UNAVAILABLE, "status_msg": "Away"})
 
         mocked_set.assert_called_with("apple",
-                {"state": 1, "status_msg": "Away"})
+                {"state": UNAVAILABLE, "status_msg": "Away"})
         self.mock_start.assert_called_with(self.u_apple,
-                state={"state": 1, "status_msg": "Away",
+                state={"state": UNAVAILABLE, "status_msg": "Away",
                        "displayname": "Frank",
                        "avatar_url": "http://foo"})
 
@@ -206,7 +220,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
                 content={
                     "push": [
                         {"user_id": "@apple:test",
-                         "state": 2,
+                         "state": "online",
                          "displayname": "Frank",
                          "avatar_url": "http://foo"},
                     ],
@@ -224,7 +238,7 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
                 "remote", "m.presence", {
                     "push": [
                         {"user_id": "@potato:remote",
-                         "state": 2,
+                         "state": "online",
                          "displayname": "Frank",
                          "avatar_url": "http://foo"},
                     ],
diff --git a/tests/handlers/test_profile.py b/tests/handlers/test_profile.py
index a4408e9fd3..34af7afd01 100644
--- a/tests/handlers/test_profile.py
+++ b/tests/handlers/test_profile.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.trial import unittest
 from twisted.internet import defer
diff --git a/tests/handlers/test_room.py b/tests/handlers/test_room.py
index 26b233bccd..99067da6a5 100644
--- a/tests/handlers/test_room.py
+++ b/tests/handlers/test_room.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.internet import defer
 from twisted.trial import unittest
diff --git a/tests/rest/__init__.py b/tests/rest/__init__.py
index 40a96afc6f..2216c0f1ca 100644
--- a/tests/rest/__init__.py
+++ b/tests/rest/__init__.py
@@ -1 +1,15 @@
 # -*- 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.
+
diff --git a/tests/rest/test_events.py b/tests/rest/test_events.py
index fa40e049ea..e1b3cb4ddf 100644
--- a/tests/rest/test_events.py
+++ b/tests/rest/test_events.py
@@ -1,4 +1,18 @@
 # -*- 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.
+
 """ Tests REST events for /events paths."""
 from twisted.trial import unittest
 
diff --git a/tests/rest/test_presence.py b/tests/rest/test_presence.py
index 3a2e86e5c4..7c54e067c9 100644
--- a/tests/rest/test_presence.py
+++ b/tests/rest/test_presence.py
@@ -1,4 +1,18 @@
 # -*- 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.
+
 """Tests REST events for /presence paths."""
 
 from twisted.trial import unittest
@@ -17,7 +31,7 @@ logging.getLogger().addHandler(logging.NullHandler())
 
 
 OFFLINE = PresenceState.OFFLINE
-BUSY = PresenceState.BUSY
+UNAVAILABLE = PresenceState.UNAVAILABLE
 ONLINE = PresenceState.ONLINE
 
 
@@ -55,7 +69,7 @@ class PresenceStateTestCase(unittest.TestCase):
     def test_get_my_status(self):
         mocked_get = self.mock_handler.get_state
         mocked_get.return_value = defer.succeed(
-                {"state": 2, "status_msg": "Available"})
+                {"state": ONLINE, "status_msg": "Available"})
 
         (code, response) = yield self.mock_server.trigger("GET",
                 "/presence/%s/status" % (myid), None)
@@ -73,12 +87,12 @@ class PresenceStateTestCase(unittest.TestCase):
 
         (code, response) = yield self.mock_server.trigger("PUT",
                 "/presence/%s/status" % (myid),
-                '{"state": 1, "status_msg": "Away"}')
+                '{"state": "unavailable", "status_msg": "Away"}')
 
         self.assertEquals(200, code)
         mocked_set.assert_called_with(target_user=self.u_apple,
                 auth_user=self.u_apple,
-                state={"state": 1, "status_msg": "Away"})
+                state={"state": UNAVAILABLE, "status_msg": "Away"})
 
 
 class PresenceListTestCase(unittest.TestCase):
@@ -220,7 +234,7 @@ class PresenceEventStreamTestCase(unittest.TestCase):
         # I'll already get my own presence state change
         self.assertEquals({"start": "0", "end": "1", "chunk": [
             {"type": "m.presence",
-             "content": {"user_id": "@apple:test", "state": 2}},
+             "content": {"user_id": "@apple:test", "state": ONLINE}},
         ]}, response)
 
         self.mock_datastore.set_presence_state.return_value = defer.succeed(
@@ -237,5 +251,5 @@ class PresenceEventStreamTestCase(unittest.TestCase):
         self.assertEquals(200, code)
         self.assertEquals({"start": "1", "end": "2", "chunk": [
             {"type": "m.presence",
-             "content": {"user_id": "@banana:test", "state": 2}},
+             "content": {"user_id": "@banana:test", "state": ONLINE}},
         ]}, response)
diff --git a/tests/rest/test_profile.py b/tests/rest/test_profile.py
index 13342b61e5..46e6137775 100644
--- a/tests/rest/test_profile.py
+++ b/tests/rest/test_profile.py
@@ -1,4 +1,18 @@
 # -*- 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.
+
 """Tests REST events for /profile paths."""
 
 from twisted.trial import unittest
diff --git a/tests/rest/test_rooms.py b/tests/rest/test_rooms.py
index 29e82fc13c..86025103db 100644
--- a/tests/rest/test_rooms.py
+++ b/tests/rest/test_rooms.py
@@ -1,4 +1,18 @@
 # -*- 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.
+
 """Tests REST events for /rooms paths."""
 
 # twisted imports
diff --git a/tests/rest/utils.py b/tests/rest/utils.py
index 7e4e570eff..a543f049c4 100644
--- a/tests/rest/utils.py
+++ b/tests/rest/utils.py
@@ -1,4 +1,18 @@
 # -*- 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.
+
 # twisted imports
 from twisted.internet import defer
 
diff --git a/tests/storage/test_base.py b/tests/storage/test_base.py
index 72869ef910..5567480921 100644
--- a/tests/storage/test_base.py
+++ b/tests/storage/test_base.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.trial import unittest
 from twisted.internet import defer
diff --git a/tests/test_distributor.py b/tests/test_distributor.py
index 36cbf6c52d..21c91f335b 100644
--- a/tests/test_distributor.py
+++ b/tests/test_distributor.py
@@ -1,3 +1,18 @@
+# -*- 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.
+
 import unittest
 
 from twisted.internet import defer
diff --git a/tests/test_state.py b/tests/test_state.py
index 8d0251b1e7..a2908a2eac 100644
--- a/tests/test_state.py
+++ b/tests/test_state.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.internet import defer
 from twisted.trial import unittest
 
diff --git a/tests/test_types.py b/tests/test_types.py
index 0f3c9492d0..522d52363d 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -1,3 +1,18 @@
+# -*- 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.
+
 import unittest
 
 from synapse.server import BaseHomeServer
diff --git a/tests/util/__init__.py b/tests/util/__init__.py
index 40a96afc6f..2216c0f1ca 100644
--- a/tests/util/__init__.py
+++ b/tests/util/__init__.py
@@ -1 +1,15 @@
 # -*- 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.
+
diff --git a/tests/util/test_lock.py b/tests/util/test_lock.py
index b7b8779fd3..dd83d204d9 100644
--- a/tests/util/test_lock.py
+++ b/tests/util/test_lock.py
@@ -1,4 +1,18 @@
 # -*- 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 twisted.internet import defer
 from twisted.trial import unittest
diff --git a/tests/utils.py b/tests/utils.py
index 13f6b31c9a..578866b4f4 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,3 +1,18 @@
+# -*- 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 synapse.http.server import HttpServer
 from synapse.api.errors import cs_error, CodeMessageException, StoreError
 from synapse.api.constants import Membership
diff --git a/webclient/app-controller.js b/webclient/app-controller.js
index e7601cc213..41055bdcd2 100644
--- a/webclient/app-controller.js
+++ b/webclient/app-controller.js
@@ -1,4 +1,20 @@
 /*
+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.
+*/
+
+/*
  * Main controller
  */
 
diff --git a/webclient/app.css b/webclient/app.css
index fd548c117b..15b6c91300 100644
--- a/webclient/app.css
+++ b/webclient/app.css
@@ -43,7 +43,7 @@ h1 {
 }
 
 .inputBarTable tr td {
-    padding: 4px;
+    padding: 1px 4px;
 }
 
 .mainInput {
@@ -106,7 +106,7 @@ h1 {
     background-color: #38AF00;
 }
 
-.away {
+.unavailable {
     background-color: #FFCC00;
 }
 
@@ -118,7 +118,7 @@ h1 {
    overflow separetely. 
   */
 .room .page {
-    height: calc(100vh - 198px); 
+    height: calc(100vh - 220px); 
 }
 
 /*** Message table ***/
@@ -187,6 +187,14 @@ h1 {
     border: 0px ! important;
 }
 
+.image {
+    display: block;
+    max-width:320px;
+    max-height:320px;
+    width: auto;
+    height: auto;
+}
+
 .bubble {
     padding: 6px;
     padding-left: 1em;
diff --git a/webclient/app.js b/webclient/app.js
index 2a75560edd..2133a98cbf 100644
--- a/webclient/app.js
+++ b/webclient/app.js
@@ -1,3 +1,19 @@
+/*
+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.
+*/
+
 var matrixWebClient = angular.module('matrixWebClient', [
     'ngRoute',
     'MatrixWebClientController',
diff --git a/webclient/components/matrix/matrix-service.js b/webclient/components/matrix/matrix-service.js
index f98874dc74..f054bf301e 100644
--- a/webclient/components/matrix/matrix-service.js
+++ b/webclient/components/matrix/matrix-service.js
@@ -1,3 +1,19 @@
+/*
+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.
+*/
+
 'use strict';
 
 angular.module('matrixService', [])
@@ -181,6 +197,17 @@ angular.module('matrixService', [])
             return this.sendMessage(room_id, msg_id, content);
         },
 
+        // Send an image message
+        sendImageMessage: function(room_id, image_url, image_alt, msg_id) {
+            var content = {
+                 msgtype: "m.image",
+                 url: image_url,
+                 body: image_alt
+            };
+
+            return this.sendMessage(room_id, msg_id, content);
+        },
+
         // Send an emote message
         sendEmoteMessage: function(room_id, body, msg_id) {
             var content = {
diff --git a/webclient/room/room-controller.js b/webclient/room/room-controller.js
index e54751ef6d..5d1c65641e 100644
--- a/webclient/room/room-controller.js
+++ b/webclient/room/room-controller.js
@@ -1,3 +1,19 @@
+/*
+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.
+*/
+
 angular.module('RoomController', [])
 .controller('RoomController', ['$scope', '$http', '$timeout', '$routeParams', '$location', 'matrixService',
                                function($scope, $http, $timeout, $routeParams, $location, matrixService) {
@@ -11,7 +27,8 @@ angular.module('RoomController', [])
     $scope.messages = [];
     $scope.members = {};
     $scope.stopPoll = false;
-    
+
+    $scope.imageURLToSend = "";
     $scope.userIDToInvite = "";
 
     var shortPoll = function() {
@@ -106,17 +123,14 @@ angular.module('RoomController', [])
         var member = $scope.members[chunk.content.user_id];
 
         if ("state" in chunk.content) {
-            var ONLINE = 2;
-            var AWAY = 1;
-            var OFFLINE = 0;
-            if (chunk.content.state === ONLINE) {
+            if (chunk.content.state === "online") {
                 member.presenceState = "online";
             }
-            else if (chunk.content.state === OFFLINE) {
+            else if (chunk.content.state === "offline") {
                 member.presenceState = "offline";
             }
-            else if (chunk.content.state === AWAY) {
-                member.presenceState = "away";
+            else if (chunk.content.state === "unavailable") {
+                member.presenceState = "unavailable";
             }
         }
 
@@ -208,6 +222,16 @@ angular.module('RoomController', [])
             });
     };
 
+    $scope.sendImage = function(url) {
+        matrixService.sendImageMessage($scope.room_id, url).then(
+            function() {
+                console.log("Image sent");
+            },
+            function(reason) {
+                $scope.feedback = "Failed to send image: " + reason;
+            });
+    };
+
     $scope.$on('$destroy', function(e) {
         console.log("onDestroyed: Stopping poll.");
         $scope.stopPoll = true;
diff --git a/webclient/room/room.html b/webclient/room/room.html
index 5e947a03e1..87d3458af5 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -14,7 +14,7 @@
                     <img class="userAvatarGradient" src="img/gradient.png" width="80" height="24"/>
                     <div class="userName">{{ info.displayname || name }}</div>
                 </td>
-                <td class="userPresence" ng-class="info.presenceState === 'online' ? 'online' : (info.presenceState === 'away' ? 'away' : '')" />
+                <td class="userPresence" ng-class="info.presenceState === 'online' ? 'online' : (info.presenceState === 'unavailable' ? 'unavailable' : '')" />
         </table>
     </div>
     
@@ -32,7 +32,8 @@
                 <td ng-class="!msg.content.membership_target ? (msg.content.msgtype === 'm.emote' ? 'emote text' : 'text') : ''">
                     <div class="bubble">
                         {{ msg.content.msgtype === "m.emote" ? ("* " + (members[msg.user_id].displayname || msg.user_id) + " ") : "" }}
-                        {{ msg.content.body }}
+                        {{ msg.content.msgtype === "m.text" ? msg.content.body : "" }}
+                        <img class="image" ng-hide='msg.content.msgtype !== "m.image"' src="{{ msg.content.url }}" alt="{{ msg.content.body }}"/>
                     </div>
                 </td>
                 <td class="rightBlock">
@@ -62,6 +63,18 @@
                         {{ feedback }}
                     </td>
                 </tr>
+                <tr>
+                    <td>
+                    </td>
+                    <td>
+                        <input class="mainInput" ng-model="imageURLToSend" ng-enter="sendImage()" placeholder="Image URL"/>
+                    </td>
+                    <td  width="100px">
+                        <button ng-click="sendImage(imageURLToSend)">Send Image</button>
+                    </td>
+                    <td>
+                    </td>
+                </tr>
             </table>
 
             <span>
diff --git a/webclient/rooms/rooms-controller.js b/webclient/rooms/rooms-controller.js
index 33912d63f9..a4499761bc 100644
--- a/webclient/rooms/rooms-controller.js
+++ b/webclient/rooms/rooms-controller.js
@@ -1,3 +1,19 @@
+/*
+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.
+*/
+
 'use strict';
 
 angular.module('RoomsController', ['matrixService'])