summary refs log tree commit diff
diff options
context:
space:
mode:
authorMark Haines <mark.haines@matrix.org>2014-10-17 17:33:58 +0100
committerMark Haines <mark.haines@matrix.org>2014-10-17 17:33:58 +0100
commitdc3c2823ac425c9b5bbe6883b7d33a12059a08fb (patch)
tree22d02a68df93c3f98b0be7de8a07040268420369
parentRename 'meta' to 'unsigned' (diff)
parentkeep 'origin_server_ts' as 'ts' in the database to avoid needlessly updating ... (diff)
downloadsynapse-dc3c2823ac425c9b5bbe6883b7d33a12059a08fb.tar.xz
Merge branch 'develop' into event_signing
Conflicts:
	synapse/federation/replication.py
-rw-r--r--scripts/basic.css510
-rwxr-xr-xscripts/gendoc.sh14
-rw-r--r--scripts/nature.css270
-rw-r--r--synapse/api/events/factory.py4
-rw-r--r--synapse/federation/pdu_codec.py4
-rw-r--r--synapse/federation/persistence.py2
-rw-r--r--synapse/federation/replication.py4
-rw-r--r--synapse/federation/units.py17
-rw-r--r--synapse/handlers/message.py16
-rw-r--r--synapse/rest/presence.py4
-rw-r--r--synapse/storage/__init__.py2
-rw-r--r--synapse/storage/_base.py3
-rw-r--r--synapse/storage/transactions.py13
-rw-r--r--tests/federation/test_federation.py10
-rw-r--r--tests/federation/test_pdu_codec.py4
-rw-r--r--tests/handlers/test_federation.py6
-rw-r--r--tests/handlers/test_presence.py2
-rw-r--r--tests/handlers/test_typing.py2
-rw-r--r--tests/test_state.py2
-rw-r--r--webclient/app-filter.js57
-rw-r--r--webclient/room/room.html4
21 files changed, 101 insertions, 849 deletions
diff --git a/scripts/basic.css b/scripts/basic.css
deleted file mode 100644
index 6411570ee6..0000000000
--- a/scripts/basic.css
+++ /dev/null
@@ -1,510 +0,0 @@
-/*
- * basic.css
- * ~~~~~~~~~
- *
- * Sphinx stylesheet -- basic theme.
- *
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-/* -- main layout ----------------------------------------------------------- */
-
-div.clearer {
-    clear: both;
-}
-
-/* -- relbar ---------------------------------------------------------------- */
-
-div.related {
-    width: 100%;
-    font-size: 90%;
-}
-
-div.related h3 {
-    display: none;
-}
-
-div.related ul {
-    margin: 0;
-    padding: 0 0 0 10px;
-    list-style: none;
-}
-
-div.related li {
-    display: inline;
-}
-
-div.related li.right {
-    float: right;
-    margin-right: 5px;
-}
-
-/* -- sidebar --------------------------------------------------------------- */
-
-div.sphinxsidebarwrapper {
-    padding: 10px 5px 0 10px;
-}
-
-div.sphinxsidebar {
-    float: left;
-    width: 230px;
-    margin-left: -100%;
-    font-size: 90%;
-}
-
-div.sphinxsidebar ul {
-    list-style: none;
-}
-
-div.sphinxsidebar ul ul,
-div.sphinxsidebar ul.want-points {
-    margin-left: 20px;
-    list-style: square;
-}
-
-div.sphinxsidebar ul ul {
-    margin-top: 0;
-    margin-bottom: 0;
-}
-
-div.sphinxsidebar form {
-    margin-top: 10px;
-}
-
-div.sphinxsidebar input {
-    border: 1px solid #98dbcc;
-    font-family: sans-serif;
-    font-size: 1em;
-}
-
-img {
-    border: 0;
-}
-
-/* -- search page ----------------------------------------------------------- */
-
-ul.search {
-    margin: 10px 0 0 20px;
-    padding: 0;
-}
-
-ul.search li {
-    padding: 5px 0 5px 20px;
-    background-image: url(file.png);
-    background-repeat: no-repeat;
-    background-position: 0 7px;
-}
-
-ul.search li a {
-    font-weight: bold;
-}
-
-ul.search li div.context {
-    color: #888;
-    margin: 2px 0 0 30px;
-    text-align: left;
-}
-
-ul.keywordmatches li.goodmatch a {
-    font-weight: bold;
-}
-
-/* -- index page ------------------------------------------------------------ */
-
-table.contentstable {
-    width: 90%;
-}
-
-table.contentstable p.biglink {
-    line-height: 150%;
-}
-
-a.biglink {
-    font-size: 1.3em;
-}
-
-span.linkdescr {
-    font-style: italic;
-    padding-top: 5px;
-    font-size: 90%;
-}
-
-/* -- general index --------------------------------------------------------- */
-
-table.indextable {
-    width: 100%;
-}
-
-table.indextable td {
-    text-align: left;
-    vertical-align: top;
-}
-
-table.indextable dl, table.indextable dd {
-    margin-top: 0;
-    margin-bottom: 0;
-}
-
-table.indextable tr.pcap {
-    height: 10px;
-}
-
-table.indextable tr.cap {
-    margin-top: 10px;
-    background-color: #f2f2f2;
-}
-
-img.toggler {
-    margin-right: 3px;
-    margin-top: 3px;
-    cursor: pointer;
-}
-
-div.modindex-jumpbox {
-    border-top: 1px solid #ddd;
-    border-bottom: 1px solid #ddd;
-    margin: 1em 0 1em 0;
-    padding: 0.4em;
-}
-
-div.genindex-jumpbox {
-    border-top: 1px solid #ddd;
-    border-bottom: 1px solid #ddd;
-    margin: 1em 0 1em 0;
-    padding: 0.4em;
-}
-
-/* -- general body styles --------------------------------------------------- */
-
-a.headerlink {
-    visibility: hidden;
-}
-
-h1:hover > a.headerlink,
-h2:hover > a.headerlink,
-h3:hover > a.headerlink,
-h4:hover > a.headerlink,
-h5:hover > a.headerlink,
-h6:hover > a.headerlink,
-dt:hover > a.headerlink {
-    visibility: visible;
-}
-
-div.document p.caption {
-    text-align: inherit;
-}
-
-div.document td {
-    text-align: left;
-}
-
-.field-list ul {
-    padding-left: 1em;
-}
-
-.first {
-    margin-top: 0 !important;
-}
-
-p.rubric {
-    margin-top: 30px;
-    font-weight: bold;
-}
-
-.align-left {
-    text-align: left;
-}
-
-.align-center {
-    clear: both;
-    text-align: center;
-}
-
-.align-right {
-    text-align: right;
-}
-
-/* -- sidebars -------------------------------------------------------------- */
-
-div.sidebar {
-    margin: 0 0 0.5em 1em;
-    border: 1px solid #ddb;
-    padding: 7px 7px 0 7px;
-    background-color: #ffe;
-    width: 40%;
-    float: right;
-}
-
-p.sidebar-title {
-    font-weight: bold;
-}
-
-/* -- topics ---------------------------------------------------------------- */
-
-div.topic {
-    border: 1px solid #ccc;
-    padding: 7px 7px 0 7px;
-    margin: 10px 0 10px 0;
-}
-
-p.topic-title {
-    font-size: 1.1em;
-    font-weight: bold;
-    margin-top: 10px;
-}
-
-/* -- admonitions ----------------------------------------------------------- */
-
-div.admonition {
-    margin-top: 10px;
-    margin-bottom: 10px;
-    padding: 7px;
-}
-
-div.admonition dt {
-    font-weight: bold;
-}
-
-div.admonition dl {
-    margin-bottom: 0;
-}
-
-p.admonition-title {
-    margin: 0px 10px 5px 0px;
-    font-weight: bold;
-}
-
-div.document p.centered {
-    text-align: center;
-    margin-top: 25px;
-}
-
-/* -- tables ---------------------------------------------------------------- */
-
-table.docutils {
-    border: 0;
-    border-collapse: collapse;
-}
-
-table.docutils td, table.docutils th {
-    padding: 1px 8px 1px 5px;
-    border-top: 0;
-    border-left: 0;
-    border-right: 0;
-    border-bottom: 1px solid #aaa;
-}
-
-table.field-list td, table.field-list th {
-    border: 0 !important;
-}
-
-table.footnote td, table.footnote th {
-    border: 0 !important;
-}
-
-th {
-    text-align: left;
-    padding-right: 5px;
-}
-
-table.citation {
-    border-left: solid 1px gray;
-    margin-left: 1px;
-}
-
-table.citation td {
-    border-bottom: none;
-}
-
-/* -- other body styles ----------------------------------------------------- */
-
-ol.arabic {
-    list-style: decimal;
-}
-
-ol.loweralpha {
-    list-style: lower-alpha;
-}
-
-ol.upperalpha {
-    list-style: upper-alpha;
-}
-
-ol.lowerroman {
-    list-style: lower-roman;
-}
-
-ol.upperroman {
-    list-style: upper-roman;
-}
-
-dl {
-    margin-bottom: 15px;
-}
-
-dd p {
-    margin-top: 0px;
-}
-
-dd ul, dd table {
-    margin-bottom: 10px;
-}
-
-dd {
-    margin-top: 3px;
-    margin-bottom: 10px;
-    margin-left: 30px;
-}
-
-dt:target, .highlighted {
-    background-color: #fbe54e;
-}
-
-dl.glossary dt {
-    font-weight: bold;
-    font-size: 1.1em;
-}
-
-.field-list ul {
-    margin: 0;
-    padding-left: 1em;
-}
-
-.field-list p {
-    margin: 0;
-}
-
-.refcount {
-    color: #060;
-}
-
-.optional {
-    font-size: 1.3em;
-}
-
-.versionmodified {
-    font-style: italic;
-}
-
-.system-message {
-    background-color: #fda;
-    padding: 5px;
-    border: 3px solid red;
-}
-
-.footnote:target  {
-    background-color: #ffa
-}
-
-.line-block {
-    display: block;
-    margin-top: 1em;
-    margin-bottom: 1em;
-}
-
-.line-block .line-block {
-    margin-top: 0;
-    margin-bottom: 0;
-    margin-left: 1.5em;
-}
-
-.guilabel, .menuselection {
-    font-family: sans-serif;
-}
-
-.accelerator {
-    text-decoration: underline;
-}
-
-.classifier {
-    font-style: oblique;
-}
-
-/* -- code displays --------------------------------------------------------- */
-
-pre {
-    overflow: auto;
-}
-
-td.linenos pre {
-    padding: 5px 0px;
-    border: 0;
-    background-color: transparent;
-    color: #aaa;
-}
-
-table.highlighttable {
-    margin-left: 0.5em;
-}
-
-table.highlighttable td {
-    padding: 0 0.5em 0 0.5em;
-}
-
-tt.descname {
-    background-color: transparent;
-    font-weight: bold;
-    font-size: 1.2em;
-}
-
-tt.descclassname {
-    background-color: transparent;
-}
-
-tt.xref, a tt {
-    background-color: transparent;
-    font-weight: bold;
-}
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
-    background-color: transparent;
-}
-
-.viewcode-link {
-    float: right;
-}
-
-.viewcode-back {
-    float: right;
-    font-family: sans-serif;
-}
-
-div.viewcode-block:target {
-    margin: -1px -10px;
-    padding: 0 10px;
-}
-
-/* -- math display ---------------------------------------------------------- */
-
-img.math {
-    vertical-align: middle;
-}
-
-div.document div.math p {
-    text-align: center;
-}
-
-span.eqno {
-    float: right;
-}
-
-/* -- printout stylesheet --------------------------------------------------- */
-
-@media print {
-    div.document,
-    div.documentwrapper,
-    div.bodywrapper {
-        margin: 0 !important;
-        width: 100%;
-    }
-
-    div.sphinxsidebar,
-    div.related,
-    div.footer,
-    #top-link {
-        display: none;
-    }
-}
-
diff --git a/scripts/gendoc.sh b/scripts/gendoc.sh
deleted file mode 100755
index 64aff3155e..0000000000
--- a/scripts/gendoc.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-MATRIXDOTORG=$HOME/workspace/matrix.org
-
-rst2html-2.7.py --stylesheet=basic.css,nature.css ../docs/specification.rst > $MATRIXDOTORG/docs/spec/index.html
-rst2html-2.7.py --stylesheet=basic.css,nature.css ../docs/client-server/howto.rst > $MATRIXDOTORG/docs/howtos/client-server.html
-
-perl -pi -e 's#<head>#<head><link rel="stylesheet" href="/site.css">#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html
-
-perl -pi -e 's#<body>#<body><div id="header"><div id="headerContent">&nbsp;</div></div><div id="page"><div id="wrapper"><div style="text-align: center; padding: 40px;"><a href="/"><img src="/matrix.png" width="305" height="130" alt="[matrix]"/></a></div>#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html
-
-perl -pi -e 's#</body>#</div></div><div id="footer"><div id="footerContent">&copy 2014 Matrix.org</div></div></body>#' $MATRIXDOTORG/docs/spec/index.html $MATRIXDOTORG/docs/howtos/client-server.html
-
-scp -r $MATRIXDOTORG/docs matrix@ldc-prd-matrix-001:/sites/matrix
\ No newline at end of file
diff --git a/scripts/nature.css b/scripts/nature.css
deleted file mode 100644
index b8147f10ee..0000000000
--- a/scripts/nature.css
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * nature.css_t
- * ~~~~~~~~~~~~
- *
- * Sphinx stylesheet -- nature theme.
- *
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
- 
-/* -- page layout ----------------------------------------------------------- */
- 
-body {
-    font-family: Arial, sans-serif;
-    font-size: 100%;
-    /*background-color: #111;*/
-    color: #555;
-    margin: 0;
-    padding: 0;
-}
-
-div.documentwrapper {
-    float: left;
-    width: 100%;
-}
-
-div.bodywrapper {
-    margin: 0 0 0 230px;
-}
-
-hr {
-    border: 1px solid #B1B4B6;
-}
- 
-/*
-div.document {
-    background-color: #eee;
-}
-*/
- 
-div.document {
-    background-color: #ffffff;
-    color: #3E4349;
-    padding: 0 30px 30px 30px;
-    font-size: 0.9em;
-}
- 
-div.footer {
-    color: #555;
-    width: 100%;
-    padding: 13px 0;
-    text-align: center;
-    font-size: 75%;
-}
- 
-div.footer a {
-    color: #444;
-    text-decoration: underline;
-}
- 
-div.related {
-    background-color: #6BA81E;
-    line-height: 32px;
-    color: #fff;
-    text-shadow: 0px 1px 0 #444;
-    font-size: 0.9em;
-}
- 
-div.related a {
-    color: #E2F3CC;
-}
- 
-div.sphinxsidebar {
-    font-size: 0.75em;
-    line-height: 1.5em;
-}
-
-div.sphinxsidebarwrapper{
-    padding: 20px 0;
-}
- 
-div.sphinxsidebar h3,
-div.sphinxsidebar h4 {
-    font-family: Arial, sans-serif;
-    color: #222;
-    font-size: 1.2em;
-    font-weight: normal;
-    margin: 0;
-    padding: 5px 10px;
-    background-color: #ddd;
-    text-shadow: 1px 1px 0 white
-}
-
-div.sphinxsidebar h4{
-    font-size: 1.1em;
-}
- 
-div.sphinxsidebar h3 a {
-    color: #444;
-}
- 
- 
-div.sphinxsidebar p {
-    color: #888;
-    padding: 5px 20px;
-}
- 
-div.sphinxsidebar p.topless {
-}
- 
-div.sphinxsidebar ul {
-    margin: 10px 20px;
-    padding: 0;
-    color: #000;
-}
- 
-div.sphinxsidebar a {
-    color: #444;
-}
- 
-div.sphinxsidebar input {
-    border: 1px solid #ccc;
-    font-family: sans-serif;
-    font-size: 1em;
-}
-
-div.sphinxsidebar input[type=text]{
-    margin-left: 20px;
-}
- 
-/* -- body styles ----------------------------------------------------------- */
- 
-a {
-    color: #005B81;
-    text-decoration: none;
-}
- 
-a:hover {
-    color: #E32E00;
-    text-decoration: underline;
-}
- 
-div.document h1,
-div.document h2,
-div.document h3,
-div.document h4,
-div.document h5,
-div.document h6 {
-    font-family: Arial, sans-serif;
-    background-color: #BED4EB;
-    font-weight: normal;
-    color: #212224;
-    margin: 30px 0px 10px 0px;
-    padding: 5px 0 5px 10px;
-    text-shadow: 0px 1px 0 white
-}
- 
-div.document h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
-div.document h2 { font-size: 150%; background-color: #C8D5E3; }
-div.document h3 { font-size: 120%; background-color: #D8DEE3; }
-div.document h4 { font-size: 110%; background-color: #D8DEE3; }
-div.document h5 { font-size: 100%; background-color: #D8DEE3; }
-div.document h6 { font-size: 100%; background-color: #D8DEE3; }
- 
-a.headerlink {
-    color: #c60f0f;
-    font-size: 0.8em;
-    padding: 0 4px 0 4px;
-    text-decoration: none;
-}
- 
-a.headerlink:hover {
-    background-color: #c60f0f;
-    color: white;
-}
- 
-div.document p, div.document dd, div.document li {
-    line-height: 1.5em;
-}
- 
-div.admonition p.admonition-title + p {
-    display: inline;
-}
-
-div.highlight{
-    background-color: white;
-}
-
-div.note {
-    background-color: #eee;
-    border: 1px solid #ccc;
-}
- 
-div.seealso {
-    background-color: #ffc;
-    border: 1px solid #ff6;
-}
- 
-div.topic {
-    background-color: #eee;
-}
- 
-div.warning {
-    background-color: #ffe4e4;
-    border: 1px solid #f66;
-}
- 
-p.admonition-title {
-    display: inline;
-}
- 
-p.admonition-title:after {
-    content: ":";
-}
- 
-pre {
-    padding: 10px;
-    background-color: White;
-    color: #222;
-    line-height: 1.2em;
-    border: 1px solid #C6C9CB;
-    font-size: 1.1em;
-    margin: 1.5em 0 1.5em 0;
-    -webkit-box-shadow: 1px 1px 1px #d8d8d8;
-    -moz-box-shadow: 1px 1px 1px #d8d8d8;
-}
- 
-tt {
-    background-color: #ecf0f3;
-    color: #222;
-    /* padding: 1px 2px; */
-    font-size: 1.1em;
-    font-family: monospace;
-}
-
-.viewcode-back {
-    font-family: Arial, sans-serif;
-}
-
-div.viewcode-block:target {
-    background-color: #f4debf;
-    border-top: 1px solid #ac9;
-    border-bottom: 1px solid #ac9;
-}
-
-p {
-	margin: 0;
-}
-
-ul li dd {
-	margin-top: 0;
-}
-
-ul li dl {
-	margin-bottom: 0;
-}
-
-li dl dd {
-	margin-bottom: 0;
-}
-
-dd ul {
-	padding-left: 0;
-}
-
-li dd ul {
-	margin-bottom: 0;
-}
-
diff --git a/synapse/api/events/factory.py b/synapse/api/events/factory.py
index 0d94850cec..74d0ef77f4 100644
--- a/synapse/api/events/factory.py
+++ b/synapse/api/events/factory.py
@@ -58,8 +58,8 @@ class EventFactory(object):
                 random_string(10), self.hs.hostname
             )
 
-        if "ts" not in kwargs:
-            kwargs["ts"] = int(self.clock.time_msec())
+        if "origin_server_ts" not in kwargs:
+            kwargs["origin_server_ts"] = int(self.clock.time_msec())
 
         # The "age" key is a delta timestamp that should be converted into an
         # absolute timestamp the minute we see it.
diff --git a/synapse/federation/pdu_codec.py b/synapse/federation/pdu_codec.py
index 7e574f451d..991aae2a56 100644
--- a/synapse/federation/pdu_codec.py
+++ b/synapse/federation/pdu_codec.py
@@ -95,8 +95,8 @@ class PduCodec(object):
             if k not in ["event_id", "room_id", "type"]
         })
 
-        if "ts" not in kwargs:
-            kwargs["ts"] = int(self.clock.time_msec())
+        if "origin_server_ts" not in kwargs:
+            kwargs["origin_server_ts"] = int(self.clock.time_msec())
 
         pdu = Pdu(**kwargs)
         pdu = add_event_pdu_content_hash(pdu)
diff --git a/synapse/federation/persistence.py b/synapse/federation/persistence.py
index de36a80e41..7043fcc504 100644
--- a/synapse/federation/persistence.py
+++ b/synapse/federation/persistence.py
@@ -157,7 +157,7 @@ class TransactionActions(object):
         transaction.prev_ids = yield self.store.prep_send_transaction(
             transaction.transaction_id,
             transaction.destination,
-            transaction.ts,
+            transaction.origin_server_ts,
             [(p["pdu_id"], p["origin"]) for p in transaction.pdus]
         )
 
diff --git a/synapse/federation/replication.py b/synapse/federation/replication.py
index f2a5d4d5e2..4a9414c1d4 100644
--- a/synapse/federation/replication.py
+++ b/synapse/federation/replication.py
@@ -427,7 +427,7 @@ class ReplicationLayer(object):
         return Transaction(
             origin=self.server_name,
             pdus=pdus,
-            ts=int(time_now),
+            origin_server_ts=int(time_now),
             destination=None,
         )
 
@@ -595,7 +595,7 @@ class _TransactionQueue(object):
             logger.debug("TX [%s] Persisting transaction...", destination)
 
             transaction = Transaction.create_new(
-                ts=int(self._clock.time_msec()),
+                origin_server_ts=int(self._clock.time_msec()),
                 transaction_id=str(self._next_txn_id),
                 origin=self.server_name,
                 destination=destination,
diff --git a/synapse/federation/units.py b/synapse/federation/units.py
index c629e5793e..b81e162512 100644
--- a/synapse/federation/units.py
+++ b/synapse/federation/units.py
@@ -41,7 +41,7 @@ class Pdu(JsonEncodedObject):
 
         {
             "pdu_id": "78c",
-            "ts": 1404835423000,
+            "origin_server_ts": 1404835423000,
             "origin": "bar",
             "prev_ids": [
                 ["23b", "foo"],
@@ -56,7 +56,7 @@ class Pdu(JsonEncodedObject):
         "pdu_id",
         "context",
         "origin",
-        "ts",
+        "origin_server_ts",
         "pdu_type",
         "destinations",
         "transaction_id",
@@ -84,7 +84,7 @@ class Pdu(JsonEncodedObject):
         "pdu_id",
         "context",
         "origin",
-        "ts",
+        "origin_server_ts",
         "pdu_type",
         "content",
     ]
@@ -122,6 +122,7 @@ class Pdu(JsonEncodedObject):
         """
         if pdu_tuple:
             d = copy.copy(pdu_tuple.pdu_entry._asdict())
+            d["origin_server_ts"] = d.pop("ts")
 
             for k in d.keys():
                 if d[k] is None:
@@ -212,7 +213,7 @@ class Transaction(JsonEncodedObject):
         "transaction_id",
         "origin",
         "destination",
-        "ts",
+        "origin_server_ts",
         "previous_ids",
         "pdus",
         "edus",
@@ -229,7 +230,7 @@ class Transaction(JsonEncodedObject):
         "transaction_id",
         "origin",
         "destination",
-        "ts",
+        "origin_server_ts",
         "pdus",
     ]
 
@@ -251,10 +252,10 @@ class Transaction(JsonEncodedObject):
     @staticmethod
     def create_new(pdus, **kwargs):
         """ Used to create a new transaction. Will auto fill out
-        transaction_id and ts keys.
+        transaction_id and origin_server_ts keys.
         """
-        if "ts" not in kwargs:
-            raise KeyError("Require 'ts' to construct a Transaction")
+        if "origin_server_ts" not in kwargs:
+            raise KeyError("Require 'origin_server_ts' to construct a Transaction")
         if "transaction_id" not in kwargs:
             raise KeyError(
                 "Require 'transaction_id' to construct a Transaction"
diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py
index 317ef2c80c..7b2b8549ed 100644
--- a/synapse/handlers/message.py
+++ b/synapse/handlers/message.py
@@ -64,7 +64,7 @@ class MessageHandler(BaseHandler):
         defer.returnValue(None)
 
     @defer.inlineCallbacks
-    def send_message(self, event=None, suppress_auth=False, stamp_event=True):
+    def send_message(self, event=None, suppress_auth=False):
         """ Send a message.
 
         Args:
@@ -72,7 +72,6 @@ class MessageHandler(BaseHandler):
             suppress_auth (bool) : True to suppress auth for this message. This
             is primarily so the home server can inject messages into rooms at
             will.
-            stamp_event (bool) : True to stamp event content with server keys.
         Raises:
             SynapseError if something went wrong.
         """
@@ -82,9 +81,6 @@ class MessageHandler(BaseHandler):
         user = self.hs.parse_userid(event.user_id)
         assert user.is_mine, "User must be our own: %s" % (user,)
 
-        if stamp_event:
-            event.content["hsob_ts"] = int(self.clock.time_msec())
-
         snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)
 
         if not suppress_auth:
@@ -132,7 +128,7 @@ class MessageHandler(BaseHandler):
         defer.returnValue(chunk)
 
     @defer.inlineCallbacks
-    def store_room_data(self, event=None, stamp_event=True):
+    def store_room_data(self, event=None):
         """ Stores data for a room.
 
         Args:
@@ -151,9 +147,6 @@ class MessageHandler(BaseHandler):
 
         yield self.auth.check(event, snapshot, raises=True)
 
-        if stamp_event:
-            event.content["hsob_ts"] = int(self.clock.time_msec())
-
         yield self.state_handler.handle_new_event(event, snapshot)
 
         yield self._on_new_room_event(event, snapshot)
@@ -221,10 +214,7 @@ class MessageHandler(BaseHandler):
         defer.returnValue(None)
 
     @defer.inlineCallbacks
-    def send_feedback(self, event, stamp_event=True):
-        if stamp_event:
-            event.content["hsob_ts"] = int(self.clock.time_msec())
-
+    def send_feedback(self, event):
         snapshot = yield self.store.snapshot_room(event.room_id, event.user_id)
 
         yield self.auth.check(event, snapshot, raises=True)
diff --git a/synapse/rest/presence.py b/synapse/rest/presence.py
index 7fc8ce4404..138cc88a05 100644
--- a/synapse/rest/presence.py
+++ b/synapse/rest/presence.py
@@ -68,7 +68,7 @@ class PresenceStatusRestServlet(RestServlet):
         yield self.handlers.presence_handler.set_state(
             target_user=user, auth_user=auth_user, state=state)
 
-        defer.returnValue((200, ""))
+        defer.returnValue((200, {}))
 
     def on_OPTIONS(self, request):
         return (200, {})
@@ -141,7 +141,7 @@ class PresenceListRestServlet(RestServlet):
 
         yield defer.DeferredList(deferreds)
 
-        defer.returnValue((200, ""))
+        defer.returnValue((200, {}))
 
     def on_OPTIONS(self, request):
         return (200, {})
diff --git a/synapse/storage/__init__.py b/synapse/storage/__init__.py
index 1738260cc1..e4f708b6ad 100644
--- a/synapse/storage/__init__.py
+++ b/synapse/storage/__init__.py
@@ -163,6 +163,8 @@ class DataStore(RoomMemberStore, RoomStore,
 
         cols["unrecognized_keys"] = json.dumps(unrec_keys)
 
+        cols["ts"] = cols.pop("origin_server_ts")
+
         logger.debug("Persisting: %s", repr(cols))
 
         for hash_alg, hash_base64 in pdu.hashes.items():
diff --git a/synapse/storage/_base.py b/synapse/storage/_base.py
index dba50f1213..65a86e9056 100644
--- a/synapse/storage/_base.py
+++ b/synapse/storage/_base.py
@@ -354,6 +354,7 @@ class SQLBaseStore(object):
         d.pop("stream_ordering", None)
         d.pop("topological_ordering", None)
         d.pop("processed", None)
+        d["origin_server_ts"] = d.pop("ts", 0)
 
         d.update(json.loads(row_dict["unrecognized_keys"]))
         d["content"] = json.loads(d["content"])
@@ -361,7 +362,7 @@ class SQLBaseStore(object):
 
         if "age_ts" not in d:
             # For compatibility
-            d["age_ts"] = d["ts"] if "ts" in d else 0
+            d["age_ts"] = d.get("origin_server_ts", 0)
 
         return self.event_factory.create_event(
             etype=d["type"],
diff --git a/synapse/storage/transactions.py b/synapse/storage/transactions.py
index ab4599b468..2ba8e30efe 100644
--- a/synapse/storage/transactions.py
+++ b/synapse/storage/transactions.py
@@ -87,7 +87,8 @@ class TransactionStore(SQLBaseStore):
 
         txn.execute(query, (code, response_json, transaction_id, origin))
 
-    def prep_send_transaction(self, transaction_id, destination, ts, pdu_list):
+    def prep_send_transaction(self, transaction_id, destination,
+                              origin_server_ts, pdu_list):
         """Persists an outgoing transaction and calculates the values for the
         previous transaction id list.
 
@@ -97,7 +98,7 @@ class TransactionStore(SQLBaseStore):
         Args:
             transaction_id (str)
             destination (str)
-            ts (int)
+            origin_server_ts (int)
             pdu_list (list)
 
         Returns:
@@ -106,11 +107,11 @@ class TransactionStore(SQLBaseStore):
 
         return self.runInteraction(
             self._prep_send_transaction,
-            transaction_id, destination, ts, pdu_list
+            transaction_id, destination, origin_server_ts, pdu_list
         )
 
-    def _prep_send_transaction(self, txn, transaction_id, destination, ts,
-                               pdu_list):
+    def _prep_send_transaction(self, txn, transaction_id, destination,
+                               origin_server_ts, pdu_list):
 
         # First we find out what the prev_txs should be.
         # Since we know that we are only sending one transaction at a time,
@@ -131,7 +132,7 @@ class TransactionStore(SQLBaseStore):
             None,
             transaction_id=transaction_id,
             destination=destination,
-            ts=ts,
+            ts=origin_server_ts,
             response_code=0,
             response_json=None
         ))
diff --git a/tests/federation/test_federation.py b/tests/federation/test_federation.py
index eed50e6335..3a14c7c3b9 100644
--- a/tests/federation/test_federation.py
+++ b/tests/federation/test_federation.py
@@ -158,7 +158,7 @@ class FederationTestCase(unittest.TestCase):
                 origin="red",
                 destinations=["remote"],
                 context="my-context",
-                ts=123456789002,
+                origin_server_ts=123456789002,
                 pdu_type="m.test",
                 content={"testing": "content here"},
                 depth=1,
@@ -170,14 +170,14 @@ class FederationTestCase(unittest.TestCase):
                 "remote",
                 path="/_matrix/federation/v1/send/1000000/",
                 data={
-                    "ts": 1000000,
+                    "origin_server_ts": 1000000,
                     "origin": "test",
                     "pdus": [
                         {
                             "origin": "red",
                             "pdu_id": "abc123def456",
                             "prev_pdus": [],
-                            "ts": 123456789002,
+                            "origin_server_ts": 123456789002,
                             "context": "my-context",
                             "pdu_type": "m.test",
                             "is_state": False,
@@ -209,7 +209,7 @@ class FederationTestCase(unittest.TestCase):
                 path="/_matrix/federation/v1/send/1000000/",
                 data={
                     "origin": "test",
-                    "ts": 1000000,
+                    "origin_server_ts": 1000000,
                     "pdus": [],
                     "edus": [
                         {
@@ -236,7 +236,7 @@ class FederationTestCase(unittest.TestCase):
                 "/_matrix/federation/v1/send/1001000/",
                 """{
                     "origin": "remote",
-                    "ts": 1001000,
+                    "origin_server_ts": 1001000,
                     "pdus": [],
                     "edus": [
                         {
diff --git a/tests/federation/test_pdu_codec.py b/tests/federation/test_pdu_codec.py
index 0ad8cf6641..fd0f72c734 100644
--- a/tests/federation/test_pdu_codec.py
+++ b/tests/federation/test_pdu_codec.py
@@ -75,7 +75,7 @@ class PduCodecTestCase(unittest.TestCase):
             context="rooooom",
             pdu_type="m.room.message",
             origin="bar.com",
-            ts=12345,
+            origin_server_ts=12345,
             depth=5,
             prev_pdus=[("alice", "bob.com")],
             is_state=False,
@@ -130,7 +130,7 @@ class PduCodecTestCase(unittest.TestCase):
             context="rooooom",
             pdu_type="m.room.topic",
             origin="bar.com",
-            ts=12345,
+            origin_server_ts=12345,
             depth=5,
             prev_pdus=[("alice", "bob.com")],
             is_state=True,
diff --git a/tests/handlers/test_federation.py b/tests/handlers/test_federation.py
index 35c3a4df7b..219b2c4c5e 100644
--- a/tests/handlers/test_federation.py
+++ b/tests/handlers/test_federation.py
@@ -68,7 +68,7 @@ class FederationTestCase(unittest.TestCase):
             pdu_type=MessageEvent.TYPE,
             context="foo",
             content={"msgtype": u"fooo"},
-            ts=0,
+            origin_server_ts=0,
             pdu_id="a",
             origin="b",
         )
@@ -95,7 +95,7 @@ class FederationTestCase(unittest.TestCase):
             target_host=self.hostname,
             context=room_id,
             content={},
-            ts=0,
+            origin_server_ts=0,
             pdu_id="a",
             origin="b",
         )
@@ -127,7 +127,7 @@ class FederationTestCase(unittest.TestCase):
             state_key="@red:not%s" % self.hostname,
             context=room_id,
             content={},
-            ts=0,
+            origin_server_ts=0,
             pdu_id="a",
             origin="b",
         )
diff --git a/tests/handlers/test_presence.py b/tests/handlers/test_presence.py
index 84985a8066..1850deacf5 100644
--- a/tests/handlers/test_presence.py
+++ b/tests/handlers/test_presence.py
@@ -39,7 +39,7 @@ ONLINE = PresenceState.ONLINE
 def _expect_edu(destination, edu_type, content, origin="test"):
     return {
         "origin": origin,
-        "ts": 1000000,
+        "origin_server_ts": 1000000,
         "pdus": [],
         "edus": [
             {
diff --git a/tests/handlers/test_typing.py b/tests/handlers/test_typing.py
index b685373deb..f1d3b27f74 100644
--- a/tests/handlers/test_typing.py
+++ b/tests/handlers/test_typing.py
@@ -29,7 +29,7 @@ from synapse.handlers.typing import TypingNotificationHandler
 def _expect_edu(destination, edu_type, content, origin="test"):
     return {
         "origin": origin,
-        "ts": 1000000,
+        "origin_server_ts": 1000000,
         "pdus": [],
         "edus": [
             {
diff --git a/tests/test_state.py b/tests/test_state.py
index b1624f0b25..4b1feaf410 100644
--- a/tests/test_state.py
+++ b/tests/test_state.py
@@ -599,7 +599,7 @@ def new_fake_pdu(pdu_id, context, pdu_type, state_key, prev_state_id,
         prev_state_id=prev_state_id,
         origin="example.com",
         context="context",
-        ts=1405353060021,
+        origin_server_ts=1405353060021,
         depth=depth,
         content_json="{}",
         unrecognized_keys="{}",
diff --git a/webclient/app-filter.js b/webclient/app-filter.js
index fc16492ef3..39ea1d637d 100644
--- a/webclient/app-filter.js
+++ b/webclient/app-filter.js
@@ -1,12 +1,12 @@
 /*
  Copyright 2014 OpenMarket Ltd
- 
+
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
- 
+
  http://www.apache.org/licenses/LICENSE-2.0
- 
+
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -80,4 +80,53 @@ angular.module('matrixWebClient')
     return function(text) {
         return $sce.trustAsHtml(text);
     };
-}]);
\ No newline at end of file
+}])
+// Exactly the same as ngSanitize's linky but instead of pushing sanitized
+// text in the addText function, we just push the raw text.
+.filter('unsanitizedLinky', ['$sanitize', function($sanitize) {
+  var LINKY_URL_REGEXP =
+        /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,
+      MAILTO_REGEXP = /^mailto:/;
+
+  return function(text, target) {
+    if (!text) return text;
+    var match;
+    var raw = text;
+    var html = [];
+    var url;
+    var i;
+    while ((match = raw.match(LINKY_URL_REGEXP))) {
+      // We can not end in these as they are sometimes found at the end of the sentence
+      url = match[0];
+      // if we did not match ftp/http/mailto then assume mailto
+      if (match[2] == match[3]) url = 'mailto:' + url;
+      i = match.index;
+      addText(raw.substr(0, i));
+      addLink(url, match[0].replace(MAILTO_REGEXP, ''));
+      raw = raw.substring(i + match[0].length);
+    }
+    addText(raw);
+    return $sanitize(html.join(''));
+
+    function addText(text) {
+      if (!text) {
+        return;
+      }
+      html.push(text);
+    }
+
+    function addLink(url, text) {
+      html.push('<a ');
+      if (angular.isDefined(target)) {
+        html.push('target="');
+        html.push(target);
+        html.push('" ');
+      }
+      html.push('href="');
+      html.push(url);
+      html.push('">');
+      addText(text);
+      html.push('</a>');
+    }
+  };
+}]);
diff --git a/webclient/room/room.html b/webclient/room/room.html
index b99413cbbf..79a60585a5 100644
--- a/webclient/room/room.html
+++ b/webclient/room/room.html
@@ -121,7 +121,9 @@
                         <span ng-show='msg.content.msgtype === "m.text"' 
                               class="message"
                               ng-class="containsBingWord(msg.content.body) && msg.user_id != state.user_id ? msg.echo_msg_state + ' messageBing' : msg.echo_msg_state"
-                              ng-bind-html="((msg.content.msgtype === 'm.text') ? msg.content.body : '') | linky:'_blank'"/>
+                              ng-bind-html="(msg.content.msgtype === 'm.text' && msg.type === 'm.room.message' && msg.content.format === 'org.matrix.custom.html') ? 
+                                                                                        (msg.content.formatted_body | unsanitizedLinky) :
+                                             (msg.content.msgtype === 'm.text' && msg.type === 'm.room.message') ? (msg.content.body | linky:'_blank') : '' "/>
 
                         <span ng-show='msg.type === "m.call.invite" && msg.user_id == state.user_id'>Outgoing Call{{ isWebRTCSupported ? '' : ' (But your browser does not support VoIP)' }}</span>
                         <span ng-show='msg.type === "m.call.invite" && msg.user_id != state.user_id'>Incoming Call{{ isWebRTCSupported ? '' : ' (But your browser does not support VoIP)' }}</span>