Compare commits

...

1264 Commits

Author SHA1 Message Date
Erik Johnston
bcb8d2fe54 Merge branch 'hotfixes-v0.6.1c' of github.com:matrix-org/synapse 2015-02-02 13:09:48 +00:00
Erik Johnston
83c31735d0 Use >= for version of webclient 2015-02-02 13:07:43 +00:00
Erik Johnston
3b33529dfd Bump version 2015-02-02 13:03:25 +00:00
Erik Johnston
c934760014 Bump version of webclient pulled in. 2015-02-02 13:03:03 +00:00
David Baker
e32ded7b3e Add matrix.org as a trusted ID server because it's now passed through on ports 80/443 and the web client defaults to that now. Fixes email validation (including signing up with an email address). 2015-01-28 10:09:54 +00:00
Erik Johnston
b1a38c39ad Merge branch 'hotfixes-v0.6.1b' of github.com:matrix-org/synapse 2015-01-13 10:00:47 +00:00
Erik Johnston
1d3d37937d Bump version 2015-01-13 09:59:47 +00:00
Erik Johnston
39585bf556 Insert 'age' into top level when returning events to clients 2015-01-13 09:57:32 +00:00
Mark Haines
f3467d4646 Merge branch 'hotfixes-v0.6.1' 2015-01-08 09:43:01 +00:00
Mark Haines
5a0e687d5c Bump version 2015-01-08 09:42:23 +00:00
Mark Haines
c9d2cecac9 SYN-231: User agent header broken 2015-01-08 09:41:11 +00:00
Erik Johnston
bacaa215eb Merge branch 'release-v0.6.1' of github.com:matrix-org/synapse 2015-01-07 14:34:00 +00:00
Erik Johnston
72d8d1265b Improve change log 2015-01-07 14:16:38 +00:00
Erik Johnston
89fc09c3d1 Bump version and changelog 2015-01-07 13:56:56 +00:00
Erik Johnston
a039e2544c Remove unused import 2015-01-07 09:48:03 +00:00
Erik Johnston
6536161e2a Merge branch 'erikj-perf' of github.com:matrix-org/synapse into develop 2015-01-07 09:45:42 +00:00
Erik Johnston
1497e50649 Merge branch 'develop' of github.com:matrix-org/synapse into erikj-perf 2015-01-07 09:40:42 +00:00
Mark Haines
5cf45c4319 Merge branch 'master' into develop 2015-01-06 19:48:53 +00:00
Erik Johnston
dfa05f0cd6 Optimize FrozenEvent creation 2015-01-06 18:51:03 +00:00
Erik Johnston
36a2a877e2 Use time.time() instead of time.clock() 2015-01-06 16:34:41 +00:00
Erik Johnston
d5ae67e67d Fix typo where we used wrong var. 2015-01-06 16:05:01 +00:00
Erik Johnston
fd9a8db7ea Only fetch the columns we need. 2015-01-06 15:59:31 +00:00
Erik Johnston
9e5545a6fa RoomsForUser now has sender instead of user_id 2015-01-06 15:53:50 +00:00
Erik Johnston
a01416cf21 Add delta and bump DB version 2015-01-06 15:42:18 +00:00
Erik Johnston
f6da237c35 Add index on transaction_id to sent_transcations 2015-01-06 15:40:38 +00:00
Erik Johnston
9bd07bed23 Actually time that function 2015-01-06 15:28:56 +00:00
Erik Johnston
03a501456c Time how long calls to _get_destination_retry_timings take 2015-01-06 15:22:28 +00:00
Erik Johnston
52b2c6c9c7 Don't include None's in _get_events_txn 2015-01-06 14:56:57 +00:00
Erik Johnston
8a12df8cf3 Merge branch 'erikj-perf' of github.com:matrix-org/synapse into develop 2015-01-06 14:45:57 +00:00
Erik Johnston
96707ed718 Name 'user_rooms_intersect' transaction 2015-01-06 14:44:27 +00:00
Erik Johnston
76ec154e95 We don't need the full events for get_rooms_for_user_where_membership_is 2015-01-06 14:37:00 +00:00
Mark Haines
bc2ec808f4 SYN-32 Use the ANTIALIAS resize method for thumbnailing images 2015-01-06 14:14:17 +00:00
Matrix
0529a7e2e9 Add some logging for when we are sending transactions. 2015-01-06 14:06:25 +00:00
Mark Haines
b9f77d1ae1 Increase default maximum attachment size to 10M 2015-01-06 14:04:58 +00:00
Mark Haines
5e23a19204 Merge pull request #28 from matrix-org/erikj-perf
Database performance improvements.
2015-01-06 13:33:40 +00:00
Mark Haines
adb04b1e57 Update copyright notices 2015-01-06 13:21:39 +00:00
Erik Johnston
af1c7c7808 PEP8 2015-01-06 13:13:17 +00:00
Erik Johnston
12819d5082 Remove debug lines 2015-01-06 13:12:30 +00:00
Erik Johnston
52d8519008 Don't do batching when getting events. 2015-01-06 13:10:27 +00:00
Mark Haines
773de09774 Set a content-length for JSON responses 2015-01-06 13:05:19 +00:00
Erik Johnston
98933e3db6 Only fetch prev_content when a client is streaming/paginating. Use transactions for event streams. 2015-01-06 13:03:23 +00:00
Kegan Dougal
78edb47cc5 SYN-208/SYN-228: Add runtime checks on startup to enforce that JPEG/PNG support is included when installing pillow. 2015-01-06 11:43:04 +00:00
Mark Haines
3c8c3bf3b7 SYN-229: Include Content-Length when downloading files 2015-01-06 11:32:36 +00:00
Erik Johnston
3e26720e05 Temporarily turn off 'redacted_because' and 'prev_content' keys 2015-01-06 11:26:58 +00:00
Erik Johnston
f4ea78e9e2 More debug logging 2015-01-06 11:24:18 +00:00
Erik Johnston
753126b8cc Add some debug logging 2015-01-06 11:18:12 +00:00
Erik Johnston
d7e8ea67b3 Reformat 2015-01-06 11:18:02 +00:00
Erik Johnston
f0128f9600 Add RoomMemberStore.get_users_in_room, so that we can get the list of joined users without having to retrieve the full events 2015-01-06 10:55:43 +00:00
Erik Johnston
96a5ba41f5 Merge branch 'develop' of github.com:matrix-org/synapse into erikj-perf 2015-01-06 10:53:04 +00:00
Mark Haines
90d60e3fe4 Merge branch 'hotfixes-v0.6.0a' 2014-12-29 14:01:07 +00:00
Mark Haines
af61c29527 Return the argument passed to the callback in a deferred callback, otherwise twisted will replace the deferred result with 'None' 2014-12-29 13:54:05 +00:00
Matthew Hodgson
0e93e01fcb spell out that VoIP needs TURN 2014-12-24 19:45:28 +00:00
Matthew Hodgson
407c299828 improve error msg 2014-12-24 17:50:42 +00:00
Matthew Hodgson
1eb319806b clarify these instructions a media-repo specific 2014-12-24 16:56:32 +00:00
Matthew Hodgson
d90e586c85 spell out that upgrading is just installing over the top 2014-12-24 16:56:20 +00:00
Mark Haines
24b5d01853 Include version in User-Agent and Server headers 2014-12-22 10:16:02 +00:00
Erik Johnston
74ee4048c2 Merge branch 'master' of github.com:matrix-org/synapse into erikj-perf 2014-12-21 11:47:45 +00:00
Mark Haines
420ccfc925 Merge branch 'hotfixes-v0.6.0' 2014-12-19 17:52:58 +00:00
Kegan Dougal
4640239d34 Mock ratelimiter to make tests pass. 2014-12-19 17:49:47 +00:00
Matthew Hodgson
2a5b53bc4a more changelogs 2014-12-19 17:39:43 +00:00
Kegan Dougal
67a406a754 Rate limit display names and avatar urls per request rather than per event. 2014-12-19 17:36:33 +00:00
Erik Johnston
d61109f578 Merge branch 'hotfixes-v0.6.0' of github.com:matrix-org/synapse into erikj-perf 2014-12-19 16:37:08 +00:00
Mark Haines
efd27ff01b Set a state_key for the topic and room name, otherwise they won't be treated as room state 2014-12-19 15:31:27 +00:00
Mark Haines
9c71d945d6 Look for name, topic in the event content rather than the event itself when persisting room name and topic events 2014-12-19 15:16:48 +00:00
Mark Haines
f70e622d59 bump_presence_active_time when sending a message event 2014-12-19 14:30:57 +00:00
Mark Haines
a999f0dec3 Don't ratelimit room create events 2014-12-19 14:18:27 +00:00
Mark Haines
45a6869cb4 Merge branch 'release-v0.6.0' 2014-12-19 13:40:02 +00:00
Mark Haines
1e4a56c3a9 Bump web sdk version to 0.6.0 2014-12-19 13:39:24 +00:00
Mark Haines
1e7f83b91d Set display name when joining via alias 2014-12-19 12:31:46 +00:00
Mark Haines
5dbe820e9a Remove unneeded federation keys from events 2014-12-19 12:16:26 +00:00
Mark Haines
390e48a8b0 SYN-203: Handle requests for thunbnails for images that are small 2014-12-19 12:05:38 +00:00
Mark Haines
5739e6c606 s/user_id/sender/ 2014-12-19 11:43:46 +00:00
Mark Haines
4e38b0800d Merge branch 'develop' into release-v0.6.0 2014-12-19 11:21:40 +00:00
Erik Johnston
41ce544abe Merge branch 'release-v0.6.0' of github.com:matrix-org/synapse into erikj-perf 2014-12-18 18:57:21 +00:00
Mark Haines
041ac476a5 Supply auth_chain along with current state in '/state/', fetch auth events from a remote server if we are missing some of them 2014-12-18 18:47:13 +00:00
Mark Haines
dbe77ec79a Replace distributor deferred list, with a simple for loop until I understand why the former breaks and the latter doesn't 2014-12-18 17:47:00 +00:00
Kegsay
20923ffd43 Update README.rst
Add gotcha: The content repository requires additional cygwin packages.
2014-12-18 14:44:48 +00:00
Kegsay
f8cc8a66b4 Update README.rst
Add windows (cygwin) install instructions.
2014-12-18 14:16:31 +00:00
Mark Haines
dea5d4b03b Don't yield on sending the event accross federation. 2014-12-18 11:29:46 +00:00
Erik Johnston
f3788e3c78 Test some ideas that might help performance a bit 2014-12-17 23:37:08 +00:00
Erik Johnston
dec5b62339 Use _get_events_txn instead of _parse_events_txn 2014-12-16 19:16:41 +00:00
Erik Johnston
21cab3a7ec Fix where we pulled in event.state_events from hotfixes branch 2014-12-16 19:16:15 +00:00
Erik Johnston
2215faa361 Merge branch 'hotfixes-v0.5.4a' of github.com:matrix-org/synapse into release-v0.6.0 2014-12-16 19:11:13 +00:00
Erik Johnston
3defd5b3ee Add FIXME 2014-12-16 19:07:20 +00:00
Erik Johnston
96779d2490 Fix bug where we did not send the full auth chain to people that joined over federation 2014-12-16 18:57:36 +00:00
Erik Johnston
2d7716d4d0 Make error messages slightly more helpful 2014-12-16 18:41:48 +00:00
Erik Johnston
f76269392b Merge branch 'develop' of github.com:matrix-org/synapse into release-v0.6.0
Conflicts:
	synapse/state.py
2014-12-16 18:35:46 +00:00
Erik Johnston
52f99243ab Use is_outlier() so that we don't get AttributeError 2014-12-16 18:33:50 +00:00
Erik Johnston
5b39cfff69 Don't assume an event exists 2014-12-16 18:25:24 +00:00
Erik Johnston
9550ba94f2 Mention that we should pull in new deps before running upgrade script 2014-12-16 17:31:39 +00:00
Mark Haines
56db465047 Merge branch 'release-v0.6.0' into develop 2014-12-16 17:29:49 +00:00
Erik Johnston
28f71ecf0d Change upgrade script to not check hashes or signatures 2014-12-16 17:29:22 +00:00
Kegan Dougal
4dcad143dd SYN-142: Use a default log file 'homeserver.log' so people get logging by default. 2014-12-16 17:24:49 +00:00
Erik Johnston
f06161a307 Enable rate limiting for all events 2014-12-16 16:10:17 +00:00
Mark Haines
627e4f01d2 Remove send_message since nothing was calling it. Remove Snapshot because only send_message was using it 2014-12-16 16:07:41 +00:00
Erik Johnston
23da4a4051 Fix typo where we thought a list was a dict 2014-12-16 15:59:40 +00:00
Mark Haines
c3eae8a88c Construct the EventContext in the state handler rather than constructing one and then immediately calling state_handler.annotate_context_with_state 2014-12-16 15:59:17 +00:00
Mark Haines
3c7857e49b clean up coding style a bit 2014-12-16 15:24:03 +00:00
Erik Johnston
42b725ce52 Fix upgrade script to run all the missing deltas. 2014-12-16 15:13:34 +00:00
Mark Haines
8b8beba194 Remove annotate_event_with_state as nothing was using it. Update state tests to call annotate_context_with_state 2014-12-16 15:08:37 +00:00
Erik Johnston
b3c793e362 Do run all deltas up to missing delta 10 2014-12-16 14:44:53 +00:00
Erik Johnston
d2ca24087f Bump UPGRADES and CHANGES 2014-12-16 14:36:31 +00:00
Erik Johnston
2e44714214 Make failure to run appropraite upgrade scripts more helpful. 2014-12-16 14:20:32 +00:00
Erik Johnston
592ba14b36 Fix bugs in upgrade script.
Handle the case when there are colons in server_name. Handle http
exceptions more gracefully.
2014-12-16 14:07:05 +00:00
Erik Johnston
cb91ce5bba Rename upgrade script 2014-12-16 13:58:57 +00:00
Erik Johnston
bab1e790ae Include database bump in upgrade script 2014-12-16 13:58:38 +00:00
Erik Johnston
ef5a141050 Bump database version 2014-12-16 13:57:47 +00:00
Erik Johnston
96cc7c8740 Bump version 2014-12-16 13:57:27 +00:00
Mark Haines
2af40cfa14 Merge pull request #25 from matrix-org/events_refactor
Event refactor
2014-12-16 13:53:43 +00:00
Erik Johnston
5a465b67ba Fix pyflakes 2014-12-16 13:41:43 +00:00
Erik Johnston
58168498b0 Remove FrozenEncoder 2014-12-16 13:38:38 +00:00
Erik Johnston
8133cdcc88 Better english in docstrings are helpful. 2014-12-16 13:32:06 +00:00
Erik Johnston
35f4f6b070 Update upgrade script 2014-12-16 13:27:53 +00:00
Erik Johnston
882dc8dcab Persist internal_metadata 2014-12-16 13:17:09 +00:00
Erik Johnston
4afac88390 Add basic docstring to annotate_context_with_state 2014-12-16 13:09:44 +00:00
Erik Johnston
3c77d13aa5 Kill off synapse.api.events.* 2014-12-16 11:29:05 +00:00
Erik Johnston
6a1da99fab Add fixme to raising of AuthError in federation land 2014-12-16 09:35:31 +00:00
Mark Haines
400327d128 Add a script for talking matrix federation adding X-Matrix Authorization
headers.
2014-12-15 17:38:56 +00:00
Erik Johnston
65b2e49429 Fix pyflakes 2014-12-15 17:35:37 +00:00
Erik Johnston
9c49054f1d Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor 2014-12-15 17:33:23 +00:00
Erik Johnston
f280929a12 Use frozenutils 2014-12-15 17:31:36 +00:00
Erik Johnston
009e4b5637 User.is_mine is no longer a thing. Use hs.is_mine instead. 2014-12-15 17:17:51 +00:00
Erik Johnston
cf6e5f1dbf Rename MessageHandler.handle_event. Add a few comments. 2014-12-15 17:01:12 +00:00
Kegsay
67c9585656 Update media_repository.py
_ not -
2014-12-15 16:57:53 +00:00
Erik Johnston
670dcdfc14 Remove unused functions 2014-12-15 16:16:58 +00:00
Paul "LeoNerd" Evans
0c1deca574 Remember to hook up the typing event stream to the notifier as well 2014-12-15 16:14:53 +00:00
Erik Johnston
b75adaedca Finish up upgrade script 2014-12-15 16:14:34 +00:00
Erik Johnston
65cdf4e724 Get current member state from current_state snapshot. Fix leave test. 2014-12-15 15:03:27 +00:00
Erik Johnston
57e0e619f3 Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor
Conflicts:
	tests/handlers/test_room.py
2014-12-15 14:45:59 +00:00
Paul "LeoNerd" Evans
20beed9dd4 Still send typing notifications to myself if I'm the only one in the room (it's a lonely life...) 2014-12-15 14:37:12 +00:00
Mark Haines
3610641a62 Update docs in media_repository 2014-12-15 13:56:43 +00:00
Erik Johnston
616f88027c Add beginnings of upgrade script 2014-12-15 13:55:41 +00:00
Erik Johnston
c8dd3314d6 Fix bug where we ignored event_edge_hashes table 2014-12-15 13:55:22 +00:00
Mark Haines
58fa6d3fc6 return an mxc uri rather than a content_token. 2014-12-15 13:54:10 +00:00
Paul "LeoNerd" Evans
0aa8c08478 Merge branch 'develop' into typing_notifications 2014-12-15 11:19:30 +00:00
Erik Johnston
3983c7fb0f Merge branch 'hotfixes-v0.5.4' of github.com:matrix-org/synapse into develop 2014-12-13 18:16:12 +00:00
Erik Johnston
88484f684f Merge branch 'hotfixes-v0.5.4' of github.com:matrix-org/synapse 2014-12-13 18:15:14 +00:00
Erik Johnston
eea58b8076 Bump version and change log 2014-12-13 18:04:37 +00:00
Erik Johnston
6380ead2ee Fix bug while generating the error message when a file path specified in the config doesn't exist 2014-12-13 18:03:01 +00:00
Erik Johnston
23c7cb6220 Remove unused imports 2014-12-12 16:31:59 +00:00
Erik Johnston
fc409096ac Make auth module use EventTypes constants 2014-12-12 16:31:50 +00:00
Erik Johnston
1fc2a0e33e Fix tests and remove debug logging 2014-12-12 15:08:29 +00:00
Erik Johnston
7b43a503f3 Consistently url decode and decode as utf 8 the URL parts 2014-12-12 15:05:37 +00:00
Erik Johnston
c39beb5559 Store json as UTF-8 and not bytes 2014-12-12 14:53:37 +00:00
Erik Johnston
75085bb4d1 Pyflakes 2014-12-12 14:34:34 +00:00
Erik Johnston
ebf2ec3ce6 Fix membership handler test 2014-12-12 14:32:44 +00:00
Erik Johnston
41ff21c907 Fix test. 2014-12-12 14:10:32 +00:00
Paul "LeoNerd" Evans
b0bb1756a9 Send list of typing user IDs as 'user_ids' list within 'content', so that m.typing stream events have a toplevel content, for consistency with others 2014-12-12 11:59:46 +00:00
Erik Johnston
63810c777d Validate message, topic and name event contents 2014-12-12 11:01:09 +00:00
Erik Johnston
fa4b610ae3 Fix stream test. Make sure we add join to auth_events for invitiations 2014-12-12 10:42:27 +00:00
Paul "LeoNerd" Evans
0b70023373 Merge branch 'develop' into typing_notifications 2014-12-11 18:35:05 +00:00
Paul "LeoNerd" Evans
57b5094545 Merge branch 'develop' of github.com:matrix-org/synapse into develop 2014-12-11 18:34:26 +00:00
Paul "LeoNerd" Evans
3e84896481 Merge remote-tracking branch 'origin' into typing_notifications 2014-12-11 18:33:29 +00:00
Paul "LeoNerd" Evans
cfb963af03 When users leave rooms mark them as no longer typing in them 2014-12-11 18:33:09 +00:00
Paul "LeoNerd" Evans
f25764943c Add a 'user_left_room' distributor signal analogous to 'user_joined_room' 2014-12-11 18:27:01 +00:00
Mark Haines
b3e34a5399 Fix typo in media repository doc string 2014-12-11 18:21:08 +00:00
Mark Haines
64bf9f54cc Fix media repository doc string to include server_name 2014-12-11 18:18:58 +00:00
Paul "LeoNerd" Evans
5ebc994f84 Actually auth-check to ensure people can only send typing notifications for rooms they're actually in 2014-12-11 18:11:43 +00:00
Paul "LeoNerd" Evans
966c4b2b04 Add a sprinkling of logger.debug() into typing notification handler 2014-12-11 18:00:15 +00:00
Paul "LeoNerd" Evans
6e1531682b Move typing-notification REST tests into their own .py file 2014-12-11 17:54:42 +00:00
Paul "LeoNerd" Evans
1f26e56de0 Actually unit-test the event stream around REST typing tests 2014-12-11 17:54:42 +00:00
Erik Johnston
cde840a82c Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor
Conflicts:
	setup.py
2014-12-11 17:48:48 +00:00
Erik Johnston
85574cfbf0 Merge pull request #23 from matrix-org/media_repository
Media repository
2014-12-11 17:46:23 +00:00
Erik Johnston
3fecacd86b Fix replication tests 2014-12-11 17:11:06 +00:00
Erik Johnston
d3eb12c7b8 Fix federation test 2014-12-11 17:01:27 +00:00
Mark Haines
03d9024cbc Allow only one download for a given image at a time, so that we don't end up downloading the same image twice if two clients request a remote image at the same time 2014-12-11 16:48:11 +00:00
Erik Johnston
c161b6cf96 Fix room creation test 2014-12-11 16:43:30 +00:00
Paul "LeoNerd" Evans
3b2cc26053 Initial hack at unit tests of room typing REST API 2014-12-11 16:03:12 +00:00
Erik Johnston
0b04369238 Fix public room joining by making sure replaces_state never points to itself. 2014-12-11 15:56:06 +00:00
Erik Johnston
9191292b0f Fix prev_content 2014-12-11 15:16:55 +00:00
Mark Haines
d80d505b1f Limit the size of images that are thumbnailed serverside. Limit the size of file that a server will download from a remote server 2014-12-11 14:19:32 +00:00
Erik Johnston
e72b16f9a3 Fix redaction tests 2014-12-11 13:38:52 +00:00
Erik Johnston
8cdebce470 Fix redactions. Fix 'age' key 2014-12-11 13:25:19 +00:00
Paul "LeoNerd" Evans
0ca072b3b6 Initial tiny hack at REST API for setting room typing notification status 2014-12-11 10:55:36 +00:00
Mark Haines
ead8fc5e38 doc the thumbnail methods 2014-12-11 10:41:43 +00:00
Mark Haines
b5eb9124f7 Make sure we pass a tuple to string '%' formatting 2014-12-11 10:08:09 +00:00
Paul "LeoNerd" Evans
5f49914dee Avoid cyclic dependency in handler setup 2014-12-10 21:17:48 +00:00
Paul "LeoNerd" Evans
1a75ff5c23 Hook up the event stream to typing notifications 2014-12-10 21:01:49 +00:00
Paul "LeoNerd" Evans
4006d58335 Store serial numbers per room for typing event stream purposes 2014-12-10 20:48:25 +00:00
Paul "LeoNerd" Evans
9eb819e828 First hack at implementing timeouts in typing notification handler 2014-12-10 19:39:01 +00:00
Paul "LeoNerd" Evans
4551afc6d2 Implement .cancel_call_later() in MockClock 2014-12-10 19:26:52 +00:00
Paul "LeoNerd" Evans
38da9884e7 Implement .call_later() in MockClock 2014-12-10 19:24:12 +00:00
Paul "LeoNerd" Evans
be9a8d68e0 Trivial test of MockClock() 2014-12-10 19:13:50 +00:00
Erik Johnston
4d6af0dde3 Fix some tests 2014-12-10 18:00:57 +00:00
Erik Johnston
4c682143c8 .from_string() no longer takes a HS 2014-12-10 18:00:49 +00:00
Erik Johnston
02e4c18171 Remove dead code 2014-12-10 18:00:36 +00:00
Erik Johnston
b245ee34ed Add some basic event validation 2014-12-10 17:59:47 +00:00
Mark Haines
4f37c0ea9d Merge branch 'develop' into media_repository 2014-12-10 16:55:06 +00:00
Mark Haines
7f193b9958 update media repository implementation docs 2014-12-10 16:54:37 +00:00
Mark Haines
61fc37e467 Merge branch 'develop' into media_repository 2014-12-10 16:14:17 +00:00
Erik Johnston
6a8148f15b Add new event graphing tool 2014-12-10 16:10:25 +00:00
Mark Haines
2d265ef3bd import Image as PIL.Image. 2014-12-10 16:09:18 +00:00
Erik Johnston
1d2a0040cf Fix bug where we clobbered old state group values 2014-12-10 15:55:03 +00:00
Mark Haines
e5275d856e Get the code actually working 2014-12-10 15:46:18 +00:00
Mark Haines
cc84d3ea78 Thumbnail uploaded and cached images 2014-12-10 15:40:52 +00:00
Erik Johnston
cabead6194 Actually fix bug when uploading state with empty state_key 2014-12-10 14:49:52 +00:00
Erik Johnston
02db7eb209 Fix bug when uploading state with empty state_key 2014-12-10 14:02:48 +00:00
Matthew Hodgson
8ffbb52eee oops 2014-12-10 13:43:34 +00:00
Erik Johnston
aae8a37e63 Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor 2014-12-10 13:18:40 +00:00
Matthew Hodgson
32bc2b4fc1 update codestyle based on debate on #matrix-dev 2014-12-10 13:11:43 +00:00
Erik Johnston
02db1fd2e7 Fix AttributeError 2014-12-10 12:00:05 +00:00
Erik Johnston
018443cb59 Make depth increase. 2014-12-10 11:59:53 +00:00
Erik Johnston
102d2373b4 Add __str__ to FrozenEvent 2014-12-10 11:38:08 +00:00
Erik Johnston
95aa903ffa Try and figure out how and why signatures are being changed. 2014-12-10 11:37:47 +00:00
Erik Johnston
6497caee7c Merge pull request #22 from matrix-org/federation_retries
Federation retries
2014-12-10 10:35:57 +00:00
Matthew Hodgson
0f4dcab238 turn back on per-request transaction retries, so that every time we try to hit a dead server we actually end up hammering 5 times :| 2014-12-10 10:28:27 +00:00
Erik Johnston
08aceea82e Add newline back in 2014-12-10 10:26:12 +00:00
Erik Johnston
f26ec14b21 Remove whitespace 2014-12-10 10:25:21 +00:00
Erik Johnston
b8d30899b1 Code style. 2014-12-10 10:16:09 +00:00
Matthew Hodgson
71da2bed55 plateau retries after 1h 2014-12-10 00:18:44 +00:00
Matthew Hodgson
faf12b64f8 add errbacks to enqueue_pdu deferreds; change logging for failed federation sends to warn rather than exception 2014-12-10 00:12:51 +00:00
Matthew Hodgson
2b1acb7671 squidge to 79 columns as per pep8 2014-12-10 00:03:55 +00:00
Matthew Hodgson
8ada2d2018 fix UTs by telling all the mock stores about the new methods for tracking retries 2014-12-09 23:53:07 +00:00
Erik Johnston
b63cea9660 This is to test jenkins 2014-12-09 16:35:00 +00:00
Erik Johnston
26e293abbe This is to test jenkins 2014-12-09 16:33:47 +00:00
Erik Johnston
50fd5014c2 This is to test jenkins 2014-12-09 16:33:04 +00:00
Erik Johnston
7e8d5c2606 This is to test jenkins 2014-12-09 16:31:27 +00:00
Erik Johnston
d45c030652 Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor 2014-12-09 14:53:30 +00:00
Erik Johnston
008303b245 PEP8 2014-12-09 14:49:11 +00:00
Erik Johnston
5eca288d28 Fix joining from an invite 2014-12-09 14:47:27 +00:00
Erik Johnston
aa3f66cf7f Change the way we implement get_events to be less sucky 2014-12-09 13:35:26 +00:00
Erik Johnston
90d022441f Delete test file 2014-12-09 13:14:38 +00:00
Erik Johnston
d7277398b9 This is to test jenkins 2014-12-09 12:20:49 +00:00
Erik Johnston
4a7a0ed949 This is to test jenkins 2014-12-09 11:41:32 +00:00
Erik Johnston
bdbcd8a638 This is to test jenkins 2014-12-09 11:36:38 +00:00
Erik Johnston
3654825b02 This is to test jenkins 2014-12-09 11:25:41 +00:00
Erik Johnston
2ef499ab84 This is to test jenkins 2014-12-09 11:19:39 +00:00
Erik Johnston
3986c775c4 This is to test jenkins 2014-12-09 11:16:03 +00:00
Erik Johnston
bc6564bac0 Add PEP8 newlines 2014-12-09 11:01:44 +00:00
Erik Johnston
8c48450682 Add PEP8 newlines 2014-12-09 10:58:31 +00:00
Erik Johnston
1c8ee06877 Remove unused snapshot 2014-12-09 10:53:34 +00:00
Erik Johnston
4e57943cc5 Remove unused import 2014-12-09 10:51:36 +00:00
Matthew Hodgson
c46ce4fca2 Merge branch 'develop' into federation_retries 2014-12-08 19:37:07 +00:00
Matthew Hodgson
8529fba02d fix a million stupid bugs and make it actually work 2014-12-08 19:34:51 +00:00
Erik Johnston
609c31e8df More bug fixes 2014-12-08 17:50:56 +00:00
Matthew Hodgson
0d3fa1ac6e add a write-through cache on the retry schedule 2014-12-08 17:48:57 +00:00
Erik Johnston
ee3df06183 More bug fixes 2014-12-08 14:50:48 +00:00
Erik Johnston
ba3d1e2fc0 Remove unused import 2014-12-08 12:01:25 +00:00
Erik Johnston
617dde2ba9 Ignore pycharm dir 2014-12-08 10:18:50 +00:00
Erik Johnston
e8323b9e34 More bug fixes 2014-12-08 10:16:18 +00:00
Erik Johnston
a295a3c691 Fix registration 2014-12-08 09:24:37 +00:00
Erik Johnston
d45f28f8bd Ignore pycharm dir 2014-12-08 09:24:08 +00:00
Erik Johnston
721482c83e Add forgotten file 2014-12-08 09:10:12 +00:00
Erik Johnston
d044121168 Various typos and bug fixes. 2014-12-08 09:08:26 +00:00
Matthew Hodgson
9c43b258ec actually reset retry schedule if we can successfuly talk to it 2014-12-08 00:17:12 +00:00
Matthew Hodgson
5cd43d4b9f fix stupid syntax thinkos 2014-12-07 23:44:16 +00:00
Matthew Hodgson
aed62a3583 track replication destination health, and perform exponential back-off when sending transactions. does *not* yet retry transactions, but drops them on the floor if waiting for a server to recover. 2014-12-07 02:26:07 +00:00
Mark Haines
63b0b946be point the entry_point for synapse-homeserver at the right method 2014-12-05 18:01:05 +00:00
Mark Haines
a953be097f Add a method field to thumbnail storage 2014-12-05 16:31:56 +00:00
Mark Haines
05e48c5d4b Add pillow to dependencies 2014-12-05 16:29:36 +00:00
Erik Johnston
6630e1b579 Start making more things use EventContext rather than event.* 2014-12-05 16:20:48 +00:00
Mark Haines
0363820122 Add a class for generating thumbnails using PIL 2014-12-05 16:12:37 +00:00
Erik Johnston
ce212eb83a Pull in latest matrix-angular_sdk 2014-12-05 11:55:24 +00:00
Erik Johnston
1c72e22c4f Pull in latest matrix-angular_sdk 2014-12-05 11:21:56 +00:00
Erik Johnston
c5c32266d8 Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor 2014-12-04 15:58:24 +00:00
Erik Johnston
c31dba86ec Convert rest and handlers to use new event structure 2014-12-04 15:50:01 +00:00
Mark Haines
c01fd5573c Implement download support for media_repository 2014-12-04 14:22:31 +00:00
Erik Johnston
5d7c9ab789 Begin converting things to use the new Event structure 2014-12-04 11:27:59 +00:00
Paul "LeoNerd" Evans
f5d2514fc0 @log_function on PresenceStream's get_new_events_for_user() 2014-12-03 19:48:14 +00:00
Paul "LeoNerd" Evans
52f1d3c886 Store any incoming presence push in the local cache anyway, even if there's no interested observers (yet *hint*) (SYN-115) 2014-12-03 19:06:24 +00:00
Erik Johnston
370cd9011e Merge branch 'release-v0.5.4' of github.com:matrix-org/synapse into develop 2014-12-03 18:03:42 +00:00
Erik Johnston
036516d647 Merge branch 'release-v0.5.4' of github.com:matrix-org/synapse 2014-12-03 18:02:33 +00:00
Erik Johnston
797193283e Bump changes and version 2014-12-03 18:01:47 +00:00
Erik Johnston
75b4329aaa WIP for new way of managing events. 2014-12-03 16:07:21 +00:00
Erik Johnston
6941a19715 Merge branch 'develop' of github.com:matrix-org/synapse into events_refactor 2014-12-03 11:56:49 +00:00
Erik Johnston
d06dfc70b0 Fix bug where did not always resolve all the deferreds in _attempt_new_transaction 2014-12-03 11:50:23 +00:00
Paul "LeoNerd" Evans
66b0596b7a Don't log synapse.storage at DEBUG level when under -v; require -v -v for that 2014-12-02 22:28:18 +00:00
Paul "LeoNerd" Evans
10eb8f070c Workaround for non-uniqueness of room member events in the database confusing HAVING COUNT() test of room membership intersection (with thanks to Tom Molesworth) 2014-12-02 21:36:53 +00:00
Paul "LeoNerd" Evans
274d137b00 Extra verbosity on logging of received presence info 2014-12-02 20:51:28 +00:00
Paul "LeoNerd" Evans
307f94dcbe Squash room_ids list to a set() to remove duplicates - with TODO marker as I have no idea /why/ the dups are happening in the first place 2014-12-02 20:51:12 +00:00
Mark Haines
2f804a7072 Fix pyflakes and pep8 warnings 2014-12-02 19:55:18 +00:00
Mark Haines
5da65085d1 Get uploads working with new media repo 2014-12-02 19:51:47 +00:00
Paul "LeoNerd" Evans
3bf2b4bc92 Merge branch 'develop' of github.com:matrix-org/synapse into develop 2014-12-02 18:01:32 +00:00
Paul "LeoNerd" Evans
afb646dc1e Allow GET /room/:room_id/messages without 'limit' parameter to default to 10 2014-12-02 18:01:16 +00:00
Paul "LeoNerd" Evans
d8b35250a5 Remove editor junk 2014-12-02 18:01:16 +00:00
David Baker
48f90036d8 Add non-working jitsi meet bridge 2014-12-02 18:01:16 +00:00
Matthew Hodgson
293e89e2da re-add OSX troubleshooting instructions (not sure how they got lost) 2014-12-02 18:01:16 +00:00
Matthew Hodgson
68cdcbdcab fix IO::Async abuse - thanks leo 2014-12-02 18:01:16 +00:00
Matthew Hodgson
9f4abd59f5 ignore rogue matrix call events; support verto.media RPC as a horrible hack; fix NA::Matrix dep 2014-12-02 18:01:16 +00:00
Matthew Hodgson
72c1cc743e clean up a vertobot a bit 2014-12-02 18:01:16 +00:00
Matthew Hodgson
0d149ae6e9 a simple matrix->verto bot, distantly related to the irc bridge 2014-12-02 18:01:16 +00:00
Erik Johnston
3aa333ec7c Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-12-02 17:46:33 +00:00
Erik Johnston
23a2c42469 Bump version 2014-12-02 17:45:40 +00:00
Mark Haines
c9f5af1de7 Merge branch 'master' into develop 2014-12-02 17:21:17 +00:00
Mark Haines
7993e3d10d SYN-187: Set a more sensible default for the content_addr 2014-12-02 17:20:02 +00:00
Paul "LeoNerd" Evans
481f5a2cbe Remove editor junk 2014-12-02 17:20:02 +00:00
Mark Haines
279c48c8b4 Write the upload portion of version 1 of the media repository 2014-12-02 17:13:14 +00:00
David Baker
758052d7f8 Add non-working jitsi meet bridge 2014-12-02 12:01:30 +00:00
Erik Johnston
c1e66800a9 Begin fleshing out a new Event object 2014-12-02 11:40:22 +00:00
Erik Johnston
9d53228158 Change DomainSpecificString so that it doesn't use a HomeServer object 2014-12-02 10:42:28 +00:00
Erik Johnston
740e95ee08 Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-12-02 10:29:34 +00:00
Erik Johnston
bde9ee5a4c Merge pull request #21 from tjardick/master
Added the needed libssl-dev package
2014-12-02 10:27:55 +00:00
Tjardick van der Kraan
f9846a27b6 Added the needed libssl-dev package 2014-12-02 11:22:00 +01:00
Erik Johnston
ec2b5d8c28 Store full JSON of events in db 2014-12-01 16:22:07 +00:00
Matthew Hodgson
dbc98c4e43 re-add OSX troubleshooting instructions (not sure how they got lost) 2014-11-30 23:21:47 +00:00
Matthew Hodgson
e43139ac5e fix IO::Async abuse - thanks leo 2014-11-28 16:16:41 +00:00
Matthew Hodgson
08bf2aaab5 ignore rogue matrix call events; support verto.media RPC as a horrible hack; fix NA::Matrix dep 2014-11-28 14:43:28 +00:00
Matthew Hodgson
f7fb4675a6 clean up a vertobot a bit 2014-11-28 06:55:05 +00:00
Matthew Hodgson
76a2e92658 a simple matrix->verto bot, distantly related to the irc bridge 2014-11-28 06:45:10 +00:00
Erik Johnston
c4bdeb5f0e Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-11-27 17:30:31 +00:00
Erik Johnston
ab74afdd8d Bump version 2014-11-27 17:30:08 +00:00
Erik Johnston
7cb21a24d4 Bump pinned version of pynacl pulled from github 2014-11-27 17:29:29 +00:00
Erik Johnston
d160873886 Merge branch 'release-v0.5.3' of github.com:matrix-org/synapse into develop 2014-11-27 17:17:15 +00:00
Erik Johnston
5e26f6f3ae Merge branch 'release-v0.5.3' of github.com:matrix-org/synapse 2014-11-27 17:16:24 +00:00
Erik Johnston
cce32f8dc5 Bump version and changelog 2014-11-27 17:15:32 +00:00
Erik Johnston
1505055334 Don't return outliers when we get recent events for rooms. 2014-11-27 16:38:50 +00:00
Erik Johnston
027542e2e5 Fix bugs when joining a remote room that has dodgy event graphs. This should also fix the number of times a HS will trigger a GET /event/ 2014-11-27 16:02:26 +00:00
Erik Johnston
0294fba042 on_receive_pdu takes more args 2014-11-27 14:46:33 +00:00
Erik Johnston
07699b5871 Change the way we get missing auth and state events 2014-11-27 14:31:43 +00:00
Erik Johnston
b8849c8cbf Re-sign events when we return them via federation as a temporary hack to work around the problem where we reconstruct events differently than when they were signed 2014-11-27 13:53:31 +00:00
Erik Johnston
00ab5cd6f2 Attempt to fix bug where we 500d an event stream due to trying to cancel a timer twice 2014-11-26 18:04:33 +00:00
Erik Johnston
858e87ab0d Add a workaround for bug where some initial join events don't reference creation events in their auth_events 2014-11-26 16:29:03 +00:00
Erik Johnston
6c485c282d Catch exceptions when trying to add an entry to rooms tables 2014-11-26 16:06:20 +00:00
Erik Johnston
4bae6851d1 Spelling 2014-11-26 15:30:30 +00:00
Erik Johnston
5288a7dc9a Bump version and changelog 2014-11-26 15:19:08 +00:00
Erik Johnston
516deb22aa Merge branch 'develop' of github.com:matrix-org/synapse 2014-11-26 15:17:40 +00:00
Erik Johnston
4e2ffe79a4 Don't delete the entire current_state_events table 2014-11-26 15:17:08 +00:00
Erik Johnston
47256cdde6 Merge branch 'release-v0.5.1' of github.com:matrix-org/synapse into develop 2014-11-26 12:07:28 +00:00
Erik Johnston
48ee9ddb22 Merge branch 'release-v0.5.1' of github.com:matrix-org/synapse 2014-11-26 12:06:36 +00:00
Erik Johnston
ad13f14432 Bump version numbers and change log 2014-11-26 11:53:12 +00:00
Erik Johnston
4e34e8f1c2 Use correct default port in scripts/check_signature.py 2014-11-26 11:47:31 +00:00
Erik Johnston
cb76945688 Add update delta for schema change 2014-11-26 11:17:19 +00:00
Erik Johnston
87538711b6 Update schema to support multiple signatures 2014-11-26 11:14:30 +00:00
Erik Johnston
822b15ea43 Fix tests. 2014-11-26 10:45:37 +00:00
Erik Johnston
3598c11c8d Correctly handle the case where we get an event for an unknown room, which turns out we are actually in 2014-11-26 10:41:08 +00:00
Matthew Hodgson
d45325b6d7 upgrade script depends on sqlite3 cli 2014-11-26 00:59:01 +00:00
Erik Johnston
64fc859dac Fix bugs in invite/join dances.
We now do more implement more of the auth on the events so that we
don't reject valid events.
2014-11-25 17:59:49 +00:00
Kegan Dougal
3536fd7d60 Don't double url-decode state event types. 2014-11-25 11:02:19 +00:00
Mark Haines
15099fade5 Drop log level for incorrect logging contexts to WARN if the context is wrong and DEBUG if the context is missing 2014-11-25 10:57:31 +00:00
Matthew Hodgson
6fe5899639 pip uninstall syweb 2014-11-24 17:57:48 +00:00
Erik Johnston
4961a4fab1 Mark the auth events as possible outlier 2014-11-24 13:55:49 +00:00
Erik Johnston
e549aac127 Add missing None check 2014-11-24 13:47:58 +00:00
Erik Johnston
2bca242fdc Ask for any auth events that we don't have 2014-11-24 13:46:41 +00:00
Erik Johnston
4bd0ab76c6 We don't always want to Auth get_persisted_pdu 2014-11-24 12:56:17 +00:00
Erik Johnston
a46e5ef621 SYN-163: Add an order by rowid to selects.
This should fix the bug where the edges of the graph get returned in a
different order than they were inserted in, and so no get_event no
longer returned the exact same JSON as was inserted. This meant that
signature checks failed.
2014-11-24 10:56:36 +00:00
Matthew Hodgson
ae8ad55cb8 typos 2014-11-24 01:41:12 +00:00
Matthew Hodgson
84b1c9d8c2 rst bugs 2014-11-24 01:41:05 +00:00
Mark Haines
fd40a80a68 Return 404 M_NOT_FOUND when trying to look up a room alias that doesn't exist 2014-11-21 15:11:48 +00:00
Paul "LeoNerd" Evans
5f19c55731 SYN-58: Allow passing explicit limit=0 to initialSync to request no messages at all; missing still implies default 10 2014-11-20 19:33:45 +00:00
Mark Haines
610c2ea131 Fix pep8 and pyflakes warnings 2014-11-20 18:00:10 +00:00
Mark Haines
8f8c484bc6 Merge pull request #20 from matrix-org/http_client_refactor
Http client refactor
2014-11-20 17:54:40 +00:00
David Baker
f1c7f8e813 Merge branch 'develop' into http_client_refactor 2014-11-20 17:49:48 +00:00
David Baker
e377d33652 Separate out the matrix http client completely because just about all of its code it now separate from the simple case we need for standard HTTP(S) 2014-11-20 17:41:56 +00:00
Mark Haines
db9ce032a4 Fix pep8 codestyle warnings 2014-11-20 17:26:36 +00:00
Mark Haines
dfdda2c871 Use module loggers rather than the root logger. Exceptions caused by bad clients shouldn't cause ERROR level logging. Fix sql logging to use 'repr' rather than 'str' 2014-11-20 17:10:37 +00:00
Mark Haines
32090aee16 Add a few missing yields, Move deferred lists inside PreserveLoggingContext because they don't interact well with the logging contexts 2014-11-20 16:24:00 +00:00
David Baker
20326054da Oops, I removed this param. 2014-11-20 15:24:38 +00:00
David Baker
dc60eee50e Refactor the HTTP clients a little. 2014-11-20 13:53:34 +00:00
David Baker
cf66532ac1 CaptchaServerHttpClient should extend the base, not matrix http client. 2014-11-20 12:48:21 +00:00
Mark Haines
217950b9ad Merge branch 'master' into develop 2014-11-20 11:02:30 +00:00
Mark Haines
f3ee8d6322 Use tagged version of matrix-angular-sdk 2014-11-20 10:51:04 +00:00
Mark Haines
b2aeaa2dcc Merge branch 'master' into develop 2014-11-20 10:00:13 +00:00
Mark Haines
dcb99e4972 SYN-153: Fix formatting of federation error message 2014-11-20 09:58:23 +00:00
Matthew Hodgson
25fd4d9f2c typoe 2014-11-19 15:25:23 -08:00
Erik Johnston
bf7940d7ff Add note about rerunning setup.py develop 2014-11-19 20:07:21 +00:00
Erik Johnston
19977b4659 Merge branch 'release-v0.5.0' of github.com:matrix-org/synapse 2014-11-19 18:03:57 +00:00
Erik Johnston
1a9551db82 Merge branch 'develop' of github.com:matrix-org/synapse into release-v0.5.0 2014-11-19 18:03:03 +00:00
Erik Johnston
5b46ce579b Bump version, changelog and upgrade.rst 2014-11-19 18:00:57 +00:00
Erik Johnston
493055731e Fix tests from prev commit 2014-11-19 18:00:07 +00:00
Erik Johnston
415ddf59bb Don't add a 'prev' key to m.room.member messages 2014-11-19 17:59:51 +00:00
Paul "LeoNerd" Evans
03dc63f6c8 Initialise UserPresenceCache instances to always contain a 'presence' key 2014-11-19 17:31:46 +00:00
Erik Johnston
4eada9a908 Fix backfill request 2014-11-19 17:22:37 +00:00
Erik Johnston
512993b57f Only users can set state events which have their own user_id 2014-11-19 17:22:37 +00:00
Mark Haines
ca91bb2f7f Sometimes there isn't a current logging context 2014-11-19 17:18:55 +00:00
Mark Haines
8993affdc0 SYN-153: Raise 404 if room alias is not found 2014-11-19 17:14:14 +00:00
Mark Haines
ff23e5ba37 remove demo webserver since synapse serves up the webclient itself 2014-11-19 16:45:25 +00:00
Mark Haines
0d1221155e remove unused import 2014-11-19 16:40:01 +00:00
Mark Haines
c5eabe3143 replace user_id with sender 2014-11-19 16:38:40 +00:00
Mark Haines
97c7c34f6f Preserve logging context in a few more places, drop the logging context after it has been stashed to reduce potential for confusion 2014-11-19 16:37:43 +00:00
Mark Haines
3e54d70ae2 SYN-141: Encode query params as UTF-8. 2014-11-18 19:43:08 +00:00
Matthew Hodgson
a7f470d1d9 more README fixes 2014-11-18 11:23:17 -08:00
Mark Haines
428581dd05 SYN-144: Remove bad keys from pdu json objects, convert age_ts to age
for all pdus sent.
2014-11-18 19:20:25 +00:00
Paul "LeoNerd" Evans
572a1ca42a Remember also to UTF-8 decode bytes in room alias names in directory server URLs 2014-11-18 18:06:35 +00:00
Paul "LeoNerd" Evans
3bfc3dd45b Remember to URL decode the room_id in room initialSync 2014-11-18 17:44:55 +00:00
Mark Haines
db7e8b5619 SYN-141: Decode the query params as UTF-8 2014-11-18 17:17:57 +00:00
Mark Haines
54c438d8d3 Remove unused variable 2014-11-18 16:46:12 +00:00
Mark Haines
1731af3f29 SYN-104: When going backwards the end token should be before the last event 2014-11-18 16:45:06 +00:00
Paul Evans
11fd81e398 Merge pull request #17 from matrix-org/room-initial-sync
Room initial sync
2014-11-18 16:44:25 +00:00
Paul "LeoNerd" Evans
88dfa7baa6 Ensure to parse a real pagination config object out of room initialSync request and pass it on 2014-11-18 16:34:43 +00:00
Paul "LeoNerd" Evans
75e95c45a2 Rename message handler's new snapshot_room to room_initial_sync() as that better suits its purpose 2014-11-18 16:02:44 +00:00
Erik Johnston
c6ea29d916 Revert accidental commit of bad file 2014-11-18 15:57:00 +00:00
Paul "LeoNerd" Evans
e9f587ecba Merge remote-tracking branch 'origin/develop' into room-initial-sync 2014-11-18 15:48:30 +00:00
Mark Haines
3553101eb3 Null check when determining default power levels 2014-11-18 15:43:17 +00:00
Mark Haines
b01dd76be1 SYN-149: Enable auth for events added during room creation since they should pass auth checks 2014-11-18 15:42:53 +00:00
Erik Johnston
95614e5220 Fix auth to correctly handle initial creation of rooms 2014-11-18 15:36:41 +00:00
Mark Haines
ae9c2ab165 SYN-149: Send join event immediately after the room create event 2014-11-18 15:29:48 +00:00
Paul "LeoNerd" Evans
33d328d967 Include room members' presence in room initialSync 2014-11-18 15:28:58 +00:00
Paul "LeoNerd" Evans
759db7d7d5 Added ability to .get_state() from the PresenceHandler by returning a complete m.presence event 2014-11-18 15:25:55 +00:00
Paul "LeoNerd" Evans
4c18e08036 Don't expect all _user_cachemap entries to definitely contain a "last_active" key 2014-11-18 15:10:11 +00:00
Mark Haines
a5b88c489e Split out sending the room alias events from creating the alias so that we can do them in the right point when creating a room 2014-11-18 15:03:13 +00:00
Paul "LeoNerd" Evans
17f977a9de Include 'messages' snapshot in room initialSync 2014-11-18 14:07:51 +00:00
Matthew Hodgson
c571dd4f0e warn about memory 2014-11-17 11:44:53 -08:00
Matthew Hodgson
94ed41f236 update the README.rst to reflect the develop branch 2014-11-17 11:42:27 -08:00
Mark Haines
26fc878944 Stop before starting when restarting 2014-11-17 19:16:15 +00:00
Matthew Hodgson
b57e9f58fd yet another installation gotcha 2014-11-17 11:11:35 -08:00
Matthew Hodgson
d18fc97717 Merge branch 'develop' of git+ssh://github.com/matrix-org/synapse into develop 2014-11-17 11:11:15 -08:00
Matthew Hodgson
b80d1925ff clarify install instructions further still 2014-11-17 10:52:12 -08:00
Paul "LeoNerd" Evans
31a049eb69 Merge branch 'develop' into room-initial-sync
Conflicts:
	synapse/handlers/message.py
2014-11-17 16:59:24 +00:00
Mark Haines
cf45e57d9c SYN-148: Add the alias after creating the room 2014-11-17 16:37:33 +00:00
Mark Haines
1b91c26409 Mark synapse as not zip-safe since it needs to be able to read schema files from the filesystem 2014-11-17 16:36:24 +00:00
Mark Haines
5d273a0c76 Remove syweb directory. pull in syweb as a dependency from github 2014-11-17 12:55:24 +00:00
Kegan Dougal
da6df07a9d SYWEB-152: Remove room join logic from RoomController and put it in eventHandlerService.joinRoom. 2014-11-17 11:04:10 +00:00
Kegan Dougal
7799e14121 Add clearRooms() to wipe data when you logout. 2014-11-17 11:04:10 +00:00
Mark Haines
2eaf689f71 These lines aren't doing anything 2014-11-17 10:41:35 +00:00
Erik Johnston
8c45c8b8b9 Merge pull request #14 from matrix-org/merge_pdu_event_objects
Merge pdu and event objects
2014-11-17 10:29:23 +00:00
Mark Haines
1d3ef8734c Merge remote-tracking branch 'origin/develop' into merge_pdu_event_objects 2014-11-17 10:21:51 +00:00
Kegan Dougal
547adda446 Move getLastMessage to modelService. 2014-11-17 10:04:36 +00:00
Kegan Dougal
fbf8003237 s/eventHandlerService.getUsersCountInRoom/modelService.getUserCountInRoom/g 2014-11-17 09:33:22 +00:00
Matthew Hodgson
4d922a0f9b do *not* depend on external websites to host our JS - if nothing else, it makes hacking on synapse when offline (e.g. on planes) a huge PITA :( 2014-11-15 23:21:24 +00:00
Matthew Hodgson
8413c38295 doc 2014-11-15 01:52:08 +00:00
Matthew Hodgson
adf582dba7 merge in msg.__room_member usage to new message display template 2014-11-15 01:34:33 +00:00
Matthew Hodgson
921d95357d improve notif setting text 2014-11-15 01:30:42 +00:00
Matthew Hodgson
1f70929e53 spell useCaptcha right... 2014-11-15 01:30:42 +00:00
Matthew Hodgson
a7ddcc9c0f do not use captcha by default 2014-11-15 01:30:42 +00:00
Mark Haines
cb4b6c844a Merge PDUs and Events into one object 2014-11-14 21:25:02 +00:00
Mark Haines
8c2b5ea7c4 Fix PDU and event signatures 2014-11-14 19:11:04 +00:00
Mark Haines
de1ec90133 Validate signatures on incoming events 2014-11-14 19:11:04 +00:00
Kegan Dougal
44a24605ad Add event-stream-service unit tests. 2014-11-14 17:30:17 +00:00
Kegan Dougal
570db98548 Unbreak tab complete... 2014-11-14 17:01:09 +00:00
Kegan Dougal
d22d9b22b1 Add more modelService unit tests. 2014-11-14 16:36:02 +00:00
Kegan Dougal
b93804529d Move getUserPowerLevel to modelService. 2014-11-14 16:15:32 +00:00
Kegan Dougal
78bf5648e7 Fix bug which caused notifications to appear for old messages. 2014-11-14 15:57:18 +00:00
Kegan Dougal
c3278a8262 Tidy up room.html member list to use member again, now that scope.members is gone. 2014-11-14 15:39:47 +00:00
Kegan Dougal
d4f6d65e1d Add extra checks to duration filter. 2014-11-14 15:34:19 +00:00
Kegan Dougal
5ebd004a10 Actually look for last_active_ago in the right place.. 2014-11-14 15:30:49 +00:00
Kegan Dougal
459863bcff Remove scope.members from RoomController and use modelService instead. This may make things unstable. 2014-11-14 14:26:05 +00:00
David Baker
fe3401e037 Be more helpful and tell the user how to generate a config too. 2014-11-14 13:30:06 +00:00
David Baker
933ce76057 Adding --generate-config will not help if the user has not specified a config file. 2014-11-14 13:24:12 +00:00
Kegan Dougal
d5a42e9d9c Use modelService for getting current presence state rather than RoomController.members 2014-11-14 12:59:22 +00:00
Erik Johnston
b8eca1ffbf Merge pull request #13 from matrix-org/request_logging
Request logging
2014-11-14 11:46:07 +00:00
Kegan Dougal
49a1b4262d Use modelService to access room member power levels rather than RoomController. 2014-11-14 11:29:50 +00:00
Mark Haines
e903c941cb Merge branch 'develop' into request_logging
Conflicts:
	setup.py
	synapse/storage/_base.py
	synapse/util/async.py
2014-11-14 11:16:50 +00:00
Kegan Dougal
974206ebe1 Use mUserDisplayName filter in more places. Store power_level[norm] for each RoomMember. 2014-11-14 11:13:03 +00:00
Kegan Dougal
687662c990 Add notification-service unit tests. 2014-11-14 10:33:42 +00:00
Kegan Dougal
d1df3cd4d5 Add mUserDisplayName unit tests. 2014-11-14 09:52:53 +00:00
Kegan Dougal
656bf2c60c Unskip unit tests; fix filter dependency. 2014-11-14 09:20:36 +00:00
Kegan Dougal
633137d501 Remove getUserDisplayName and move that logic the filter mUserDisplayName. Update references. Skip tests for now as there are some unresolved DI issues for filters. 2014-11-13 17:59:08 +00:00
Kegan Dougal
3916e23bbd Remove rootScope.presence and replaced with modelService.getUser/setUser. 2014-11-13 16:43:53 +00:00
Kegan Dougal
afd2e214bc SYWEB-152: Move up/down history fully to a directive.
Previously, there was some of it in a lovely generic directive, but the
core of it was hard coded id attributes in RoomController. It's now all
generic in a directive: the room history you get when you up/down arrow
is determined by the value of the attribute e.g. command-history="!foo:bar"
would present the history for !foo:bar. In practice, this is {{room_id}}
in the html.
2014-11-13 16:12:17 +00:00
Mark Haines
8d8a133c89 SYN-103: Remove "origin" and "destination" keys from edus 2014-11-13 15:49:03 +00:00
Kegan Dougal
d085807070 Migrate random bits of desktop notification logic out of roomController and into eventHandlerService where everything else is. 2014-11-13 15:21:50 +00:00
David Baker
58ddff0881 remove stray unmatched css comment 2014-11-13 14:45:29 +00:00
David Baker
bfe20c11c3 remove now-unused styles 2014-11-13 14:42:31 +00:00
Mark Haines
e7c6d2c9d9 SYN-138: Rewrite synctl in python and include it in the python distribution 2014-11-13 14:39:30 +00:00
David Baker
cdb8d746ef Merge with Matthew's killing of ng-animate
Conflicts:
	syweb/webclient/app-controller.js
	syweb/webclient/index.html
2014-11-13 14:37:43 +00:00
Kegan Dougal
cadcc6cabe Add commands-service unit tests. 2014-11-13 14:35:58 +00:00
Matthew Hodgson
11da8d0dff remove nganimate dependency as it seems to feature disproportionately highly in the FF profiler, and removing it seems to have stopped my FF stalling for seconds on end 2014-11-13 16:34:51 +02:00
David Baker
f842bca471 Kill ng-animate with fire because it's terrible (was causing the page to be very sluggish). Do the call icons in pure CSS3 and use one less image to boot (in some browsers the phone icon will be the wrong browser but they can deal). 2014-11-13 14:34:03 +00:00
Kegan Dougal
0a699df5e8 Wipe the selected room ID on the home screen. 2014-11-13 12:33:43 +00:00
Kegan Dougal
5180285456 SYWEB-152: Unbreak /me 2014-11-13 11:58:28 +00:00
Kegan Dougal
8ce69e802d SYWEB-152: Migrate IRC command logic to commands-service. 2014-11-13 11:55:49 +00:00
David Baker
0046df4b51 This gives just enough space for the vertical scrollbar to be shown without adding a horizontal scrollbar. 2014-11-13 10:19:09 +00:00
Matthew Hodgson
c2609b239f suggest ~/.synapse 2014-11-13 11:59:33 +02:00
Matthew Hodgson
28408a9f64 Merge branch 'develop' of git+ssh://github.com/matrix-org/synapse into develop 2014-11-13 11:58:54 +02:00
David Baker
9950ce2334 Detect OpenWebRTC and add workarounds, but comment out the turn server removal for now so we have a live demo of it not working. 2014-11-12 17:34:00 +00:00
David Baker
2b64c573c3 Oops, change videoElement / selector in audio call too. 2014-11-12 17:31:03 +00:00
Kegan Dougal
f4a3b194da Fix ability to invite users. Remove unused variables. 2014-11-12 17:06:12 +00:00
Erik Johnston
f04b3d5042 Store all signatures on events rather than just dropping them 2014-11-12 17:02:34 +00:00
Kegan Dougal
59cf6f5ec9 Add more recents service unit tests. 2014-11-12 16:32:17 +00:00
Kegan Dougal
3d3f692fd8 Add test coverage to the webclient. Update .gitignore 2014-11-12 16:22:22 +00:00
Erik Johnston
b2596c660b Add a few more comments to the federation handler 2014-11-12 16:20:30 +00:00
Erik Johnston
e715741abc Update some of the docs in event_federation 2014-11-12 16:20:30 +00:00
Kegan Dougal
813125e122 Make earlier versions of jasmine happy by doing explicit object comparisons 2014-11-12 16:01:01 +00:00
Kegan Dougal
92ea45070c Add recentsService unit tests. 2014-11-12 15:58:30 +00:00
David Baker
9412110c82 comment typo 2014-11-12 15:36:05 +00:00
Kegan Dougal
960b28c90a SYWEB-57: Highlight rooms which have had their bingers go off in blue.
Priority is the same as xchat so selected > blue > red.
2014-11-12 15:31:06 +00:00
Matthew Hodgson
ca386a4b25 various fixes based on truphone feedback 2014-11-12 17:26:50 +02:00
Kegan Dougal
99c445a6d6 Migrate unread messages logic to recentsService. 2014-11-12 15:11:34 +00:00
Kegan Dougal
96cd467cfa Add recents-service to store shared state between recents-controllers.
Remove the selectedRoomId from rootScope and instead store it in
recents-service. Add a broadcast to notify listeners (recents-controller)
to updates of this.
2014-11-12 14:57:36 +00:00
Erik Johnston
e24d5cb97d Document StateStore and use transactions 2014-11-12 14:33:48 +00:00
Erik Johnston
58c0ef90c9 Add indices to state group tables 2014-11-12 14:33:48 +00:00
Kegan Dougal
e632fcd933 SYWEB-57: Highlight rooms where the history has changed.
This highlights rooms when something has happened and you haven't viewed
it yet. It highlights entries in a slightly red background colour.
2014-11-12 14:31:30 +00:00
Kegan Dougal
78ff63a9c7 Remove getRoomAliasAndDisplayName: room name logic is in mRoomName filter, and this method was only used for /publicRooms requests. 2014-11-12 11:49:27 +00:00
Kegan Dougal
e7ccd26c70 SYWEB-140: Redact button layout. 2014-11-12 11:40:28 +00:00
Erik Johnston
3db0efa69f Fix pyflake warnings and add a FIXME comment to deal with auth_chains received when joining 2014-11-12 11:27:02 +00:00
Erik Johnston
6fea478d2e Fix bugs with invites/joins across federatiom.
Both in terms of auth and not trying to fetch missing PDUs for invites,
joins etc.
2014-11-12 11:24:11 +00:00
Kegan Dougal
2c400363e8 SYWEB-146: Fix room ID leaking on recents page when the name of the room is just an alias. 2014-11-12 11:24:05 +00:00
Kegan Dougal
9d0efedaee Move room alias/id mapping logic from matrixService to modelService. 2014-11-12 11:14:19 +00:00
Matthew Hodgson
33e9e0fb2d move model/ into matrix-doc/drafts 2014-11-12 01:16:38 +02:00
Matthew Hodgson
ef1eb4c888 this got merged into matrix-doc/specification/00_basis.rst by someone 2014-11-12 01:14:06 +02:00
Matthew Hodgson
0ac2dc388e move OLD_specification into matrix-doc/drafts 2014-11-12 01:04:32 +02:00
Matthew Hodgson
a0bc0fdf21 vestigial readme for sphinx 2014-11-12 00:18:26 +02:00
Matthew Hodgson
192fce51d7 hide crap from gitignore 2014-11-12 00:18:25 +02:00
Matthew Hodgson
774cff3c72 move swagger impl to matrix-doc 2014-11-12 00:18:25 +02:00
Matthew Hodgson
0c59bc5e35 move stuff out of implementation-notes - /everything/ here should be implementation-notes now 2014-11-12 00:18:25 +02:00
Matthew Hodgson
64bc36304f typo 2014-11-12 00:18:25 +02:00
Matthew Hodgson
7e1779d48c this is ancient and has been moved to matrix-doc/drafts/federated_versioning_design_notes.rst 2014-11-12 00:18:25 +02:00
Matthew Hodgson
b6c48a694b haven't i already moved you to matrix-doc twice? :/ 2014-11-12 00:18:25 +02:00
Matthew Hodgson
216d5f6b52 this is obsolete and lives in matrix-doc in specification/30_server_server_api.rst now 2014-11-12 00:17:06 +02:00
Matthew Hodgson
bebca337c4 this has been merged into matrix-doc/specification/30_server_server_api.rst 2014-11-12 00:17:05 +02:00
Erik Johnston
61ecb13bf0 PEP8ify 2014-11-11 18:00:13 +00:00
Erik Johnston
37900a92db Only allow people in a room to look up room state. 2014-11-11 17:55:32 +00:00
Erik Johnston
997ed151db synapse.state docs. 2014-11-11 17:45:46 +00:00
Erik Johnston
3db2c0d43e Rename annotate_state_groups to annotate_event_with_state 2014-11-11 16:58:53 +00:00
Mark Haines
a8ceeec0fd Merge pull request #12 from matrix-org/federation_authorization
Federation authorization
2014-11-11 16:40:50 +00:00
Matthew Hodgson
83a1cce1ea no evil horizontal textarea resizing 2014-11-11 16:15:01 +00:00
Matthew Hodgson
548ace0115 make image buttons more buttony 2014-11-11 15:17:51 +00:00
Erik Johnston
092979b8cc Fix bugs which broke federation due to changes in function signatures. 2014-11-11 14:19:13 +00:00
Erik Johnston
02ebb9f0c3 Fix state tests 2014-11-11 14:16:48 +00:00
Erik Johnston
5ff0bfb81d Fix bug where we /always/ created a new state group 2014-11-11 14:16:41 +00:00
Erik Johnston
ed8b7d400c Fix validation tests 2014-11-11 10:31:59 +00:00
Erik Johnston
2cdff00788 Fix typo in validator 2014-11-11 10:31:47 +00:00
Erik Johnston
339c11dd86 Fix rest.test_rooms 2014-11-11 08:09:42 +00:00
Erik Johnston
0292d991af Add EventValidator module 2014-11-11 08:09:28 +00:00
Matthew Hodgson
bf944d9219 fix stupid truncation bug 2014-11-11 05:50:55 +00:00
Matthew Hodgson
7df8c8c287 apply some cache headers to try to make the content repo less nutso 2014-11-11 05:36:39 +00:00
Matthew Hodgson
217c082ac1 linky topics 2014-11-11 05:27:18 +00:00
Matthew Hodgson
588dcf492b wrap fully qualified user IDs more intelligently 2014-11-11 05:16:03 +00:00
Matthew Hodgson
2fdf939ca9 fix weird shaped message table rows 2014-11-11 05:02:24 +00:00
Matthew Hodgson
5f38625f21 fix lines with wrapped userids 2014-11-11 04:48:40 +00:00
Matthew Hodgson
d669eb6d05 add new peity dep to tests 2014-11-11 04:45:32 +00:00
Matthew Hodgson
e9d5a91def fix button spacing 2014-11-11 04:40:39 +00:00
Matthew Hodgson
b765dc005b major CSS overhaul to try to make things look a bit cleaner 2014-11-11 04:39:30 +00:00
Matthew Hodgson
303b455965 trivial spacing fix 2014-11-11 04:39:30 +00:00
Erik Johnston
f45a6a7004 Fix RST sublist formatting bug 2014-11-10 22:07:08 +00:00
Matthew Hodgson
f987393b32 moar boxes. 2014-11-10 21:56:52 +00:00
Paul "LeoNerd" Evans
c23afed39a Include room membership in room initialSync 2014-11-10 19:34:47 +00:00
Paul "LeoNerd" Evans
1fd8139138 Put room state in room initialSync output - I guess this is right; I really can't find any other tests similar... 2014-11-10 19:29:58 +00:00
Paul "LeoNerd" Evans
269f80bf8e Have room initialSync return the room's room_id 2014-11-10 19:02:19 +00:00
Matthew Hodgson
0b51d970b4 document up the current architecture a bit based on the workshop the other week 2014-11-10 18:43:16 +00:00
Erik Johnston
a8e565eca8 Add an EventValidator. Fix bugs in auth ++ storage 2014-11-10 18:25:42 +00:00
Paul "LeoNerd" Evans
50c8e3fcda Initial (empty) test that room initialSync at least returns 200 OK 2014-11-10 18:07:55 +00:00
Erik Johnston
ec824927c1 Fix rest.test_events. Convert to use SQLiteMemoryDbPool 2014-11-10 15:37:53 +00:00
Erik Johnston
4ebdb19682 Fix SQLBaseStoreTestCase 2014-11-10 15:32:35 +00:00
Erik Johnston
3cd9c02f71 Fix stream test. 2014-11-10 15:29:19 +00:00
Erik Johnston
e2cebe26e8 Fix room_member storage test 2014-11-10 15:24:15 +00:00
Erik Johnston
c174d19d1e Fix redaction storage test 2014-11-10 15:21:41 +00:00
Erik Johnston
cdc1b5d629 Fix regression where we did not return redacted events. 2014-11-10 15:21:30 +00:00
Erik Johnston
b01159f234 Fix room handler test 2014-11-10 14:58:33 +00:00
Erik Johnston
5d439b127b PEP8 2014-11-10 13:46:44 +00:00
Erik Johnston
c46088405a Remove useless comments 2014-11-10 13:39:33 +00:00
Erik Johnston
003668cfaa Add auth to the various server-server APIs 2014-11-10 13:37:24 +00:00
Erik Johnston
6447db063a Fix backfill to work. Add auth to backfill request 2014-11-10 11:59:51 +00:00
Erik Johnston
65f846ade0 Notify users about invites. 2014-11-10 11:15:02 +00:00
Erik Johnston
407d8a5019 Fix invite auth 2014-11-10 10:35:43 +00:00
Erik Johnston
6cb6cb9e69 Tidy up some of the unused sql tables 2014-11-10 10:31:00 +00:00
Erik Johnston
1c06806f90 Finish redaction algorithm. 2014-11-10 10:21:32 +00:00
David Baker
7d15452c30 Various fixes to try & make openwebrtc safari extension work (still doesn't work). 2014-11-07 17:56:28 +00:00
Erik Johnston
07286a73b1 Use current state to get room hosts, rather than querying the database 2014-11-07 16:03:31 +00:00
Erik Johnston
02c3b1c9e2 Add '/event_auth/' federation api 2014-11-07 15:35:53 +00:00
Erik Johnston
d2fb2b8095 Implement invite part of invite join dance 2014-11-07 13:41:00 +00:00
Erik Johnston
328dab2463 Remove /context/ request 2014-11-07 11:40:38 +00:00
Erik Johnston
97a096b507 Add hash of current state to events 2014-11-07 11:37:06 +00:00
Erik Johnston
3b4dec442d Return auth chain when handling send_join 2014-11-07 11:22:12 +00:00
Erik Johnston
16a0815fac Fix bug in _get_auth_chain_txn 2014-11-07 11:21:20 +00:00
Erik Johnston
3cb678f84c Merge branch 'develop' of github.com:matrix-org/synapse into federation_authorization 2014-11-07 10:55:28 +00:00
Erik Johnston
49948d72f3 Fix joining over federation 2014-11-07 10:53:38 +00:00
Erik Johnston
8b0e96474b Implement method to get auth_chain from a given event_id 2014-11-07 10:53:38 +00:00
Erik Johnston
bf6b72eb55 Start implementing auth chains 2014-11-07 10:53:38 +00:00
Erik Johnston
8421cabb9d Neaten things up a bit 2014-11-07 10:53:38 +00:00
Erik Johnston
46de65cab9 Don't query the DB for user power levels 2014-11-07 10:53:38 +00:00
Erik Johnston
351c64e99e Amalgamate all power levels.
Remove concept of reqired power levels, something similiar can be done
using the new power level event.
2014-11-06 16:59:13 +00:00
David Baker
1a62f1299d Detect call type by examining the SDP always rather than just in Firefox as it seems Chrome's behaviour is the odd one out here. 2014-11-06 16:55:15 +00:00
David Baker
4b256cab31 Don't cache isWebRTCSupported because whether webRTC is supported might change part-way through the page's lifecycle if your webrtc support comes from some kind of injected content script (hello OpenWebRTC Sarafi extension) 2014-11-06 16:49:33 +00:00
Erik Johnston
233969bb58 Update to use replaces_state rather than prev_state 2014-11-06 15:25:03 +00:00
Erik Johnston
c6766d45b5 Don't send prev_state to clients anymore 2014-11-06 15:19:00 +00:00
Erik Johnston
4317c8e583 Implement new replace_state and changed prev_state
`prev_state` is now a list of previous state ids, similiar to
prev_events. `replace_state` now points to what we think was replaced.
2014-11-06 15:10:55 +00:00
Kegan Dougal
e3c3f5a6d0 Swap from using raw m.room.member events for room members to using actual RoomMember objects, so User objects can be tacked on. Update tests. 2014-11-06 14:52:22 +00:00
Kegan Dougal
d4c20c472b Use mRoomName on join notifications as well. 2014-11-06 14:23:14 +00:00
Kegan Dougal
b77cce4ec5 Add modelService test. Thin for now but will be expanded upon. 2014-11-06 14:18:23 +00:00
Kegan Dougal
8bcd36377a Factor out room name logic: mRoomName is the canonical source. 2014-11-06 13:37:05 +00:00
Kegan Dougal
c9c2e39531 Use .not.toEqual instead of .toNotEqual which is in a newer version of Jasmine. 2014-11-06 12:00:03 +00:00
Kegan Dougal
dd8af5565b Start adding regression tests. First up, register-controller for SYWEB-109. 2014-11-06 11:55:07 +00:00
Kegan Dougal
a92092340b Fix broken tests which were previously skipped. 2014-11-06 11:14:31 +00:00
Kegan Dougal
c5eec32c58 Add mRoomName and orderMembersList filter tests. Mark FIXME on broken tests for now. 2014-11-06 11:04:43 +00:00
Kegan Dougal
7465250141 State *.js in karma.conf rather than *.* so *.js~ files are ignored. 2014-11-06 09:34:35 +00:00
Kegan Dougal
69c396825b Add duration filter unit tests. 2014-11-05 17:49:03 +00:00
Kegan Dougal
6aba43f6cc Add a few eventHandlerService tests. 2014-11-05 15:32:35 +00:00
Kegan Dougal
988a8526b5 Finish matrixService unit tests. Add missing encodeURIComponent to path args. 2014-11-05 14:35:41 +00:00
Erik Johnston
3791b75000 Fix a couple more storage tests 2014-11-05 13:48:36 +00:00
Erik Johnston
2fcce3b3c5 Remove stale tests 2014-11-05 13:43:36 +00:00
Erik Johnston
da80ebcc6b Fix redaction storage test 2014-11-05 13:28:57 +00:00
Erik Johnston
cc44ecc62f Get correct prev_events 2014-11-05 13:23:35 +00:00
Kegan Dougal
0881a8ae6f Add more tests and a TODO. 2014-11-05 12:32:28 +00:00
Kegan Dougal
d3a02ec038 Fix url decoding bugs with /redact and /send APIs. 2014-11-05 12:05:11 +00:00
Kegan Dougal
42081b1937 Don't urlencode event types just yet so older HSes don't 500.
Skip the tests which test for urlencoding, and add a TODO
in matrixService.
2014-11-05 11:28:22 +00:00
Kegan Dougal
9f6d1b10ad Be sure to urlencode/decode event types correctly in both the web client and HS. 2014-11-05 11:21:55 +00:00
Erik Johnston
1616df2f61 Fix typing tests 2014-11-05 11:15:56 +00:00
Erik Johnston
c670ce416b Fix room tests 2014-11-05 11:15:15 +00:00
Erik Johnston
f48fce8bd3 Fix profile tests 2014-11-05 11:13:58 +00:00
Erik Johnston
24e2da4557 Fix presencelike test 2014-11-05 11:13:01 +00:00
Erik Johnston
416ab4ebf0 Don't execute empty tests. Formatting. 2014-11-05 11:12:47 +00:00
Kegan Dougal
a2aafeb959 Add a bunch more unit tests for matrixService. 2014-11-05 11:11:36 +00:00
Erik Johnston
34c4614682 Fix presence test 2014-11-05 11:10:54 +00:00
Erik Johnston
9e429239ab Fix Federation test 2014-11-05 11:10:36 +00:00
Erik Johnston
96c001e668 Fix auth checks to all use the given old_event_state 2014-11-05 11:07:54 +00:00
Kegan Dougal
4facbe02fb URL encoding bugfix and add more tests. 2014-11-04 17:48:47 +00:00
Kegan Dougal
a70765ed90 Add matrix-service unit tests. Update angular-mocks. 2014-11-04 17:19:49 +00:00
Erik Johnston
4a5e95511e PEP8 2014-11-04 17:13:21 +00:00
Erik Johnston
dfb3d21a6d Fix room handler tests 2014-11-04 17:12:39 +00:00
Erik Johnston
b0554682ed Fix federation handler tests. I've removed the invite/join dance ones as they are completely out of date. 2014-11-04 16:51:59 +00:00
Erik Johnston
da4a09f977 Don't bother locking 2014-11-04 16:51:23 +00:00
Mark Haines
3068210a93 SYN-112: Get pynacl from github instead of PyPI 2014-11-04 16:45:33 +00:00
Erik Johnston
7f4c7fe4e8 PEP8 2014-11-04 16:35:38 +00:00
Erik Johnston
dd3711bdbd Fix tests.handlers.test_directory 2014-11-04 16:33:52 +00:00
Erik Johnston
b15e8d5bbc event <-> pdu mappings are now trivial and will soon be scrapped 2014-11-04 16:20:02 +00:00
Mark Haines
dca3ba2f77 Determine webclient path by the python module it is contained in 2014-11-04 16:19:16 +00:00
Erik Johnston
24305ba5bf Fix up federation tests 2014-11-04 16:15:30 +00:00
Kegan Dougal
4e52f9699b Update .gitignore to ignore config.js files in syweb/webclient. 2014-11-04 16:15:13 +00:00
Mark Haines
89ba802b23 Move webclient to a python module so that it can be installed 2014-11-04 15:57:23 +00:00
Kegsay
020fc15d98 Merge pull request #11 from matrix-org/webclient-room-data-restructure
Webclient room data restructure
2014-11-04 15:44:58 +00:00
Kegan Dougal
1273023ac3 Don't need this; should be on -g path. 2014-11-04 15:35:34 +00:00
Mark Haines
4a73c366fa exclude tests 2014-11-04 15:18:43 +00:00
Erik Johnston
a5a4ef3fd7 Fix bug in replication 2014-11-04 15:16:43 +00:00
Erik Johnston
2a49f177fe On AuthError, raise a FederationError 2014-11-04 15:10:43 +00:00
Erik Johnston
8918422156 Move FederationError to synapse.api.errors 2014-11-04 15:10:27 +00:00
Erik Johnston
fc7b2b11a2 PEP8 2014-11-04 15:09:34 +00:00
Mark Haines
402d080990 Fix installation instructions in README 2014-11-04 15:08:13 +00:00
Kegan Dougal
ae48e75ad7 Use phantomjs as the default browser and not chrome. 2014-11-04 14:38:45 +00:00
Erik Johnston
440cbd5235 Add support for sending failures 2014-11-04 14:17:55 +00:00
Erik Johnston
d7412c4df1 Remove unused interface 2014-11-04 14:16:19 +00:00
Erik Johnston
aa76bf39ab Remove unused imports 2014-11-04 14:14:02 +00:00
Kegan Dougal
29b54d6638 Update karma.conf.js to A: actually run the tests, and B: generate JUnit XML. 2014-11-04 14:01:25 +00:00
Kegan Dougal
f7cf978f68 SYWEB-136: Send m.images according to the spec. 2014-11-04 11:26:03 +00:00
Kegan Dougal
1ac1cd6c14 SYWEB-133: JSON dialog now appears on dblclick to allow users to highlight text. 2014-11-04 11:06:31 +00:00
Kegan Dougal
5949571fe7 SYWEB-116: Implement historical display name support.
This works for both live and paginated events. Each 'message' event has
an associated '__room_member' key which points to the state of the sender
at that point in time. Invites have an additional key '__target_room_member'
which point to the state of the invitee at that point in time. This obviates
the need for mapping user_ids to *current* displaynames in the message list,
though this is still required for the user/presence list.
2014-11-04 10:30:34 +00:00
Kegan Dougal
1c86ec5b8d Rejig display names when paginating to lie less. 2014-11-04 10:18:46 +00:00
Kegan Dougal
43e7ad1b1c Rejig order of checks/state updates/message insertions for m.room.member. Mark known issue. 2014-11-03 17:58:11 +00:00
Kegan Dougal
2438b8b66b Fix off-by-one bug when displaying display names / avatar urls when paginating. 2014-11-03 17:52:41 +00:00
Erik Johnston
68698e0ac8 Fix bugs in generating event signatures and hashing 2014-11-03 17:51:42 +00:00
Kegan Dougal
efb0f6e23b Backwards compat for old-style avatar_urls 2014-11-03 17:49:09 +00:00
Kegan Dougal
4b3f743885 Reference the room_member key on messages which adjusts for current vs old_room_state. This displays names for historical users correctly, but is off by one (referencing content not prev_content). 2014-11-03 17:44:14 +00:00
Erik Johnston
bab2846513 Merge branch 'event_signing' of github.com:matrix-org/synapse into federation_authorization 2014-11-03 16:35:48 +00:00
Mark Haines
af83bf6712 Script for checking event hashes 2014-11-03 16:35:24 +00:00
Mark Haines
fe6832fae8 handle server names with embeded ports 2014-11-03 16:08:22 +00:00
Mark Haines
2221a13a4d script for checking signatures on signed json 2014-11-03 15:58:00 +00:00
Kegan Dougal
f3dbcdc7b3 Variable renaming, general cleanup. Don't feed state events from /initialSync twice. 2014-11-03 15:17:32 +00:00
Erik Johnston
af7ae048f8 Add option to not bind to HTTPS port. This is useful if running behind an ssl load balancer 2014-11-03 15:06:40 +00:00
Kegan Dougal
1071d063ab Fix broken redact enable logic. 2014-11-03 15:05:35 +00:00
Kegan Dougal
7614d8f87a Fix hidden event keys being incorrectly shown in the even info dialog. 2014-11-03 15:02:16 +00:00
Kegan Dougal
f4e50079de Fix bug which prevented pagination from bumping the list down, causing infini-pagination. 2014-11-03 14:22:09 +00:00
Kegan Dougal
92e2ff4985 Fix bug which prevented room name invites appearing correctly. 2014-11-03 13:48:08 +00:00
Kegan Dougal
9b1ca64a75 Fix bug which prevented the number of users being visible on the recents view. 2014-11-03 13:26:50 +00:00
Erik Johnston
ad6eacb3e9 Rename PDU fields to match that of events. 2014-11-03 13:06:58 +00:00
Kegan Dougal
fd535183ee Remove events.rooms[room_id] entirely from event-handler-service.
Everything now uses modelService, but there are still one or two
minor teething problems to fix.
2014-11-03 12:18:22 +00:00
Kegan Dougal
6bc1dc4020 Make recents[controller/filter/html] use modelService.
This breaks functionality whilst both events.rooms and modelService
are in use.
2014-11-03 11:44:39 +00:00
Erik Johnston
d59aa6af25 For now, don't store txn -> pdu mappings. 2014-11-03 11:35:19 +00:00
Erik Johnston
f139c02e95 Formatting 2014-11-03 11:34:49 +00:00
Erik Johnston
7249785bcb Sign events we create. 2014-11-03 11:33:28 +00:00
Erik Johnston
0a8b026ccf Add 'origin' key to events 2014-11-03 11:32:42 +00:00
Erik Johnston
82a6b83524 Don't assume event has hashes key already 2014-11-03 11:32:12 +00:00
Erik Johnston
9024a19658 Remove dead code. 2014-11-03 11:31:47 +00:00
Kegan Dougal
53da1099d1 Make call stuff use modelService. 2014-11-03 10:33:38 +00:00
Kegan Dougal
395bb64b26 Keep matrixService stateless and make matrixFilter use modelService. 2014-11-03 10:23:14 +00:00
Erik Johnston
7a07263281 Merge branch 'develop' of github.com:matrix-org/synapse into federation_authorization 2014-11-03 10:17:37 +00:00
Erik Johnston
1c6825cc7a Use python logger, not the twisted logger 2014-11-03 10:16:28 +00:00
Kegan Dougal
5ab9929cbb Prevent EventStreamService from knowing too much about the EventHandlerService by changing the contract to just be a single initialSync response callback. Leave it up the handler to deal with splitting out information from /initialSync. 2014-11-03 09:44:20 +00:00
Erik Johnston
36d730229a Merge branch 'release-v0.4.2' of github.com:matrix-org/synapse into develop 2014-10-31 17:50:06 +00:00
Erik Johnston
b63691f6e2 Merge branch 'release-v0.4.2' of github.com:matrix-org/synapse 2014-10-31 17:48:05 +00:00
Erik Johnston
13fad06239 Bump version numbers and change log 2014-10-31 17:23:01 +00:00
Kegan Dougal
f21960ec9d Replace lots of .events.rooms[room_id] with .room 2014-10-31 17:13:27 +00:00
Erik Johnston
ecabff7eb4 Sign evnets 2014-10-31 17:08:52 +00:00
Erik Johnston
80b2710e6f Remove unused signature storage methods 2014-10-31 17:08:36 +00:00
Kegan Dougal
b0f0b7b75e room.html now displays messages from model-service. Add debugging fields. Hook up the room member *at the time* to the message so it can display the right historical member info. 2014-10-31 16:22:15 +00:00
Erik Johnston
fb3a01fa3a Remove unused sql file. 2014-10-31 16:04:27 +00:00
Erik Johnston
d30d79b5be Make prev_event signing work again. 2014-10-31 15:35:39 +00:00
Kegan Dougal
ea80b9208d Hook into more of event-handler-service and mimic its functions for now. 2014-10-31 15:16:43 +00:00
Kegan Dougal
394f77c3ff Parse /initialSync data and populate the new data structures. 2014-10-31 14:50:31 +00:00
Erik Johnston
2f39dc19a2 Remove more references to dead PDU tables 2014-10-31 14:27:14 +00:00
Kegan Dougal
2aa79f4fbe Added model-service.js to store model data. 2014-10-31 14:26:51 +00:00
Erik Johnston
bfa36a72b9 Remove PDU tables. 2014-10-31 14:00:32 +00:00
Kegan Dougal
71ef8f0636 SYWEB-102: Fix desktop notification msg when a user with no display name joins a room. 2014-10-31 11:56:36 +00:00
Kegan Dougal
20cf0b7aeb Factor out notification logic. 2014-10-31 11:54:04 +00:00
Erik Johnston
946d02536b Remove unused functions. 2014-10-31 11:45:08 +00:00
Kegan Dougal
ac2a177070 Add notification-service.js to handle binging/notifications. Shift logic to this service. 2014-10-31 11:20:07 +00:00
Erik Johnston
21fe249d62 Actually don't store any PDUs 2014-10-31 10:47:34 +00:00
Erik Johnston
d84f5b30b8 old_state_events should be a dict not list 2014-10-31 10:47:04 +00:00
Kegan Dougal
188de756be SYWEB-45: Display the user_id of a user when hovering over their avatar next to their messages. 2014-10-31 10:06:28 +00:00
Kegan Dougal
baf472f83f SYWEB-63: Fix desktop notification message when notifying for an image. 2014-10-31 10:02:56 +00:00
Erik Johnston
841df4da71 Don't store any PDUs 2014-10-31 09:59:59 +00:00
Erik Johnston
f2de2d644a Move the impl of backfill to use events. 2014-10-31 09:59:02 +00:00
Erik Johnston
d9a9e9eb30 Merge branch 'develop' of github.com:matrix-org/synapse into federation_authorization 2014-10-31 09:49:43 +00:00
Erik Johnston
4a1597f295 Fix bug in redaction auth.
This caused a 500 when sending a redaction due to a typo in a method
invocation.
2014-10-31 09:48:59 +00:00
Kegan Dougal
86d3180666 SYWEB-12: You'll be needing this. 2014-10-30 17:33:14 +00:00
Kegan Dougal
864de6a7a4 SYWEB-12: Minor layout tweaks. 2014-10-30 17:23:11 +00:00
Kegan Dougal
ea6bec96d3 SYWEB-12: UX tweaks. 2014-10-30 17:16:16 +00:00
Kegan Dougal
f618f99ece SYWEB-12: Add ability to add new state events. 2014-10-30 17:01:17 +00:00
Erik Johnston
12ce441e67 Convert event ids to be of the form :example.com 2014-10-30 17:00:11 +00:00
Kegan Dougal
0985bfb775 SYWEB-12: Allow edited state events to be submitted. 2014-10-30 16:31:47 +00:00
Kegan Dougal
9de9661baa SYWEB-12: More formatting and tweaking of state event JSON.
Use a proper elastic directive to make the <textarea> resize dynamically.
Use an 'asjson' directive to turn an ngModel of a JSON object into a
formatted JSON string so it can be displayed on the textarea. Also, deep
copy the state events being displayed, else it actually alters the underlying
data structures when playing around with the JSON in the textarea!
2014-10-30 16:21:27 +00:00
Kegan Dougal
6f3f631fd1 SYWEB-12: More formatting. 2014-10-30 13:24:40 +00:00
Erik Johnston
da511334d2 Make federation return the old current state, so that we can use it to do auth 2014-10-30 11:53:35 +00:00
Kegan Dougal
40342af459 SYWEB-12: Format room info dialog better. 2014-10-30 11:53:28 +00:00
Kegan Dougal
8e8bbb00f5 SYWEB-12: Store unknown state events so they are displayed in the Room Info dialog. 2014-10-30 11:22:47 +00:00
Erik Johnston
ef9c4476a0 Merge branch 'develop' of github.com:matrix-org/synapse into federation_authorization 2014-10-30 11:18:28 +00:00
Kegan Dougal
d5aa965522 SYWEB-12: Add a 'Room Info' button which displays all state content.
Content displayed in a modal dialog. Currently only read-only.
2014-10-30 11:15:44 +00:00
Mark Haines
7a756e5d9d Remove unused 'context' variables to appease pyflakes 2014-10-30 11:15:39 +00:00
Mark Haines
7c06399512 Merge branch 'develop' into request_logging
Conflicts:
	synapse/config/logger.py
2014-10-30 11:13:58 +00:00
Mark Haines
7d709542ca Fix pep8 warnings 2014-10-30 11:10:17 +00:00
Mark Haines
fa955cc2a4 Pep8 and a few doc strings 2014-10-30 10:13:46 +00:00
Erik Johnston
aa80900a8e Fix SQL so that accepts we may want to persist events twice. 2014-10-30 10:11:06 +00:00
Kegan Dougal
b4b492824e SYWEB-112: Use the right user ID when determining invites for display on the recents list. 2014-10-30 10:05:43 +00:00
Mark Haines
b29517bd01 Add a request-id to each log line 2014-10-30 01:21:33 +00:00
Kegan Dougal
0f192579ac SYWEB-48: Better regex for binging on usernames.
This uses /\blocalpart\b|\bdisplayname\b/i which is overall a lot
better than before. This specifically gets @localpart references
which the bug was originally for.
2014-10-29 17:44:57 +00:00
Paul "LeoNerd" Evans
beae9acfcc Use floating-point rather than integer division to handle timeouts so that non-zero but sub-second waits don't collapse to zero 2014-10-29 17:03:02 +00:00
Erik Johnston
53216a500d Add a run_on_reactor function 2014-10-29 17:02:22 +00:00
Erik Johnston
e7858b6d7e Start filling out and using new events tables 2014-10-29 16:59:24 +00:00
Kegan Dougal
0d278f5da8 SYWEB-127: Open event info modal dialog when the bubble is clicked.
This allows images to be clicked by clicking on the edge with the bubble.
This is important since Redactions are only visible on the event info
screen.
2014-10-29 16:35:33 +00:00
Paul "LeoNerd" Evans
b1ee6fd7ed Fix an off-by-one bug in presence event stream pagination; this might be responsible for any number of bug reports 2014-10-29 16:16:01 +00:00
Paul "LeoNerd" Evans
d6bcffa929 Construct a source-specific 'SourcePaginationConfig' to pass into get_pagination_rows; meaning each source doesn't have to care about its own name any more 2014-10-29 16:16:01 +00:00
Paul "LeoNerd" Evans
c5a25f610a Remove redundant (and incorrect) presence pagination fetching code 2014-10-29 16:16:01 +00:00
Matthew Hodgson
194e1e9151 oops - fix css on desktop 2014-10-29 17:02:16 +01:00
Kegan Dougal
c2f2e26ec5 SYWEB-98: Handle incoming m.room.redaction events.
UI for redactions is now complete.
2014-10-29 15:48:41 +00:00
Kegan Dougal
6d4617960d SYWEB-98: Add redactEvent matrix API call. 2014-10-29 15:31:50 +00:00
Kegan Dougal
70137409ed SYWEB-98: Add a 'Redact' button to the event info modal dialog.
I think this is better than overriding the right-click contextual menu.
Currently clicking this button does nothing.
2014-10-29 15:02:30 +00:00
Kegan Dougal
ed241ba032 Implement SYWEB-58: Clicking a notification now takes you to that room. 2014-10-29 11:29:26 +00:00
Kegan Dougal
2a44558fbd Fix SYWEB-128 : Auto-scroll broken if not exactly at bottom of list.
Added a small 10px buffer so if the list isn't quite at the bottom it
still actually scrolls.
2014-10-29 11:05:05 +00:00
Erik Johnston
a10c2ec88d Don't reference PDU when persisting event 2014-10-28 17:15:32 +00:00
Erik Johnston
2d1dfb3b34 Begin implementing all the PDU storage stuff in Events land 2014-10-28 16:42:35 +00:00
Erik Johnston
da1dda3e1d Add transaction level logging and timing information. Add a _simple_delete method 2014-10-28 11:18:04 +00:00
Erik Johnston
967ce43b59 Clean up LoggingTransaction 2014-10-28 10:53:11 +00:00
Erik Johnston
8e358ef35a Add timer to LoggingTransaction 2014-10-28 10:34:05 +00:00
Matthew Hodgson
51b81b472d fix mobile CSS layout 2014-10-28 10:03:59 +01:00
Kegan Dougal
4f6acf114c Fix SYWEB-110 : Prevent room ID leaking by looking for an m.room.name 2014-10-27 17:05:13 +00:00
Mark Haines
7c7d9d6326 Merge branch 'develop' into event_signing 2014-10-27 16:56:08 +00:00
Mark Haines
4841b6d4ba Remove duplicate join_event from create_room 2014-10-27 16:55:51 +00:00
Kegan Dougal
fc121f9785 Fix SYWEB-114 : Error message when trying to invite a user already in the room. 2014-10-27 16:48:43 +00:00
Kegan Dougal
332b2869ef Don't clobber existing css 2014-10-27 16:42:19 +00:00
Erik Johnston
c372929ab6 Remove duplicate import 2014-10-27 16:31:39 +00:00
Kegan Dougal
f4e64ac253 SYWEB-121: Have some bootstrap files. 2014-10-27 16:31:10 +00:00
Kegan Dougal
da87990bd6 Implement SYWEB-121 : Display JSON when clicking messages.
JSON is displayed as a modal dialog via AngularJS' bootstrap module,
"ui.bootstrap".
2014-10-27 16:30:07 +00:00
giomfo
cf1feee21d HandleRoomMember: handle correctly prev_content 2014-10-27 14:17:16 +01:00
Erik Johnston
ad9226eeec Merge branch 'event_signing' of github.com:matrix-org/synapse into federation_authorization
Conflicts:
	synapse/storage/__init__.py
2014-10-27 11:58:32 +00:00
Kegan Dougal
6603e39e6a Fix SYWEB-109 : No error if HS rejects the username in registration.
Display all error messages from the server when registering, rather
than just the types of errors the client recognises.
2014-10-27 11:58:23 +00:00
Mark Haines
5e2236f9ff fix pyflakes warnings 2014-10-27 11:19:15 +00:00
Mark Haines
acb2d171e8 Merge branch 'develop' into event_signing 2014-10-27 11:14:11 +00:00
Kegan Dougal
f3bb3943c9 Remove test_pyflakes. 2014-10-27 11:13:04 +00:00
Mark Haines
7bd604e3be Test pyflakes jenikns integration 2014-10-27 10:56:38 +00:00
Mark Haines
d56e389a95 Fix pyflakes warnings 2014-10-27 10:33:17 +00:00
Erik Johnston
bb4a20174c Merge branch 'develop' of github.com:matrix-org/synapse into federation_authorization
Conflicts:
	synapse/federation/transport.py
	synapse/handlers/message.py
2014-10-27 10:20:44 +00:00
Mark Haines
15be181642 Add log message if we can't enable ECC. Require pyopenssl>=0.14 since 0.13 doesn't seem to have ECC 2014-10-24 19:27:12 +01:00
Mark Haines
db2e350e29 Wrap preparing the database in a transaction. Otherwise it will take many seconds to complete because sqlite will create a transaction per statement 2014-10-24 19:04:26 +01:00
Matthew Hodgson
1342bcedaf switch from the deprecated msg.content.prev to msg.prev_content.membership, and fix the bug where kicks of unjoined users aren't displayed sensibly in the history 2014-10-24 16:14:47 +01:00
Mark Haines
be6d41ffe5 Merge branch 'master' into develop 2014-10-24 10:57:38 +01:00
Kegan Dougal
53f69bf089 Added pylint config file: ignore missing-docstring messages. 2014-10-24 10:22:09 +01:00
David Baker
51edfeb3d0 Coturn's timestamps are in seconds, not milliseconds 2014-10-21 18:57:13 +01:00
manuroe
9e57ed2b1f Added a param (--no-rate-limit) to demo/start.sh to disable the HS rate limit 2014-10-20 18:35:39 +01:00
Erik Johnston
4ae0844ee3 Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-10-20 17:53:18 +01:00
Mark Haines
06a5a40e90 use a tagged version of syutil rather than master 2014-10-20 15:11:01 +01:00
Mark Haines
f0382357ca Use https link to download syutil as not everyone has ssh access to github. 2014-10-20 14:43:37 +01:00
Mark Haines
4be99c2989 Add get_json method to 3pid http client. Better logging for errors in 3pid requests 2014-10-20 14:10:08 +01:00
Mark Haines
9c0826592c Fix auto generating signing_keys 2014-10-18 16:56:44 +01:00
Matthew Hodgson
8f0997d17d improve changelog slightly 2014-10-18 11:46:11 +01:00
Matthew Hodgson
58b1a891ce fix timestamps some more
Merge branch 'develop' of git+ssh://github.com/matrix-org/synapse
2014-10-17 23:54:21 +01:00
Matthew Hodgson
e9abbe89f3 more timestamp fixes 2014-10-17 23:53:24 +01:00
Erik Johnston
b3e6cd59a1 Bump the other version 2014-10-18 00:29:55 +02:00
Erik Johnston
f22d023c4b Bump version 2014-10-18 00:29:15 +02:00
Erik Johnston
4c8111ef98 Bunp to change log. 2014-10-18 00:28:36 +02:00
Erik Johnston
f05dce54a7 Merge pull request #9 from matrix-org/develop
Fix issue with timestamps in webclient
2014-10-18 00:25:57 +02:00
Matthew Hodgson
514e0fd4b6 fix webclient to know about right timestamps 2014-10-17 23:11:55 +01:00
Erik Johnston
cb939ed450 I can't type apparently 2014-10-17 21:29:28 +01:00
Erik Johnston
7ea38a0c9d Update changelong and upgrade 2014-10-17 21:28:32 +01:00
Erik Johnston
f1ddbfaae4 Merge branch 'release-v0.4.0' of github.com:matrix-org/synapse into develop 2014-10-17 21:04:07 +01:00
Erik Johnston
449739e6a3 Merge branch 'release-v0.4.0' of github.com:matrix-org/synapse 2014-10-17 21:02:16 +01:00
Erik Johnston
ac9345b47a Check that we have auth headers and fail nicely 2014-10-17 21:00:58 +01:00
Erik Johnston
cd198dfea8 More log lines. 2014-10-17 20:58:47 +01:00
Mark Haines
3187b5ba2d add log line for checking verifying signatures 2014-10-17 20:56:21 +01:00
Mark Haines
eea3a29699 Add script to hash exisitng history 2014-10-17 20:36:04 +01:00
Erik Johnston
5356044b77 Bump syutil dependency 2014-10-17 20:35:28 +01:00
Erik Johnston
71e6a94af7 Bump version and changelog 2014-10-17 20:26:26 +01:00
Erik Johnston
5662be894e Bump database version number. 2014-10-17 20:26:18 +01:00
Erik Johnston
a065becea5 Merge branch 'docs-restructuring' of github.com:matrix-org/synapse into develop 2014-10-17 20:18:02 +01:00
Erik Johnston
bf8cdda2f5 It doesn't want a dict 2014-10-17 20:10:34 +01:00
Mark Haines
8afbece683 Remove signatures from pdu when computing hashes to use for prev pdus, make sure is_state is a boolean. 2014-10-17 19:41:32 +01:00
Erik Johnston
b3b1961496 Fix bug where people could join private rooms 2014-10-17 19:37:41 +01:00
Erik Johnston
5ffe5ab43f Use state groups to get current state. Make join dance actually work. 2014-10-17 18:56:42 +01:00
Mark Haines
dc3c2823ac Merge branch 'develop' into event_signing
Conflicts:
	synapse/federation/replication.py
2014-10-17 17:33:58 +01:00
Mark Haines
82c5820767 keep 'origin_server_ts' as 'ts' in the database to avoid needlessly updating schema 2014-10-17 17:31:48 +01:00
Mark Haines
f5cf7ac25b SPEC-7: Rename 'ts' to 'origin_server_ts' 2014-10-17 17:12:25 +01:00
Mark Haines
456017e0ae SPEC-7: Don't stamp event contents with 'hsob_ts' 2014-10-17 16:55:55 +01:00
Mark Haines
c5cec1cc77 Rename 'meta' to 'unsigned' 2014-10-17 16:50:04 +01:00
Mark Haines
4d1a7624f4 move 'age' into 'meta' subdict so that it is clearer that it is not part of the signed data 2014-10-17 15:27:11 +01:00
Erik Johnston
f71627567b Finish implementing the new join dance. 2014-10-17 15:04:17 +01:00
Mark Haines
c8f996e29f Hash the same content covered by the signature when referencing previous PDUs rather than reusing the PDU content hashes 2014-10-17 11:40:35 +01:00
Matthew Hodgson
be2a9a8d1a move gendoc into matrix-doc project 2014-10-17 02:09:07 +01:00
Mark Haines
bb04447c44 Include hashes of previous pdus when referencing them 2014-10-16 23:25:12 +01:00
Erik Johnston
1116f5330e Start implementing the invite/join dance. Continue moving auth to use event.state_events 2014-10-16 16:56:51 +01:00
Mark Haines
66104da10c Sign outgoing PDUs. 2014-10-16 00:09:48 +01:00
Mark Haines
1c445f88f6 persist hashes and origin signatures for PDUs 2014-10-15 17:09:04 +01:00
Erik Johnston
e7bc1291a0 Begin making auth use event.old_state_events 2014-10-15 16:06:59 +01:00
Kegan Dougal
79bd6e77b8 Remove warning since the end result is still $sanitize'd 2014-10-15 14:45:38 +01:00
Kegan Dougal
da19fd0d1a Add unsanitizedLinky filter to fix links in formatted messages.
This filter is identical to ngSanitize's linky but instead of
sanitizing text which isn't linkified in the addText function,
it doesn't.
2014-10-15 14:42:14 +01:00
Kegan Dougal
07890b43ca Remove org.matrix.custom.text.html event type and replace it with 'format' and 'formatted_body' keys on m.text messages 2014-10-15 13:57:19 +01:00
Mark Haines
27d0c1ecc2 Merge branch 'develop' into event_signing 2014-10-15 13:57:12 +01:00
Erik Johnston
80472ac198 Add missing package storate.state 2014-10-15 10:04:55 +01:00
Kegan Dougal
f4667f86af Add support for org.matrix.custom.text.html
This format will remain undocumented as it is not yet suitable for
introduction into the specification.
2014-10-15 09:32:02 +01:00
Erik Johnston
5fefc12d1e Begin implementing state groups. 2014-10-14 16:59:51 +01:00
Paul "LeoNerd" Evans
13b560971e Make sure to return an empty JSON object ({}) from presence PUT/POST requests rather than an empty string ("") because most deserialisers won't like the latter 2014-10-14 16:48:15 +01:00
Mark Haines
9aed791fc3 SYN-103: Ignore the 'origin' key in received EDUs. Instead take the origin from the transaction itself 2014-10-14 16:44:27 +01:00
Mark Haines
3dac27a8a9 Storage for pdu signatures 2014-10-14 14:58:31 +01:00
Mark Haines
f74e850b5c remove debugging logging for signing requests 2014-10-14 11:46:13 +01:00
Erik Johnston
4fe5dfa74c Note that this breaks federation 2014-10-14 10:30:50 +01:00
Mark Haines
636a0dbde7 Merge pull request #8 from matrix-org/server2server_signing
Server2server signing
2014-10-14 10:06:04 +01:00
Matthew Hodgson
c18a6433d4 typoe 2014-10-13 23:24:14 +01:00
Mark Haines
34034af1c9 Better response message when signature is missing or unsupported 2014-10-13 16:47:23 +01:00
Mark Haines
07639c79d9 Respond with more helpful error messages for unsigned requests 2014-10-13 16:39:15 +01:00
Mark Haines
25d80f35f1 Raise a SynapseError if the authorisation header is missing or malformed 2014-10-13 15:53:18 +01:00
Mark Haines
75e517a2da Remove debug logging, raise a proper SynapseError if the auth header is missing 2014-10-13 15:41:20 +01:00
Mark Haines
6684855767 Verify signatures for server2server requests 2014-10-13 14:37:46 +01:00
Mark Haines
10ef8e6e4b SYN-75 sign at the request level rather than the transaction level 2014-10-13 11:49:55 +01:00
Mark Haines
cecda27d73 Merge branch 'develop' into server2server_signing 2014-10-13 11:06:36 +01:00
Mark Haines
984e207b59 Merge branch develop into server2server_signing
Conflicts:
	synapse/app/homeserver.py
2014-10-13 10:58:50 +01:00
Mark Haines
693d0b8f45 Replace on_send_callback with something a bit clearer so that we can sign messages 2014-10-13 10:49:04 +01:00
Matthew Hodgson
66df7f1aaf remove wishlist in favour of jira 2014-10-12 00:00:37 +01:00
Matthew Hodgson
259b5e8451 move swagger JSON from synapse project to matrix-doc project 2014-10-09 20:43:07 +02:00
Matthew Hodgson
e1170d4edb move matrix-generic content to new matrix-doc git project 2014-10-09 20:38:00 +02:00
Kegan Dougal
81b956c70d Add spec-additions.rst with info on recaptcha and common event fields. 2014-10-09 18:08:19 +01:00
Kegan Dougal
868eb478d8 Fixed test. 2014-10-09 15:55:07 +01:00
Kegan Dougal
3db09c4d15 Still broken. 2014-10-09 15:53:40 +01:00
Kegan Dougal
83c53113af Break a test. 2014-10-09 15:51:05 +01:00
Kegan Dougal
d224358e21 Restructure specification sections. 2014-10-09 11:08:06 +01:00
Kegan Dougal
72aef114ab Fix unit test. 2014-10-08 15:18:19 +01:00
Kegan Dougal
6045bd89fb Break unit test. 2014-10-08 15:16:03 +01:00
Erik Johnston
5b096cc3db Merge pull request #7 from matrix-org/paul/doc
Clarify that room alias domain names will be server-scoped; nonlocal edi...
2014-10-07 16:35:32 +01:00
Paul "LeoNerd" Evans
917af4705b Clarify that room alias domain names will be server-scoped; nonlocal edits are unliekly to work but nonlocal lookups will 2014-10-07 16:23:12 +01:00
Erik Johnston
9ac53ef8cf SPEC-3: First hack at defining some of the various event related concepts 2014-10-07 11:38:02 +01:00
Erik Johnston
2fc00508fb Add quick and dirty doc about state resolution 2014-10-06 17:34:44 +01:00
Kegan Dougal
c72074b48e Clarify how-to some more. 2014-10-06 14:57:26 +01:00
Kegan Dougal
3ef2c946d5 Update JSFiddles/how-to to support the new registration format. 2014-10-06 14:52:46 +01:00
Kegan Dougal
aaf1d499bf Add more section headings. 2014-10-06 13:18:52 +01:00
Kegan Dougal
94982392be Clarify room permission / power level information. 2014-10-06 12:41:48 +01:00
Kegan Dougal
51276c60bf Add information about the initialSync API.
Outline and describe the keys from the initial sync API. Hide room-scoped
initial sync API for now as it is not implemented and needs more thought before
it can be specced.
2014-10-06 10:32:04 +01:00
Kegan Dougal
78a3f43d9d swagger: Added DELETE method for directory server. 2014-10-06 09:23:19 +01:00
Kegan Dougal
02a44664b9 More spec work. 2014-10-03 17:38:30 +01:00
Erik Johnston
1fa0454288 Merge pull request #6 from matrix-org/paul/doc
Clarify how m.room.alias event works
2014-10-03 14:50:08 +01:00
Paul "LeoNerd" Evans
ca0e8dedfb Clarify how m.room.alias event works 2014-10-03 14:45:42 +01:00
Kegan Dougal
ba11afafb9 Flesh out room alias section. 2014-10-03 14:39:58 +01:00
Kegan Dougal
7e1437c6b1 Add more information to TODOs. Explain m.room.join_rules. 2014-10-03 10:34:29 +01:00
Paul "LeoNerd" Evans
1aa5cc9178 Federation protocol URLs should have an H2 heading, not H1 2014-10-02 18:11:04 +01:00
Paul "LeoNerd" Evans
bc1d685a8c Remove TODO note about VoIP events as they now have their own entire section 2014-10-02 18:00:31 +01:00
Erik Johnston
f6b9853ad0 Merge pull request #5 from matrix-org/paul/doc
Document the Profile system
2014-10-02 17:37:21 +01:00
Paul "LeoNerd" Evans
de38f54f22 Document the Profile system 2014-10-02 17:18:32 +01:00
Paul Evans
96213f69a2 Merge pull request #4 from matrix-org/erikj-spec-changes
Erikj spec changes
2014-10-02 14:46:40 +01:00
Erik Johnston
036333412d Add todo notes 2014-10-02 14:38:53 +01:00
Erik Johnston
82e278029c Remove incorrect reasons for empty PDU lists. 2014-10-02 14:38:22 +01:00
Mark Haines
b9cdc443d7 Fix pyflakes errors 2014-10-02 14:37:30 +01:00
Erik Johnston
1561ef56ed Remove note about assymetry of having left a room.
Currently, if you leave a room you still appear in the members list.
This is basically a bug with the current implementation/spec, rather
than something that should happen.
2014-10-02 14:35:39 +01:00
Erik Johnston
f368ad946e m.room.ops_levels includes redact_level 2014-10-02 14:33:26 +01:00
Erik Johnston
918e71adb7 Don't use spaces in example room alias 2014-10-02 14:31:21 +01:00
Erik Johnston
cf3188352b Fix default value and key names. 2014-10-02 14:30:25 +01:00
Erik Johnston
6860a18c12 Be less alarmist about not using an ID server. 2014-10-02 14:27:35 +01:00
Erik Johnston
ff553cc9dd Alias lookups return a server list. 2014-10-02 14:26:58 +01:00
Mark Haines
574377636e Add a keyword argument to get_json to avoid retrying on DNS failures. Rather than passing MatrixHttpClient.RETRY_DNS_LOOKUP_FAILURES as a fake query string parameter 2014-10-02 14:26:13 +01:00
Erik Johnston
b2d41b1cd9 All room state is currently shared. 2014-10-02 14:25:47 +01:00
David Baker
9435830351 Merge branch 'master' into develop 2014-10-02 14:11:17 +01:00
David Baker
d694619a95 Fix ncorrect ports in documentation and add notes on how generate-config also generates certs bound to whatever hostname you give with --generate-config.
SYN-87 #resolved
2014-10-02 14:09:27 +01:00
Mark Haines
4f11518934 Split PlainHttpClient into separate clients for talking to Identity servers and talking to Capatcha servers 2014-10-02 14:03:26 +01:00
Mark Haines
2d55d43d40 Merge branch 'master' into develop 2014-10-02 11:03:13 +01:00
Mark Haines
45f7677bdc Trivial formatting fixes for README. 2014-10-02 11:00:21 +01:00
Mark Haines
099083ea6b Merge remote-tracking branch 'origin/master' into develop 2014-10-02 10:46:41 +01:00
David Baker
7a322b6326 Update README setup instructions to be correct. Make synapse spit out explanatory note when generating config to tell people to look at it and customise it. 2014-10-02 10:43:22 +01:00
David Baker
d1adb19b8a Re-apply a0b1b34c71 to master (fixing synctl) 2014-10-02 10:38:11 +01:00
David Baker
a0b1b34c71 Make instructions synctl gives for generateing a config file actuall generate a config file. Also, make synctil run synapse correctly by invoking a module such that the path is correct to pull in other bits from the working directory rather than requiring them to be on the PYTHONPATH (which would lead to people being very confused when they edit source in the working directory and their changes do not take effect). 2014-10-02 09:55:26 +01:00
Paul "LeoNerd" Evans
bf8b9b90cd Added a TODO-doc marker about the presence timing system 2014-10-01 19:37:18 +01:00
Paul "LeoNerd" Evans
c5757a0266 Define the client and server APIs for Presence 2014-10-01 19:35:13 +01:00
Paul "LeoNerd" Evans
ee447abcad Continue moving content out of docs/model/presence into the main spec; delete model docs that are duplicated 2014-10-01 18:34:08 +01:00
Erik Johnston
a940a87ddc SPEC-25: Add details on how to prune redacted events.
SPEC-25 #comment I've added the details of what the server should do on
receipt of a redaction event. In reality it can do whatever it wants,
and its probably a reasonable implementation to flag it up to a server
admin for verification before actually redacting an event.
2014-10-01 18:18:44 +01:00
Paul "LeoNerd" Evans
5813e81dc6 Move documented but-unimplemented 'presence idle times' into a new document to contain such features 2014-10-01 17:59:55 +01:00
Erik Johnston
a6d3be4dbf s/m.room.redacted/m.room.redaction/ 2014-10-01 17:55:31 +01:00
Paul "LeoNerd" Evans
166bec0c08 Nuke the entire 'Typing Notifications' spec section given as they don't exist yet in the implementation 2014-10-01 17:33:18 +01:00
Mark Haines
c8d67beb9c remove "red", "blue" and "green" server_name mappings 2014-10-01 15:52:07 +01:00
Paul "LeoNerd" Evans
392dc8af59 Annotate all the 'TODO' marks as relating to either the specification itself or the documentation thereof 2014-09-30 18:11:24 +01:00
Mark Haines
9605593d11 Merge branch 'develop' into server2server_signing
Conflicts:
	synapse/storage/__init__.py
	tests/rest/test_presence.py
2014-09-30 17:55:06 +01:00
Mark Haines
b95a178584 SYN-75 Verify signatures on server to server transactions 2014-09-30 15:15:10 +01:00
Erik Johnston
fbf6320614 pyflakes cleanup 2014-09-30 12:38:38 +01:00
Erik Johnston
e06adc6d7e SYN-2: Allow server admins to delete room aliases 2014-09-30 11:31:42 +01:00
Paul "LeoNerd" Evans
1f76377a7c Re-wrap content after latest additions 2014-09-29 18:40:15 +01:00
Paul "LeoNerd" Evans
dca75a08ba Merge remote-tracking branch 'origin/develop' into develop 2014-09-29 18:37:28 +01:00
Paul "LeoNerd" Evans
2d61dbc774 Extended docs about the registration/login flows 2014-09-29 18:36:10 +01:00
Paul "LeoNerd" Evans
3ee9a67aa4 Reörder the specification sections, to move 'Registration and Login' first, where it logically belongs 2014-09-29 18:36:10 +01:00
Paul "LeoNerd" Evans
ae953b0884 Huge whitespace hackery - reflow all (content) paragraphs at tw=80 2014-09-29 18:36:10 +01:00
Paul "LeoNerd" Evans
d5bf210998 No longer need the Freenode verification key file 2014-09-29 18:36:10 +01:00
Erik Johnston
389285585d Add a 'Redactions' section. 2014-09-29 17:19:45 +01:00
Erik Johnston
3656eb4740 Add m.room.redacted in events list 2014-09-29 16:39:08 +01:00
Erik Johnston
f1bdf40dda Merge branch 'whois' of github.com:matrix-org/synapse into develop 2014-09-29 15:59:05 +01:00
Erik Johnston
d96cb61f26 Unbreak tests after changing storage API 2014-09-29 15:35:57 +01:00
Erik Johnston
7151615260 Update docstring 2014-09-29 15:35:54 +01:00
Erik Johnston
1550ab9e2f SYN-48: Delete dead code 2014-09-29 15:04:47 +01:00
Erik Johnston
1132663cc7 SYN-48: Fix typo. Get the whois for requested user rather tahan the requester 2014-09-29 15:04:04 +01:00
Erik Johnston
3ccb17ce59 SYN-48: Implement WHOIS rest servlet 2014-09-29 14:59:52 +01:00
Paul "LeoNerd" Evans
472ef19100 No longer need the Freenode verification key file 2014-09-29 14:22:21 +01:00
Erik Johnston
c65306f877 Add auth check to test if a user is an admin or not. 2014-09-29 13:35:38 +01:00
Erik Johnston
f7d80930f2 SYN-48: Track User-Agents as well as IPs for client devices. 2014-09-29 13:35:15 +01:00
Erik Johnston
0fdf308874 Track the IP users connect with. Add an admin column to users table. 2014-09-26 16:36:24 +01:00
Erik Johnston
7a8307fe7c Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-09-25 18:21:42 +01:00
Erik Johnston
697f6714a4 Merge branch 'release-v0.3.4' of github.com:matrix-org/synapse 2014-09-25 18:21:00 +01:00
David Baker
ec5fb77a66 Just use a yaml list for turn servers 2014-09-25 19:18:32 +02:00
Erik Johnston
f1c9ab4e4f More change log lines 2014-09-25 18:10:02 +01:00
Erik Johnston
3b0fb6aae8 Bump version and changelog 2014-09-25 18:05:06 +01:00
David Baker
6e72ee62ae Add realm to coturn options (it needs it). 2014-09-25 17:21:52 +01:00
Erik Johnston
37bfe44046 Merge branch 'deletions' of github.com:matrix-org/synapse into develop 2014-09-25 17:02:53 +01:00
David Baker
48ea055781 fix rst warnings 2014-09-25 17:01:27 +01:00
Erik Johnston
dcadfbbd4a Don't strip out null's in serialized events, as that is not need anymore and it's not in the spec (yet) 2014-09-25 17:00:17 +01:00
David Baker
9bcedf224e add howto for setting up your very own TURN server 2014-09-25 16:58:21 +01:00
Erik Johnston
69ddec6589 Don't strip of False values from events when serializing 2014-09-25 16:49:02 +01:00
Erik Johnston
72e80dbe0e Rename redaction test case to something helpful 2014-09-25 15:52:23 +01:00
Erik Johnston
c818aa13eb Add LIMIT to scalar subquery 2014-09-25 15:51:21 +01:00
Erik Johnston
ba87eb6753 Fix bug where we tried to insert state events with null state key 2014-09-25 14:45:27 +01:00
Emmanuel ROHEE
d170fbdb9f BF: Do a pagination when opening a room from an invitation 2014-09-25 14:46:11 +02:00
David Baker
c58eb0d5a3 Merge branch 'turn' into develop 2014-09-25 13:09:56 +01:00
Erik Johnston
59f2bef187 Fix test where we changed arguments used to call the notifier 2014-09-25 13:04:33 +01:00
Erik Johnston
1ca51c8586 SYN-46: An invite received from fedearation didn't wake up the event stream for the invited user. 2014-09-25 13:01:05 +01:00
David Baker
c0936b103c Add stun server fallback and I-told-you-so message if we get no TURN server and the connection fails. 2014-09-25 11:14:29 +01:00
Emmanuel ROHEE
9d3246ed12 Fixed SYWEB-36: use getUserDisplayName for disambiguating display name in member list and message sender name. This method is robust when disambiguation is no more required 2014-09-25 11:49:43 +02:00
Emmanuel ROHEE
ef99a5d972 getUserDisplayName: Disambiguate users who have the same displayname in the room.
Displayname are then disambiguate where it is necessary
2014-09-25 11:45:01 +02:00
David Baker
a31bf77776 Make turn server endpoint return an empty object if no turn servers to
match the normal response. Don't break if the turn_uris option isn't
present.
2014-09-25 11:24:49 +02:00
Erik Johnston
24e4c48468 More tests. 2014-09-25 10:19:16 +01:00
Erik Johnston
2721f5ccc9 Add test for redactions 2014-09-25 10:02:20 +01:00
David Baker
6806caffc7 Refresh turn server before the ttl runs out. Support firefox. 2014-09-24 17:57:34 +01:00
Mark Haines
52ca867670 Sign federation transactions 2014-09-24 17:25:41 +01:00
Erik Johnston
72eb360f2d Don't set the room name to be the room alias on room creation if the client didn't supply a name 2014-09-24 16:59:57 +01:00
Emmanuel ROHEE
2b4736afcd Fixed getUserDisplayname when the user has a null displayname 2014-09-24 17:42:40 +02:00
David Baker
7dc7c53029 The REST API spec only alows for returning a single server so name the
endpoint appropriately.
2014-09-24 17:28:47 +02:00
Erik Johnston
327dcc98e3 SYN-70: And fix another bug where I can't type 2014-09-24 16:19:29 +01:00
Erik Johnston
87deaf1658 SYN-70: Fix typo 2014-09-24 16:15:58 +01:00
David Baker
7679ee7321 Hopefully implement turn in the web client (probably wrong for Firefox because Firefox is a special snowflake) 2014-09-24 16:08:31 +01:00
David Baker
4553651138 Oops 2014-09-24 17:04:33 +02:00
David Baker
5383ba5587 rename endpoint to better reflect what it is and allow specifying multiple uris 2014-09-24 16:01:36 +01:00
Emmanuel ROHEE
432e8ef2bc Fixed SYWEB-74: Emote desktop notifications sometimes lack a name: "undefined waves" 2014-09-24 16:52:48 +02:00
Erik Johnston
70899d3ab2 Rename deletions to redactions 2014-09-24 15:27:59 +01:00
David Baker
b42b0d3fe5 Use standard base64 encoding with padding to get the same result as
coturn.
2014-09-24 15:29:24 +02:00
Erik Johnston
7d9a84a445 Make deleting deletes not undelete 2014-09-24 14:18:08 +01:00
Erik Johnston
1e6c5b205c Fix bug where we didn't correctly pull out the event_id of the deletion 2014-09-24 13:29:20 +01:00
Emmanuel ROHEE
c7620cca6f SYWEB-27: Public rooms with 2 users must not considered as 1:1 chat room and so, they must no be renamed 2014-09-24 13:17:47 +02:00
Emmanuel ROHEE
b02bb18a70 Fixed SYWEB-28: show displayname changes in recents 2014-09-24 12:48:24 +02:00
Erik Johnston
4e79b09dd9 Fill out the prune_event method. 2014-09-24 11:37:14 +01:00
Emmanuel ROHEE
6f5970a2e1 Added hasOwnProperty tests when required to be robust to random properties added to he Object prototype 2014-09-24 12:22:40 +02:00
Erik Johnston
3d2cca6762 Fix test. 2014-09-24 11:17:43 +01:00
Erik Johnston
4354590a69 Add v4 deltas to current sql. 2014-09-24 11:06:41 +01:00
Emmanuel ROHEE
ef5b39c410 State data now provides up-to-date users displaynames. So use it first.
Continue to use presence data as fallback solution which is required when users do not join the room yet.
Created eventHandlerService.getUserDisplayName() as a single point to compute display name.
2014-09-24 11:04:27 +02:00
Matthew Hodgson
7b8e24a588 close buttons on recents (SYWEB-68) 2014-09-24 01:12:59 +01:00
Matthew Hodgson
53841642a8 close buttons on recents (SYWEB-68) 2014-09-24 01:12:45 +01:00
Matthew Hodgson
b08112f936 on safari at least keypress's event.which returns ASCII rather than keycodes, so 38 & 40 was swallowing ( and & rather than up-arrow and down-arrow(!) 2014-09-23 23:35:17 +01:00
Matthew Hodgson
53ae5bce13 comment-convo with kegan 2014-09-23 23:25:56 +01:00
Matthew Hodgson
e8e80fe6b5 fix yet more room id leak disasters 2014-09-23 20:27:09 +01:00
Matthew Hodgson
0e848d73f9 oops, stupid bug on room/$room/state 2014-09-23 20:01:32 +01:00
Matthew Hodgson
cbea225d97 manu: what's going on here? 2014-09-23 20:01:32 +01:00
Paul "LeoNerd" Evans
a7d53227de Bugfix for older Pythons that lack hmac.compare_digest() 2014-09-23 19:07:16 +01:00
Matthew Hodgson
437969eac9 use all new /rooms/<room id>/state to actually gather the state for rooms whenever join them. a bit ugly, as we don't currently have a nice place to gather housekeeping after joining a room, so horrible code duplication... 2014-09-23 18:50:39 +01:00
Mark Haines
bf4b224fcf Fix a few pyflakes errors in the server_key_resource 2014-09-23 18:43:34 +01:00
Mark Haines
e3117a2a23 Add a _matrix/key/v1 resource with the verification keys of the local server 2014-09-23 18:43:34 +01:00
Mark Haines
c6a8e7d9b9 Read signing keys using methods from syutil. convert keys that are in the wrong format 2014-09-23 18:43:34 +01:00
David Baker
c96ab4fcbb The config is not hierarchical 2014-09-23 19:17:24 +02:00
Erik Johnston
efea61dc50 Rename 'pruned' to 'pruned_because' 2014-09-23 17:40:58 +01:00
Erik Johnston
bc250a6afa SYN-12: Implement auth for deletion by adding a 'delete_level' on the ops levels event
SYN-12 # comment Auth has been added.
2014-09-23 17:36:24 +01:00
Matthew Hodgson
284fac379c patch over another scenario whe we leak room IDs. i have *zero* idea why or where the webclient is overriding message.membership to be "join" though, when it comes down the events pipe as "invite" (which was causing this failure mode) 2014-09-23 17:31:13 +01:00
Matthew Hodgson
5aa13b9084 fix a case of rampaging SYWEB-78 2014-09-23 17:31:13 +01:00
David Baker
14ed6799d7 Add support for TURN servers as per the TURN REST API (http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00) 2014-09-23 17:16:13 +01:00
Kegan Dougal
a7420ff2b5 Fix SYWEB-72 : Improve performance when typing.
Swapped ng-keydown to a directive, which does the same thing (check if up/down
arrow then call history.goUp/goDown). This has *dramatically* improved
performance when typing in rooms which have lots (>100) of messages loaded.
2014-09-23 16:56:54 +01:00
Emmanuel ROHEE
e4e8ad6780 SYWEB-28: Fixed weird members list ordering: sort members on their last activity absolute time 2014-09-23 17:33:16 +02:00
Paul "LeoNerd" Evans
c0673c50e6 Merge branch 'jira/SYN-60' into develop 2014-09-23 16:15:54 +01:00
Matthew Hodgson
7d94913efb remove old commented-out code 2014-09-23 16:12:25 +01:00
Matthew Hodgson
c9f73bd325 fix one cause of SYWEB-53 2014-09-23 16:12:25 +01:00
Paul "LeoNerd" Evans
c03176af59 Send an HMAC(SHA1) protecting the User ID for the ReCAPTCHA bypass, rather than simply the secret itself, so it's useless if that HMAC leaks 2014-09-23 15:58:44 +01:00
Kegan Dougal
2771efb51c Update API docs to include notes on /rooms/$roomid/state 2014-09-23 15:39:04 +01:00
Erik Johnston
932b376b4e Add prune_event method 2014-09-23 15:37:32 +01:00
Kegan Dougal
0c4ae63ad5 Implemented /rooms/$roomid/state API. 2014-09-23 15:35:58 +01:00
Erik Johnston
b99f6eb904 Make sure we don't persist the 'pruned' key 2014-09-23 15:29:27 +01:00
Erik Johnston
78af6bbb98 Add m.room.deletion. If an event is deleted it will be returned to clients 'pruned', i.e. all client specified keys will be removed. 2014-09-23 15:28:32 +01:00
Paul "LeoNerd" Evans
537c7e1137 Config values are almost never 'None', but they might be empty string. Detect their presence by truth 2014-09-23 15:18:59 +01:00
Paul "LeoNerd" Evans
5f16439752 Make sure the config actually /has/ a captcha_bypass_secret set before trying to compare it 2014-09-23 15:16:47 +01:00
Paul "LeoNerd" Evans
3a8a94448a Allow a (hidden undocumented) key to m.login.recaptcha to specify a shared secret to allow bots to bypass the ReCAPTCHA test (SYN-60) 2014-09-23 14:29:08 +01:00
Emmanuel ROHEE
e9c88ae4f4 Partial fix of SYWEB-28: If members do not have last_active_ago, compare their presence state to order them 2014-09-23 15:19:03 +02:00
Matthew Hodgson
4847045259 send messages to users from the home page (SYWEB-19) 2014-09-23 13:36:58 +01:00
Matthew Hodgson
997a016122 fix NPE 2014-09-23 13:01:12 +01:00
Kegan Dougal
512f2cc9c4 Fix SYWEB-8 : Buggy tab-complete.
The first red blink was caused by an uninitialised search index. There is no
caching of entries, since this then wouldn't update if someone joined/left
during the tab. Instead, set to search index to MAX_VALUE then fix it to a
valid index AFTER the search is complete. Also ditched trailing space on ": ".
2014-09-23 12:22:14 +01:00
Matthew Hodgson
6876b1a25b fix grammatics 2014-09-22 21:45:50 +01:00
Mark Haines
107e7d5d91 Add section to explain how to sign events such that we can redact message contents 2014-09-22 19:42:07 +01:00
Mark Haines
09d79b0a9b Merge branch 'develop' into server2server_signing 2014-09-22 18:54:00 +01:00
Kegan Dougal
b5c9d99424 Show display name changes in the message list. 2014-09-22 17:46:53 +01:00
Erik Johnston
176e3fd141 Bump versions and changelog 2014-09-22 17:42:09 +01:00
Kegan Dougal
95acf63ea3 Add working protractor e2e test.
This uses the ignoreSynchronization flag because of the longpoll on the event
stream. It would be better to use $interval, but couldn't get that to
*reliably* work when testing. I suspect that $interval won't help us here,
since there is genuinely an open $http connection, as we're doing a long
poll. https://github.com/angular/protractor/issues/49 for more info.
2014-09-22 16:50:12 +01:00
Kegan Dougal
90f5eb1270 Set required environment variables for e2e testing.
Added an 'id' to the login button so it can be automatically triggered.
Also, added an onPrepare section to protractor.conf to do the login.
2014-09-22 15:00:23 +01:00
Kegan Dougal
7dfcba1649 Updated test README to include a section on environment-protractor.js
The environment file is .gitignored so random selenium servers aren't accidentally pushed.
2014-09-22 14:36:06 +01:00
Kegan Dougal
e3152188ef Added boilerplate for running end-to-end tests.\nThis is done using Protractor, which looks for a .gitignored file environment-protractor.js which contains the selenium endpoint url. 2014-09-22 14:29:12 +01:00
Erik Johnston
231afe464a Add a deletions table 2014-09-22 13:42:52 +01:00
Erik Johnston
e68dc04900 Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-09-22 13:02:47 +01:00
David Baker
4696622b0a Propagate failure reason to the other party. 2014-09-22 11:44:15 +01:00
David Baker
83ea3c96ec Better logging of ICE candidates and fail the call when ICE fails. 2014-09-22 10:55:01 +01:00
Kegan Dougal
333e63156e Fixed unit test; it all actually works. Added a README for running the tests with karma/jasmine. 2014-09-22 10:27:03 +01:00
Matthew Hodgson
a0c3da17b4 go back to the original behaviour of only notifying if we think the app is backgrounded or idle... 2014-09-20 01:40:29 +01:00
Matthew Hodgson
4c7a1abd39 remove insanely busy logging which is killing CPU 2014-09-20 01:14:01 +01:00
Matthew Hodgson
9fda37158a remove the ng-model attribute from mainInput textarea to stop the digest being run every time you press a key (SYWEB-4) 2014-09-20 00:49:45 +01:00
David Baker
648fd2a622 Notify a callee that their browser doesn't support VoIP too.
SYWEB-14 #resolved
2014-09-19 18:22:14 +01:00
David Baker
99b0c9900e Move video background element up as it was causing the page to scroll. 2014-09-19 17:40:00 +01:00
David Baker
f6258221c1 Join rooms if we're not already in them when accepting a call coming from that room.
SYWEB-55 #resolve
2014-09-19 17:23:55 +01:00
Emmanuel ROHEE
68e534777c SYWEB-32: made all input/textearea inherit the font of their parent 2014-09-19 18:00:16 +02:00
David Baker
29686f63ac Fix the "is webrtc supported" titles on buttons and make the video / voice call buttons appear in multi-user rooms but be greyed out with approriate titles. 2014-09-19 16:52:45 +01:00
Erik Johnston
dcc1965bfe Test that prev_content get's added if there is a prev_state key (in the event stream). 2014-09-19 16:44:16 +01:00
David Baker
03ac0c91ae Merge branch 'videocalls' into develop
Conflicts:
	webclient/room/room.html
2014-09-19 16:26:46 +01:00
Emmanuel ROHEE
709b8ac2b7 SYWEB-13 SYWEB-14: disabled "Call" button if the browser does not support all required WebRTC features 2014-09-19 17:20:33 +02:00
Emmanuel ROHEE
e9670fd144 SYWEB-13: disabled "Send image" button if the browser does not support HTML5 file API 2014-09-19 17:20:33 +02:00
Emmanuel ROHEE
f9688d7519 SYWEB-13: Do not start the app if the browser does not support WEBStorage.
Internet Explorer case: Launch the app only for versions 9 and higher.
2014-09-19 17:20:33 +02:00
David Baker
da8b5a5367 First working version of UI chrome for video calls. 2014-09-19 16:18:15 +01:00
Erik Johnston
28bcd01e8d SYN-47: Fix bug where we still returned events for rooms we had left.
SYN-47 #resolve
2014-09-19 14:45:21 +01:00
Kegan Dougal
fba67ef951 Small formatting fixes 2014-09-19 14:19:02 +01:00
Kegan Dougal
3fa01be9e4 formatting 2014-09-19 12:04:26 +01:00
David Baker
270825ab2a Fix undefined variable error 2014-09-19 11:41:49 +01:00
Emmanuel ROHEE
008515c844 A kind of the typo in the fix of SYWEB-44 2014-09-19 09:25:51 +02:00
Emmanuel ROHEE
301ef1bdc6 Room id leaks: log them when then happens. Plus log the conditions that made them happen 2014-09-19 09:17:18 +02:00
Emmanuel ROHEE
cf1e167034 Fixed SYWEB-16: When sending an invite over federation, the remote user sees the name of the resulting invite room as *their* name rather than the inviters 2014-09-19 09:07:16 +02:00
Erik Johnston
beed1ba089 Merge branch 'develop' of github.com:matrix-org/synapse 2014-09-18 18:25:23 +01:00
Matthew Hodgson
2ab7e23790 fix SYWEB-41 (hopefully) 2014-09-18 18:18:30 +01:00
Mark Haines
fceb5f7b22 SYN-39: Add documentation explaining how to check a signature 2014-09-18 18:15:50 +01:00
Emmanuel ROHEE
0dac2f7a8d Fixed missing component dependency which created a crash 2014-09-18 19:12:21 +02:00
Kegan Dougal
6a6a718898 Added test directory, karma conf, and angular-mocks. Expect it to work? Pah, not yet. 2014-09-18 17:59:15 +01:00
Emmanuel ROHEE
faec6f7f31 Oops. Removed dev logs 2014-09-18 17:48:20 +02:00
Emmanuel ROHEE
26dda48e50 SYWEB-14: BF: rooms invitations were not visible in recents after launching/refreshing the web page 2014-09-18 17:34:26 +02:00
Erik Johnston
3108accdee Remove lie from change log. 2014-09-18 16:31:18 +01:00
Erik Johnston
e0f060d89b Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-09-18 16:22:14 +01:00
Erik Johnston
380852b58e Bump Changelog and version 2014-09-18 16:20:53 +01:00
Kegan Dougal
3dea0d2806 undefined is empty. Fixed bug where empty bingWords with old accounts which hadn't logged in didn't send notifications. 2014-09-18 16:17:29 +01:00
David Baker
0505014152 add unprefixed filter css as well 2014-09-18 16:15:48 +01:00
David Baker
3bd8cbc62f Prettier and stabler video with basic support for viewing mode. For now, transition into 'large' mode is disabled. 2014-09-18 15:51:30 +01:00
Matthew Hodgson
d583aaa0c3 fix wordwrap 2014-09-18 15:25:25 +01:00
Matthew Hodgson
3a7375f15e fix binger description 2014-09-18 15:25:11 +01:00
Erik Johnston
79a5fb469b Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-09-18 14:52:19 +01:00
Erik Johnston
9fd0c74e90 Bump changelog and versions 2014-09-18 14:46:23 +01:00
Erik Johnston
335e5d131c Merge branch 'test-sqlite-memory' of github.com:matrix-org/synapse into develop
Conflicts:
	tests/handlers/test_profile.py
2014-09-18 14:31:47 +01:00
Emmanuel ROHEE
b7d42c1e93 SYWEB-40: Only local rooms are shown in the recents list.
Removed an old patch that deduplicated join events. This patch is now useless. Plus it is buggy since it compared event.content and event.prev_content only on the membership field whereas these objects contain more data now like displayname...
2014-09-18 15:28:52 +02:00
Emmanuel ROHEE
0db0528e8e Reverted patches done for SYWEB-40 2014-09-18 15:19:35 +02:00
Erik Johnston
704e7e9f44 Merge branch 'release-v0.3.0' of github.com:matrix-org/synapse 2014-09-18 13:05:07 +01:00
Erik Johnston
c58f7f293d Merge branch 'develop' of github.com:matrix-org/synapse into release-v0.3.0 2014-09-18 12:03:30 +01:00
Erik Johnston
19095552aa Update Change log 2014-09-18 12:03:08 +01:00
Kegan Dougal
a64ff63a41 SYWEB-3 : Boldify if the join_rule is public, rather than visibility so it plays nicer with federation. 2014-09-18 12:02:52 +01:00
Erik Johnston
17db2b27bf Update version in UPGRADE 2014-09-18 12:02:52 +01:00
Kegan Dougal
ac8d73b258 Patch for SYWEB-40 : isStateEvent is not being set correctly, and really shouldn't be a configurable arg in the first place. As a result of being undefined, the events.rooms[rid].members object was not being updated in some cases, which combined with the recents-filter bug (32808e4), caused federated rooms to not appear in the recents list. 2014-09-18 12:02:52 +01:00
Kegan Dougal
a6f5c88b47 Still add the room to the filtered list even if you can't work out the number of users in the room. 2014-09-18 12:02:51 +01:00
David Baker
1c0408de08 unbreak calls in firefox 2014-09-18 11:59:27 +01:00
Kegan Dougal
9cebfd9d90 SYWEB-3 : Boldify if the join_rule is public, rather than visibility so it plays nicer with federation. 2014-09-18 11:35:59 +01:00
David Baker
e932e5237e WIP video chat layout 2014-09-18 11:04:45 +01:00
Kegan Dougal
fbf221ae6d Patch for SYWEB-40 : isStateEvent is not being set correctly, and really shouldn't be a configurable arg in the first place. As a result of being undefined, the events.rooms[rid].members object was not being updated in some cases, which combined with the recents-filter bug (32808e4), caused federated rooms to not appear in the recents list. 2014-09-18 10:35:44 +01:00
Kegan Dougal
32808e4111 Still add the room to the filtered list even if you can't work out the number of users in the room. 2014-09-18 10:05:34 +01:00
Matthew Hodgson
9f94f9de48 freenode verification 2014-09-17 23:53:53 +01:00
Paul "LeoNerd" Evans
4571cf7baa Merge branch 'develop' into test-sqlite-memory 2014-09-17 18:27:47 +01:00
Paul "LeoNerd" Evans
bfae582fa3 Remark on remaining storage modules that still need unit tests 2014-09-17 18:27:30 +01:00
David Baker
575852e6b5 add note to upgrade.rst about web client spec breaking change. 2014-09-17 17:51:22 +01:00
Erik Johnston
10b4291b54 Bump versions 2014-09-17 17:49:01 +01:00
Paul "LeoNerd" Evans
bcf5121937 Neaten more of the storage layer tests with assertObjectHasAttributes; more standardisation on test layout 2014-09-17 16:58:59 +01:00
Kegan Dougal
aeaeceb92c Create room entries for public rooms too so their public state is transferred over correctly when you join it. 2014-09-17 16:38:40 +01:00
Kegan Dougal
16f55d4275 webclient SYWEB-3 : Public rooms are bold. Can't think of a nicer way which doesn't clutter the recents list. 2014-09-17 16:38:40 +01:00
Paul "LeoNerd" Evans
b588ce920d Unit tests for (some) room events via the RoomStore 2014-09-17 16:31:11 +01:00
David Baker
1fb2c831e8 Video calling (in a tiny box at the moment) 2014-09-17 16:26:35 +01:00
Emmanuel ROHEE
246f5d2e20 SYWEB-30: BF: When switching between rooms, pagination flickered between the top of the room before jumping to the bottom of the page 2014-09-17 17:13:07 +02:00
Kegan Dougal
c707b7d128 SYWEB-3 : Added 'visibility' key to rooms returned via /initialSync 2014-09-17 16:09:07 +01:00
Paul "LeoNerd" Evans
ba41ca45fa Use new assertObjectHasAttributes() in tests/storage/test_room.py 2014-09-17 16:04:05 +01:00
Paul "LeoNerd" Evans
7aacd6834a Added a useful unit test primitive for asserting object attributes 2014-09-17 15:56:40 +01:00
Paul "LeoNerd" Evans
de14853237 More RoomStore tests 2014-09-17 15:33:10 +01:00
Paul "LeoNerd" Evans
9973298e2a Print expected-vs-actual data types on typecheck failure from check_json() 2014-09-17 15:27:45 +01:00
Emmanuel ROHEE
65c37cc852 SYWEB-22: Format emote('/me') messages correctly in desktop notification 2014-09-17 16:13:09 +02:00
Erik Johnston
b6818fd4d2 SYN-40: When a user updates their displayname or avatar update all their join events for all the rooms they are currently in. 2014-09-17 15:05:14 +01:00
Emmanuel ROHEE
fe7af80198 BF: edit the actual room name not the displayed room name (which has been computed) 2014-09-17 15:46:12 +02:00
Emmanuel ROHEE
9aed6a06cf SYWEB-15: Always show the room alias as well as its name in the UI 2014-09-17 15:38:20 +02:00
Emmanuel ROHEE
b3a0961c6c SYWEB-7: Use sessionStorage to make per-room history survives when the user navigates through rooms 2014-09-17 14:38:33 +02:00
Emmanuel ROHEE
d9a9a47075 SYWEB-7: Up & down keys let user step through the history as per readline or xchat 2014-09-17 14:18:39 +02:00
Emmanuel ROHEE
f9bb000ccf WEB-35: joins/parts should trigger desktop notifications 2014-09-17 09:41:21 +02:00
Kegan Dougal
d6c0cff3bd Bugfix when content isn't a string. 2014-09-16 16:31:16 +01:00
Kegan Dougal
95e171e19a Don't bing for sent messages. Handle cases where the member is unknown rather than erroring out. 2014-09-16 16:23:20 +01:00
Kegan Dougal
d7b206cc93 Added basic RegExp support. 2014-09-16 16:10:48 +01:00
Emmanuel ROHEE
06dfbdf7c8 WEB-27: We don't need to show the user-count in Recents in the room sidepanel - takes up too much room 2014-09-16 17:07:47 +02:00
Kegan Dougal
3395a3305f Bing on all the things if there are 0 bing words. 2014-09-16 15:47:29 +01:00
Kegan Dougal
5aaa3c09c1 hidden/minimise/focus disaster disclaimer with the TODO 2014-09-16 15:35:23 +01:00
Kegan Dougal
b36a0c71d1 Added utility function containsBingWord and hook up some css to it. 2014-09-16 15:35:23 +01:00
Kegan Dougal
a402e0c5e6 Added bing detection logic. Persist the display name of the user in localstorage for use when binging. 2014-09-16 15:35:23 +01:00
Kegan Dougal
660364d6a7 Move the notification logic out of an individual room controller and into the general event handler, so we can notify for >1 room. 2014-09-16 15:35:23 +01:00
Kegan Dougal
b170fe921e Added a section on bing words if you enable desktop notifications. 2014-09-16 15:35:23 +01:00
David Baker
84372cef4a Time out calls from both ends properly. 2014-09-16 15:26:22 +01:00
Emmanuel ROHEE
890178cf25 Fixed scroll flickering when opening the room 2014-09-16 16:16:11 +02:00
Emmanuel ROHEE
a284de73e6 If an initialSync has been already done on a room, we do not need to paginate back to get more messages 2014-09-16 16:16:11 +02:00
Emmanuel ROHEE
45592ccdfd WEB-29: Improve room page content loading
InitialSync: load the 30 last messages of each room so that a full page of messages can be displayed without additionnal request
2014-09-16 16:16:11 +02:00
David Baker
f4094c5eb3 Update spec with the lifetime field. 2014-09-16 14:54:52 +01:00
David Baker
dd2b933a0d Use event age to recognise which calls are current and which aren't and hence support answering calls that were placed before we loaded the page. 2014-09-16 14:47:10 +01:00
Kegan Dougal
c099b36af3 Comment out password reset for now, until the mechanism is fully discussed (IS token auth vs HS auth) 2014-09-16 13:32:33 +01:00
Kegan Dougal
cc83b06cd1 Added support for the HS to send emails. Use it to send password resets. Added email_smtp_server and email_from_address config args. Added emailutils. 2014-09-16 12:36:39 +01:00
Kegan Dougal
5f30a69a9e Added PasswordResetRestServlet. Hit the IS to confirm the email/user. Need to send email. 2014-09-16 11:22:40 +01:00
Emmanuel ROHEE
faee41c303 Merge remote-tracking branch 'origin/develop' into webclient_data_centralisation 2014-09-16 08:50:53 +02:00
Paul "LeoNerd" Evans
e32cfed1d8 Initial pass at a RoomStore test 2014-09-15 18:41:24 +01:00
Erik Johnston
1e4b971f95 Fix bug where we didn't always get 'prev_content' key 2014-09-15 17:43:46 +01:00
Emmanuel ROHEE
b0483cd47d Filter room where the user has been banned 2014-09-15 18:22:38 +02:00
Erik Johnston
40d2f38abe Fix bug where we incorrectly calculated 'age_ts' from 'age' key rather than the reverse. Don't transmit age_ts to clients for now. 2014-09-15 16:55:39 +01:00
Erik Johnston
59516a8bb1 Correctly handle receiving 'missing' Pdus from federation, rather than just discarding them. 2014-09-15 16:40:44 +01:00
Emmanuel ROHEE
8aa4b7bf7f Recents must not show temporary fake messages 2014-09-15 17:31:07 +02:00
Erik Johnston
e639a3516d Improve logging in federation handler. 2014-09-15 16:24:03 +01:00
Erik Johnston
0897a09f49 Fix unit tests after adding extra argument on put_json 2014-09-15 16:24:03 +01:00
Erik Johnston
6ac0b4ade8 Fix 'age' key to update on retries 2014-09-15 16:24:03 +01:00
Kegan Dougal
34d7896b06 More helpful 400 error messages. 2014-09-15 16:05:51 +01:00
Kegan Dougal
688c37ebf4 Updated CHANGES and UPGRADE to reflect registration API changes. 2014-09-15 15:53:05 +01:00
Kegan Dougal
2c00e1ecd9 Be consistent when associating keys with login types for registration/login. 2014-09-15 15:38:29 +01:00
Emmanuel ROHEE
42f5b0a6b8 Recents uses data directly from $rootscope.events 2014-09-15 16:31:59 +02:00
Kegan Dougal
14bc4ed59f Merge branch 'develop' of github.com:matrix-org/synapse into registration-api-changes in preparation for re-merge to develop. 2014-09-15 15:27:58 +01:00
Kegan Dougal
0b8a3bc3b9 Update spec to include m.login.email.identity 2014-09-15 15:27:17 +01:00
Kegan Dougal
c04caff55c Fix unit tests. 2014-09-15 15:14:19 +01:00
Kegan Dougal
7f23425e59 Updated cmdclient to use new registration logic. 2014-09-15 15:09:21 +01:00
Paul "LeoNerd" Evans
1aaa429081 Also unittest RoomMemberStore's joined_hosts_for_room() 2014-09-15 15:00:14 +01:00
Kegan Dougal
04fbda46dd Make captcha work again with the new registration logic. 2014-09-15 14:52:39 +01:00
Kegan Dougal
d821755b49 Updated webclient to support the new registration logic. 2014-09-15 14:31:53 +01:00
Paul "LeoNerd" Evans
ae7dfeb5b6 Use new 'tests.unittest' in new storage level tests 2014-09-15 14:19:16 +01:00
Paul "LeoNerd" Evans
b0406b9ead Merge remote-tracking branch 'origin/develop' into test-sqlite-memory 2014-09-15 14:15:10 +01:00
Erik Johnston
5bd9369a62 Correctly handle the 'age' key in events and pdus 2014-09-15 13:26:11 +01:00
Kegan Dougal
285ecaacd0 Split out password/captcha/email logic. 2014-09-15 12:42:36 +01:00
Kegan Dougal
34878bc26a Added LoginType constants. Created general structure for processing registrations. 2014-09-15 10:23:20 +01:00
Emmanuel ROHEE
76217890c0 BF: inviter field has moved to the room root object 2014-09-15 11:14:10 +02:00
Kegan Dougal
bf6fa6dd3d Merge branch 'develop' of github.com:matrix-org/synapse into registration-api-changes 2014-09-15 09:46:33 +01:00
Emmanuel ROHEE
a9da2ec895 BF: presence and eventMap were not reset at logout. 2014-09-15 10:39:30 +02:00
Emmanuel ROHEE
f3d3441d02 Use "white-space: pre-wrap" for "Text will wrap when necessary, and on line breaks" 2014-09-15 10:22:57 +02:00
Emmanuel ROHEE
3292f70071 Merge remote-tracking branch 'origin/master' into develop 2014-09-15 10:08:47 +02:00
Matthew Hodgson
49b5dd56b5 unbreak wordwrapping by breaking multiline paste for now 2014-09-13 11:38:45 +01:00
Matthew Hodgson
32acb7e903 always scroll to bottom when entering a room 2014-09-13 11:35:36 +01:00
Matthew Hodgson
276b9f1839 more wishlist 2014-09-13 11:26:16 +01:00
Paul "LeoNerd" Evans
7a77aabb4b Define a CLOS-like 'around' modifier as a decorator, to neaten up the 'orig_*' noise of wrapping the setUp()/tearDown() methods 2014-09-12 19:07:29 +01:00
Paul "LeoNerd" Evans
aeb69c0f8c Add some docstrings 2014-09-12 18:46:13 +01:00
Paul "LeoNerd" Evans
d9f3f322c5 Additionally look first for a 'loglevel' attribute on the running test method, before the TestCase 2014-09-12 18:46:13 +01:00
Paul "LeoNerd" Evans
33c4dd4c2d Define a (class) decorator for easily setting a DEBUG logging level on a TestCase 2014-09-12 18:46:13 +01:00
Paul "LeoNerd" Evans
ca8349a897 Allow a TestCase to set a 'loglevel' attribute, which overrides the logging level while that testcase runs 2014-09-12 18:46:13 +01:00
Paul "LeoNerd" Evans
cd62ee3f29 Have all unit tests import from our own subclass of trial's unittest TestCase; set up logging in ONE PLACE ONLY 2014-09-12 18:46:13 +01:00
Erik Johnston
958b52596c Update CHANGES.rst 2014-09-12 18:36:45 +01:00
Erik Johnston
c7bcd87f37 Merge branch 'master' of github.com:matrix-org/synapse into develop 2014-09-12 18:27:44 +01:00
Erik Johnston
80852d1135 Spellcheck 2014-09-12 18:27:04 +01:00
Erik Johnston
84326e2491 Add note about glare support 2014-09-12 18:26:19 +01:00
Erik Johnston
e3aec9bc81 Merge branch 'release-v0.2.3' of github.com:matrix-org/synapse
Conflicts:
	webclient/room/room-controller.js
2014-09-12 18:19:32 +01:00
David Baker
21b45d2a5b Update the spec document to replace the candidate message with the candidates message. 2014-09-12 18:19:19 +01:00
David Baker
842898df15 Send multiple candidates at once instead of all individually. Changes spec to include multiple candidates in a candidate(s) message. 2014-09-12 18:19:19 +01:00
Erik Johnston
afb7f173cf Bump version and change log 2014-09-12 18:13:05 +01:00
Erik Johnston
14975ce5bc Fix bug where we relied on the current_state_events being updated when we are handling type specific persistence 2014-09-12 17:57:02 +01:00
Erik Johnston
667e747ed1 Fix bug where we no longer stored user_id on Pdus 2014-09-12 17:56:21 +01:00
Paul "LeoNerd" Evans
1c51c8ab7d Merge remote-tracking branch 'origin/develop' into test-sqlite-memory
Conflicts:
	synapse/storage/pdu.py
2014-09-12 17:20:06 +01:00
Erik Johnston
39e3fc69e5 Make the state resolution use actual power levels rather than taking them from a Pdu key. 2014-09-12 17:11:09 +01:00
Erik Johnston
b42fe05c51 Fix bug where we incorrectly removed a remote host from the list of hosts in a room when any user from that host left that room even if they weren't the last user from that host in that room 2014-09-12 17:11:09 +01:00
Erik Johnston
ca1ae7cf9b Fix bug where we didn't return a tuple when expected. 2014-09-12 17:11:09 +01:00
Paul "LeoNerd" Evans
2026942b05 Initial hack at some RoomMemberStore unit tests 2014-09-12 16:44:07 +01:00
Paul "LeoNerd" Evans
aa525e4a63 More accurate docs / clearer paramter names in RoomMemberStore 2014-09-12 16:43:49 +01:00
Emmanuel ROHEE
3ed39ad20e Clean data when user logs out 2014-09-12 17:43:35 +02:00
David Baker
cc2cee4af6 Retry sending events that fail to send. 2014-09-12 16:32:22 +01:00
Emmanuel ROHEE
6c81752e46 Fixed displayname resolution of emote sender 2014-09-12 17:01:49 +02:00
Paul "LeoNerd" Evans
a87eac4308 Revert recent changes to RoomMemberStore 2014-09-12 15:51:51 +01:00
Emmanuel ROHEE
a2cd942a95 Fixed public room name and users count alignement
Put data into a table to ease layout and manage long strings
2014-09-12 16:46:20 +02:00
Paul "LeoNerd" Evans
a840ff8f3f Now don't need the other logger.debug() call in _execute 2014-09-12 14:38:27 +01:00
Paul "LeoNerd" Evans
1c20249884 Logging of all SQL queries via the 'synapse.storage.SQL' logger 2014-09-12 14:37:55 +01:00
Paul "LeoNerd" Evans
e53d77b501 Add a .runInteraction() method on SQLBaseStore itself to wrap the .db_pool 2014-09-12 14:28:07 +01:00
David Baker
09a59ce2d3 Some words about glare 2014-09-12 14:24:56 +01:00
David Baker
8b28f7d14e Always pick the incoming call if we've not yet sent out our invite, otherwise the remorte party will see their call get rejected and our call won't come in until our user clicks allow. 2014-09-12 14:06:35 +01:00
David Baker
a81ec21762 Remove the local AV stream from ourselves when handing it off to a new call or we'll close it when we hang up. 2014-09-12 11:51:57 +01:00
Emmanuel ROHEE
9819b3619e CSS m.room.topic and m.room.name events in the history 2014-09-12 11:56:08 +02:00
Emmanuel ROHEE
311dc61803 Handle NAME_EVENT to get room name update event
(TODO: recents needs to be directly plugged to $rootScope.events.rooms)
2014-09-12 10:51:05 +02:00
Emmanuel ROHEE
d934328904 Added edition of room name 2014-09-12 10:48:06 +02:00
Emmanuel ROHEE
6ea20f3503 Show room name updates in room history and recents.
Update it with the latest value
2014-09-12 10:12:56 +02:00
Emmanuel ROHEE
8b3ce85183 BF: temp workaround while /initialSync on a particular room is not available
initRoom on a new room is not called. Call it for any received events
2014-09-12 08:54:18 +02:00
David Baker
a059ca6915 few fixes for errors in glare conditions. still seem to end up with no audio if both calls are placed at the same time. 2014-09-11 19:16:57 +01:00
David Baker
1e05e30472 Put back the line that adds the stream to the invite, otherwise caller->callee audio won't work... 2014-09-11 18:59:22 +01:00
Paul "LeoNerd" Evans
249e8f2277 Add a better _store_room_member_txn() method that takes separated fields instead of an event object; also add FIXME comment about a big bug in the logic 2014-09-11 18:52:35 +01:00
Paul "LeoNerd" Evans
aaf9ab68c6 Rename _store_room_member_txn to _store_room_member_from_event_txn so we can create another, more sensible function of that name 2014-09-11 18:44:04 +01:00
Paul "LeoNerd" Evans
3d6aee079e Unit-test for RegistrationStore using SQLiteMemoryDbPool 2014-09-11 17:44:00 +01:00
David Baker
81d061e74e Fix bug where web client wold break trying to add the earliest token without having initialised the room if your first page of history contained only events which didn't call initRoom. Just call initRoom in handleMessages since we use it there rather than leaving it to the individual event handling methods. 2014-09-11 17:40:38 +01:00
Paul "LeoNerd" Evans
fb93a4a9e3 Perform PresenceInvitesTestCase against real SQLiteMemoryDbPool 2014-09-11 16:22:44 +01:00
Emmanuel ROHEE
ceec607e7f Clearly show when an user cannot join a room.
In realtime show who kicked or banned him.
2014-09-11 16:54:57 +02:00
David Baker
fb082cf50f start towards glare support (currently not much better but no worse than before) including fixing a lot of self/var self/this fails that caused chaos when we started to have more than one call in play. 2014-09-11 15:24:18 +01:00
Paul "LeoNerd" Evans
493b1e6d3c Need to prepare() the SQLiteMemoryDbPool before passing it to HomeServer constructor, as DataStore's constructor will want it ready 2014-09-11 15:21:15 +01:00
Emmanuel ROHEE
806c49a690 Added support of copy/paste of multi lines content 2014-09-11 15:46:24 +02:00
Emmanuel ROHEE
aa347b52ba Use autofill-event.js to workaround browsers issue: Form model doesn't update on autocomplete
https://github.com/angular/angular.js/issues/1460
2014-09-11 15:07:44 +02:00
Paul "LeoNerd" Evans
4385eadc28 Start of converting PresenceHandler unit tests to use SQLiteMemoryDbPool - just the 'State' test case for now 2014-09-11 13:57:17 +01:00
Emmanuel ROHEE
6b20fef52a Invite: reset the input when the invitation has been done 2014-09-11 13:52:07 +02:00
Emmanuel ROHEE
c92740e8a9 Enable enter key in the invite input 2014-09-11 13:43:55 +02:00
Paul "LeoNerd" Evans
d13d0bba51 Unit-test DirectoryHandler against (real) SQLite memory store, not mocked storage layer 2014-09-11 11:59:48 +01:00
Paul "LeoNerd" Evans
d83202b938 Added unit tests of DirectoryStore 2014-09-11 11:32:46 +01:00
Emmanuel ROHEE
cc049851d0 On member avatar mouseover, show user_id and power level 2014-09-11 12:01:44 +02:00
Emmanuel ROHEE
14a9652324 Room topic: if the request fails, show the error in the feedback 2014-09-11 11:53:37 +02:00
Emmanuel ROHEE
af44e9556d BF: made input autofocus work when opening the room topic input 2014-09-11 11:49:59 +02:00
Emmanuel ROHEE
7e7eb0efc1 Show room topic change in the chat history and in the recents 2014-09-11 11:31:24 +02:00
Emmanuel ROHEE
8dcb6f24b5 getRoomEventIndex: improved speed for what it is used 2014-09-11 09:11:24 +02:00
Paul "LeoNerd" Evans
79fe6083eb Test ProfileHandler against the real datastore layer using SQLite :memory: 2014-09-10 18:11:32 +01:00
Paul "LeoNerd" Evans
dd1a9100c5 Added unit tests for PresenceDataStore too 2014-09-10 17:51:05 +01:00
Emmanuel ROHEE
44998ca450 Merge remote-tracking branch 'origin/develop' into webclient_initialSync 2014-09-10 18:35:05 +02:00
Emmanuel ROHEE
7a153b5c94 Show echoed emote with transparency 2014-09-10 18:29:52 +02:00
Emmanuel ROHEE
5a06f5c5fc Reenabled transparent echo message. It turns to opaque without flickering now. 2014-09-10 18:24:03 +02:00
Paul "LeoNerd" Evans
dc7f39677f Remember to kill now-dead import in test_profile.py 2014-09-10 16:56:52 +01:00
Paul "LeoNerd" Evans
08f5c48fc8 Move SQLiteMemoryDbPool implementation into tests.utils 2014-09-10 16:56:02 +01:00
Paul "LeoNerd" Evans
9774949cc9 It's considered polite to actually wait for DB prepare before running tests 2014-09-10 16:50:09 +01:00
Paul "LeoNerd" Evans
53d0f69dc3 Also test avatar_url profile field 2014-09-10 16:49:34 +01:00
Paul "LeoNerd" Evans
6081f4947e Tiny trivial PoC unit-test using SQLite in :memory: mode 2014-09-10 16:42:31 +01:00
Emmanuel ROHEE
6d18b52931 Clean previous request feedback when doing a new request 2014-09-10 17:40:34 +02:00
Emmanuel ROHEE
81ecaf945d BF: Made /op work when providing no power value. 50 is used as default in this case 2014-09-10 17:37:51 +02:00
Paul "LeoNerd" Evans
55397f6347 prepare_database() on db_conn, not plain name, so we can pass in the connection from outside 2014-09-10 16:23:58 +01:00
Paul "LeoNerd" Evans
2faffc52ee Make sure not to open our TCP ports until /after/ the DB is nicely prepared ready for use 2014-09-10 16:16:24 +01:00
Paul "LeoNerd" Evans
6c1f0055dc No need for a tiny run() function any more, just use reactor.run() directly 2014-09-10 16:07:44 +01:00
Emmanuel ROHEE
811716592c Made users count auto updating. Do show it if the info is not available (ex:user has not joined the room yet) 2014-09-10 16:46:06 +02:00
David Baker
e2d2d63bcd Animation on call end icon. 2014-09-10 15:45:09 +01:00
David Baker
dde7ec8e64 Upgrade angularjs to 1.3.0-rc1 since this is new development 2014-09-10 15:43:27 +01:00
Paul "LeoNerd" Evans
ce55a8cc4b Move database preparing code out of homserver.py into storage where it belongs 2014-09-10 15:42:15 +01:00
Emmanuel ROHEE
30bfa911fc Member event: store use the the latest one 2014-09-10 16:26:11 +02:00
Emmanuel ROHEE
da3f842b8c Removed wrong comments about recents-controller.js: it uses $rootScope.rooms not $rootScope.events.rooms managed by event-handler-service.js and used by other controllers 2014-09-10 14:53:03 +02:00
Emmanuel ROHEE
130cbdd7af dedup events: state events conflict with messages events. Do not consider them in deduplication 2014-09-10 14:45:32 +02:00
Emmanuel ROHEE
b099634ba1 Reenabled handle of room states events in initialSync but do not add them to the displayed messages in the room page.
Show the m.room.member events only when they come from room.messages (from initialSync of pagination) not from room.state.
2014-09-10 14:36:30 +02:00
Emmanuel ROHEE
c2afc6cd0a Presence events do not have event id. Do not discard them 2014-09-10 13:48:33 +02:00
David Baker
80b5470663 Add text for incoming calls 2014-09-10 11:35:14 +01:00
David Baker
7411794fa1 Show mxid in call bar for users with no displayname 2014-09-10 11:21:20 +01:00
David Baker
55fe0d8adc Less buggy rejection of calls when busy 2014-09-10 11:12:02 +01:00
Emmanuel ROHEE
b63dd9506e Improved requests: pagination is done from the data received in initialSync 2014-09-10 12:01:00 +02:00
David Baker
6f256e6380 reject calls if there's already a call in progress 2014-09-10 10:32:05 +01:00
Kegan Dougal
2bd4346075 More rst formatting. 2014-09-09 15:13:50 -07:00
Kegan Dougal
f23e5b17b6 Extra restrictions to make parsing easier. 2014-09-09 15:11:06 -07:00
Kegan Dougal
56a358481e Tyops 2014-09-09 15:00:48 -07:00
Kegan Dougal
d5704cf2a3 Added initial draft for human-readable ID rules. 2014-09-09 14:53:35 -07:00
Kegan Dougal
550e8f32ac Move model to client-server for now. 2014-09-09 13:51:13 -07:00
David Baker
f90ce04a83 Hangup call if user denies media access. 2014-09-09 18:21:03 +01:00
David Baker
ccfb42e4ff Don't try setting up the call if the user has canceled it before allowing permission. 2014-09-09 17:58:26 +01:00
David Baker
25e96f82db Don't break if you press the hangup button before allowing media permission. 2014-09-09 17:52:01 +01:00
David Baker
253c327252 Don't play an engaged tone if we hang up locally. 2014-09-09 17:38:40 +01:00
Erik Johnston
a75f8686ba Fix bug where we used an unbound local variable if we ended up rolling back the persist_event transaction 2014-09-09 16:27:59 +01:00
Emmanuel ROHEE
1ef51e7939 Improved room page loading flow: do pagination only when the members list is available.
Killed an unexpected pagination trigger when the page load: paginateMore
2014-09-09 16:46:30 +02:00
Emmanuel ROHEE
746ed57c0e When the user has been kicked or banned from a room, remove the room from his recents list 2014-09-09 16:31:50 +02:00
Emmanuel ROHEE
5132fcdb8b Made recents list display something when joining a room which we do not have state data yet 2014-09-09 16:10:20 +02:00
Emmanuel ROHEE
332986ba43 BF: prevent joined messages to be displayed twice when joining a room.
Do this by synchronizing the m.room.member joined event from the events stream and the start of the pagination
2014-09-09 16:10:20 +02:00
David Baker
472b4fe48c make calls work in Firefox 2014-09-09 14:54:06 +01:00
Emmanuel ROHEE
fd2d3fcfd7 Removed historical code: recents does not need to manage presences. It is already done by initialSync in eventStreamService 2014-09-09 12:47:42 +02:00
Emmanuel ROHEE
967ac65586 BF: Made the grey background of the current room cover all the cell width 2014-09-09 12:47:42 +02:00
David Baker
16b40cbede Show call invites in the message table 2014-09-09 11:45:36 +01:00
Kegan Dougal
75890d7bdd CSS tweakage 2014-09-08 19:02:23 -07:00
Kegan Dougal
e8f19b4c0d Display a 'Set Topic' button if there is no topic or it's a 0-len string. 2014-09-08 18:59:26 -07:00
Kegan Dougal
6bdb23449a Add ability to set topic by double-clicking on the topic text then hitting enter. 2014-09-08 18:40:34 -07:00
Kegan Dougal
f64cc237fc Fixed bug which displayed an older room topic because it was being returned from /initialSync messages key. Check the ts of the event before clobbering state. 2014-09-08 17:27:51 -07:00
Kegan Dougal
ef2111099a long topic is long. CSS support it 2014-09-08 17:19:04 -07:00
Kegan Dougal
df50a6823f Display public room topics if they exist on the public room list. 2014-09-08 17:14:58 -07:00
Kegan Dougal
324020d5fe Display the room topic in the room, underneath the name of the room. 2014-09-08 15:36:52 -07:00
Kegan Dougal
544691ab05 Update jsfiddles to have more helpful error messages when there is no connection when logging in. 2014-09-08 14:54:10 -07:00
Erik Johnston
5236de5b03 Add slightly helpful advice on how to generate config if you don'y already have one 2014-09-08 22:52:50 +01:00
Erik Johnston
91b370650a Don't autogen config in synctl for the same reasons we don't turn of --generate-config by default on the homeserver - it is liable to confuse people who have moved the config file or have chosen a non standard location.
Also, don't override log file location.
2014-09-08 22:48:46 +01:00
Erik Johnston
e062f2dfa8 Apparently we can't do txn.rollback(), so raise and catch an exception instead. 2014-09-08 22:37:19 +01:00
Kegan Dougal
c1a25756c2 Added demo.details 2014-09-08 14:24:28 -07:00
Kegan Dougal
d692994ea4 Updated jsfiddle links to point to github 2014-09-08 14:16:22 -07:00
Kegan Dougal
a3590dfa26 Bodge to default to '1 users' when you create a room, which is better than blindly assuming a recents controller is writing to rootScope.rooms and setting numUsersInRoom there. 2014-09-08 14:01:34 -07:00
Kegan Dougal
da9b7b0368 Added big massive TODOs on a huge design problem with initial sync 2014-09-08 13:54:09 -07:00
Kegan Dougal
054fad5360 Float right the num users, apply room highlight to user count. 2014-09-08 13:28:55 -07:00
Kegan Dougal
e0954f3b36 Better checks are better. 2014-09-08 12:15:29 -07:00
Kegan Dougal
76fe7d4eba Added num_joined_users key to /publicRooms for each room. Show this information in the webclient. 2014-09-08 12:15:29 -07:00
Erik Johnston
942d8412c4 Handle the case where we don't have a common ancestor 2014-09-08 20:13:27 +01:00
Kegan Dougal
2eaa199e6a Added number of users in recent rooms. 2014-09-08 11:55:29 -07:00
Erik Johnston
83ce57302d Fix bug in state handling where we incorrectly identified a missing pdu. Update tests to catch this case. 2014-09-08 19:50:59 +01:00
Kegan Dougal
de727f854a Make #matrix public rooms bold to make them stand out from the other public rooms. Ideally this would be metadata in /publicRooms to say something like 'featured channel', but for now, just make it a client side check. 2014-09-08 11:33:12 -07:00
Kegan Dougal
0627366b2f Sort the public room list by display name. 2014-09-08 11:17:44 -07:00
Kegan Dougal
586e0df62d Updated spec and api docs to desired new format. 2014-09-08 11:07:52 -07:00
Erik Johnston
c0577ea87a Rollback if we try and insert duplicate events 2014-09-08 18:34:18 +01:00
Emmanuel ROHEE
d81e7dc00e Added /join description 2014-09-08 18:25:56 +02:00
Emmanuel ROHEE
9a5f224931 matrixService.rooms must be renamed matrixService.initialSync now 2014-09-08 18:21:41 +02:00
Emmanuel ROHEE
21d6ce2380 App startup improvements:
- do one and only one initialSync when the app starts. (recents-controller does not do its own anymore)
 - initialSync: get only the last message per room instead of default number of messages (10)

Prevent recents-controller from loosing its data each time the page URL changes
2014-09-08 18:14:35 +02:00
David Baker
972f664b6b add sounds to the calling interface 2014-09-08 16:10:36 +01:00
Emmanuel ROHEE
1dc4ad1efa Merge branch 'origin/release-v0.2.2' into develop 2014-09-08 11:29:47 +02:00
Matthew Hodgson
a0a609e8af fix embarassing bug where in-progress messages get vaped when the previous one gets delivered 2014-09-08 11:28:51 +02:00
Matthew Hodgson
dc1f202eca fix desktop notifs, which were broken in eab463fd 2014-09-08 11:28:51 +02:00
Kegan Dougal
ce5cd2202f Center recaptcha dialog. 2014-09-08 11:28:51 +02:00
Erik Johnston
2df5cb114d Remove disabled change from CHANGES 2014-09-08 11:28:50 +02:00
Matthew Hodgson
ef0304beff disable broken event dup suppression, and fix echo for /me 2014-09-08 11:28:50 +02:00
Kegan Dougal
dd2ae64120 Set the room_alias field when we encounter a new one, rather than only from local storage. 2014-09-08 11:28:50 +02:00
Kegan Dougal
cde6bdfa77 Use the room_display_name when presenting on the home page, and not the room_alias which may not be set. 2014-09-08 11:28:50 +02:00
Kegan Dougal
f397b2264c https when loading recaptcha js 2014-09-08 11:28:50 +02:00
Erik Johnston
768ff1a850 Fix race in presence handler where we evicted things from cache while handling a key therein 2014-09-08 11:28:50 +02:00
Erik Johnston
7735aad9d6 Bump version and changelog 2014-09-08 11:28:50 +02:00
Kegan Dougal
7bff9b6269 Minor spec tweaks. 2014-09-08 11:28:50 +02:00
Emmanuel ROHEE
24f0bb4af5 Revert "BF: Made notification work again (forgot to renamed "offline" to "unavailable")"
This reverts commit c3f9d8e41b.
2014-09-08 11:09:14 +02:00
Emmanuel ROHEE
c3f9d8e41b BF: Made notification work again (forgot to renamed "offline" to "unavailable") 2014-09-08 10:28:07 +02:00
Matthew Hodgson
64b6f09b0d fix embarassing bug where in-progress messages get vaped when the previous one gets delivered 2014-09-06 17:48:16 -07:00
Erik Johnston
a73104b566 Merge branch 'release-v0.2.2' of github.com:matrix-org/synapse 2014-09-06 18:28:24 +01:00
Matthew Hodgson
41907209bb fix desktop notifs, which were broken in eab463fd 2014-09-06 10:26:41 -07:00
Mark Haines
e0fa4cf874 Spelling 2014-09-05 18:22:24 +01:00
Mark Haines
9243f0c5e3 Add docs on how to sign json 2014-09-05 17:42:54 +01:00
321 changed files with 20648 additions and 56566 deletions

15
.gitignore vendored
View File

@@ -1,6 +1,7 @@
*.pyc
.*.swp
.DS_Store
_trial_temp/
logs/
dbs/
@@ -11,6 +12,14 @@ docs/build/
cmdclient_config.json
homeserver*.db
homeserver*.log
homeserver*.pid
homeserver*.yaml
*.signing.key
*.tls.crt
*.tls.dh
*.tls.key
.coverage
htmlcov
@@ -24,6 +33,10 @@ graph/*.svg
graph/*.png
graph/*.dot
webclient/config.js
**/webclient/config.js
**/webclient/test/coverage/
**/webclient/test/environment-protractor.js
uploads
.idea/

View File

@@ -1,3 +1,249 @@
Changes in synapse 0.6.1 (2015-01-07)
=====================================
* Major optimizations to improve performance of initial sync and event sending
in large rooms (by up to 10x)
* Media repository now includes a Content-Length header on media downloads.
* Improve quality of thumbnails by changing resizing algorithm.
Changes in synapse 0.6.0 (2014-12-16)
=====================================
* Add new API for media upload and download that supports thumbnailing.
* Replicate media uploads over multiple homeservers so media is always served
to clients from their local homeserver. This obsoletes the
--content-addr parameter and confusion over accessing content directly
from remote homeservers.
* Implement exponential backoff when retrying federation requests when
sending to remote homeservers which are offline.
* Implement typing notifications.
* Fix bugs where we sent events with invalid signatures due to bugs where
we incorrectly persisted events.
* Improve performance of database queries involving retrieving events.
Changes in synapse 0.5.4a (2014-12-13)
======================================
* Fix bug while generating the error message when a file path specified in
the config doesn't exist.
Changes in synapse 0.5.4 (2014-12-03)
=====================================
* Fix presence bug where some rooms did not display presence updates for
remote users.
* Do not log SQL timing log lines when started with "-v"
* Fix potential memory leak.
Changes in synapse 0.5.3c (2014-12-02)
======================================
* Change the default value for the `content_addr` option to use the HTTP
listener, as by default the HTTPS listener will be using a self-signed
certificate.
Changes in synapse 0.5.3 (2014-11-27)
=====================================
* Fix bug that caused joining a remote room to fail if a single event was not
signed correctly.
* Fix bug which caused servers to continuously try and fetch events from other
servers.
Changes in synapse 0.5.2 (2014-11-26)
=====================================
Fix major bug that caused rooms to disappear from peoples initial sync.
Changes in synapse 0.5.1 (2014-11-26)
=====================================
See UPGRADES.rst for specific instructions on how to upgrade.
* Fix bug where we served up an Event that did not match its signatures.
* Fix regression where we no longer correctly handled the case where a
homeserver receives an event for a room it doesn't recognise (but is in.)
Changes in synapse 0.5.0 (2014-11-19)
=====================================
This release includes changes to the federation protocol and client-server API
that is not backwards compatible.
This release also changes the internal database schemas and so requires servers to
drop their current history. See UPGRADES.rst for details.
Homeserver:
* Add authentication and authorization to the federation protocol. Events are
now signed by their originating homeservers.
* Implement the new authorization model for rooms.
* Split out web client into a seperate repository: matrix-angular-sdk.
* Change the structure of PDUs.
* Fix bug where user could not join rooms via an alias containing 4-byte
UTF-8 characters.
* Merge concept of PDUs and Events internally.
* Improve logging by adding request ids to log lines.
* Implement a very basic room initial sync API.
* Implement the new invite/join federation APIs.
Webclient:
* The webclient has been moved to a seperate repository.
Changes in synapse 0.4.2 (2014-10-31)
=====================================
Homeserver:
* Fix bugs where we did not notify users of correct presence updates.
* Fix bug where we did not handle sub second event stream timeouts.
Webclient:
* Add ability to click on messages to see JSON.
* Add ability to redact messages.
* Add ability to view and edit all room state JSON.
* Handle incoming redactions.
* Improve feedback on errors.
* Fix bugs in mobile CSS.
* Fix bugs with desktop notifications.
Changes in synapse 0.4.1 (2014-10-17)
=====================================
Webclient:
* Fix bug with display of timestamps.
Changes in synpase 0.4.0 (2014-10-17)
=====================================
This release includes changes to the federation protocol and client-server API
that is not backwards compatible.
The Matrix specification has been moved to a separate git repository:
http://github.com/matrix-org/matrix-doc
You will also need an updated syutil and config. See UPGRADES.rst.
Homeserver:
* Sign federation transactions to assert strong identity over federation.
* Rename timestamp keys in PDUs and events from 'ts' and 'hsob_ts' to 'origin_server_ts'.
Changes in synapse 0.3.4 (2014-09-25)
=====================================
This version adds support for using a TURN server. See docs/turn-howto.rst on
how to set one up.
Homeserver:
* Add support for redaction of messages.
* Fix bug where inviting a user on a remote home server could take up to
20-30s.
* Implement a get current room state API.
* Add support specifying and retrieving turn server configuration.
Webclient:
* Add button to send messages to users from the home page.
* Add support for using TURN for VoIP calls.
* Show display name change messages.
* Fix bug where the client didn't get the state of a newly joined room
until after it has been refreshed.
* Fix bugs with tab complete.
* Fix bug where holding down the down arrow caused chrome to chew 100% CPU.
* Fix bug where desktop notifications occasionally used "Undefined" as the
display name.
* Fix more places where we sometimes saw room IDs incorrectly.
* Fix bug which caused lag when entering text in the text box.
Changes in synapse 0.3.3 (2014-09-22)
=====================================
Homeserver:
* Fix bug where you continued to get events for rooms you had left.
Webclient:
* Add support for video calls with basic UI.
* Fix bug where one to one chats were named after your display name rather
than the other person's.
* Fix bug which caused lag when typing in the textarea.
* Refuse to run on browsers we know won't work.
* Trigger pagination when joining new rooms.
* Fix bug where we sometimes didn't display invitations in recents.
* Automatically join room when accepting a VoIP call.
* Disable outgoing and reject incoming calls on browsers we don't support
VoIP in.
* Don't display desktop notifications for messages in the room you are
non-idle and speaking in.
Changes in synapse 0.3.2 (2014-09-18)
=====================================
Webclient:
* Fix bug where an empty "bing words" list in old accounts didn't send
notifications when it should have done.
Changes in synapse 0.3.1 (2014-09-18)
=====================================
This is a release to hotfix v0.3.0 to fix two regressions.
Webclient:
* Fix a regression where we sometimes displayed duplicate events.
* Fix a regression where we didn't immediately remove rooms you were
banned in from the recents list.
Changes in synapse 0.3.0 (2014-09-18)
=====================================
See UPGRADE for information about changes to the client server API, including
breaking backwards compatibility with VoIP calls and registration API.
Homeserver:
* When a user changes their displayname or avatar the server will now update
all their join states to reflect this.
* The server now adds "age" key to events to indicate how old they are. This
is clock independent, so at no point does any server or webclient have to
assume their clock is in sync with everyone else.
* Fix bug where we didn't correctly pull in missing PDUs.
* Fix bug where prev_content key wasn't always returned.
* Add support for password resets.
Webclient:
* Improve page content loading.
* Join/parts now trigger desktop notifications.
* Always show room aliases in the UI if one is present.
* No longer show user-count in the recents side panel.
* Add up & down arrow support to the text box for message sending to step
through your sent history.
* Don't display notifications for our own messages.
* Emotes are now formatted correctly in desktop notifications.
* The recents list now differentiates between public & private rooms.
* Fix bug where when switching between rooms the pagination flickered before
the view jumped to the bottom of the screen.
* Add bing word support.
Registration API:
* The registration API has been overhauled to function like the login API. In
practice, this means registration requests must now include the following:
'type':'m.login.password'. See UPGRADE for more information on this.
* The 'user_id' key has been renamed to 'user' to better match the login API.
* There is an additional login type: 'm.login.email.identity'.
* The command client and web client have been updated to reflect these changes.
Changes in synapse 0.2.3 (2014-09-12)
=====================================
Homeserver:
* Fix bug where we stopped sending events to remote home servers if a
user from that home server left, even if there were some still in the
room.
* Fix bugs in the state conflict resolution where it was incorrectly
rejecting events.
Webclient:
* Display room names and topics.
* Allow setting/editing of room names and topics.
* Display information about rooms on the main page.
* Handle ban and kick events in real time.
* VoIP UI and reliability improvements.
* Add glare support for VoIP.
* Improvements to initial startup speed.
* Don't display duplicate join events.
* Local echo of messages.
* Differentiate sending and sent of local echo.
* Various minor bug fixes.
Changes in synapse 0.2.2 (2014-09-06)
=====================================

View File

@@ -1,3 +1,4 @@
recursive-include docs *
recursive-include tests *.py
recursive-include synapse/persistence/schema *.sql
recursive-include synapse/storage/schema *.sql
recursive-include syweb/webclient *

View File

@@ -4,84 +4,47 @@ Introduction
Matrix is an ambitious new ecosystem for open federated Instant Messaging and
VoIP. 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 aliases like ``#matrix:matrix.org`` or
``#test:localhost:8008`` 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)
- Everything in Matrix happens in a room. Rooms are distributed and do not
exist on any single server. Rooms can be located using convenience aliases
like ``#matrix:matrix.org`` or ``#test:localhost:8008``.
- 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
https://somewhere.org/_matrix https://elsewhere.net/_matrix
WARNING
=======
``#matrix:matrix.org`` is the official support room for Matrix, and can be
accessed by the web client at http://matrix.org/alpha or via an IRC bridge at
irc://irc.freenode.net/matrix.
**Synapse is currently in a state of rapid development, and not all features are yet functional.
Critically, some security features are still in development, which means Synapse can *not*
be considered secure or reliable at this point.** For instance:
Synapse is currently in rapid development, but as of version 0.5 we believe it
is sufficiently stable to be run as an internet-facing service for real usage!
- **SSL Certificates used by server-server federation are not yet validated.**
- **Room permissions are not yet enforced on traffic received via federation.**
- **Homeservers do not yet cryptographically sign their events to avoid tampering**
- Default configuration provides open signup to the service from the internet
Despite this, we believe Synapse is more than useful as a way for experimenting and
exploring Synapse, and the missing features will land shortly. **Until then, please do *NOT*
use Synapse for any remotely important or secure communication.**
Quick Start
===========
System requirements:
- POSIX-compliant system (tested on Linux & OSX)
- Python 2.7
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:8008, install synapse with
``python setup.py develop --user`` and then run ``./synctl start`` twice (once to
generate a config; once to actually run) - you will find a webclient running at
http://localhost:8008. Please use a recent Chrome, Safari or Firefox for now...
- To run a **public** homeserver and let it exchange messages with other homeservers
and participate in the global Matrix federation, you must expose port 8448 to the
internet and edit homeserver.yaml to specify server_name (the public DNS entry for
this server) and then run ``synctl start``. If you changed the server_name, you may
need to move the old database (homeserver.db) out of the way first. Then come join
``#matrix:matrix.org`` and say hi! :)
For more detailed setup instructions, please see further down this document.
About Matrix
============
Matrix specifies a set of pragmatic RESTful HTTP JSON APIs as an open standard,
which handle:
- Creating and managing fully distributed chat rooms with no
single points of control or failure
- Eventually-consistent cryptographically secure[1] 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]
- Inviting, joining, leaving, kicking, banning room members
- Managing user accounts (registration, login, logout)
- Using 3rd Party IDs (3PIDs) such as email addresses, phone numbers,
Facebook accounts to authenticate, identify and discover users on Matrix.
- Placing 1:1 VoIP and Video calls
- 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
- Sending and receiving extensible messages in a room with (optional)
end-to-end encryption[1]
- Inviting, joining, leaving, kicking, banning room members
- Managing user accounts (registration, login, logout)
- Using 3rd Party IDs (3PIDs) such as email addresses, phone numbers,
Facebook accounts to authenticate, identify and discover users on Matrix.
- Placing 1:1 VoIP and Video calls
These APIs are intended to be implemented on a wide range of servers, services
and clients, letting developers build messaging and VoIP functionality on top of
the entirely open Matrix ecosystem rather than using closed or proprietary
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.
@@ -96,63 +59,166 @@ In Matrix, every user runs one or more Matrix clients, which connect through to
a Matrix homeserver which stores all their personal chat history and user
account information - much as a mail client connects through to an IMAP/SMTP
server. Just like email, you can either run your own Matrix homeserver and
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.
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).
command line 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.
Meanwhile, iOS and Android SDKs and clients are currently in development and available from:
Thanks for trying Matrix!
- https://github.com/matrix-org/matrix-ios-sdk
- https://github.com/matrix-org/matrix-android-sdk
[1] Cryptographic signing of messages isn't turned on yet
We'd like to invite you to join #matrix:matrix.org (via http://matrix.org/alpha), run a homeserver, take a look at the Matrix spec at
http://matrix.org/docs/spec, experiment with the APIs and the demo
clients, and report any bugs via http://matrix.org/jira.
[2] End-to-end encryption is currently in development
Thanks for using Matrix!
[1] 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.
System requirements:
- POSIX-compliant system (tested on Linux & OSX)
- Python 2.7
Installing prerequisites on ubuntu::
Synapse is written in python but some of the libraries is uses are written in
C. So before we can install synapse itself we need a working C compiler and the
header files for python C extensions.
$ sudo apt-get install build-essential python2.7-dev libffi-dev
Installing prerequisites on Ubuntu or Debian::
Installing prerequisites on Mac OS X::
$ sudo apt-get install build-essential python2.7-dev libffi-dev \
python-pip python-setuptools sqlite3 \
libssl-dev
Installing prerequisites on Mac OS X::
$ xcode-select --install
To install the synapse homeserver run::
$ pip install --user --process-dependency-links https://github.com/matrix-org/synapse/tarball/master
This installs synapse, along with the libraries it uses, into
``$HOME/.local/lib/`` on Linux or ``$HOME/Library/Python/2.7/lib/`` on OSX.
For reliable VoIP calls to be routed via this homeserver, you MUST configure
a TURN server. See docs/turn-howto.rst for details.
Troubleshooting Installation
----------------------------
Synapse requires pip 1.7 or later, so if your OS provides too old a version and
you get errors about ``error: no such option: --process-dependency-links`` you
may need to manually upgrade it::
$ sudo pip install --upgrade pip
If pip crashes mid-installation for reason (e.g. lost terminal), pip may
refuse to run until you remove the temporary installation directory it
created. To reset the installation::
$ rm -rf /tmp/pip_install_matrix
pip seems to leak *lots* of memory during installation. For instance, a Linux
host with 512MB of RAM may run out of memory whilst installing Twisted. If this
happens, you will have to individually install the dependencies which are
failing, e.g.::
$ pip install --user twisted
On OSX, if you encounter clang: error: unknown argument: '-mno-fused-madd' you
will need to export CFLAGS=-Qunused-arguments.
Windows Install
---------------
Synapse can be installed on Cygwin. It requires the following Cygwin packages:
- gcc
- git
- libffi-devel
- openssl (and openssl-devel, python-openssl)
- python
- python-setuptools
The content repository requires additional packages and will be unable to process
uploads without them:
- libjpeg8
- libjpeg8-devel
- zlib
If you choose to install Synapse without these packages, you will need to reinstall
``pillow`` for changes to be applied, e.g. ``pip uninstall pillow`` ``pip install
pillow --user``
Troubleshooting:
- You may need to upgrade ``setuptools`` to get this to work correctly:
``pip install setuptools --upgrade``.
- You may encounter errors indicating that ``ffi.h`` is missing, even with
``libffi-devel`` installed. If you do, copy the ``.h`` files:
``cp /usr/lib/libffi-3.0.13/include/*.h /usr/include``
- You may need to install libsodium from source in order to install PyNacl. If
you do, you may need to create a symlink to ``libsodium.a`` so ``ld`` can find
it: ``ln -s /usr/local/lib/libsodium.a /usr/lib/libsodium.a``
Running Your Homeserver
=======================
To actually run your new homeserver, pick a working directory for Synapse to run
(e.g. ``~/.synapse``), and::
$ mkdir ~/.synapse
$ cd ~/.synapse
$ # on Linux
$ ~/.local/bin/synctl start
$ # on OSX
$ ~/Library/Python/2.7/bin/synctl start
Troubleshooting Running
-----------------------
If ``synctl`` fails with ``pkg_resources.DistributionNotFound`` errors you may
need a newer version of setuptools than that provided by your OS.::
$ sudo pip install setuptools --upgrade
If synapse fails with ``missing "sodium.h"`` crypto errors, you may need
to manually upgrade PyNaCL, as synapse uses NaCl (http://nacl.cr.yp.to/) for
encryption and digital signatures.
Unfortunately PyNACL currently has a few issues
(https://github.com/pyca/pynacl/issues/53) and
(https://github.com/pyca/pynacl/issues/79) that mean it may not install
correctly, causing all tests to fail with errors about missing "sodium.h". To
fix try re-installing from PyPI or directly from
(https://github.com/pyca/pynacl)::
$ # Install from PyPI
$ pip install --user --upgrade --force pynacl
$ # Install from github
$ pip install --user https://github.com/pyca/pynacl/tarball/master
Homeserver Development
======================
To check out a homeserver for development, clone the git repo into a working
directory of your choice::
$ git clone https://github.com/matrix-org/synapse.git
$ cd synapse
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 don't have access to github, then you may need to install ``syutil``
manually by checking it out and running ``python setup.py develop --user`` on it
too.
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).
On OSX, if you encounter ``clang: error: unknown argument: '-mno-fused-madd'`` you will
need to ``export CFLAGS=-Qunused-arguments``.
This will run a process of downloading and installing into your
user's .local/lib directory all of the required dependencies that are
@@ -173,9 +239,14 @@ This should end with a 'PASSED' result::
Upgrading an existing homeserver
================================
Before upgrading an existing homeserver to a new version, please refer to
UPGRADE.rst for any additional instructions.
IMPORTANT: Before upgrading an existing homeserver to a new version, please
refer to UPGRADE.rst for any additional instructions.
Otherwise, simply re-install the new codebase over the current one - e.g.
by ``pip install --user --process-dependency-links
https://github.com/matrix-org/synapse/tarball/master``
if using pip, or by ``git pull`` if running off a git working copy.
Setting up Federation
=====================
@@ -185,27 +256,25 @@ be publicly visible on the internet, and they will need to know its host name.
You have two choices here, which will influence the form of your Matrix user
IDs:
1) Use the machine's own hostname as available on public DNS in the form of its
A or AAAA records. This is easier to set up initially, perhaps for testing,
but lacks the flexibility of SRV.
1) Use the machine's own hostname as available on public DNS in the form of
its A or AAAA records. This is easier to set up initially, perhaps for
testing, but lacks the flexibility of SRV.
2) Set up a SRV record for your domain name. This requires you create a SRV
record in DNS, but gives the flexibility to run the server on your own
choice of TCP port, on a machine that might not be the same name as the
domain name.
2) Set up a SRV record for your domain name. This requires you create a SRV
record in DNS, but gives the flexibility to run the server on your own
choice of TCP port, on a machine that might not be the same name as the
domain name.
For the first form, simply pass the required hostname (of the machine) as the
--host parameter::
--server-name parameter::
$ python synapse/app/homeserver.py \
$ python -m synapse.app.homeserver \
--server-name machine.my.domain.name \
--config-path homeserver.config \
--generate-config
$ python synapse/app/homeserver.py --config-path homeserver.config
Alternatively, you can run synapse via synctl - running ``synctl start`` to generate a
homeserver.yaml config file, where you can then edit server-name to specify
machine.my.domain.name, and then set the actual server running again with synctl start.
$ python -m synapse.app.homeserver --config-path homeserver.config
Alternatively, you can run ``synctl start`` to guide you through the process.
For the second form, first create your SRV record and publish it in DNS. This
needs to be named _matrix._tcp.YOURDOMAIN, and point at at least one hostname
@@ -213,17 +282,19 @@ and port where the server is running. (At the current time synapse does not
support clustering multiple servers into a single logical homeserver). The DNS
record would then look something like::
$ dig -t srv _matrix._tcp.machine.my.domaine.name
_matrix._tcp IN SRV 10 0 8448 machine.my.domain.name.
At this point, you should then run the homeserver with the hostname of this
SRV record, as that is the name other machines will expect it to have::
$ python synapse/app/homeserver.py \
$ python -m synapse.app.homeserver \
--server-name YOURDOMAIN \
--bind-port 8448 \
--config-path homeserver.config \
--generate-config
$ python synapse/app/homeserver.py --config-path homeserver.config
$ python -m synapse.app.homeserver --config-path homeserver.config
You may additionally want to pass one or more "-v" options, in order to
@@ -242,11 +313,13 @@ private federation (``localhost:8080``, ``localhost:8081`` and
http://localhost:8080. Simply run::
$ demo/start.sh
This is mainly useful just for development purposes.
Running The Demo Web Client
===========================
The homeserver runs a web client by default at http://localhost:8080.
The homeserver runs a web client by default at https://localhost:8448/.
If this is the first time you have used the client from that browser (it uses
HTML5 local storage to remember its config), you will need to log in to your
@@ -266,8 +339,8 @@ account. Your name will take the form of::
Specify your desired localpart in the topmost box of the "Register for an
account" form, and click the "Register" button. Hostnames can contain ports if
required due to lack of SRV records (e.g. @matthew:localhost:8080 on an internal
synapse sandbox running on localhost)
required due to lack of SRV records (e.g. @matthew:localhost:8448 on an
internal synapse sandbox running on localhost)
Logging In To An Existing Account
@@ -282,9 +355,9 @@ Identity Servers
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.
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
@@ -293,19 +366,21 @@ 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 are running a single identity server (http://matrix.org:8090) at the current time.
we are running a single identity server (http://matrix.org:8090) at the current
time.
Where's the spec?!
==================
For now, please go spelunking in the ``docs/`` directory to find out.
The source of the matrix spec lives at https://github.com/matrix-org/matrix-doc.
A recent HTML snapshot of this lives at http://matrix.org/docs/spec
Building Internal API Documentation
===================================
Before building internal API documentation install spinx and
Before building internal API documentation install sphinx and
sphinxcontrib-napoleon::
$ pip install sphinx

View File

@@ -1,3 +1,112 @@
Upgrading to v0.6.0
===================
To pull in new dependencies, run::
python setup.py develop --user
This update includes a change to the database schema. To upgrade you first need
to upgrade the database by running::
python scripts/upgrade_db_to_v0.6.0.py <db> <server_name> <signing_key>
Where `<db>` is the location of the database, `<server_name>` is the
server name as specified in the synapse configuration, and `<signing_key>` is
the location of the signing key as specified in the synapse configuration.
This may take some time to complete. Failures of signatures and content hashes
can safely be ignored.
Upgrading to v0.5.1
===================
Depending on precisely when you installed v0.5.0 you may have ended up with
a stale release of the reference matrix webclient installed as a python module.
To uninstall it and ensure you are depending on the latest module, please run::
$ pip uninstall syweb
Upgrading to v0.5.0
===================
The webclient has been split out into a seperate repository/pacakage in this
release. Before you restart your homeserver you will need to pull in the
webclient package by running::
python setup.py develop --user
This release completely changes the database schema and so requires upgrading
it before starting the new version of the homeserver.
The script "database-prepare-for-0.5.0.sh" should be used to upgrade the
database. This will save all user information, such as logins and profiles,
but will otherwise purge the database. This includes messages, which
rooms the home server was a member of and room alias mappings.
If you would like to keep your history, please take a copy of your database
file and ask for help in #matrix:matrix.org. The upgrade process is,
unfortunately, non trivial and requires human intervention to resolve any
resulting conflicts during the upgrade process.
Before running the command the homeserver should be first completely
shutdown. To run it, simply specify the location of the database, e.g.:
./database-prepare-for-0.5.0.sh "homeserver.db"
Once this has successfully completed it will be safe to restart the
homeserver. You may notice that the homeserver takes a few seconds longer to
restart than usual as it reinitializes the database.
On startup of the new version, users can either rejoin remote rooms using room
aliases or by being reinvited. Alternatively, if any other homeserver sends a
message to a room that the homeserver was previously in the local HS will
automatically rejoin the room.
Upgrading to v0.4.0
===================
This release needs an updated syutil version. Run::
python setup.py develop
You will also need to upgrade your configuration as the signing key format has
changed. Run::
python -m synapse.app.homeserver --config-path <CONFIG> --generate-config
Upgrading to v0.3.0
===================
This registration API now closely matches the login API. This introduces a bit
more backwards and forwards between the HS and the client, but this improves
the overall flexibility of the API. You can now GET on /register to retrieve a list
of valid registration flows. Upon choosing one, they are submitted in the same
way as login, e.g::
{
type: m.login.password,
user: foo,
password: bar
}
The default HS supports 2 flows, with and without Identity Server email
authentication. Enabling captcha on the HS will add in an extra step to all
flows: ``m.login.recaptcha`` which must be completed before you can transition
to the next stage. There is a new login type: ``m.login.email.identity`` which
contains the ``threepidCreds`` key which were previously sent in the original
register request. For more information on this, see the specification.
Web Client
----------
The VoIP specification has changed between v0.2.0 and v0.3.0. Users should
refresh any browser tabs to get the latest web client code. Users on
v0.2.0 of the web client will not be able to call those on v0.3.0 and
vice versa.
Upgrading to v0.2.0
===================

View File

@@ -1 +1 @@
0.2.2
0.6.1c

View File

@@ -1,7 +0,0 @@
Broad-sweeping stuff which would be nice to have
================================================
- Additional SQL backends beyond sqlite
- homeserver implementation in go
- homeserver implementation in node.js
- client SDKs

View File

@@ -145,35 +145,50 @@ class SynapseCmd(cmd.Cmd):
<noupdate> : Do not automatically clobber config values.
"""
args = self._parse(line, ["userid", "noupdate"])
path = "/register"
password = None
pwd = None
pwd2 = "_"
while pwd != pwd2:
pwd = getpass.getpass("(Optional) Type a password for this user: ")
if len(pwd) == 0:
print "Not using a password for this user."
break
pwd = getpass.getpass("Type a password for this user: ")
pwd2 = getpass.getpass("Retype the password: ")
if pwd != pwd2:
if pwd != pwd2 or len(pwd) == 0:
print "Password mismatch."
pwd = None
else:
password = pwd
body = {}
body = {
"type": "m.login.password"
}
if "userid" in args:
body["user_id"] = args["userid"]
body["user"] = args["userid"]
if password:
body["password"] = password
reactor.callFromThread(self._do_register, "POST", path, body,
reactor.callFromThread(self._do_register, body,
"noupdate" not in args)
@defer.inlineCallbacks
def _do_register(self, method, path, data, update_config):
url = self._url() + path
json_res = yield self.http_client.do_request(method, url, data=data)
def _do_register(self, data, update_config):
# check the registration flows
url = self._url() + "/register"
json_res = yield self.http_client.do_request("GET", url)
print json.dumps(json_res, indent=4)
passwordFlow = None
for flow in json_res["flows"]:
if flow["type"] == "m.login.recaptcha" or ("stages" in flow and "m.login.recaptcha" in flow["stages"]):
print "Unable to register: Home server requires captcha."
return
if flow["type"] == "m.login.password" and "stages" not in flow:
passwordFlow = flow
break
if not passwordFlow:
return
json_res = yield self.http_client.do_request("POST", url, data=data)
print json.dumps(json_res, indent=4)
if update_config and "user_id" in json_res:
self.config["user"] = json_res["user_id"]

View File

@@ -0,0 +1,260 @@
#!/usr/bin/env python
"""
This is an attempt at bridging matrix clients into a Jitis meet room via Matrix
video call. It uses hard-coded xml strings overg XMPP BOSH. It can display one
of the streams from the Jitsi bridge until the second lot of SDP comes down and
we set the remote SDP at which point the stream ends. Our video never gets to
the bridge.
Requires:
npm install jquery jsdom
"""
import gevent
import grequests
from BeautifulSoup import BeautifulSoup
import json
import urllib
import subprocess
import time
#ACCESS_TOKEN="" #
MATRIXBASE = 'https://matrix.org/_matrix/client/api/v1/'
MYUSERNAME = '@davetest:matrix.org'
HTTPBIND = 'https://meet.jit.si/http-bind'
#HTTPBIND = 'https://jitsi.vuc.me/http-bind'
#ROOMNAME = "matrix"
ROOMNAME = "pibble"
HOST="guest.jit.si"
#HOST="jitsi.vuc.me"
TURNSERVER="turn.guest.jit.si"
#TURNSERVER="turn.jitsi.vuc.me"
ROOMDOMAIN="meet.jit.si"
#ROOMDOMAIN="conference.jitsi.vuc.me"
class TrivialMatrixClient:
def __init__(self, access_token):
self.token = None
self.access_token = access_token
def getEvent(self):
while True:
url = MATRIXBASE+'events?access_token='+self.access_token+"&timeout=60000"
if self.token:
url += "&from="+self.token
req = grequests.get(url)
resps = grequests.map([req])
obj = json.loads(resps[0].content)
print "incoming from matrix",obj
if 'end' not in obj:
continue
self.token = obj['end']
if len(obj['chunk']):
return obj['chunk'][0]
def joinRoom(self, roomId):
url = MATRIXBASE+'rooms/'+roomId+'/join?access_token='+self.access_token
print url
headers={ 'Content-Type': 'application/json' }
req = grequests.post(url, headers=headers, data='{}')
resps = grequests.map([req])
obj = json.loads(resps[0].content)
print "response: ",obj
def sendEvent(self, roomId, evType, event):
url = MATRIXBASE+'rooms/'+roomId+'/send/'+evType+'?access_token='+self.access_token
print url
print json.dumps(event)
headers={ 'Content-Type': 'application/json' }
req = grequests.post(url, headers=headers, data=json.dumps(event))
resps = grequests.map([req])
obj = json.loads(resps[0].content)
print "response: ",obj
xmppClients = {}
def matrixLoop():
while True:
ev = matrixCli.getEvent()
print ev
if ev['type'] == 'm.room.member':
print 'membership event'
if ev['membership'] == 'invite' and ev['state_key'] == MYUSERNAME:
roomId = ev['room_id']
print "joining room %s" % (roomId)
matrixCli.joinRoom(roomId)
elif ev['type'] == 'm.room.message':
if ev['room_id'] in xmppClients:
print "already have a bridge for that user, ignoring"
continue
print "got message, connecting"
xmppClients[ev['room_id']] = TrivialXmppClient(ev['room_id'], ev['user_id'])
gevent.spawn(xmppClients[ev['room_id']].xmppLoop)
elif ev['type'] == 'm.call.invite':
print "Incoming call"
#sdp = ev['content']['offer']['sdp']
#print "sdp: %s" % (sdp)
#xmppClients[ev['room_id']] = TrivialXmppClient(ev['room_id'], ev['user_id'])
#gevent.spawn(xmppClients[ev['room_id']].xmppLoop)
elif ev['type'] == 'm.call.answer':
print "Call answered"
sdp = ev['content']['answer']['sdp']
if ev['room_id'] not in xmppClients:
print "We didn't have a call for that room"
continue
# should probably check call ID too
xmppCli = xmppClients[ev['room_id']]
xmppCli.sendAnswer(sdp)
elif ev['type'] == 'm.call.hangup':
if ev['room_id'] in xmppClients:
xmppClients[ev['room_id']].stop()
del xmppClients[ev['room_id']]
class TrivialXmppClient:
def __init__(self, matrixRoom, userId):
self.rid = 0
self.matrixRoom = matrixRoom
self.userId = userId
self.running = True
def stop(self):
self.running = False
def nextRid(self):
self.rid += 1
return '%d' % (self.rid)
def sendIq(self, xml):
fullXml = "<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' sid='%s'>%s</body>" % (self.nextRid(), self.sid, xml)
#print "\t>>>%s" % (fullXml)
return self.xmppPoke(fullXml)
def xmppPoke(self, xml):
headers = {'Content-Type': 'application/xml'}
req = grequests.post(HTTPBIND, verify=False, headers=headers, data=xml)
resps = grequests.map([req])
obj = BeautifulSoup(resps[0].content)
return obj
def sendAnswer(self, answer):
print "sdp from matrix client",answer
p = subprocess.Popen(['node', 'unjingle/unjingle.js', '--sdp'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
jingle, out_err = p.communicate(answer)
jingle = jingle % {
'tojid': self.callfrom,
'action': 'session-accept',
'initiator': self.callfrom,
'responder': self.jid,
'sid': self.callsid
}
print "answer jingle from sdp",jingle
res = self.sendIq(jingle)
print "reply from answer: ",res
self.ssrcs = {}
jingleSoup = BeautifulSoup(jingle)
for cont in jingleSoup.iq.jingle.findAll('content'):
if cont.description:
self.ssrcs[cont['name']] = cont.description['ssrc']
print "my ssrcs:",self.ssrcs
gevent.joinall([
gevent.spawn(self.advertiseSsrcs)
])
def advertiseSsrcs(self):
time.sleep(7)
print "SSRC spammer started"
while self.running:
ssrcMsg = "<presence to='%(tojid)s' xmlns='jabber:client'><x xmlns='http://jabber.org/protocol/muc'/><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://jitsi.org/jitsimeet' ver='0WkSdhFnAUxrz4ImQQLdB80GFlE='/><nick xmlns='http://jabber.org/protocol/nick'>%(nick)s</nick><stats xmlns='http://jitsi.org/jitmeet/stats'><stat name='bitrate_download' value='175'/><stat name='bitrate_upload' value='176'/><stat name='packetLoss_total' value='0'/><stat name='packetLoss_download' value='0'/><stat name='packetLoss_upload' value='0'/></stats><media xmlns='http://estos.de/ns/mjs'><source type='audio' ssrc='%(assrc)s' direction='sendre'/><source type='video' ssrc='%(vssrc)s' direction='sendre'/></media></presence>" % { 'tojid': "%s@%s/%s" % (ROOMNAME, ROOMDOMAIN, self.shortJid), 'nick': self.userId, 'assrc': self.ssrcs['audio'], 'vssrc': self.ssrcs['video'] }
res = self.sendIq(ssrcMsg)
print "reply from ssrc announce: ",res
time.sleep(10)
def xmppLoop(self):
self.matrixCallId = time.time()
res = self.xmppPoke("<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' to='%s' xml:lang='en' wait='60' hold='1' content='text/xml; charset=utf-8' ver='1.6' xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh'/>" % (self.nextRid(), HOST))
print res
self.sid = res.body['sid']
print "sid %s" % (self.sid)
res = self.sendIq("<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>")
res = self.xmppPoke("<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' sid='%s' to='%s' xml:lang='en' xmpp:restart='true' xmlns:xmpp='urn:xmpp:xbosh'/>" % (self.nextRid(), self.sid, HOST))
res = self.sendIq("<iq type='set' id='_bind_auth_2' xmlns='jabber:client'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>")
print res
self.jid = res.body.iq.bind.jid.string
print "jid: %s" % (self.jid)
self.shortJid = self.jid.split('-')[0]
res = self.sendIq("<iq type='set' id='_session_auth_2' xmlns='jabber:client'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>")
#randomthing = res.body.iq['to']
#whatsitpart = randomthing.split('-')[0]
#print "other random bind thing: %s" % (randomthing)
# advertise preence to the jitsi room, with our nick
res = self.sendIq("<iq type='get' to='%s' xmlns='jabber:client' id='1:sendIQ'><services xmlns='urn:xmpp:extdisco:1'><service host='%s'/></services></iq><presence to='%s@%s/d98f6c40' xmlns='jabber:client'><x xmlns='http://jabber.org/protocol/muc'/><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://jitsi.org/jitsimeet' ver='0WkSdhFnAUxrz4ImQQLdB80GFlE='/><nick xmlns='http://jabber.org/protocol/nick'>%s</nick></presence>" % (HOST, TURNSERVER, ROOMNAME, ROOMDOMAIN, self.userId))
self.muc = {'users': []}
for p in res.body.findAll('presence'):
u = {}
u['shortJid'] = p['from'].split('/')[1]
if p.c and p.c.nick:
u['nick'] = p.c.nick.string
self.muc['users'].append(u)
print "muc: ",self.muc
# wait for stuff
while True:
print "waiting..."
res = self.sendIq("")
print "got from stream: ",res
if res.body.iq:
jingles = res.body.iq.findAll('jingle')
if len(jingles):
self.callfrom = res.body.iq['from']
self.handleInvite(jingles[0])
elif 'type' in res.body and res.body['type'] == 'terminate':
self.running = False
del xmppClients[self.matrixRoom]
return
def handleInvite(self, jingle):
self.initiator = jingle['initiator']
self.callsid = jingle['sid']
p = subprocess.Popen(['node', 'unjingle/unjingle.js', '--jingle'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
print "raw jingle invite",str(jingle)
sdp, out_err = p.communicate(str(jingle))
print "transformed remote offer sdp",sdp
inviteEvent = {
'offer': {
'type': 'offer',
'sdp': sdp
},
'call_id': self.matrixCallId,
'version': 0,
'lifetime': 30000
}
matrixCli.sendEvent(self.matrixRoom, 'm.call.invite', inviteEvent)
matrixCli = TrivialMatrixClient(ACCESS_TOKEN)
gevent.joinall([
gevent.spawn(matrixLoop)
])

View File

@@ -0,0 +1,188 @@
diff --git a/syweb/webclient/app/components/matrix/matrix-call.js b/syweb/webclient/app/components/matrix/matrix-call.js
index 9fbfff0..dc68077 100644
--- a/syweb/webclient/app/components/matrix/matrix-call.js
+++ b/syweb/webclient/app/components/matrix/matrix-call.js
@@ -16,6 +16,45 @@ limitations under the License.
'use strict';
+
+function sendKeyframe(pc) {
+ console.log('sendkeyframe', pc.iceConnectionState);
+ if (pc.iceConnectionState !== 'connected') return; // safe...
+ pc.setRemoteDescription(
+ pc.remoteDescription,
+ function () {
+ pc.createAnswer(
+ function (modifiedAnswer) {
+ pc.setLocalDescription(
+ modifiedAnswer,
+ function () {
+ // noop
+ },
+ function (error) {
+ console.log('triggerKeyframe setLocalDescription failed', error);
+ messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('triggerKeyframe createAnswer failed', error);
+ messageHandler.showError();
+ }
+ );
+ },
+ function (error) {
+ console.log('triggerKeyframe setRemoteDescription failed', error);
+ messageHandler.showError();
+ }
+ );
+}
+
+
+
+
+
+
+
var forAllVideoTracksOnStream = function(s, f) {
var tracks = s.getVideoTracks();
for (var i = 0; i < tracks.length; i++) {
@@ -83,7 +122,7 @@ angular.module('MatrixCall', [])
}
// FIXME: we should prevent any calls from being placed or accepted before this has finished
- MatrixCall.getTurnServer();
+ //MatrixCall.getTurnServer();
MatrixCall.CALL_TIMEOUT = 60000;
MatrixCall.FALLBACK_STUN_SERVER = 'stun:stun.l.google.com:19302';
@@ -132,6 +171,22 @@ angular.module('MatrixCall', [])
pc.onsignalingstatechange = function() { self.onSignallingStateChanged(); };
pc.onicecandidate = function(c) { self.gotLocalIceCandidate(c); };
pc.onaddstream = function(s) { self.onAddStream(s); };
+
+ var datachan = pc.createDataChannel('RTCDataChannel', {
+ reliable: false
+ });
+ console.log("data chan: "+datachan);
+ datachan.onopen = function() {
+ console.log("data channel open");
+ };
+ datachan.onmessage = function() {
+ console.log("data channel message");
+ };
+ pc.ondatachannel = function(event) {
+ console.log("have data channel");
+ event.channel.binaryType = 'blob';
+ };
+
return pc;
}
@@ -200,6 +255,12 @@ angular.module('MatrixCall', [])
}, this.msg.lifetime - event.age);
};
+ MatrixCall.prototype.receivedInvite = function(event) {
+ console.log("Got second invite for call "+this.call_id);
+ this.peerConn.setRemoteDescription(new RTCSessionDescription(this.msg.offer), this.onSetRemoteDescriptionSuccess, this.onSetRemoteDescriptionError);
+ };
+
+
// perverse as it may seem, sometimes we want to instantiate a call with a hangup message
// (because when getting the state of the room on load, events come in reverse order and
// we want to remember that a call has been hung up)
@@ -349,7 +410,7 @@ angular.module('MatrixCall', [])
'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': this.type == 'video'
- },
+ }
};
this.peerConn.createAnswer(function(d) { self.createdAnswer(d); }, function(e) {}, constraints);
// This can't be in an apply() because it's called by a predecessor call under glare conditions :(
@@ -359,8 +420,20 @@ angular.module('MatrixCall', [])
MatrixCall.prototype.gotLocalIceCandidate = function(event) {
if (event.candidate) {
console.log("Got local ICE "+event.candidate.sdpMid+" candidate: "+event.candidate.candidate);
- this.sendCandidate(event.candidate);
- }
+ //this.sendCandidate(event.candidate);
+ } else {
+ console.log("have all candidates, sending answer");
+ var content = {
+ version: 0,
+ call_id: this.call_id,
+ answer: this.peerConn.localDescription
+ };
+ this.sendEventWithRetry('m.call.answer', content);
+ var self = this;
+ $rootScope.$apply(function() {
+ self.state = 'connecting';
+ });
+ }
}
MatrixCall.prototype.gotRemoteIceCandidate = function(cand) {
@@ -418,15 +491,6 @@ angular.module('MatrixCall', [])
console.log("Created answer: "+description);
var self = this;
this.peerConn.setLocalDescription(description, function() {
- var content = {
- version: 0,
- call_id: self.call_id,
- answer: self.peerConn.localDescription
- };
- self.sendEventWithRetry('m.call.answer', content);
- $rootScope.$apply(function() {
- self.state = 'connecting';
- });
}, function() { console.log("Error setting local description!"); } );
};
@@ -448,6 +512,9 @@ angular.module('MatrixCall', [])
$rootScope.$apply(function() {
self.state = 'connected';
self.didConnect = true;
+ /*$timeout(function() {
+ sendKeyframe(self.peerConn);
+ }, 1000);*/
});
} else if (this.peerConn.iceConnectionState == 'failed') {
this.hangup('ice_failed');
@@ -518,6 +585,7 @@ angular.module('MatrixCall', [])
MatrixCall.prototype.onRemoteStreamEnded = function(event) {
console.log("Remote stream ended");
+ return;
var self = this;
$rootScope.$apply(function() {
self.state = 'ended';
diff --git a/syweb/webclient/app/components/matrix/matrix-phone-service.js b/syweb/webclient/app/components/matrix/matrix-phone-service.js
index 55dbbf5..272fa27 100644
--- a/syweb/webclient/app/components/matrix/matrix-phone-service.js
+++ b/syweb/webclient/app/components/matrix/matrix-phone-service.js
@@ -48,6 +48,13 @@ angular.module('matrixPhoneService', [])
return;
}
+ // do we already have an entry for this call ID?
+ var existingEntry = matrixPhoneService.allCalls[msg.call_id];
+ if (existingEntry) {
+ existingEntry.receivedInvite(msg);
+ return;
+ }
+
var call = undefined;
if (!isLive) {
// if this event wasn't live then this call may already be over
@@ -108,7 +115,7 @@ angular.module('matrixPhoneService', [])
call.hangup();
}
} else {
- $rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call);
+ $rootScope.$broadcast(matrixPhoneService.INCOMING_CALL_EVENT, call);
}
} else if (event.type == 'm.call.answer') {
var call = matrixPhoneService.allCalls[msg.call_id];

View File

@@ -0,0 +1,712 @@
/* jshint -W117 */
// SDP STUFF
function SDP(sdp) {
this.media = sdp.split('\r\nm=');
for (var i = 1; i < this.media.length; i++) {
this.media[i] = 'm=' + this.media[i];
if (i != this.media.length - 1) {
this.media[i] += '\r\n';
}
}
this.session = this.media.shift() + '\r\n';
this.raw = this.session + this.media.join('');
}
exports.SDP = SDP;
var jsdom = require("jsdom");
var window = jsdom.jsdom().parentWindow;
var $ = require('jquery')(window);
var SDPUtil = require('./strophe.jingle.sdp.util.js').SDPUtil;
/**
* Returns map of MediaChannel mapped per channel idx.
*/
SDP.prototype.getMediaSsrcMap = function() {
var self = this;
var media_ssrcs = {};
for (channelNum = 0; channelNum < self.media.length; channelNum++) {
modified = true;
tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc:');
var type = SDPUtil.parse_mid(SDPUtil.find_line(self.media[channelNum], 'a=mid:'));
var channel = new MediaChannel(channelNum, type);
media_ssrcs[channelNum] = channel;
tmp.forEach(function (line) {
var linessrc = line.substring(7).split(' ')[0];
// allocate new ChannelSsrc
if(!channel.ssrcs[linessrc]) {
channel.ssrcs[linessrc] = new ChannelSsrc(linessrc, type);
}
channel.ssrcs[linessrc].lines.push(line);
});
tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc-group:');
tmp.forEach(function(line){
var semantics = line.substr(0, idx).substr(13);
var ssrcs = line.substr(14 + semantics.length).split(' ');
if (ssrcs.length != 0) {
var ssrcGroup = new ChannelSsrcGroup(semantics, ssrcs);
channel.ssrcGroups.push(ssrcGroup);
}
});
}
return media_ssrcs;
};
/**
* Returns <tt>true</tt> if this SDP contains given SSRC.
* @param ssrc the ssrc to check.
* @returns {boolean} <tt>true</tt> if this SDP contains given SSRC.
*/
SDP.prototype.containsSSRC = function(ssrc) {
var channels = this.getMediaSsrcMap();
var contains = false;
Object.keys(channels).forEach(function(chNumber){
var channel = channels[chNumber];
//console.log("Check", channel, ssrc);
if(Object.keys(channel.ssrcs).indexOf(ssrc) != -1){
contains = true;
}
});
return contains;
};
/**
* Returns map of MediaChannel that contains only media not contained in <tt>otherSdp</tt>. Mapped by channel idx.
* @param otherSdp the other SDP to check ssrc with.
*/
SDP.prototype.getNewMedia = function(otherSdp) {
// this could be useful in Array.prototype.
function arrayEquals(array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
}
var myMedia = this.getMediaSsrcMap();
var othersMedia = otherSdp.getMediaSsrcMap();
var newMedia = {};
Object.keys(othersMedia).forEach(function(channelNum) {
var myChannel = myMedia[channelNum];
var othersChannel = othersMedia[channelNum];
if(!myChannel && othersChannel) {
// Add whole channel
newMedia[channelNum] = othersChannel;
return;
}
// Look for new ssrcs accross the channel
Object.keys(othersChannel.ssrcs).forEach(function(ssrc) {
if(Object.keys(myChannel.ssrcs).indexOf(ssrc) === -1) {
// Allocate channel if we've found ssrc that doesn't exist in our channel
if(!newMedia[channelNum]){
newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType);
}
newMedia[channelNum].ssrcs[ssrc] = othersChannel.ssrcs[ssrc];
}
});
// Look for new ssrc groups across the channels
othersChannel.ssrcGroups.forEach(function(otherSsrcGroup){
// try to match the other ssrc-group with an ssrc-group of ours
var matched = false;
for (var i = 0; i < myChannel.ssrcGroups.length; i++) {
var mySsrcGroup = myChannel.ssrcGroups[i];
if (otherSsrcGroup.semantics == mySsrcGroup.semantics
&& arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
matched = true;
break;
}
}
if (!matched) {
// Allocate channel if we've found an ssrc-group that doesn't
// exist in our channel
if(!newMedia[channelNum]){
newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType);
}
newMedia[channelNum].ssrcGroups.push(otherSsrcGroup);
}
});
});
return newMedia;
};
// remove iSAC and CN from SDP
SDP.prototype.mangle = function () {
var i, j, mline, lines, rtpmap, newdesc;
for (i = 0; i < this.media.length; i++) {
lines = this.media[i].split('\r\n');
lines.pop(); // remove empty last element
mline = SDPUtil.parse_mline(lines.shift());
if (mline.media != 'audio')
continue;
newdesc = '';
mline.fmt.length = 0;
for (j = 0; j < lines.length; j++) {
if (lines[j].substr(0, 9) == 'a=rtpmap:') {
rtpmap = SDPUtil.parse_rtpmap(lines[j]);
if (rtpmap.name == 'CN' || rtpmap.name == 'ISAC')
continue;
mline.fmt.push(rtpmap.id);
newdesc += lines[j] + '\r\n';
} else {
newdesc += lines[j] + '\r\n';
}
}
this.media[i] = SDPUtil.build_mline(mline) + '\r\n';
this.media[i] += newdesc;
}
this.raw = this.session + this.media.join('');
};
// remove lines matching prefix from session section
SDP.prototype.removeSessionLines = function(prefix) {
var self = this;
var lines = SDPUtil.find_lines(this.session, prefix);
lines.forEach(function(line) {
self.session = self.session.replace(line + '\r\n', '');
});
this.raw = this.session + this.media.join('');
return lines;
}
// remove lines matching prefix from a media section specified by mediaindex
// TODO: non-numeric mediaindex could match mid
SDP.prototype.removeMediaLines = function(mediaindex, prefix) {
var self = this;
var lines = SDPUtil.find_lines(this.media[mediaindex], prefix);
lines.forEach(function(line) {
self.media[mediaindex] = self.media[mediaindex].replace(line + '\r\n', '');
});
this.raw = this.session + this.media.join('');
return lines;
}
// add content's to a jingle element
SDP.prototype.toJingle = function (elem, thecreator) {
var i, j, k, mline, ssrc, rtpmap, tmp, line, lines;
var self = this;
// new bundle plan
if (SDPUtil.find_line(this.session, 'a=group:')) {
lines = SDPUtil.find_lines(this.session, 'a=group:');
for (i = 0; i < lines.length; i++) {
tmp = lines[i].split(' ');
var semantics = tmp.shift().substr(8);
elem.c('group', {xmlns: 'urn:xmpp:jingle:apps:grouping:0', semantics:semantics});
for (j = 0; j < tmp.length; j++) {
elem.c('content', {name: tmp[j]}).up();
}
elem.up();
}
}
// old bundle plan, to be removed
var bundle = [];
if (SDPUtil.find_line(this.session, 'a=group:BUNDLE')) {
bundle = SDPUtil.find_line(this.session, 'a=group:BUNDLE ').split(' ');
bundle.shift();
}
for (i = 0; i < this.media.length; i++) {
mline = SDPUtil.parse_mline(this.media[i].split('\r\n')[0]);
if (!(mline.media === 'audio' ||
mline.media === 'video' ||
mline.media === 'application'))
{
continue;
}
if (SDPUtil.find_line(this.media[i], 'a=ssrc:')) {
ssrc = SDPUtil.find_line(this.media[i], 'a=ssrc:').substring(7).split(' ')[0]; // take the first
} else {
ssrc = false;
}
elem.c('content', {creator: thecreator, name: mline.media});
if (SDPUtil.find_line(this.media[i], 'a=mid:')) {
// prefer identifier from a=mid if present
var mid = SDPUtil.parse_mid(SDPUtil.find_line(this.media[i], 'a=mid:'));
elem.attrs({ name: mid });
// old BUNDLE plan, to be removed
if (bundle.indexOf(mid) !== -1) {
elem.c('bundle', {xmlns: 'http://estos.de/ns/bundle'}).up();
bundle.splice(bundle.indexOf(mid), 1);
}
}
if (SDPUtil.find_line(this.media[i], 'a=rtpmap:').length)
{
elem.c('description',
{xmlns: 'urn:xmpp:jingle:apps:rtp:1',
media: mline.media });
if (ssrc) {
elem.attrs({ssrc: ssrc});
}
for (j = 0; j < mline.fmt.length; j++) {
rtpmap = SDPUtil.find_line(this.media[i], 'a=rtpmap:' + mline.fmt[j]);
elem.c('payload-type', SDPUtil.parse_rtpmap(rtpmap));
// put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
if (SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j])) {
tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(this.media[i], 'a=fmtp:' + mline.fmt[j]));
for (k = 0; k < tmp.length; k++) {
elem.c('parameter', tmp[k]).up();
}
}
this.RtcpFbToJingle(i, elem, mline.fmt[j]); // XEP-0293 -- map a=rtcp-fb
elem.up();
}
if (SDPUtil.find_line(this.media[i], 'a=crypto:', this.session)) {
elem.c('encryption', {required: 1});
var crypto = SDPUtil.find_lines(this.media[i], 'a=crypto:', this.session);
crypto.forEach(function(line) {
elem.c('crypto', SDPUtil.parse_crypto(line)).up();
});
elem.up(); // end of encryption
}
if (ssrc) {
// new style mapping
elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
// FIXME: group by ssrc and support multiple different ssrcs
var ssrclines = SDPUtil.find_lines(this.media[i], 'a=ssrc:');
ssrclines.forEach(function(line) {
idx = line.indexOf(' ');
var linessrc = line.substr(0, idx).substr(7);
if (linessrc != ssrc) {
elem.up();
ssrc = linessrc;
elem.c('source', { ssrc: ssrc, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
}
var kv = line.substr(idx + 1);
elem.c('parameter');
if (kv.indexOf(':') == -1) {
elem.attrs({ name: kv });
} else {
elem.attrs({ name: kv.split(':', 2)[0] });
elem.attrs({ value: kv.split(':', 2)[1] });
}
elem.up();
});
elem.up();
// old proprietary mapping, to be removed at some point
tmp = SDPUtil.parse_ssrc(this.media[i]);
tmp.xmlns = 'http://estos.de/ns/ssrc';
tmp.ssrc = ssrc;
elem.c('ssrc', tmp).up(); // ssrc is part of description
// XEP-0339 handle ssrc-group attributes
var ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
ssrc_group_lines.forEach(function(line) {
idx = line.indexOf(' ');
var semantics = line.substr(0, idx).substr(13);
var ssrcs = line.substr(14 + semantics.length).split(' ');
if (ssrcs.length != 0) {
elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
ssrcs.forEach(function(ssrc) {
elem.c('source', { ssrc: ssrc })
.up();
});
elem.up();
}
});
}
if (SDPUtil.find_line(this.media[i], 'a=rtcp-mux')) {
elem.c('rtcp-mux').up();
}
// XEP-0293 -- map a=rtcp-fb:*
this.RtcpFbToJingle(i, elem, '*');
// XEP-0294
if (SDPUtil.find_line(this.media[i], 'a=extmap:')) {
lines = SDPUtil.find_lines(this.media[i], 'a=extmap:');
for (j = 0; j < lines.length; j++) {
tmp = SDPUtil.parse_extmap(lines[j]);
elem.c('rtp-hdrext', { xmlns: 'urn:xmpp:jingle:apps:rtp:rtp-hdrext:0',
uri: tmp.uri,
id: tmp.value });
if (tmp.hasOwnProperty('direction')) {
switch (tmp.direction) {
case 'sendonly':
elem.attrs({senders: 'responder'});
break;
case 'recvonly':
elem.attrs({senders: 'initiator'});
break;
case 'sendrecv':
elem.attrs({senders: 'both'});
break;
case 'inactive':
elem.attrs({senders: 'none'});
break;
}
}
// TODO: handle params
elem.up();
}
}
elem.up(); // end of description
}
// map ice-ufrag/pwd, dtls fingerprint, candidates
this.TransportToJingle(i, elem);
if (SDPUtil.find_line(this.media[i], 'a=sendrecv', this.session)) {
elem.attrs({senders: 'both'});
} else if (SDPUtil.find_line(this.media[i], 'a=sendonly', this.session)) {
elem.attrs({senders: 'initiator'});
} else if (SDPUtil.find_line(this.media[i], 'a=recvonly', this.session)) {
elem.attrs({senders: 'responder'});
} else if (SDPUtil.find_line(this.media[i], 'a=inactive', this.session)) {
elem.attrs({senders: 'none'});
}
if (mline.port == '0') {
// estos hack to reject an m-line
elem.attrs({senders: 'rejected'});
}
elem.up(); // end of content
}
elem.up();
return elem;
};
SDP.prototype.TransportToJingle = function (mediaindex, elem) {
var i = mediaindex;
var tmp;
var self = this;
elem.c('transport');
// XEP-0343 DTLS/SCTP
if (SDPUtil.find_line(this.media[mediaindex], 'a=sctpmap:').length)
{
var sctpmap = SDPUtil.find_line(
this.media[i], 'a=sctpmap:', self.session);
if (sctpmap)
{
var sctpAttrs = SDPUtil.parse_sctpmap(sctpmap);
elem.c('sctpmap',
{
xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',
number: sctpAttrs[0], /* SCTP port */
protocol: sctpAttrs[1], /* protocol */
});
// Optional stream count attribute
if (sctpAttrs.length > 2)
elem.attrs({ streams: sctpAttrs[2]});
elem.up();
}
}
// XEP-0320
var fingerprints = SDPUtil.find_lines(this.media[mediaindex], 'a=fingerprint:', this.session);
fingerprints.forEach(function(line) {
tmp = SDPUtil.parse_fingerprint(line);
tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
elem.c('fingerprint').t(tmp.fingerprint);
delete tmp.fingerprint;
line = SDPUtil.find_line(self.media[mediaindex], 'a=setup:', self.session);
if (line) {
tmp.setup = line.substr(8);
}
elem.attrs(tmp);
elem.up(); // end of fingerprint
});
tmp = SDPUtil.iceparams(this.media[mediaindex], this.session);
if (tmp) {
tmp.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';
elem.attrs(tmp);
// XEP-0176
if (SDPUtil.find_line(this.media[mediaindex], 'a=candidate:', this.session)) { // add any a=candidate lines
var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=candidate:', this.session);
lines.forEach(function (line) {
elem.c('candidate', SDPUtil.candidateToJingle(line)).up();
});
}
}
elem.up(); // end of transport
}
SDP.prototype.RtcpFbToJingle = function (mediaindex, elem, payloadtype) { // XEP-0293
var lines = SDPUtil.find_lines(this.media[mediaindex], 'a=rtcp-fb:' + payloadtype);
lines.forEach(function (line) {
var tmp = SDPUtil.parse_rtcpfb(line);
if (tmp.type == 'trr-int') {
elem.c('rtcp-fb-trr-int', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', value: tmp.params[0]});
elem.up();
} else {
elem.c('rtcp-fb', {xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0', type: tmp.type});
if (tmp.params.length > 0) {
elem.attrs({'subtype': tmp.params[0]});
}
elem.up();
}
});
};
SDP.prototype.RtcpFbFromJingle = function (elem, payloadtype) { // XEP-0293
var media = '';
var tmp = elem.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
if (tmp.length) {
media += 'a=rtcp-fb:' + '*' + ' ' + 'trr-int' + ' ';
if (tmp.attr('value')) {
media += tmp.attr('value');
} else {
media += '0';
}
media += '\r\n';
}
tmp = elem.find('>rtcp-fb[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');
tmp.each(function () {
media += 'a=rtcp-fb:' + payloadtype + ' ' + $(this).attr('type');
if ($(this).attr('subtype')) {
media += ' ' + $(this).attr('subtype');
}
media += '\r\n';
});
return media;
};
// construct an SDP from a jingle stanza
SDP.prototype.fromJingle = function (jingle) {
var self = this;
this.raw = 'v=0\r\n' +
'o=- ' + '1923518516' + ' 2 IN IP4 0.0.0.0\r\n' +// FIXME
's=-\r\n' +
't=0 0\r\n';
// http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04#section-8
if ($(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').length) {
$(jingle).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]').each(function (idx, group) {
var contents = $(group).find('>content').map(function (idx, content) {
return content.getAttribute('name');
}).get();
if (contents.length > 0) {
self.raw += 'a=group:' + (group.getAttribute('semantics') || group.getAttribute('type')) + ' ' + contents.join(' ') + '\r\n';
}
});
} else if ($(jingle).find('>group[xmlns="urn:ietf:rfc:5888"]').length) {
// temporary namespace, not to be used. to be removed soon.
$(jingle).find('>group[xmlns="urn:ietf:rfc:5888"]').each(function (idx, group) {
var contents = $(group).find('>content').map(function (idx, content) {
return content.getAttribute('name');
}).get();
if (group.getAttribute('type') !== null && contents.length > 0) {
self.raw += 'a=group:' + group.getAttribute('type') + ' ' + contents.join(' ') + '\r\n';
}
});
} else {
// for backward compability, to be removed soon
// assume all contents are in the same bundle group, can be improved upon later
var bundle = $(jingle).find('>content').filter(function (idx, content) {
//elem.c('bundle', {xmlns:'http://estos.de/ns/bundle'});
return $(content).find('>bundle').length > 0;
}).map(function (idx, content) {
return content.getAttribute('name');
}).get();
if (bundle.length) {
this.raw += 'a=group:BUNDLE ' + bundle.join(' ') + '\r\n';
}
}
this.session = this.raw;
jingle.find('>content').each(function () {
var m = self.jingle2media($(this));
self.media.push(m);
});
// reconstruct msid-semantic -- apparently not necessary
/*
var msid = SDPUtil.parse_ssrc(this.raw);
if (msid.hasOwnProperty('mslabel')) {
this.session += "a=msid-semantic: WMS " + msid.mslabel + "\r\n";
}
*/
this.raw = this.session + this.media.join('');
};
// translate a jingle content element into an an SDP media part
SDP.prototype.jingle2media = function (content) {
var media = '',
desc = content.find('description'),
ssrc = desc.attr('ssrc'),
self = this,
tmp;
var sctp = content.find(
'>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]');
tmp = { media: desc.attr('media') };
tmp.port = '1';
if (content.attr('senders') == 'rejected') {
// estos hack to reject an m-line.
tmp.port = '0';
}
if (content.find('>transport>fingerprint').length || desc.find('encryption').length) {
if (sctp.length)
tmp.proto = 'DTLS/SCTP';
else
tmp.proto = 'RTP/SAVPF';
} else {
tmp.proto = 'RTP/AVPF';
}
if (!sctp.length)
{
tmp.fmt = desc.find('payload-type').map(
function () { return this.getAttribute('id'); }).get();
media += SDPUtil.build_mline(tmp) + '\r\n';
}
else
{
media += 'm=application 1 DTLS/SCTP ' + sctp.attr('number') + '\r\n';
media += 'a=sctpmap:' + sctp.attr('number') +
' ' + sctp.attr('protocol');
var streamCount = sctp.attr('streams');
if (streamCount)
media += ' ' + streamCount + '\r\n';
else
media += '\r\n';
}
media += 'c=IN IP4 0.0.0.0\r\n';
if (!sctp.length)
media += 'a=rtcp:1 IN IP4 0.0.0.0\r\n';
//tmp = content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
tmp = content.find('>bundle>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]');
//console.log('transports: '+content.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]').length);
//console.log('bundle.transports: '+content.find('>bundle>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]').length);
//console.log("tmp fingerprint: "+tmp.find('>fingerprint').innerHTML);
if (tmp.length) {
if (tmp.attr('ufrag')) {
media += SDPUtil.build_iceufrag(tmp.attr('ufrag')) + '\r\n';
}
if (tmp.attr('pwd')) {
media += SDPUtil.build_icepwd(tmp.attr('pwd')) + '\r\n';
}
tmp.find('>fingerprint').each(function () {
// FIXME: check namespace at some point
media += 'a=fingerprint:' + this.getAttribute('hash');
media += ' ' + $(this).text();
media += '\r\n';
//console.log("mline "+media);
if (this.getAttribute('setup')) {
media += 'a=setup:' + this.getAttribute('setup') + '\r\n';
}
});
}
switch (content.attr('senders')) {
case 'initiator':
media += 'a=sendonly\r\n';
break;
case 'responder':
media += 'a=recvonly\r\n';
break;
case 'none':
media += 'a=inactive\r\n';
break;
case 'both':
media += 'a=sendrecv\r\n';
break;
}
media += 'a=mid:' + content.attr('name') + '\r\n';
/*if (content.attr('name') == 'video') {
media += 'a=x-google-flag:conference' + '\r\n';
}*/
// <description><rtcp-mux/></description>
// see http://code.google.com/p/libjingle/issues/detail?id=309 -- no spec though
// and http://mail.jabber.org/pipermail/jingle/2011-December/001761.html
if (desc.find('rtcp-mux').length) {
media += 'a=rtcp-mux\r\n';
}
if (desc.find('encryption').length) {
desc.find('encryption>crypto').each(function () {
media += 'a=crypto:' + this.getAttribute('tag');
media += ' ' + this.getAttribute('crypto-suite');
media += ' ' + this.getAttribute('key-params');
if (this.getAttribute('session-params')) {
media += ' ' + this.getAttribute('session-params');
}
media += '\r\n';
});
}
desc.find('payload-type').each(function () {
media += SDPUtil.build_rtpmap(this) + '\r\n';
if ($(this).find('>parameter').length) {
media += 'a=fmtp:' + this.getAttribute('id') + ' ';
media += $(this).find('parameter').map(function () { return (this.getAttribute('name') ? (this.getAttribute('name') + '=') : '') + this.getAttribute('value'); }).get().join('; ');
media += '\r\n';
}
// xep-0293
media += self.RtcpFbFromJingle($(this), this.getAttribute('id'));
});
// xep-0293
media += self.RtcpFbFromJingle(desc, '*');
// xep-0294
tmp = desc.find('>rtp-hdrext[xmlns="urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"]');
tmp.each(function () {
media += 'a=extmap:' + this.getAttribute('id') + ' ' + this.getAttribute('uri') + '\r\n';
});
content.find('>bundle>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function () {
media += SDPUtil.candidateFromJingle(this);
});
// XEP-0339 handle ssrc-group attributes
tmp = content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
var semantics = this.getAttribute('semantics');
var ssrcs = $(this).find('>source').map(function() {
return this.getAttribute('ssrc');
}).get();
if (ssrcs.length != 0) {
media += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
}
});
tmp = content.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
tmp.each(function () {
var ssrc = this.getAttribute('ssrc');
$(this).find('>parameter').each(function () {
media += 'a=ssrc:' + ssrc + ' ' + this.getAttribute('name');
if (this.getAttribute('value') && this.getAttribute('value').length)
media += ':' + this.getAttribute('value');
media += '\r\n';
});
});
if (tmp.length === 0) {
// fallback to proprietary mapping of a=ssrc lines
tmp = content.find('description>ssrc[xmlns="http://estos.de/ns/ssrc"]');
if (tmp.length) {
media += 'a=ssrc:' + ssrc + ' cname:' + tmp.attr('cname') + '\r\n';
media += 'a=ssrc:' + ssrc + ' msid:' + tmp.attr('msid') + '\r\n';
media += 'a=ssrc:' + ssrc + ' mslabel:' + tmp.attr('mslabel') + '\r\n';
media += 'a=ssrc:' + ssrc + ' label:' + tmp.attr('label') + '\r\n';
}
}
return media;
};

View File

@@ -0,0 +1,408 @@
/**
* Contains utility classes used in SDP class.
*
*/
/**
* Class holds a=ssrc lines and media type a=mid
* @param ssrc synchronization source identifier number(a=ssrc lines from SDP)
* @param type media type eg. "audio" or "video"(a=mid frm SDP)
* @constructor
*/
function ChannelSsrc(ssrc, type) {
this.ssrc = ssrc;
this.type = type;
this.lines = [];
}
/**
* Class holds a=ssrc-group: lines
* @param semantics
* @param ssrcs
* @constructor
*/
function ChannelSsrcGroup(semantics, ssrcs, line) {
this.semantics = semantics;
this.ssrcs = ssrcs;
}
/**
* Helper class represents media channel. Is a container for ChannelSsrc, holds channel idx and media type.
* @param channelNumber channel idx in SDP media array.
* @param mediaType media type(a=mid)
* @constructor
*/
function MediaChannel(channelNumber, mediaType) {
/**
* SDP channel number
* @type {*}
*/
this.chNumber = channelNumber;
/**
* Channel media type(a=mid)
* @type {*}
*/
this.mediaType = mediaType;
/**
* The maps of ssrc numbers to ChannelSsrc objects.
*/
this.ssrcs = {};
/**
* The array of ChannelSsrcGroup objects.
* @type {Array}
*/
this.ssrcGroups = [];
}
SDPUtil = {
iceparams: function (mediadesc, sessiondesc) {
var data = null;
if (SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc) &&
SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc)) {
data = {
ufrag: SDPUtil.parse_iceufrag(SDPUtil.find_line(mediadesc, 'a=ice-ufrag:', sessiondesc)),
pwd: SDPUtil.parse_icepwd(SDPUtil.find_line(mediadesc, 'a=ice-pwd:', sessiondesc))
};
}
return data;
},
parse_iceufrag: function (line) {
return line.substring(12);
},
build_iceufrag: function (frag) {
return 'a=ice-ufrag:' + frag;
},
parse_icepwd: function (line) {
return line.substring(10);
},
build_icepwd: function (pwd) {
return 'a=ice-pwd:' + pwd;
},
parse_mid: function (line) {
return line.substring(6);
},
parse_mline: function (line) {
var parts = line.substring(2).split(' '),
data = {};
data.media = parts.shift();
data.port = parts.shift();
data.proto = parts.shift();
if (parts[parts.length - 1] === '') { // trailing whitespace
parts.pop();
}
data.fmt = parts;
return data;
},
build_mline: function (mline) {
return 'm=' + mline.media + ' ' + mline.port + ' ' + mline.proto + ' ' + mline.fmt.join(' ');
},
parse_rtpmap: function (line) {
var parts = line.substring(9).split(' '),
data = {};
data.id = parts.shift();
parts = parts[0].split('/');
data.name = parts.shift();
data.clockrate = parts.shift();
data.channels = parts.length ? parts.shift() : '1';
return data;
},
/**
* Parses SDP line "a=sctpmap:..." and extracts SCTP port from it.
* @param line eg. "a=sctpmap:5000 webrtc-datachannel"
* @returns [SCTP port number, protocol, streams]
*/
parse_sctpmap: function (line)
{
var parts = line.substring(10).split(' ');
var sctpPort = parts[0];
var protocol = parts[1];
// Stream count is optional
var streamCount = parts.length > 2 ? parts[2] : null;
return [sctpPort, protocol, streamCount];// SCTP port
},
build_rtpmap: function (el) {
var line = 'a=rtpmap:' + el.getAttribute('id') + ' ' + el.getAttribute('name') + '/' + el.getAttribute('clockrate');
if (el.getAttribute('channels') && el.getAttribute('channels') != '1') {
line += '/' + el.getAttribute('channels');
}
return line;
},
parse_crypto: function (line) {
var parts = line.substring(9).split(' '),
data = {};
data.tag = parts.shift();
data['crypto-suite'] = parts.shift();
data['key-params'] = parts.shift();
if (parts.length) {
data['session-params'] = parts.join(' ');
}
return data;
},
parse_fingerprint: function (line) { // RFC 4572
var parts = line.substring(14).split(' '),
data = {};
data.hash = parts.shift();
data.fingerprint = parts.shift();
// TODO assert that fingerprint satisfies 2UHEX *(":" 2UHEX) ?
return data;
},
parse_fmtp: function (line) {
var parts = line.split(' '),
i, key, value,
data = [];
parts.shift();
parts = parts.join(' ').split(';');
for (i = 0; i < parts.length; i++) {
key = parts[i].split('=')[0];
while (key.length && key[0] == ' ') {
key = key.substring(1);
}
value = parts[i].split('=')[1];
if (key && value) {
data.push({name: key, value: value});
} else if (key) {
// rfc 4733 (DTMF) style stuff
data.push({name: '', value: key});
}
}
return data;
},
parse_icecandidate: function (line) {
var candidate = {},
elems = line.split(' ');
candidate.foundation = elems[0].substring(12);
candidate.component = elems[1];
candidate.protocol = elems[2].toLowerCase();
candidate.priority = elems[3];
candidate.ip = elems[4];
candidate.port = elems[5];
// elems[6] => "typ"
candidate.type = elems[7];
candidate.generation = 0; // default value, may be overwritten below
for (var i = 8; i < elems.length; i += 2) {
switch (elems[i]) {
case 'raddr':
candidate['rel-addr'] = elems[i + 1];
break;
case 'rport':
candidate['rel-port'] = elems[i + 1];
break;
case 'generation':
candidate.generation = elems[i + 1];
break;
case 'tcptype':
candidate.tcptype = elems[i + 1];
break;
default: // TODO
console.log('parse_icecandidate not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
}
}
candidate.network = '1';
candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
return candidate;
},
build_icecandidate: function (cand) {
var line = ['a=candidate:' + cand.foundation, cand.component, cand.protocol, cand.priority, cand.ip, cand.port, 'typ', cand.type].join(' ');
line += ' ';
switch (cand.type) {
case 'srflx':
case 'prflx':
case 'relay':
if (cand.hasOwnAttribute('rel-addr') && cand.hasOwnAttribute('rel-port')) {
line += 'raddr';
line += ' ';
line += cand['rel-addr'];
line += ' ';
line += 'rport';
line += ' ';
line += cand['rel-port'];
line += ' ';
}
break;
}
if (cand.hasOwnAttribute('tcptype')) {
line += 'tcptype';
line += ' ';
line += cand.tcptype;
line += ' ';
}
line += 'generation';
line += ' ';
line += cand.hasOwnAttribute('generation') ? cand.generation : '0';
return line;
},
parse_ssrc: function (desc) {
// proprietary mapping of a=ssrc lines
// TODO: see "Jingle RTP Source Description" by Juberti and P. Thatcher on google docs
// and parse according to that
var lines = desc.split('\r\n'),
data = {};
for (var i = 0; i < lines.length; i++) {
if (lines[i].substring(0, 7) == 'a=ssrc:') {
var idx = lines[i].indexOf(' ');
data[lines[i].substr(idx + 1).split(':', 2)[0]] = lines[i].substr(idx + 1).split(':', 2)[1];
}
}
return data;
},
parse_rtcpfb: function (line) {
var parts = line.substr(10).split(' ');
var data = {};
data.pt = parts.shift();
data.type = parts.shift();
data.params = parts;
return data;
},
parse_extmap: function (line) {
var parts = line.substr(9).split(' ');
var data = {};
data.value = parts.shift();
if (data.value.indexOf('/') != -1) {
data.direction = data.value.substr(data.value.indexOf('/') + 1);
data.value = data.value.substr(0, data.value.indexOf('/'));
} else {
data.direction = 'both';
}
data.uri = parts.shift();
data.params = parts;
return data;
},
find_line: function (haystack, needle, sessionpart) {
var lines = haystack.split('\r\n');
for (var i = 0; i < lines.length; i++) {
if (lines[i].substring(0, needle.length) == needle) {
return lines[i];
}
}
if (!sessionpart) {
return false;
}
// search session part
lines = sessionpart.split('\r\n');
for (var j = 0; j < lines.length; j++) {
if (lines[j].substring(0, needle.length) == needle) {
return lines[j];
}
}
return false;
},
find_lines: function (haystack, needle, sessionpart) {
var lines = haystack.split('\r\n'),
needles = [];
for (var i = 0; i < lines.length; i++) {
if (lines[i].substring(0, needle.length) == needle)
needles.push(lines[i]);
}
if (needles.length || !sessionpart) {
return needles;
}
// search session part
lines = sessionpart.split('\r\n');
for (var j = 0; j < lines.length; j++) {
if (lines[j].substring(0, needle.length) == needle) {
needles.push(lines[j]);
}
}
return needles;
},
candidateToJingle: function (line) {
// a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host generation 0
// <candidate component=... foundation=... generation=... id=... ip=... network=... port=... priority=... protocol=... type=.../>
if (line.indexOf('candidate:') === 0) {
line = 'a=' + line;
} else if (line.substring(0, 12) != 'a=candidate:') {
console.log('parseCandidate called with a line that is not a candidate line');
console.log(line);
return null;
}
if (line.substring(line.length - 2) == '\r\n') // chomp it
line = line.substring(0, line.length - 2);
var candidate = {},
elems = line.split(' '),
i;
if (elems[6] != 'typ') {
console.log('did not find typ in the right place');
console.log(line);
return null;
}
candidate.foundation = elems[0].substring(12);
candidate.component = elems[1];
candidate.protocol = elems[2].toLowerCase();
candidate.priority = elems[3];
candidate.ip = elems[4];
candidate.port = elems[5];
// elems[6] => "typ"
candidate.type = elems[7];
candidate.generation = '0'; // default, may be overwritten below
for (i = 8; i < elems.length; i += 2) {
switch (elems[i]) {
case 'raddr':
candidate['rel-addr'] = elems[i + 1];
break;
case 'rport':
candidate['rel-port'] = elems[i + 1];
break;
case 'generation':
candidate.generation = elems[i + 1];
break;
case 'tcptype':
candidate.tcptype = elems[i + 1];
break;
default: // TODO
console.log('not translating "' + elems[i] + '" = "' + elems[i + 1] + '"');
}
}
candidate.network = '1';
candidate.id = Math.random().toString(36).substr(2, 10); // not applicable to SDP -- FIXME: should be unique, not just random
return candidate;
},
candidateFromJingle: function (cand) {
var line = 'a=candidate:';
line += cand.getAttribute('foundation');
line += ' ';
line += cand.getAttribute('component');
line += ' ';
line += cand.getAttribute('protocol'); //.toUpperCase(); // chrome M23 doesn't like this
line += ' ';
line += cand.getAttribute('priority');
line += ' ';
line += cand.getAttribute('ip');
line += ' ';
line += cand.getAttribute('port');
line += ' ';
line += 'typ';
line += ' ' + cand.getAttribute('type');
line += ' ';
switch (cand.getAttribute('type')) {
case 'srflx':
case 'prflx':
case 'relay':
if (cand.getAttribute('rel-addr') && cand.getAttribute('rel-port')) {
line += 'raddr';
line += ' ';
line += cand.getAttribute('rel-addr');
line += ' ';
line += 'rport';
line += ' ';
line += cand.getAttribute('rel-port');
line += ' ';
}
break;
}
if (cand.getAttribute('protocol').toLowerCase() == 'tcp') {
line += 'tcptype';
line += ' ';
line += cand.getAttribute('tcptype');
line += ' ';
}
line += 'generation';
line += ' ';
line += cand.getAttribute('generation') || '0';
return line + '\r\n';
}
};
exports.SDPUtil = SDPUtil;

View File

@@ -0,0 +1,254 @@
/**
* Wrapper for built-in http.js to emulate the browser XMLHttpRequest object.
*
* This can be used with JS designed for browsers to improve reuse of code and
* allow the use of existing libraries.
*
* Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs.
*
* @todo SSL Support
* @author Dan DeFelippi <dan@driverdan.com>
* @license MIT
*/
var Url = require("url")
,sys = require("util");
exports.XMLHttpRequest = function() {
/**
* Private variables
*/
var self = this;
var http = require('http');
var https = require('https');
// Holds http.js objects
var client;
var request;
var response;
// Request settings
var settings = {};
// Set some default headers
var defaultHeaders = {
"User-Agent": "node.js",
"Accept": "*/*",
};
var headers = defaultHeaders;
/**
* Constants
*/
this.UNSENT = 0;
this.OPENED = 1;
this.HEADERS_RECEIVED = 2;
this.LOADING = 3;
this.DONE = 4;
/**
* Public vars
*/
// Current state
this.readyState = this.UNSENT;
// default ready state change handler in case one is not set or is set late
this.onreadystatechange = function() {};
// Result & response
this.responseText = "";
this.responseXML = "";
this.status = null;
this.statusText = null;
/**
* Open the connection. Currently supports local server requests.
*
* @param string method Connection method (eg GET, POST)
* @param string url URL for the connection.
* @param boolean async Asynchronous connection. Default is true.
* @param string user Username for basic authentication (optional)
* @param string password Password for basic authentication (optional)
*/
this.open = function(method, url, async, user, password) {
settings = {
"method": method,
"url": url,
"async": async || null,
"user": user || null,
"password": password || null
};
this.abort();
setState(this.OPENED);
};
/**
* Sets a header for the request.
*
* @param string header Header name
* @param string value Header value
*/
this.setRequestHeader = function(header, value) {
headers[header] = value;
};
/**
* Gets a header from the server response.
*
* @param string header Name of header to get.
* @return string Text of the header or null if it doesn't exist.
*/
this.getResponseHeader = function(header) {
if (this.readyState > this.OPENED && response.headers[header]) {
return header + ": " + response.headers[header];
}
return null;
};
/**
* Gets all the response headers.
*
* @return string
*/
this.getAllResponseHeaders = function() {
if (this.readyState < this.HEADERS_RECEIVED) {
throw "INVALID_STATE_ERR: Headers have not been received.";
}
var result = "";
for (var i in response.headers) {
result += i + ": " + response.headers[i] + "\r\n";
}
return result.substr(0, result.length - 2);
};
/**
* Sends the request to the server.
*
* @param string data Optional data to send as request body.
*/
this.send = function(data) {
if (this.readyState != this.OPENED) {
throw "INVALID_STATE_ERR: connection must be opened before send() is called";
}
var ssl = false;
var url = Url.parse(settings.url);
// Determine the server
switch (url.protocol) {
case 'https:':
ssl = true;
// SSL & non-SSL both need host, no break here.
case 'http:':
var host = url.hostname;
break;
case undefined:
case '':
var host = "localhost";
break;
default:
throw "Protocol not supported.";
}
// Default to port 80. If accessing localhost on another port be sure
// to use http://localhost:port/path
var port = url.port || (ssl ? 443 : 80);
// Add query string if one is used
var uri = url.pathname + (url.search ? url.search : '');
// Set the Host header or the server may reject the request
this.setRequestHeader("Host", host);
// Set content length header
if (settings.method == "GET" || settings.method == "HEAD") {
data = null;
} else if (data) {
this.setRequestHeader("Content-Length", Buffer.byteLength(data));
if (!headers["Content-Type"]) {
this.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
}
}
// Use the proper protocol
var doRequest = ssl ? https.request : http.request;
var options = {
host: host,
port: port,
path: uri,
method: settings.method,
headers: headers,
agent: false
};
var req = doRequest(options, function(res) {
response = res;
response.setEncoding("utf8");
setState(self.HEADERS_RECEIVED);
self.status = response.statusCode;
response.on('data', function(chunk) {
// Make sure there's some data
if (chunk) {
self.responseText += chunk;
}
setState(self.LOADING);
});
response.on('end', function() {
setState(self.DONE);
});
response.on('error', function() {
self.handleError(error);
});
}).on('error', function(error) {
self.handleError(error);
});
req.setHeader("Connection", "Close");
// Node 0.4 and later won't accept empty data. Make sure it's needed.
if (data) {
req.write(data);
}
req.end();
};
this.handleError = function(error) {
this.status = 503;
this.statusText = error;
this.responseText = error.stack;
setState(this.DONE);
};
/**
* Aborts a request.
*/
this.abort = function() {
headers = defaultHeaders;
this.readyState = this.UNSENT;
this.responseText = "";
this.responseXML = "";
};
/**
* Changes readyState and calls onreadystatechange.
*
* @param int state New state
*/
var setState = function(state) {
self.readyState = state;
self.onreadystatechange();
}
};

View File

@@ -0,0 +1,83 @@
// This code was written by Tyler Akins and has been placed in the
// public domain. It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com
var Base64 = (function () {
var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var obj = {
/**
* Encodes a string in base64
* @param {String} input The string to encode in base64.
*/
encode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
keyStr.charAt(enc3) + keyStr.charAt(enc4);
} while (i < input.length);
return output;
},
/**
* Decodes a base64 string.
* @param {String} input The string to decode.
*/
decode: function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
// remove all characters that are not A-Z, a-z, 0-9, +, /, or =
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
do {
enc1 = keyStr.indexOf(input.charAt(i++));
enc2 = keyStr.indexOf(input.charAt(i++));
enc3 = keyStr.indexOf(input.charAt(i++));
enc4 = keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
} while (i < input.length);
return output;
}
};
return obj;
})();
// Nodify
exports.Base64 = Base64;

View File

@@ -0,0 +1,279 @@
/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
var MD5 = (function () {
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
var safe_add = function (x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
};
/*
* Bitwise rotate a 32-bit number to the left.
*/
var bit_rol = function (num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
};
/*
* Convert a string to an array of little-endian words
* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
*/
var str2binl = function (str) {
var bin = [];
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
{
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
}
return bin;
};
/*
* Convert an array of little-endian words to a string
*/
var binl2str = function (bin) {
var str = "";
var mask = (1 << chrsz) - 1;
for(var i = 0; i < bin.length * 32; i += chrsz)
{
str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
}
return str;
};
/*
* Convert an array of little-endian words to a hex string.
*/
var binl2hex = function (binarray) {
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++)
{
str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
}
return str;
};
/*
* Convert an array of little-endian words to a base-64 string
*/
var binl2b64 = function (binarray) {
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var str = "";
var triplet, j;
for(var i = 0; i < binarray.length * 4; i += 3)
{
triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) |
(((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) |
((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
for(j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > binarray.length * 32) { str += b64pad; }
else { str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); }
}
}
return str;
};
/*
* These functions implement the four basic operations the algorithm uses.
*/
var md5_cmn = function (q, a, b, x, s, t) {
return safe_add(bit_rol(safe_add(safe_add(a, q),safe_add(x, t)), s),b);
};
var md5_ff = function (a, b, c, d, x, s, t) {
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
};
var md5_gg = function (a, b, c, d, x, s, t) {
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
};
var md5_hh = function (a, b, c, d, x, s, t) {
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
};
var md5_ii = function (a, b, c, d, x, s, t) {
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
};
/*
* Calculate the MD5 of an array of little-endian words, and a bit length
*/
var core_md5 = function (x, len) {
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
var olda, oldb, oldc, oldd;
for (var i = 0; i < x.length; i += 16)
{
olda = a;
oldb = b;
oldc = c;
oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return [a, b, c, d];
};
/*
* Calculate the HMAC-MD5, of a key and some data
*/
var core_hmac_md5 = function (key, data) {
var bkey = str2binl(key);
if(bkey.length > 16) { bkey = core_md5(bkey, key.length * chrsz); }
var ipad = new Array(16), opad = new Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
return core_md5(opad.concat(hash), 512 + 128);
};
var obj = {
/*
* These are the functions you'll usually want to call.
* They take string arguments and return either hex or base-64 encoded
* strings.
*/
hexdigest: function (s) {
return binl2hex(core_md5(str2binl(s), s.length * chrsz));
},
b64digest: function (s) {
return binl2b64(core_md5(str2binl(s), s.length * chrsz));
},
hash: function (s) {
return binl2str(core_md5(str2binl(s), s.length * chrsz));
},
hmac_hexdigest: function (key, data) {
return binl2hex(core_hmac_md5(key, data));
},
hmac_b64digest: function (key, data) {
return binl2b64(core_hmac_md5(key, data));
},
hmac_hash: function (key, data) {
return binl2str(core_hmac_md5(key, data));
},
/*
* Perform a simple self-test to see if the VM is working
*/
test: function () {
return MD5.hexdigest("abc") === "900150983cd24fb0d6963f7d28e17f72";
}
};
return obj;
})();
// Nodify
exports.MD5 = MD5;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
var strophe = require("./strophe/strophe.js").Strophe;
var Strophe = strophe.Strophe;
var $iq = strophe.$iq;
var $msg = strophe.$msg;
var $build = strophe.$build;
var $pres = strophe.$pres;
var jsdom = require("jsdom");
var window = jsdom.jsdom().parentWindow;
var $ = require('jquery')(window);
var stropheJingle = require("./strophe.jingle.sdp.js");
var input = '';
process.stdin.on('readable', function() {
var chunk = process.stdin.read();
if (chunk !== null) {
input += chunk;
}
});
process.stdin.on('end', function() {
if (process.argv[2] == '--jingle') {
var elem = $(input);
// app does:
// sess.setRemoteDescription($(iq).find('>jingle'), 'offer');
//console.log(elem.find('>content'));
var sdp = new stropheJingle.SDP('');
sdp.fromJingle(elem);
console.log(sdp.raw);
} else if (process.argv[2] == '--sdp') {
var sdp = new stropheJingle.SDP(input);
var accept = $iq({to: '%(tojid)s',
type: 'set'})
.c('jingle', {xmlns: 'urn:xmpp:jingle:1',
//action: 'session-accept',
action: '%(action)s',
initiator: '%(initiator)s',
responder: '%(responder)s',
sid: '%(sid)s' });
sdp.toJingle(accept, 'responder');
console.log(Strophe.serialize(accept));
}
});

2
contrib/vertobot/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
vucbot.yaml
vertobot.yaml

300
contrib/vertobot/bot.pl Executable file
View File

@@ -0,0 +1,300 @@
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010; # //
use IO::Socket::SSL qw(SSL_VERIFY_NONE);
use IO::Async::Loop;
use Net::Async::WebSocket::Client;
use Net::Async::Matrix 0.11_002;
use JSON;
use YAML;
use Data::UUID;
use Getopt::Long;
use Data::Dumper;
binmode STDOUT, ":encoding(UTF-8)";
binmode STDERR, ":encoding(UTF-8)";
my $loop = IO::Async::Loop->new;
# Net::Async::HTTP + SSL + IO::Poll doesn't play well. See
# https://rt.cpan.org/Ticket/Display.html?id=93107
ref $loop eq "IO::Async::Loop::Poll" and
warn "Using SSL with IO::Poll causes known memory-leaks!!\n";
GetOptions(
'C|config=s' => \my $CONFIG,
'eval-from=s' => \my $EVAL_FROM,
) or exit 1;
if( defined $EVAL_FROM ) {
# An emergency 'eval() this file' hack
$SIG{HUP} = sub {
my $code = do {
open my $fh, "<", $EVAL_FROM or warn( "Cannot read - $!" ), return;
local $/; <$fh>
};
eval $code or warn "Cannot eval() - $@";
};
}
defined $CONFIG or die "Must supply --config\n";
my %CONFIG = %{ YAML::LoadFile( $CONFIG ) };
my %MATRIX_CONFIG = %{ $CONFIG{matrix} };
# No harm in always applying this
$MATRIX_CONFIG{SSL_verify_mode} = SSL_VERIFY_NONE;
# Track every Room object, so we can ->leave them all on shutdown
my %bot_matrix_rooms;
my $bridgestate = {};
my $roomid_by_callid = {};
my $bot_verto = Net::Async::WebSocket::Client->new(
on_frame => sub {
my ( $self, $frame ) = @_;
warn "[Verto] receiving $frame";
on_verto_json($frame);
},
);
$loop->add( $bot_verto );
my $sessid = lc new Data::UUID->create_str();
my $bot_matrix = Net::Async::Matrix->new(
%MATRIX_CONFIG,
on_log => sub { warn "log: @_\n" },
on_invite => sub {
my ($matrix, $invite) = @_;
warn "[Matrix] invited to: " . $invite->{room_id} . " by " . $invite->{inviter} . "\n";
$matrix->join_room( $invite->{room_id} )->get;
},
on_room_new => sub {
my ($matrix, $room) = @_;
warn "[Matrix] have a room ID: " . $room->room_id . "\n";
$bot_matrix_rooms{$room->room_id} = $room;
# log in to verto on behalf of this room
$bridgestate->{$room->room_id}->{sessid} = $sessid;
$room->configure(
on_message => \&on_room_message,
);
my $f = send_verto_json_request("login", {
'login' => $CONFIG{'verto-dialog-params'}{'login'},
'passwd' => $CONFIG{'verto-config'}{'passwd'},
'sessid' => $sessid,
});
$matrix->adopt_future($f);
# we deliberately don't paginate the room, as we only care about
# new calls
},
on_unknown_event => \&on_unknown_event,
on_error => sub {
print STDERR "Matrix failure: @_\n";
},
);
$loop->add( $bot_matrix );
sub on_unknown_event
{
my ($matrix, $event) = @_;
print Dumper($event);
my $room_id = $event->{room_id};
my %dp = %{$CONFIG{'verto-dialog-params'}};
$dp{callID} = $bridgestate->{$room_id}->{callid};
if ($event->{type} eq 'm.call.invite') {
$bridgestate->{$room_id}->{matrix_callid} = $event->{content}->{call_id};
$bridgestate->{$room_id}->{callid} = lc new Data::UUID->create_str();
$bridgestate->{$room_id}->{offer} = $event->{content}->{offer}->{sdp};
$bridgestate->{$room_id}->{gathered_candidates} = 0;
$roomid_by_callid->{ $bridgestate->{$room_id}->{callid} } = $room_id;
# no trickle ICE in verto apparently
}
elsif ($event->{type} eq 'm.call.candidates') {
# XXX: compare call IDs
if (!$bridgestate->{$room_id}->{gathered_candidates}) {
$bridgestate->{$room_id}->{gathered_candidates} = 1;
my $offer = $bridgestate->{$room_id}->{offer};
my $candidate_block = "";
foreach (@{$event->{content}->{candidates}}) {
$candidate_block .= "a=" . $_->{candidate} . "\r\n";
}
# XXX: collate using the right m= line - for now assume audio call
$offer =~ s/(a=rtcp.*[\r\n]+)/$1$candidate_block/;
my $f = send_verto_json_request("verto.invite", {
"sdp" => $offer,
"dialogParams" => \%dp,
"sessid" => $bridgestate->{$room_id}->{sessid},
});
$matrix->adopt_future($f);
}
else {
# ignore them, as no trickle ICE, although we might as well
# batch them up
# foreach (@{$event->{content}->{candidates}}) {
# push @{$bridgestate->{$room_id}->{candidates}}, $_;
# }
}
}
elsif ($event->{type} eq 'm.call.hangup') {
if ($bridgestate->{$room_id}->{matrix_callid} eq $event->{content}->{call_id}) {
my $f = send_verto_json_request("verto.bye", {
"dialogParams" => \%dp,
"sessid" => $bridgestate->{$room_id}->{sessid},
});
$matrix->adopt_future($f);
}
else {
warn "Ignoring unrecognised callid: ".$event->{content}->{call_id};
}
}
else {
warn "Unhandled event: $event->{type}";
}
}
sub on_room_message
{
my ($room, $from, $content) = @_;
my $room_id = $room->room_id;
warn "[Matrix] in $room_id: $from: " . $content->{body} . "\n";
}
my $verto_connecting = $loop->new_future;
$bot_verto->connect(
%{ $CONFIG{"verto-bot"} },
on_connected => sub {
warn("[Verto] connected to websocket");
$verto_connecting->done($bot_verto) if not $verto_connecting->is_done;
},
on_connect_error => sub { die "Cannot connect to verto - $_[-1]" },
on_resolve_error => sub { die "Cannot resolve to verto - $_[-1]" },
);
Future->needs_all(
$bot_matrix->login( %{ $CONFIG{"matrix-bot"} } )->then( sub {
$bot_matrix->start;
}),
$verto_connecting,
)->get;
$loop->attach_signal(
PIPE => sub { warn "pipe\n" }
);
$loop->attach_signal(
INT => sub { $loop->stop },
);
$loop->attach_signal(
TERM => sub { $loop->stop },
);
eval {
$loop->run;
} or my $e = $@;
# When the bot gets shut down, have it leave the rooms so it's clear to observers
# that it is no longer running.
# if( $CONFIG{"leave-on-shutdown"} // 1 ) {
# print STDERR "Removing bot from Matrix rooms...\n";
# Future->wait_all( map { $_->leave->else_done() } values %bot_matrix_rooms )->get;
# }
# else {
# print STDERR "Leaving bot users in Matrix rooms.\n";
# }
die $e if $e;
exit 0;
{
my $json_id;
my $requests;
sub send_verto_json_request
{
$json_id ||= 1;
my ($method, $params) = @_;
my $json = {
jsonrpc => "2.0",
method => $method,
params => $params,
id => $json_id,
};
my $text = JSON->new->encode( $json );
warn "[Verto] sending $text";
$bot_verto->send_frame ( $text );
my $request = $loop->new_future;
$requests->{$json_id} = $request;
$json_id++;
return $request;
}
sub send_verto_json_response
{
my ($result, $id) = @_;
my $json = {
jsonrpc => "2.0",
result => $result,
id => $id,
};
my $text = JSON->new->encode( $json );
warn "[Verto] sending $text";
$bot_verto->send_frame ( $text );
}
sub on_verto_json
{
my $json = JSON->new->decode( $_[0] );
if ($json->{method}) {
if (($json->{method} eq 'verto.answer' && $json->{params}->{sdp}) ||
$json->{method} eq 'verto.media') {
my $room_id = $roomid_by_callid->{$json->{params}->{callID}};
my $room = $bot_matrix_rooms{$room_id};
if ($json->{params}->{sdp}) {
# HACK HACK HACK HACK
$room->_do_POST_json( "/send/m.call.answer", {
call_id => $bridgestate->{$room_id}->{matrix_callid},
version => 0,
answer => {
sdp => $json->{params}->{sdp},
type => "answer",
},
})->then( sub {
send_verto_json_response( {
method => $json->{method},
}, $json->{id});
})->get;
}
}
else {
warn ("[Verto] unhandled method: " . $json->{method});
send_verto_json_response( {
method => $json->{method},
}, $json->{id});
}
}
elsif ($json->{result}) {
$requests->{$json->{id}}->done($json->{result});
}
elsif ($json->{error}) {
$requests->{$json->{id}}->fail($json->{error}->{message}, $json->{error});
}
}
}

View File

@@ -0,0 +1,29 @@
# Generic Matrix connection params
matrix:
server: 'matrix.org'
SSL: 1
# Bot-user connection details
matrix-bot:
user_id: '@vertobot:matrix.org'
password: ''
verto-bot:
host: webrtc.freeswitch.org
service: 8081
url: "ws://webrtc.freeswitch.org:8081/"
verto-config:
passwd: 1234
verto-dialog-params:
useVideo: false
useStereo: false
tag: "webcam"
login: "1008@webrtc.freeswitch.org"
destination_number: "9664"
caller_id_name: "FreeSWITCH User"
caller_id_number: "1008"
callID: ""
remote_caller_id_name: "Outbound Call"
remote_caller_id_number: "9664"

17
contrib/vertobot/cpanfile Normal file
View File

@@ -0,0 +1,17 @@
requires 'parent', 0;
requires 'Future', '>= 0.29';
requires 'Net::Async::Matrix', '>= 0.11_002';
requires 'Net::Async::Matrix::Utils';
requires 'Net::Async::WebSocket::Protocol', 0;
requires 'Data::UUID', 0;
requires 'IO::Async', '>= 0.63';
requires 'IO::Async::SSL', 0;
requires 'IO::Socket::SSL', 0;
requires 'YAML', 0;
requires 'JSON', 0;
requires 'Getopt::Long', 0;
on 'test' => sub {
requires 'Test::More', '>= 0.98';
};

View File

@@ -0,0 +1,207 @@
# JSON is shown in *reverse* chronological order.
# Send v. Receive is implicit.
{
"jsonrpc": "2.0",
"id": 7,
"result": {
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"message": "CALL ENDED",
"causeCode": 16,
"cause": "NORMAL_CLEARING",
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
}
}
{
"jsonrpc": "2.0",
"method": "verto.bye",
"params": {
"dialogParams": {
"useVideo": false,
"useStereo": true,
"tag": "webcam",
"login": "1008@webrtc.freeswitch.org",
"destination_number": "9664",
"caller_id_name": "FreeSWITCH User",
"caller_id_number": "1008",
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"remote_caller_id_name": "Outbound Call",
"remote_caller_id_number": "9664"
},
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
},
"id": 7
}
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"action": "toggleHold",
"holdState": "active",
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
}
}
{
"jsonrpc": "2.0",
"method": "verto.modify",
"params": {
"action": "toggleHold",
"dialogParams": {
"useVideo": false,
"useStereo": true,
"tag": "webcam",
"login": "1008@webrtc.freeswitch.org",
"destination_number": "9664",
"caller_id_name": "FreeSWITCH User",
"caller_id_number": "1008",
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"remote_caller_id_name": "Outbound Call",
"remote_caller_id_number": "9664"
},
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
},
"id": 6
}
{
"jsonrpc": "2.0",
"id": 5,
"result": {
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"action": "toggleHold",
"holdState": "held",
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
}
}
{
"jsonrpc": "2.0",
"method": "verto.modify",
"params": {
"action": "toggleHold",
"dialogParams": {
"useVideo": false,
"useStereo": true,
"tag": "webcam",
"login": "1008@webrtc.freeswitch.org",
"destination_number": "9664",
"caller_id_name": "FreeSWITCH User",
"caller_id_number": "1008",
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"remote_caller_id_name": "Outbound Call",
"remote_caller_id_number": "9664"
},
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
},
"id": 5
}
{
"jsonrpc": "2.0",
"id": 349819,
"result": {
"method": "verto.answer"
}
}
{
"jsonrpc": "2.0",
"id": 349819,
"method": "verto.answer",
"params": {
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"sdp": "v=0\no=FreeSWITCH 1417101432 1417101433 IN IP4 209.105.235.10\ns=FreeSWITCH\nc=IN IP4 209.105.235.10\nt=0 0\na=msid-semantic: WMS jA3rmwLVwUq1iE6TYEYHeLk2YTUlh1Vq\nm=audio 30134 RTP/SAVPF 111 126\na=rtpmap:111 opus/48000/2\na=fmtp:111 minptime=10; stereo=1\na=rtpmap:126 telephone-event/8000\na=silenceSupp:off - - - -\na=ptime:20\na=sendrecv\na=fingerprint:sha-256 F8:72:18:E9:72:89:99:22:5B:F8:B6:C6:C6:0D:C5:9B:B2:FB:BC:CA:8D:AB:13:8A:66:E1:37:38:A0:16:AA:41\na=rtcp-mux\na=rtcp:30134 IN IP4 209.105.235.10\na=ssrc:210967934 cname:rOIEajpw4FocakWY\na=ssrc:210967934 msid:jA3rmwLVwUq1iE6TYEYHeLk2YTUlh1Vq a0\na=ssrc:210967934 mslabel:jA3rmwLVwUq1iE6TYEYHeLk2YTUlh1Vq\na=ssrc:210967934 label:jA3rmwLVwUq1iE6TYEYHeLk2YTUlh1Vqa0\na=ice-ufrag:OKwTmGLapwmxn7OF\na=ice-pwd:MmaMwq8rVmtWxfLbQ7U2Ew3T\na=candidate:2372654928 1 udp 659136 209.105.235.10 30134 typ host generation 0\n"
}
}
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"message": "CALL CREATED",
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
}
}
{
"jsonrpc": "2.0",
"method": "verto.invite",
"params": {
"sdp": "v=0\r\no=- 1381685806032722557 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE audio\r\na=msid-semantic: WMS 6OOMyGAyJakjwaOOBtV7WcBCCuIW6PpuXsNg\r\nm=audio 63088 RTP/SAVPF 111 103 104 0 8 106 105 13 126\r\nc=IN IP4 81.138.8.249\r\na=rtcp:63088 IN IP4 81.138.8.249\r\na=candidate:460398169 1 udp 2122260223 10.10.79.10 49945 typ host generation 0\r\na=candidate:460398169 2 udp 2122260223 10.10.79.10 49945 typ host generation 0\r\na=candidate:3460887983 1 udp 2122194687 192.168.1.64 63088 typ host generation 0\r\na=candidate:3460887983 2 udp 2122194687 192.168.1.64 63088 typ host generation 0\r\na=candidate:945327227 1 udp 1685987071 81.138.8.249 63088 typ srflx raddr 192.168.1.64 rport 63088 generation 0\r\na=candidate:945327227 2 udp 1685987071 81.138.8.249 63088 typ srflx raddr 192.168.1.64 rport 63088 generation 0\r\na=candidate:1441981097 1 tcp 1518280447 10.10.79.10 0 typ host tcptype active generation 0\r\na=candidate:1441981097 2 tcp 1518280447 10.10.79.10 0 typ host tcptype active generation 0\r\na=candidate:2160789855 1 tcp 1518214911 192.168.1.64 0 typ host tcptype active generation 0\r\na=candidate:2160789855 2 tcp 1518214911 192.168.1.64 0 typ host tcptype active generation 0\r\na=ice-ufrag:cP4qeRhn0LpcpA88\r\na=ice-pwd:fREmgSkXsDLGUUH1bwfrBQhW\r\na=ice-options:google-ice\r\na=fingerprint:sha-256 AF:35:64:1B:62:8A:EF:27:AE:2B:88:2E:FE:78:29:0B:08:DA:64:6C:DE:02:57:E3:EE:B1:D7:86:B8:36:8F:B0\r\na=setup:actpass\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10; stereo=1\r\na=rtpmap:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:106 CN/32000\r\na=rtpmap:105 CN/16000\r\na=rtpmap:13 CN/8000\r\na=rtpmap:126 telephone-event/8000\r\na=maxptime:60\r\na=ssrc:558827154 cname:vdKHBNqa17t2gmE3\r\na=ssrc:558827154 msid:6OOMyGAyJakjwaOOBtV7WcBCCuIW6PpuXsNg bf1303fb-9833-4d7d-b9e4-b32cfe04acc3\r\na=ssrc:558827154 mslabel:6OOMyGAyJakjwaOOBtV7WcBCCuIW6PpuXsNg\r\na=ssrc:558827154 label:bf1303fb-9833-4d7d-b9e4-b32cfe04acc3\r\n",
"dialogParams": {
"useVideo": false,
"useStereo": true,
"tag": "webcam",
"login": "1008@webrtc.freeswitch.org",
"destination_number": "9664",
"caller_id_name": "FreeSWITCH User",
"caller_id_number": "1008",
"callID": "12795aa6-2a8d-84ee-ce63-2e82ffe825ef",
"remote_caller_id_name": "Outbound Call",
"remote_caller_id_number": "9664"
},
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
},
"id": 4
}
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"message": "logged in",
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
}
}
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "Authentication Required"
}
}
{
"jsonrpc": "2.0",
"method": "login",
"params": {
"login": "1008@webrtc.freeswitch.org",
"passwd": "1234",
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
},
"id": 3
}
{
"jsonrpc": "2.0",
"id": 2,
"error": {
"code": -32000,
"message": "Authentication Required"
}
}
{
"jsonrpc": "2.0",
"method": "login",
"params": {
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
},
"id": 1
}
{
"jsonrpc": "2.0",
"method": "login",
"params": {
"sessid": "03a11060-3e14-23b6-c620-51b892c52983"
},
"id": 2
}

21
database-prepare-for-0.5.0.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/bash
# This is will prepare a synapse database for running with v0.5.0 of synapse.
# It will store all the user information, but will *delete* all messages and
# room data.
set -e
cp "$1" "$1.bak"
DUMP=$(sqlite3 "$1" << 'EOF'
.dump users
.dump access_tokens
.dump presence
.dump profiles
EOF
)
rm "$1"
sqlite3 "$1" <<< "$DUMP"

View File

@@ -14,3 +14,4 @@ fi
find "$DIR" -name "*.log" -delete
find "$DIR" -name "*.db" -delete
rm -rf $DIR/etc

View File

@@ -8,6 +8,14 @@ cd "$DIR/.."
mkdir -p demo/etc
# Check the --no-rate-limit param
PARAMS=""
if [ $# -eq 1 ]; then
if [ $1 = "--no-rate-limit" ]; then
PARAMS="--rc-messages-per-second 1000 --rc-message-burst-count 1000"
fi
fi
for port in 8080 8081 8082; do
echo "Starting server on port $port... "
@@ -23,7 +31,8 @@ for port in 8080 8081 8082; do
-d "$DIR/$port.db" \
-D --pid-file "$DIR/$port.pid" \
--manhole $((port + 1000)) \
--tls-dh-params-path "demo/demo.tls.dh"
--tls-dh-params-path "demo/demo.tls.dh" \
$PARAMS $SYNAPSE_PARAMS
python -m synapse.app.homeserver \
--config-path "demo/etc/$port.config" \
@@ -31,7 +40,4 @@ for port in 8080 8081 8082; do
done
echo "Starting webclient on port 8000..."
python "demo/webserver.py" -p 8000 -P "$DIR/webserver.pid" "webclient"
cd "$CWD"

6
docs/README.rst Normal file
View File

@@ -0,0 +1,6 @@
All matrix-generic documentation now lives in its own project at
github.com/matrix-org/matrix-doc.git
Only Synapse implementation-specific documentation lives here now
(together with some older stuff will be shortly migrated over to matrix-doc)

View File

@@ -1,3 +1,9 @@
.. WARNING::
These architecture notes are spectacularly old, and date back to when Synapse
was just federation code in isolation. This should be merged into the main
spec.
= Server to Server =
== Server to Server Stack ==

68
docs/architecture.rst Normal file
View File

@@ -0,0 +1,68 @@
Synapse Architecture
====================
As of the end of Oct 2014, Synapse's overall architecture looks like::
synapse
.-----------------------------------------------------.
| Notifier |
| ^ | |
| | | |
| .------------|------. |
| | handlers/ | | |
| | v | |
| | Event*Handler <--------> rest/* <=> Client
| | Rooms*Handler | |
HSes <=> federation/* <==> FederationHandler | |
| | | PresenceHandler | |
| | | TypingHandler | |
| | '-------------------' |
| | | | |
| | state/* | |
| | | | |
| | v v |
| `--------------> storage/* |
| | |
'--------------------------|--------------------------'
v
.----.
| DB |
'----'
* Handlers: business logic of synapse itself. Follows a set contract of BaseHandler:
- BaseHandler gives us onNewRoomEvent which: (TODO: flesh this out and make it less cryptic):
+ handle_state(event)
+ auth(event)
+ persist_event(event)
+ notify notifier or federation(event)
- PresenceHandler: use distributor to get EDUs out of Federation. Very
lightweight logic built on the distributor
- TypingHandler: use distributor to get EDUs out of Federation. Very
lightweight logic built on the distributor
- EventsHandler: handles the events stream...
- FederationHandler: - gets PDU from Federation Layer; turns into an event;
follows basehandler functionality.
- RoomsHandler: does all the room logic, including members - lots of classes in
RoomsHandler.
- ProfileHandler: talks to the storage to store/retrieve profile info.
* EventFactory: generates events of particular event types.
* Notifier: Backs the events handler
* REST: Interfaces handlers and events to the outside world via HTTP/JSON.
Converts events back and forth from JSON.
* Federation: holds the HTTP client & server to talk to other servers. Does
replication to make sure there's nothing missing in the graph. Handles
reliability. Handles txns.
* Distributor: generic event bus. used for presence & typing only currently.
Notifier could be implemented using Distributor - so far we are only using for
things which actually /require/ dynamic pluggability however as it can
obfuscate the actual flow of control.
* Auth: helper singleton to say whether a given event is allowed to do a given
thing (TODO: put this on the diagram)
* State: helper singleton: does state conflict resolution. You give it an event
and it tells you if it actually updates the state or not, and annotates the
event up properly and handles merge conflict resolution.
* Storage: abstracts the storage engine.

File diff suppressed because it is too large Load Diff

View File

@@ -1,636 +0,0 @@
.. TODO kegan
Room config (specifically: message history,
public rooms). /register seems super simplistic compared to /login, maybe it
would be better if /register used the same technique as /login? /register should
be "user" not "user_id".
How to use the client-server API
================================
This guide focuses on how the client-server APIs *provided by the reference
home server* can be used. Since this is specific to a home server
implementation, there may be variations in relation to registering/logging in
which are not covered in extensive detail in this guide.
If you haven't already, get a home server up and running on
``http://localhost:8008``.
Accounts
========
Before you can send and receive messages, you must **register** for an account.
If you already have an account, you must **login** into it.
`Try out the fiddle`__
.. __: http://jsfiddle.net/4q2jyxng/
Registration
------------
The aim of registration is to get a user ID and access token which you will need
when accessing other APIs::
curl -XPOST -d '{"user_id":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/register"
{
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.AqdSzFmFYrLrTmteXc",
"home_server": "localhost",
"user_id": "@example:localhost"
}
NB: If a ``user_id`` is not specified, one will be randomly generated for you.
If you do not specify a ``password``, you will be unable to login to the account
if you forget the ``access_token``.
Implementation note: The matrix specification does not enforce how users
register with a server. It just specifies the URL path and absolute minimum
keys. The reference home server uses a username/password to authenticate user,
but other home servers may use different methods.
Login
-----
The aim when logging in is to get an access token for your existing user ID::
curl -XGET "http://localhost:8008/_matrix/client/api/v1/login"
{
"flows": [
{
"type": "m.login.password"
}
]
}
curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/login"
{
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
"home_server": "localhost",
"user_id": "@example:localhost"
}
Implementation note: Different home servers may implement different methods for
logging in to an existing account. In order to check that you know how to login
to this home server, you must perform a ``GET`` first and make sure you
recognise the login type. If you do not know how to login, you can
``GET /login/fallback`` which will return a basic webpage which you can use to
login. The reference home server implementation support username/password login,
but other home servers may support different login methods (e.g. OAuth2).
Communicating
=============
In order to communicate with another user, you must **create a room** with that
user and **send a message** to that room.
`Try out the fiddle`__
.. __: http://jsfiddle.net/zL3zto9g/
Creating a room
---------------
If you want to send a message to someone, you have to be in a room with them. To
create a room::
curl -XPOST -d '{"room_alias_name":"tutorial"}' "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token=YOUR_ACCESS_TOKEN"
{
"room_alias": "#tutorial:localhost",
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
}
The "room alias" is a human-readable string which can be shared with other users
so they can join a room, rather than the room ID which is a randomly generated
string. You can have multiple room aliases per room.
.. TODO(kegan)
How to add/remove aliases from an existing room.
Sending messages
----------------
You can now send messages to this room::
curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/send/m.room.message?access_token=YOUR_ACCESS_TOKEN"
{
"event_id": "YUwRidLecu"
}
The event ID returned is a unique ID which identifies this message.
NB: There are no limitations to the types of messages which can be exchanged.
The only requirement is that ``"msgtype"`` is specified. The Matrix
specification outlines the following standard types: ``m.text``, ``m.image``,
``m.audio``, ``m.video``, ``m.location``, ``m.emote``. See the specification for
more information on these types.
Users and rooms
===============
Each room can be configured to allow or disallow certain rules. In particular,
these rules may specify if you require an **invitation** from someone already in
the room in order to **join the room**. In addition, you may also be able to
join a room **via a room alias** if one was set up.
`Try out the fiddle`__
.. __: http://jsfiddle.net/7fhotf1b/
Inviting a user to a room
-------------------------
You can directly invite a user to a room like so::
curl -XPOST -d '{"user_id":"@myfriend:localhost"}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/invite?access_token=YOUR_ACCESS_TOKEN"
This informs ``@myfriend:localhost`` of the room ID
``!CvcvRuDYDzTOzfKKgh:localhost`` and allows them to join the room.
Joining a room via an invite
----------------------------
If you receive an invite, you can join the room::
curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/rooms/%21CvcvRuDYDzTOzfKKgh%3Alocalhost/join?access_token=YOUR_ACCESS_TOKEN"
NB: Only the person invited (``@myfriend:localhost``) can change the membership
state to ``"join"``. Repeatedly joining a room does nothing.
Joining a room via an alias
---------------------------
Alternatively, if you know the room alias for this room and the room config
allows it, you can directly join a room via the alias::
curl -XPOST -d '{}' "http://localhost:8008/_matrix/client/api/v1/join/%23tutorial%3Alocalhost?access_token=YOUR_ACCESS_TOKEN"
{
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
}
You will need to use the room ID when sending messages, not the room alias.
NB: If the room is configured to be an invite-only room, you will still require
an invite in order to join the room even though you know the room alias. As a
result, it is more common to see a room alias in relation to a public room,
which do not require invitations.
Getting events
==============
An event is some interesting piece of data that a client may be interested in.
It can be a message in a room, a room invite, etc. There are many different ways
of getting events, depending on what the client already knows.
`Try out the fiddle`__
.. __: http://jsfiddle.net/vw11mg37/
Getting all state
-----------------
If the client doesn't know any information on the rooms the user is
invited/joined on, they can get all the user's state for all rooms::
curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=YOUR_ACCESS_TOKEN"
{
"end": "s39_18_0",
"presence": [
{
"content": {
"last_active_ago": 1061436,
"user_id": "@example:localhost"
},
"type": "m.presence"
}
],
"rooms": [
{
"membership": "join",
"messages": {
"chunk": [
{
"content": {
"@example:localhost": 10,
"default": 0
},
"event_id": "wAumPSTsWF",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.power_levels",
"user_id": "@example:localhost"
},
{
"content": {
"join_rule": "public"
},
"event_id": "jrLVqKHKiI",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.join_rules",
"user_id": "@example:localhost"
},
{
"content": {
"level": 10
},
"event_id": "WpmTgsNWUZ",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.add_state_level",
"user_id": "@example:localhost"
},
{
"content": {
"level": 0
},
"event_id": "qUMBJyKsTQ",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.send_event_level",
"user_id": "@example:localhost"
},
{
"content": {
"ban_level": 5,
"kick_level": 5
},
"event_id": "YAaDmKvoUW",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.ops_levels",
"user_id": "@example:localhost"
},
{
"content": {
"avatar_url": null,
"displayname": null,
"membership": "join"
},
"event_id": "RJbPMtCutf",
"membership": "join",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@example:localhost",
"ts": 1409665586730,
"type": "m.room.member",
"user_id": "@example:localhost"
},
{
"content": {
"body": "hello",
"hsob_ts": 1409665660439,
"msgtype": "m.text"
},
"event_id": "YUwRidLecu",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"ts": 1409665660439,
"type": "m.room.message",
"user_id": "@example:localhost"
},
{
"content": {
"membership": "invite"
},
"event_id": "YjNuBKnPsb",
"membership": "invite",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@myfriend:localhost",
"ts": 1409666426819,
"type": "m.room.member",
"user_id": "@example:localhost"
},
{
"content": {
"avatar_url": null,
"displayname": null,
"membership": "join",
"prev": "join"
},
"event_id": "KWwdDjNZnm",
"membership": "join",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@example:localhost",
"ts": 1409666551582,
"type": "m.room.member",
"user_id": "@example:localhost"
},
{
"content": {
"avatar_url": null,
"displayname": null,
"membership": "join"
},
"event_id": "JFLVteSvQc",
"membership": "join",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@example:localhost",
"ts": 1409666587265,
"type": "m.room.member",
"user_id": "@example:localhost"
}
],
"end": "s39_18_0",
"start": "t1-11_18_0"
},
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state": [
{
"content": {
"creator": "@example:localhost"
},
"event_id": "dMUoqVTZca",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.create",
"user_id": "@example:localhost"
},
{
"content": {
"@example:localhost": 10,
"default": 0
},
"event_id": "wAumPSTsWF",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.power_levels",
"user_id": "@example:localhost"
},
{
"content": {
"join_rule": "public"
},
"event_id": "jrLVqKHKiI",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.join_rules",
"user_id": "@example:localhost"
},
{
"content": {
"level": 10
},
"event_id": "WpmTgsNWUZ",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.add_state_level",
"user_id": "@example:localhost"
},
{
"content": {
"level": 0
},
"event_id": "qUMBJyKsTQ",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.send_event_level",
"user_id": "@example:localhost"
},
{
"content": {
"ban_level": 5,
"kick_level": 5
},
"event_id": "YAaDmKvoUW",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.ops_levels",
"user_id": "@example:localhost"
},
{
"content": {
"membership": "invite"
},
"event_id": "YjNuBKnPsb",
"membership": "invite",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@myfriend:localhost",
"ts": 1409666426819,
"type": "m.room.member",
"user_id": "@example:localhost"
},
{
"content": {
"avatar_url": null,
"displayname": null,
"membership": "join"
},
"event_id": "JFLVteSvQc",
"membership": "join",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@example:localhost",
"ts": 1409666587265,
"type": "m.room.member",
"user_id": "@example:localhost"
}
]
}
]
}
This returns all the room information the user is invited/joined on, as well as
all of the presences relevant for these rooms. This can be a LOT of data. You
may just want the most recent event for each room. This can be achieved by
applying query parameters to ``limit`` this request::
curl -XGET "http://localhost:8008/_matrix/client/api/v1/initialSync?limit=1&access_token=YOUR_ACCESS_TOKEN"
{
"end": "s39_18_0",
"presence": [
{
"content": {
"last_active_ago": 1279484,
"user_id": "@example:localhost"
},
"type": "m.presence"
}
],
"rooms": [
{
"membership": "join",
"messages": {
"chunk": [
{
"content": {
"avatar_url": null,
"displayname": null,
"membership": "join"
},
"event_id": "JFLVteSvQc",
"membership": "join",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@example:localhost",
"ts": 1409666587265,
"type": "m.room.member",
"user_id": "@example:localhost"
}
],
"end": "s39_18_0",
"start": "t10-30_18_0"
},
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state": [
{
"content": {
"creator": "@example:localhost"
},
"event_id": "dMUoqVTZca",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.create",
"user_id": "@example:localhost"
},
{
"content": {
"@example:localhost": 10,
"default": 0
},
"event_id": "wAumPSTsWF",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.power_levels",
"user_id": "@example:localhost"
},
{
"content": {
"join_rule": "public"
},
"event_id": "jrLVqKHKiI",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.join_rules",
"user_id": "@example:localhost"
},
{
"content": {
"level": 10
},
"event_id": "WpmTgsNWUZ",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.add_state_level",
"user_id": "@example:localhost"
},
{
"content": {
"level": 0
},
"event_id": "qUMBJyKsTQ",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.send_event_level",
"user_id": "@example:localhost"
},
{
"content": {
"ban_level": 5,
"kick_level": 5
},
"event_id": "YAaDmKvoUW",
"required_power_level": 10,
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "",
"ts": 1409665585188,
"type": "m.room.ops_levels",
"user_id": "@example:localhost"
},
{
"content": {
"membership": "invite"
},
"event_id": "YjNuBKnPsb",
"membership": "invite",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@myfriend:localhost",
"ts": 1409666426819,
"type": "m.room.member",
"user_id": "@example:localhost"
},
{
"content": {
"avatar_url": null,
"displayname": null,
"membership": "join"
},
"event_id": "JFLVteSvQc",
"membership": "join",
"room_id": "!MkDbyRqnvTYnoxjLYx:localhost",
"state_key": "@example:localhost",
"ts": 1409666587265,
"type": "m.room.member",
"user_id": "@example:localhost"
}
]
}
]
}
Getting live state
------------------
Once you know which rooms the client has previously interacted with, you need to
listen for incoming events. This can be done like so::
curl -XGET "http://localhost:8008/_matrix/client/api/v1/events?access_token=YOUR_ACCESS_TOKEN"
{
"chunk": [],
"end": "s39_18_0",
"start": "s39_18_0"
}
This will block waiting for an incoming event, timing out after several seconds.
Even if there are no new events (as in the example above), there will be some
pagination stream response keys. The client should make subsequent requests
using the value of the ``"end"`` key (in this case ``s39_18_0``) as the ``from``
query parameter e.g. ``http://localhost:8008/_matrix/client/api/v1/events?access
_token=YOUR_ACCESS_TOKEN&from=s39_18_0``. This value should be stored so when the
client reopens your app after a period of inactivity, you can resume from where
you got up to in the event stream. If it has been a long period of inactivity,
there may be LOTS of events waiting for the user. In this case, you may wish to
get all state instead and then resume getting live state from a newer end token.
NB: The timeout can be changed by adding a ``timeout`` query parameter, which is
in milliseconds. A timeout of 0 will not block.
Example application
-------------------
The following example demonstrates registration and login, live event streaming,
creating and joining rooms, sending messages, getting member lists and getting
historical messages for a room. This covers most functionality of a messaging
application.
`Try out the fiddle`__
.. __: http://jsfiddle.net/uztL3yme/

View File

@@ -1,46 +0,0 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"apis": [
{
"path": "-login",
"description": "Login operations"
},
{
"path": "-registration",
"description": "Registration operations"
},
{
"path": "-rooms",
"description": "Room operations"
},
{
"path": "-profile",
"description": "Profile operations"
},
{
"path": "-presence",
"description": "Presence operations"
},
{
"path": "-events",
"description": "Event operations"
},
{
"path": "-directory",
"description": "Directory operations"
}
],
"authorizations": {
"token": {
"scopes": []
}
},
"info": {
"title": "Matrix Client-Server API Reference",
"description": "This contains the client-server API for the reference implementation of the home server",
"termsOfServiceUrl": "http://matrix.org",
"license": "Apache 2.0",
"licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
}

View File

@@ -1,85 +0,0 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/directory",
"produces": [
"application/json"
],
"apis": [
{
"path": "/directory/room/{roomAlias}",
"operations": [
{
"method": "GET",
"summary": "Get the room ID corresponding to this room alias.",
"notes": "Volatile: This API is likely to change.",
"type": "DirectoryResponse",
"nickname": "get_room_id_for_alias",
"parameters": [
{
"name": "roomAlias",
"description": "The room alias.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "PUT",
"summary": "Create a new mapping from room alias to room ID.",
"notes": "Volatile: This API is likely to change.",
"type": "void",
"nickname": "add_room_alias",
"parameters": [
{
"name": "roomAlias",
"description": "The room alias to set.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"description": "The room ID to set.",
"required": true,
"type": "RoomAliasRequest",
"paramType": "body"
}
]
}
]
}
],
"models": {
"DirectoryResponse": {
"id": "DirectoryResponse",
"properties": {
"room_id": {
"type": "string",
"description": "The fully-qualified room ID.",
"required": true
},
"servers": {
"type": "array",
"items": {
"$ref": "string"
},
"description": "A list of servers that know about this room.",
"required": true
}
}
},
"RoomAliasRequest": {
"id": "RoomAliasRequest",
"properties": {
"room_id": {
"type": "string",
"description": "The room ID to map the alias to.",
"required": true
}
}
}
}
}

View File

@@ -1,247 +0,0 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/events",
"produces": [
"application/json"
],
"apis": [
{
"path": "/events",
"operations": [
{
"method": "GET",
"summary": "Listen on the event stream",
"notes": "This can only be done by the logged in user. This will block until an event is received, or until the timeout is reached.",
"type": "PaginationChunk",
"nickname": "get_event_stream",
"parameters": [
{
"name": "from",
"description": "The token to stream from.",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "timeout",
"description": "The maximum time in milliseconds to wait for an event.",
"required": false,
"type": "integer",
"paramType": "query"
}
]
}
],
"responseMessages": [
{
"code": 400,
"message": "Bad pagination token."
}
]
},
{
"path": "/events/{eventId}",
"operations": [
{
"method": "GET",
"summary": "Get information about a single event.",
"notes": "Get information about a single event.",
"type": "Event",
"nickname": "get_event",
"parameters": [
{
"name": "eventId",
"description": "The event ID to get.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 404,
"message": "Event not found."
}
]
}
]
},
{
"path": "/initialSync",
"operations": [
{
"method": "GET",
"summary": "Get this user's current state.",
"notes": "Get this user's current state.",
"type": "InitialSyncResponse",
"nickname": "initial_sync",
"parameters": [
{
"name": "limit",
"description": "The maximum number of messages to return for each room.",
"type": "integer",
"paramType": "query",
"required": false
}
]
}
]
},
{
"path": "/publicRooms",
"operations": [
{
"method": "GET",
"summary": "Get a list of publicly visible rooms.",
"type": "PublicRoomsPaginationChunk",
"nickname": "get_public_room_list"
}
]
}
],
"models": {
"PaginationChunk": {
"id": "PaginationChunk",
"properties": {
"start": {
"type": "string",
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
"required": true
},
"end": {
"type": "string",
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
"required": true
},
"chunk": {
"type": "array",
"description": "An array of events.",
"required": true,
"items": {
"$ref": "Event"
}
}
}
},
"Event": {
"id": "Event",
"properties": {
"event_id": {
"type": "string",
"description": "An ID which uniquely identifies this event.",
"required": true
},
"room_id": {
"type": "string",
"description": "The room in which this event occurred.",
"required": true
}
}
},
"PublicRoomInfo": {
"id": "PublicRoomInfo",
"properties": {
"aliases": {
"type": "array",
"description": "A list of room aliases for this room.",
"items": {
"$ref": "string"
}
},
"name": {
"type": "string",
"description": "The name of the room, as given by the m.room.name state event."
},
"room_id": {
"type": "string",
"description": "The room ID for this public room.",
"required": true
},
"topic": {
"type": "string",
"description": "The topic of this room, as given by the m.room.topic state event."
}
}
},
"PublicRoomsPaginationChunk": {
"id": "PublicRoomsPaginationChunk",
"properties": {
"start": {
"type": "string",
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
"required": true
},
"end": {
"type": "string",
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
"required": true
},
"chunk": {
"type": "array",
"description": "A list of public room data.",
"required": true,
"items": {
"$ref": "PublicRoomInfo"
}
}
}
},
"InitialSyncResponse": {
"id": "InitialSyncResponse",
"properties": {
"end": {
"type": "string",
"description": "A streaming token which can be used with /events to continue from this snapshot of data.",
"required": true
},
"presence": {
"type": "array",
"description": "A list of presence events.",
"items": {
"$ref": "Event"
},
"required": false
},
"rooms": {
"type": "array",
"description": "A list of initial sync room data.",
"required": false,
"items": {
"$ref": "InitialSyncRoomData"
}
}
}
},
"InitialSyncRoomData": {
"id": "InitialSyncRoomData",
"properties": {
"membership": {
"type": "string",
"description": "This user's membership state in this room.",
"required": true
},
"room_id": {
"type": "string",
"description": "The ID of this room.",
"required": true
},
"messages": {
"type": "PaginationChunk",
"description": "The most recent messages for this room, governed by the limit parameter.",
"required": false
},
"state": {
"type": "array",
"description": "A list of state events representing the current state of the room.",
"required": false,
"items": {
"$ref": "Event"
}
}
}
}
}
}

View File

@@ -1,120 +0,0 @@
{
"apiVersion": "1.0.0",
"apis": [
{
"operations": [
{
"method": "GET",
"nickname": "get_login_info",
"notes": "All login stages MUST be mentioned if there is >1 login type.",
"summary": "Get the login mechanism to use when logging in.",
"type": "LoginFlows"
},
{
"method": "POST",
"nickname": "submit_login",
"notes": "If this is part of a multi-stage login, there MUST be a 'session' key.",
"parameters": [
{
"description": "A login submission",
"name": "body",
"paramType": "body",
"required": true,
"type": "LoginSubmission"
}
],
"responseMessages": [
{
"code": 400,
"message": "Bad login type"
},
{
"code": 400,
"message": "Missing JSON keys"
}
],
"summary": "Submit a login action.",
"type": "LoginResult"
}
],
"path": "/login"
}
],
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"consumes": [
"application/json"
],
"models": {
"LoginFlows": {
"id": "LoginFlows",
"properties": {
"flows": {
"description": "A list of valid login flows.",
"type": "array",
"items": {
"$ref": "LoginInfo"
}
}
}
},
"LoginInfo": {
"id": "LoginInfo",
"properties": {
"stages": {
"description": "Multi-stage login only: An array of all the login types required to login.",
"items": {
"$ref": "string"
},
"type": "array"
},
"type": {
"description": "The login type that must be used when logging in.",
"type": "string"
}
}
},
"LoginResult": {
"id": "LoginResult",
"properties": {
"access_token": {
"description": "The access token for this user's login if this is the final stage of the login process.",
"type": "string"
},
"user_id": {
"description": "The user's fully-qualified user ID.",
"type": "string"
},
"next": {
"description": "Multi-stage login only: The next login type to submit.",
"type": "string"
},
"session": {
"description": "Multi-stage login only: The session token to send when submitting the next login type.",
"type": "string"
}
}
},
"LoginSubmission": {
"id": "LoginSubmission",
"properties": {
"type": {
"description": "The type of login being submitted.",
"type": "string"
},
"session": {
"description": "Multi-stage login only: The session token from an earlier login stage.",
"type": "string"
},
"_login_type_defined_keys_": {
"description": "Keys as defined by the specified login type, e.g. \"user\", \"password\""
}
}
}
},
"produces": [
"application/json"
],
"resourcePath": "/login",
"swaggerVersion": "1.2"
}

View File

@@ -1,164 +0,0 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/presence",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"apis": [
{
"path": "/presence/{userId}/status",
"operations": [
{
"method": "PUT",
"summary": "Update this user's presence state.",
"notes": "This can only be done by the logged in user.",
"type": "void",
"nickname": "update_presence",
"parameters": [
{
"name": "body",
"description": "The new presence state",
"required": true,
"type": "PresenceUpdate",
"paramType": "body"
},
{
"name": "userId",
"description": "The user whose presence to set.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get this user's presence state.",
"notes": "Get this user's presence state.",
"type": "PresenceUpdate",
"nickname": "get_presence",
"parameters": [
{
"name": "userId",
"description": "The user whose presence to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/presence/list/{userId}",
"operations": [
{
"method": "GET",
"summary": "Retrieve a list of presences for all of this user's friends.",
"notes": "",
"type": "array",
"items": {
"$ref": "Presence"
},
"nickname": "get_presence_list",
"parameters": [
{
"name": "userId",
"description": "The user whose presence list to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "POST",
"summary": "Add or remove users from this presence list.",
"notes": "Add or remove users from this presence list.",
"type": "void",
"nickname": "modify_presence_list",
"parameters": [
{
"name": "userId",
"description": "The user whose presence list is being modified.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"description": "The modifications to make to this presence list.",
"required": true,
"type": "PresenceListModifications",
"paramType": "body"
}
]
}
]
}
],
"models": {
"PresenceUpdate": {
"id": "PresenceUpdate",
"properties": {
"presence": {
"type": "string",
"description": "Enum: The presence state.",
"enum": [
"offline",
"unavailable",
"online",
"free_for_chat"
]
},
"status_msg": {
"type": "string",
"description": "The user-defined message associated with this presence state."
}
},
"subTypes": [
"Presence"
]
},
"Presence": {
"id": "Presence",
"properties": {
"last_active_ago": {
"type": "integer",
"format": "int64",
"description": "The last time this user performed an action on their home server."
},
"user_id": {
"type": "string",
"description": "The fully qualified user ID"
}
}
},
"PresenceListModifications": {
"id": "PresenceListModifications",
"properties": {
"invite": {
"type": "array",
"description": "A list of user IDs to add to the list.",
"items": {
"type": "string",
"description": "A fully qualified user ID."
}
},
"drop": {
"type": "array",
"description": "A list of user IDs to remove from the list.",
"items": {
"type": "string",
"description": "A fully qualified user ID."
}
}
}
}
}
}

View File

@@ -1,122 +0,0 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/profile",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"apis": [
{
"path": "/profile/{userId}/displayname",
"operations": [
{
"method": "PUT",
"summary": "Set a display name.",
"notes": "This can only be done by the logged in user.",
"type": "void",
"nickname": "set_display_name",
"parameters": [
{
"name": "body",
"description": "The new display name for this user.",
"required": true,
"type": "DisplayName",
"paramType": "body"
},
{
"name": "userId",
"description": "The user whose display name to set.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get a display name.",
"notes": "This can be done by anyone.",
"type": "DisplayName",
"nickname": "get_display_name",
"parameters": [
{
"name": "userId",
"description": "The user whose display name to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/profile/{userId}/avatar_url",
"operations": [
{
"method": "PUT",
"summary": "Set an avatar URL.",
"notes": "This can only be done by the logged in user.",
"type": "void",
"nickname": "set_avatar_url",
"parameters": [
{
"name": "body",
"description": "The new avatar url for this user.",
"required": true,
"type": "AvatarUrl",
"paramType": "body"
},
{
"name": "userId",
"description": "The user whose avatar url to set.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get an avatar url.",
"notes": "This can be done by anyone.",
"type": "AvatarUrl",
"nickname": "get_avatar_url",
"parameters": [
{
"name": "userId",
"description": "The user whose avatar url to get.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
}
],
"models": {
"DisplayName": {
"id": "DisplayName",
"properties": {
"displayname": {
"type": "string",
"description": "The textual display name"
}
}
},
"AvatarUrl": {
"id": "AvatarUrl",
"properties": {
"avatar_url": {
"type": "string",
"description": "A url to an image representing an avatar."
}
}
}
}
}

View File

@@ -1,79 +0,0 @@
{
"apiVersion": "1.0.0",
"apis": [
{
"operations": [
{
"method": "POST",
"nickname": "register",
"notes": "Volatile: This API is likely to change.",
"parameters": [
{
"description": "A registration request",
"name": "body",
"paramType": "body",
"required": true,
"type": "RegistrationRequest"
}
],
"responseMessages": [
{
"code": 400,
"message": "No JSON object."
},
{
"code": 400,
"message": "User ID must only contain characters which do not require url encoding."
},
{
"code": 400,
"message": "User ID already taken."
}
],
"summary": "Register with the home server.",
"type": "RegistrationResponse"
}
],
"path": "/register"
}
],
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"consumes": [
"application/json"
],
"models": {
"RegistrationResponse": {
"id": "RegistrationResponse",
"properties": {
"access_token": {
"description": "The access token for this user.",
"type": "string"
},
"user_id": {
"description": "The fully-qualified user ID.",
"type": "string"
},
"home_server": {
"description": "The name of the home server.",
"type": "string"
}
}
},
"RegistrationRequest": {
"id": "RegistrationRequest",
"properties": {
"user_id": {
"description": "The desired user ID. If not specified, a random user ID will be allocated.",
"type": "string",
"required": false
}
}
}
},
"produces": [
"application/json"
],
"resourcePath": "/register",
"swaggerVersion": "1.2"
}

View File

@@ -1,977 +0,0 @@
{
"apiVersion": "1.0.0",
"swaggerVersion": "1.2",
"basePath": "http://localhost:8008/_matrix/client/api/v1",
"resourcePath": "/rooms",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"authorizations": {
"token": []
},
"apis": [
{
"path": "/rooms/{roomId}/send/{eventType}",
"operations": [
{
"method": "POST",
"summary": "Send a generic non-state event to this room.",
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
"type": "EventId",
"nickname": "send_non_state_event",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The event contents",
"required": true,
"type": "EventContent",
"paramType": "body"
},
{
"name": "roomId",
"description": "The room to send the message in.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "eventType",
"description": "The type of event to send.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/rooms/{roomId}/state/{eventType}/{stateKey}",
"operations": [
{
"method": "PUT",
"summary": "Send a generic state event to this room.",
"notes": "The state key can be omitted, such that you can PUT to /rooms/{roomId}/state/{eventType}. The state key defaults to a 0 length string in this case.",
"type": "void",
"nickname": "send_state_event",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The event contents",
"required": true,
"type": "EventContent",
"paramType": "body"
},
{
"name": "roomId",
"description": "The room to send the message in.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "eventType",
"description": "The type of event to send.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "stateKey",
"description": "An identifier used to specify clobbering semantics. State events with the same (roomId, eventType, stateKey) will be replaced.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/rooms/{roomId}/send/m.room.message",
"operations": [
{
"method": "POST",
"summary": "Send a message in this room.",
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
"type": "EventId",
"nickname": "send_message",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The message contents",
"required": true,
"type": "Message",
"paramType": "body"
},
{
"name": "roomId",
"description": "The room to send the message in.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/rooms/{roomId}/state/m.room.topic",
"operations": [
{
"method": "PUT",
"summary": "Set the topic for this room.",
"notes": "Set the topic for this room.",
"type": "void",
"nickname": "set_topic",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The topic contents",
"required": true,
"type": "Topic",
"paramType": "body"
},
{
"name": "roomId",
"description": "The room to set the topic in.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get the topic for this room.",
"notes": "Get the topic for this room.",
"type": "Topic",
"nickname": "get_topic",
"parameters": [
{
"name": "roomId",
"description": "The room to get topic in.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 404,
"message": "Topic not found."
}
]
}
]
},
{
"path": "/rooms/{roomId}/state/m.room.name",
"operations": [
{
"method": "PUT",
"summary": "Set the name of this room.",
"notes": "Set the name of this room.",
"type": "void",
"nickname": "set_room_name",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The name contents",
"required": true,
"type": "RoomName",
"paramType": "body"
},
{
"name": "roomId",
"description": "The room to set the name of.",
"required": true,
"type": "string",
"paramType": "path"
}
]
},
{
"method": "GET",
"summary": "Get the room's name.",
"notes": "",
"type": "RoomName",
"nickname": "get_room_name",
"parameters": [
{
"name": "roomId",
"description": "The room to get the name of.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 404,
"message": "Name not found."
}
]
}
]
},
{
"path": "/rooms/{roomId}/send/m.room.message.feedback",
"operations": [
{
"method": "POST",
"summary": "Send feedback to a message.",
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
"type": "EventId",
"nickname": "send_feedback",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The feedback contents",
"required": true,
"type": "Feedback",
"paramType": "body"
},
{
"name": "roomId",
"description": "The room to send the feedback in.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 400,
"message": "Bad feedback type."
}
]
}
]
},
{
"path": "/rooms/{roomId}/invite",
"operations": [
{
"method": "POST",
"summary": "Invite a user to this room.",
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
"type": "void",
"nickname": "invite",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "roomId",
"description": "The room which has this user.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"description": "The user to invite.",
"required": true,
"type": "InviteRequest",
"paramType": "body"
}
]
}
]
},
{
"path": "/rooms/{roomId}/join",
"operations": [
{
"method": "POST",
"summary": "Join this room.",
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
"type": "void",
"nickname": "join_room",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "roomId",
"description": "The room to join.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"required": true,
"type": "JoinRequest",
"paramType": "body"
}
]
}
]
},
{
"path": "/rooms/{roomId}/leave",
"operations": [
{
"method": "POST",
"summary": "Leave this room.",
"notes": "This operation can also be done as a PUT by suffixing /{txnId}.",
"type": "void",
"nickname": "leave",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "roomId",
"description": "The room to leave.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"required": true,
"type": "LeaveRequest",
"paramType": "body"
}
]
}
]
},
{
"path": "/rooms/{roomId}/ban",
"operations": [
{
"method": "POST",
"summary": "Ban a user in the room.",
"notes": "This operation can also be done as a PUT by suffixing /{txnId}. The caller must have the required power level to do this operation.",
"type": "void",
"nickname": "ban",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "roomId",
"description": "The room which has the user to ban.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "body",
"description": "The user to ban.",
"required": true,
"type": "BanRequest",
"paramType": "body"
}
]
}
]
},
{
"path": "/rooms/{roomId}/state/m.room.member/{userId}",
"operations": [
{
"method": "PUT",
"summary": "Change the membership state for a user in a room.",
"notes": "Change the membership state for a user in a room.",
"type": "void",
"nickname": "set_membership",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The new membership state",
"required": true,
"type": "Member",
"paramType": "body"
},
{
"name": "userId",
"description": "The user whose membership is being changed.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "roomId",
"description": "The room which has this user.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 400,
"message": "No membership key."
},
{
"code": 400,
"message": "Bad membership value."
},
{
"code": 403,
"message": "When inviting: You are not in the room."
},
{
"code": 403,
"message": "When inviting: <target> is already in the room."
},
{
"code": 403,
"message": "When joining: Cannot force another user to join."
},
{
"code": 403,
"message": "When joining: You are not invited to this room."
}
]
},
{
"method": "GET",
"summary": "Get the membership state of a user in a room.",
"notes": "Get the membership state of a user in a room.",
"type": "Member",
"nickname": "get_membership",
"parameters": [
{
"name": "userId",
"description": "The user whose membership state you want to get.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "roomId",
"description": "The room which has this user.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 404,
"message": "Member not found."
}
]
}
]
},
{
"path": "/join/{roomAliasOrId}",
"operations": [
{
"method": "POST",
"summary": "Join a room via a room alias or room ID.",
"notes": "Join a room via a room alias or room ID.",
"type": "JoinRoomInfo",
"nickname": "join",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "roomAliasOrId",
"description": "The room alias or room ID to join.",
"required": true,
"type": "string",
"paramType": "path"
}
],
"responseMessages": [
{
"code": 400,
"message": "Bad room alias."
}
]
}
]
},
{
"path": "/createRoom",
"operations": [
{
"method": "POST",
"summary": "Create a room.",
"notes": "Create a room.",
"type": "RoomInfo",
"nickname": "create_room",
"consumes": [
"application/json"
],
"parameters": [
{
"name": "body",
"description": "The desired configuration for the room. This operation can also be done as a PUT by suffixing /{txnId}.",
"required": true,
"type": "RoomConfig",
"paramType": "body"
}
],
"responseMessages": [
{
"code": 400,
"message": "Body must be JSON."
},
{
"code": 400,
"message": "Room alias already taken."
}
]
}
]
},
{
"path": "/rooms/{roomId}/messages",
"operations": [
{
"method": "GET",
"summary": "Get a list of messages for this room.",
"notes": "Get a list of messages for this room.",
"type": "MessagePaginationChunk",
"nickname": "get_messages",
"parameters": [
{
"name": "roomId",
"description": "The room to get messages in.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "from",
"description": "The token to start getting results from.",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "to",
"description": "The token to stop getting results at.",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "limit",
"description": "The maximum number of messages to return.",
"required": false,
"type": "integer",
"paramType": "query"
}
]
}
]
},
{
"path": "/rooms/{roomId}/members",
"operations": [
{
"method": "GET",
"summary": "Get a list of members for this room.",
"notes": "Get a list of members for this room.",
"type": "MemberPaginationChunk",
"nickname": "get_members",
"parameters": [
{
"name": "roomId",
"description": "The room to get a list of members from.",
"required": true,
"type": "string",
"paramType": "path"
},
{
"name": "from",
"description": "The token to start getting results from.",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "to",
"description": "The token to stop getting results at.",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "limit",
"description": "The maximum number of members to return.",
"required": false,
"type": "integer",
"paramType": "query"
}
]
}
]
},
{
"path": "/rooms/{roomId}/state",
"operations": [
{
"method": "GET",
"summary": "Get a list of all the current state events for this room.",
"notes": "NOT YET IMPLEMENTED.",
"type": "array",
"items": {
"$ref": "Event"
},
"nickname": "get_state_events",
"parameters": [
{
"name": "roomId",
"description": "The room to get a list of current state events from.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
},
{
"path": "/rooms/{roomId}/initialSync",
"operations": [
{
"method": "GET",
"summary": "Get all the current information for this room, including messages and state events.",
"notes": "NOT YET IMPLEMENTED.",
"type": "InitialSyncRoomData",
"nickname": "get_room_sync_data",
"parameters": [
{
"name": "roomId",
"description": "The room to get information for.",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
}
],
"models": {
"Topic": {
"id": "Topic",
"properties": {
"topic": {
"type": "string",
"description": "The topic text"
}
}
},
"RoomName": {
"id": "RoomName",
"properties": {
"name": {
"type": "string",
"description": "The human-readable name for the room. Can contain spaces."
}
}
},
"Message": {
"id": "Message",
"properties": {
"msgtype": {
"type": "string",
"description": "The type of message being sent, e.g. \"m.text\"",
"required": true
},
"_msgtype_defined_keys_": {
"description": "Additional keys as defined by the msgtype, e.g. \"body\""
}
}
},
"Feedback": {
"id": "Feedback",
"properties": {
"target_event_id": {
"type": "string",
"description": "The event ID being acknowledged.",
"required": true
},
"type": {
"type": "string",
"description": "The type of feedback. Either 'delivered' or 'read'.",
"required": true
}
}
},
"Member": {
"id": "Member",
"properties": {
"membership": {
"type": "string",
"description": "Enum: The membership state of this member.",
"enum": [
"invite",
"join",
"leave",
"ban"
]
}
}
},
"RoomInfo": {
"id": "RoomInfo",
"properties": {
"room_id": {
"type": "string",
"description": "The allocated room ID.",
"required": true
},
"room_alias": {
"type": "string",
"description": "The alias for the room.",
"required": false
}
}
},
"JoinRoomInfo": {
"id": "JoinRoomInfo",
"properties": {
"room_id": {
"type": "string",
"description": "The room ID joined, if joined via a room alias only.",
"required": true
}
}
},
"RoomConfig": {
"id": "RoomConfig",
"properties": {
"visibility": {
"type": "string",
"description": "Enum: The room visibility.",
"required": false,
"enum": [
"public",
"private"
]
},
"room_alias_name": {
"type": "string",
"description": "The alias to give the new room.",
"required": false
},
"name": {
"type": "string",
"description": "Sets the name of the room. Send a m.room.name event after creating the room with the 'name' key specified.",
"required": false
},
"topic": {
"type": "string",
"description": "Sets the topic for the room. Send a m.room.topic event after creating the room with the 'topic' key specified.",
"required": false
}
}
},
"PaginationRequest": {
"id": "PaginationRequest",
"properties": {
"from": {
"type": "string",
"description": "The token to start getting results from."
},
"to": {
"type": "string",
"description": "The token to stop getting results at."
},
"limit": {
"type": "integer",
"description": "The maximum number of entries to return."
}
}
},
"PaginationChunk": {
"id": "PaginationChunk",
"properties": {
"start": {
"type": "string",
"description": "A token which correlates to the first value in \"chunk\" for paginating.",
"required": true
},
"end": {
"type": "string",
"description": "A token which correlates to the last value in \"chunk\" for paginating.",
"required": true
}
},
"subTypes": [
"MessagePaginationChunk"
]
},
"MessagePaginationChunk": {
"id": "MessagePaginationChunk",
"properties": {
"chunk": {
"type": "array",
"description": "A list of message events.",
"items": {
"$ref": "MessageEvent"
},
"required": true
}
}
},
"MemberPaginationChunk": {
"id": "MemberPaginationChunk",
"properties": {
"chunk": {
"type": "array",
"description": "A list of member events.",
"items": {
"$ref": "MemberEvent"
},
"required": true
}
}
},
"Event": {
"id": "Event",
"properties": {
"event_id": {
"type": "string",
"description": "An ID which uniquely identifies this event. This is automatically set by the server.",
"required": true
},
"room_id": {
"type": "string",
"description": "The room in which this event occurred. This is automatically set by the server.",
"required": true
},
"type": {
"type": "string",
"description": "The event type.",
"required": true
}
},
"subTypes": [
"MessageEvent"
]
},
"EventId": {
"id": "EventId",
"properties": {
"event_id": {
"type": "string",
"description": "The allocated event ID for this event.",
"required": true
}
}
},
"EventContent": {
"id": "EventContent",
"properties": {
"__event_content_keys__": {
"type": "string",
"description": "Event-specific content keys and values.",
"required": false
}
}
},
"MessageEvent": {
"id": "MessageEvent",
"properties": {
"content": {
"type": "Message"
}
}
},
"MemberEvent": {
"id": "MemberEvent",
"properties": {
"content": {
"type": "Member"
}
}
},
"InviteRequest": {
"id": "InviteRequest",
"properties": {
"user_id": {
"type": "string",
"description": "The fully-qualified user ID."
}
}
},
"JoinRequest": {
"id": "JoinRequest",
"properties": {}
},
"LeaveRequest": {
"id": "LeaveRequest",
"properties": {}
},
"BanRequest": {
"id": "BanRequest",
"properties": {
"user_id": {
"type": "string",
"description": "The fully-qualified user ID."
},
"reason": {
"type": "string",
"description": "The reason for the ban."
}
}
},
"InitialSyncRoomData": {
"id": "InitialSyncRoomData",
"properties": {
"membership": {
"type": "string",
"description": "This user's membership state in this room.",
"required": true
},
"room_id": {
"type": "string",
"description": "The ID of this room.",
"required": true
},
"messages": {
"type": "MessagePaginationChunk",
"description": "The most recent messages for this room, governed by the limit parameter.",
"required": false
},
"state": {
"type": "array",
"description": "A list of state events representing the current state of the room.",
"required": false,
"items": {
"$ref": "Event"
}
}
}
}
}
}

View File

@@ -1,5 +0,0 @@
To get this running:
ln -s ../swagger_matrix
python -m SimpleHTTPServer
Go to http://localhost:8000/swagger.html

View File

@@ -1,38 +0,0 @@
// Backbone.js 0.9.2
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);

View File

@@ -1,16 +0,0 @@
/* latin */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 400;
src: local('Droid Sans'), local('DroidSans'), url(http://fonts.gstatic.com/s/droidsans/v5/s-BiyweUPV0v-yRb-cjciPk_vArhqVIZ0nv9q090hN8.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* latin */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 700;
src: local('Droid Sans Bold'), local('DroidSans-Bold'), url(http://fonts.gstatic.com/s/droidsans/v5/EFpQQyG9GqCrobXxL-KRMYWiMMZ7xLd792ULpGE4W_Y.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,18 +0,0 @@
/*
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
* http://benalman.com/projects/jquery-bbq-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);

View File

@@ -1 +0,0 @@
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);

View File

@@ -1,8 +0,0 @@
/*
jQuery Wiggle
Author: WonderGroup, Jordan Thomas
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
*/
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};

View File

@@ -1,125 +0,0 @@
/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,211 +0,0 @@
var appName;
var popupMask;
var popupDialog;
var clientId;
var realm;
function handleLogin() {
var scopes = [];
if(window.swaggerUi.api.authSchemes
&& window.swaggerUi.api.authSchemes.oauth2
&& window.swaggerUi.api.authSchemes.oauth2.scopes) {
scopes = window.swaggerUi.api.authSchemes.oauth2.scopes;
}
if(window.swaggerUi.api
&& window.swaggerUi.api.info) {
appName = window.swaggerUi.api.info.title;
}
if(popupDialog.length > 0)
popupDialog = popupDialog.last();
else {
popupDialog = $(
[
'<div class="api-popup-dialog">',
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
'<div class="api-popup-content">',
'<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
'<a href="#">Learn how to use</a>',
'</p>',
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
'<ul class="api-popup-scopes">',
'</ul>',
'<p class="error-msg"></p>',
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
'</div>',
'</div>'].join(''));
$(document.body).append(popupDialog);
popup = popupDialog.find('ul.api-popup-scopes').empty();
for (i = 0; i < scopes.length; i ++) {
scope = scopes[i];
str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
if (scope.description) {
str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
}
str += '</label></li>';
popup.append(str);
}
}
var $win = $(window),
dw = $win.width(),
dh = $win.height(),
st = $win.scrollTop(),
dlgWd = popupDialog.outerWidth(),
dlgHt = popupDialog.outerHeight(),
top = (dh -dlgHt)/2 + st,
left = (dw - dlgWd)/2;
popupDialog.css({
top: (top < 0? 0 : top) + 'px',
left: (left < 0? 0 : left) + 'px'
});
popupDialog.find('button.api-popup-cancel').click(function() {
popupMask.hide();
popupDialog.hide();
});
popupDialog.find('button.api-popup-authbtn').click(function() {
popupMask.hide();
popupDialog.hide();
var authSchemes = window.swaggerUi.api.authSchemes;
var host = window.location;
var redirectUrl = host.protocol + '//' + host.host + "/o2c.html";
var url = null;
var p = window.swaggerUi.api.authSchemes;
for (var key in p) {
if (p.hasOwnProperty(key)) {
var o = p[key].grantTypes;
for(var t in o) {
if(o.hasOwnProperty(t) && t === 'implicit') {
var dets = o[t];
url = dets.loginEndpoint.url + "?response_type=token";
window.swaggerUi.tokenName = dets.tokenName;
}
}
}
}
var scopes = []
var o = $('.api-popup-scopes').find('input:checked');
for(k =0; k < o.length; k++) {
scopes.push($(o[k]).attr("scope"));
}
window.enabledScopes=scopes;
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes);
window.open(url);
});
popupMask.show();
popupDialog.show();
return;
}
function handleLogout() {
for(key in window.authorizations.authz){
window.authorizations.remove(key)
}
window.enabledScopes = null;
$('.api-ic.ic-on').addClass('ic-off');
$('.api-ic.ic-on').removeClass('ic-on');
// set the info box
$('.api-ic.ic-warning').addClass('ic-error');
$('.api-ic.ic-warning').removeClass('ic-warning');
}
function initOAuth(opts) {
var o = (opts||{});
var errors = [];
appName = (o.appName||errors.push("missing appName"));
popupMask = (o.popupMask||$('#api-common-mask'));
popupDialog = (o.popupDialog||$('.api-popup-dialog'));
clientId = (o.clientId||errors.push("missing client id"));
realm = (o.realm||errors.push("missing realm"));
if(errors.length > 0){
log("auth unable initialize oauth: " + errors);
return;
}
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
$('.api-ic').click(function(s) {
if($(s.target).hasClass('ic-off'))
handleLogin();
else {
handleLogout();
}
false;
});
}
function onOAuthComplete(token) {
if(token) {
if(token.error) {
var checkbox = $('input[type=checkbox],.secured')
checkbox.each(function(pos){
checkbox[pos].checked = false;
});
alert(token.error);
}
else {
var b = token[window.swaggerUi.tokenName];
if(b){
// if all roles are satisfied
var o = null;
$.each($('.auth #api_information_panel'), function(k, v) {
var children = v;
if(children && children.childNodes) {
var requiredScopes = [];
$.each((children.childNodes), function (k1, v1){
var inner = v1.innerHTML;
if(inner)
requiredScopes.push(inner);
});
var diff = [];
for(var i=0; i < requiredScopes.length; i++) {
var s = requiredScopes[i];
if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
diff.push(s);
}
}
if(diff.length > 0){
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
$(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
// sorry, not all scopes are satisfied
$(o).find('.api-ic').addClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
else {
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
$(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
// all scopes are satisfied
$(o).find('.api-ic').addClass('ic-info');
$(o).find('.api-ic').removeClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
}
});
window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +0,0 @@
// Underscore.js 1.3.3
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

View File

@@ -1,78 +0,0 @@
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Matrix Client-Server API Documentation</title>
<link href="./files/css" rel="stylesheet" type="text/css">
<link href="./files/reset.css" media="screen" rel="stylesheet" type="text/css">
<link href="./files/screen.css" media="screen" rel="stylesheet" type="text/css">
<link href="./files/reset.css" media="print" rel="stylesheet" type="text/css">
<link href="./files/screen.css" media="print" rel="stylesheet" type="text/css">
<script type="text/javascript" src="./files/shred.bundle.js"></script>
<script src="./files/jquery-1.8.0.min.js" type="text/javascript"></script>
<script src="./files/jquery.slideto.min.js" type="text/javascript"></script>
<script src="./files/jquery.wiggle.min.js" type="text/javascript"></script>
<script src="./files/jquery.ba-bbq.min.js" type="text/javascript"></script>
<script src="./files/handlebars-1.0.0.js" type="text/javascript"></script>
<script src="./files/underscore-min.js" type="text/javascript"></script>
<script src="./files/backbone-min.js" type="text/javascript"></script>
<script src="./files/swagger.js" type="text/javascript"></script>
<script src="./files/swagger-ui.js" type="text/javascript"></script>
<script src="./files/highlight.7.3.pack.js" type="text/javascript"></script>
<!-- enabling this will enable oauth2 implicit scope support -->
<script src="./files/swagger-oauth.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
window.swaggerUi = new SwaggerUi({
url: "http://localhost:8000/swagger_matrix/api-docs",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function(swaggerApi, swaggerUi){
log("Loaded SwaggerUI");
if(typeof initOAuth == "function") {
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none"
});
$('#input_apiKey').change(function() {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if(key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("key", new ApiKeyAuthorization("access_token", key, "query"));
}
})
window.swaggerUi.load();
});
</script>
</head>
<body class="swagger-section">
<div id="header">
<div class="swagger-ui-wrap">
<a id="logo" href="http://swagger.wordnik.com/">swagger</a>
<form id="api_selector">
<div class="input"><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"></div>
<div class="input"><input placeholder="access_token" id="input_apiKey" name="apiKey" type="text"></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap message-fail">Can't read from server. It may not have the appropriate access-control-origin settings.</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body></html>

49
docs/code_style.rst Normal file
View File

@@ -0,0 +1,49 @@
Basically, PEP8
- NEVER tabs. 4 spaces to indent.
- Max line width: 79 chars (with flexibility to overflow by a "few chars" if
the overflowing content is not semantically significant and avoids an
explosion of vertical whitespace).
- Use camel case for class and type names
- Use underscores for functions and variables.
- Use double quotes.
- Use parentheses instead of '\\' for line continuation where ever possible
(which is pretty much everywhere)
- There should be max a single new line between:
- statements
- functions in a class
- There should be two new lines between:
- definitions in a module (e.g., between different classes)
- There should be spaces where spaces should be and not where there shouldn't be:
- a single space after a comma
- a single space before and after for '=' when used as assignment
- no spaces before and after for '=' for default values and keyword arguments.
- Indenting must follow PEP8; either hanging indent or multiline-visual indent
depending on the size and shape of the arguments and what makes more sense to
the author. In other words, both this::
print("I am a fish %s" % "moo")
and this::
print("I am a fish %s" %
"moo")
and this::
print(
"I am a fish %s" %
"moo"
)
...are valid, although given each one takes up 2x more vertical space than
the previous, it's up to the author's discretion as to which layout makes most
sense for their function invocation. (e.g. if they want to add comments
per-argument, or put expressions in the arguments, or group related arguments
together, or want to deliberately extend or preserve vertical/horizontal
space)
Comments should follow the google code style. This is so that we can generate
documentation with sphinx (http://sphinxcontrib-napoleon.readthedocs.org/en/latest/)
Code should pass pep8 --max-line-length=100 without any warnings.

View File

@@ -1,18 +0,0 @@
Basically, PEP8
- Max line width: 80 chars.
- Use camel case for class and type names
- Use underscores for functions and variables.
- Use double quotes.
- Use parentheses instead of '\' for line continuation where ever possible (which is pretty much everywhere)
- There should be max a single new line between:
- statements
- functions in a class
- There should be two new lines between:
- definitions in a module (e.g., between different classes)
- There should be spaces where spaces should be and not where there shouldn't be:
- a single space after a comma
- a single space before and after for '=' when used as assignment
- no spaces before and after for '=' for default values and keyword arguments.
Comments should follow the google code style. This is so that we can generate documentation with sphinx (http://sphinxcontrib-napoleon.readthedocs.org/en/latest/)

View File

@@ -1,43 +0,0 @@
===================
Documentation Style
===================
A brief single sentence to describe what this file contains; in this case a
description of the style to write documentation in.
Sections
========
Each section should be separated from the others by two blank lines. Headings
should be underlined using a row of equals signs (===). Paragraphs should be
separated by a single blank line, and wrap to no further than 80 columns.
[[TODO(username): if you want to leave some unanswered questions, notes for
further consideration, or other kinds of comment, use a TODO section. Make sure
to notate it with your name so we know who to ask about it!]]
Subsections
-----------
If required, subsections can use a row of dashes to underline their header. A
single blank line between subsections of a single section.
Bullet Lists
============
* Bullet lists can use asterisks with a single space either side.
* Another blank line between list elements.
Definition Lists
================
Terms:
Start in the first column, ending with a colon
Definitions:
Take a two space indent, following immediately from the term without a blank
line before it, but having a blank line afterwards.

27
docs/media_repository.rst Normal file
View File

@@ -0,0 +1,27 @@
Media Repository
================
*Synapse implementation-specific details for the media repository*
The media repository is where attachments and avatar photos are stored.
It stores attachment content and thumbnails for media uploaded by local users.
It caches attachment content and thumbnails for media uploaded by remote users.
Storage
-------
Each item of media is assigned a ``media_id`` when it is uploaded.
The ``media_id`` is a randomly chosen, URL safe 24 character string.
Metadata such as the MIME type, upload time and length are stored in the
sqlite3 database indexed by ``media_id``.
Content is stored on the filesystem under a ``"local_content"`` directory.
Thumbnails are stored under a ``"local_thumbnails"`` directory.
The item with ``media_id`` ``"aabbccccccccdddddddddddd"`` is stored under
``"local_content/aa/bb/ccccccccdddddddddddd"``. Its thumbnail with width
``128`` and height ``96`` and type ``"image/jpeg"`` is stored under
``"local_thumbnails/aa/bb/ccccccccdddddddddddd/128-96-image-jpeg"``
Remote content is cached under ``"remote_content"`` directory. Each item of
remote content is assigned a local "``filesystem_id``" to ensure that the
directory structure ``"remote_content/server_name/aa/bb/ccccccccdddddddddddd"``
is appropriate. Thumbnails for remote content are stored under
``"remote_thumbnails/server_name/..."``

View File

@@ -1,249 +0,0 @@
========
Presence
========
A description of presence information and visibility between users.
Overview
========
Each user has the concept of Presence information. This encodes a sense of the
"availability" of that user, suitable for display on other user's clients.
Presence Information
====================
The basic piece of presence information is an enumeration of a small set of
state; such as "free to chat", "online", "busy", or "offline". The default state
unless the user changes it is "online". Lower states suggest some amount of
decreased availability from normal, which might have some client-side effect
like muting notification sounds and suggests to other users not to bother them
unless it is urgent. Equally, the "free to chat" state exists to let the user
announce their general willingness to receive messages moreso than default.
Home servers should also allow a user to set their state as "hidden" - a state
which behaves as offline, but allows the user to see the client state anyway and
generally interact with client features such as reading message history or
accessing contacts in the address book.
This basic state field applies to the user as a whole, regardless of how many
client devices they have connected. The home server should synchronise this
status choice among multiple devices to ensure the user gets a consistent
experience.
Idle Time
---------
As well as the basic state field, the presence information can also show a sense
of an "idle timer". This should be maintained individually by the user's
clients, and the homeserver can take the highest reported time as that to
report. Likely this should be presented in fairly coarse granularity; possibly
being limited to letting the home server automatically switch from a "free to
chat" or "online" mode into "idle".
When a user is offline, the Home Server can still report when the user was last
seen online, again perhaps in a somewhat coarse manner.
Device Type
-----------
Client devices that may limit the user experience somewhat (such as "mobile"
devices with limited ability to type on a real keyboard or read large amounts of
text) should report this to the home server, as this is also useful information
to report as "presence" if the user cannot be expected to provide a good typed
response to messages.
Presence List
=============
Each user's home server stores a "presence list" for that user. This stores a
list of other user IDs the user has chosen to add to it (remembering any ACL
Pointer if appropriate).
To be added to a contact list, the user being added must grant permission. Once
granted, both user's HS(es) store this information, as it allows the user who
has added the contact some more abilities; see below. Since such subscriptions
are likely to be bidirectional, HSes may wish to automatically accept requests
when a reverse subscription already exists.
As a convenience, presence lists should support the ability to collect users
into groups, which could allow things like inviting the entire group to a new
("ad-hoc") chat room, or easy interaction with the profile information ACL
implementation of the HS.
Presence and Permissions
========================
For a viewing user to be allowed to see the presence information of a target
user, either
* The target user has allowed the viewing user to add them to their presence
list, or
* The two users share at least one room in common
In the latter case, this allows for clients to display some minimal sense of
presence information in a user list for a room.
Home servers can also use the user's choice of presence state as a signal for
how to handle new private one-to-one chat message requests. For example, it
might decide:
"free to chat": accept anything
"online": accept from anyone in my addres book list
"busy": accept from anyone in this "important people" group in my address
book list
API Efficiency
==============
A simple implementation of presence messaging has the ability to cause a large
amount of Internet traffic relating to presence updates. In order to minimise
the impact of such a feature, the following observations can be made:
* There is no point in a Home Server polling status for peers in a user's
presence list if the user has no clients connected that care about it.
* It is highly likely that most presence subscriptions will be symmetric - a
given user watching another is likely to in turn be watched by that user.
* It is likely that most subscription pairings will be between users who share
at least one Room in common, and so their Home Servers are actively
exchanging message PDUs or transactions relating to that Room.
* Presence update messages do not need realtime guarantees. It is acceptable to
delay delivery of updates for some small amount of time (10 seconds to a
minute).
The general model of presence information is that of a HS registering its
interest in receiving presence status updates from other HSes, which then
promise to send them when required. Rather than actively polling for the
currentt state all the time, HSes can rely on their relative stability to only
push updates when required.
A Home Server should not rely on the longterm validity of this presence
information, however, as this would not cover such cases as a user's server
crashing and thus failing to inform their peers that users it used to host are
no longer available online. Therefore, each promise of future updates should
carry with a timeout value (whether explicit in the message, or implicit as some
defined default in the protocol), after which the receiving HS should consider
the information potentially stale and request it again.
However, because of the likelyhood that two home servers are exchanging messages
relating to chat traffic in a room common to both of them, the ongoing receipt
of these messages can be taken by each server as an implicit notification that
the sending server is still up and running, and therefore that no status changes
have happened; because if they had the server would have sent them. A second,
larger timeout should be applied to this implicit inference however, to protect
against implementation bugs or other reasons that the presence state cache may
become invalid; eventually the HS should re-enquire the current state of users
and update them with its own.
The following workflows can therefore be used to handle presence updates:
1 When a user first appears online their HS sends a message to each other HS
containing at least one user to be watched; each message carrying both a
notification of the sender's new online status, and a request to obtain and
watch the target users' presence information. This message implicitly
promises the sending HS will now push updates to the target HSes.
2 The target HSes then respond a single message each, containing the current
status of the requested user(s). These messages too implicitly promise the
target HSes will themselves push updates to the sending HS.
As these messages arrive at the sending user's HS they can be pushed to the
user's client(s), possibly batched again to ensure not too many small
messages which add extra protocol overheads.
At this point, all the user's clients now have the current presence status
information for this moment in time, and have promised to send each other
updates in future.
3 The HS maintains two watchdog timers per peer HS it is exchanging presence
information with. The first timer should have a relatively small expiry
(perhaps 1 minute), and the second timer should have a much longer time
(perhaps 1 hour).
4 Any time any kind of message is received from a peer HS, the short-term
presence timer associated with it is reset.
5 Whenever either of these timers expires, an HS should push a status reminder
to the target HS whose timer has now expired, and request again from that
server the status of the subscribed users.
6 On receipt of one of these presence status reminders, an HS can reset both
of its presence watchdog timers.
To avoid bursts of traffic, implementations should attempt to stagger the expiry
of the longer-term watchdog timers for different peer HSes.
When individual users actively change their status (either by explicit requests
from clients, or inferred changes due to idle timers or client timeouts), the HS
should batch up any status changes for some reasonable amount of time (10
seconds to a minute). This allows for reduced protocol overheads in the case of
multiple messages needing to be sent to the same peer HS; as is the likely
scenario in many cases, such as a given human user having multiple user
accounts.
API Requirements
================
The data model presented here puts the following requirements on the APIs:
Client-Server
-------------
Requests that a client can make to its Home Server
* get/set current presence state
Basic enumeration + ability to set a custom piece of text
* report per-device idle time
After some (configurable?) idle time the device should send a single message
to set the idle duration. The HS can then infer a "start of idle" instant and
use that to keep the device idleness up to date. At some later point the
device can cancel this idleness.
* report per-device type
Inform the server that this device is a "mobile" device, or perhaps some
other to-be-defined category of reduced capability that could be presented to
other users.
* start/stop presence polling for my presence list
It is likely that these messages could be implicitly inferred by other
messages, though having explicit control is always useful.
* get my presence list
[implicit poll start?]
It is possible that the HS doesn't yet have current presence information when
the client requests this. There should be a "don't know" type too.
* add/remove a user to my presence list
Server-Server
-------------
Requests that Home Servers make to others
* request permission to add a user to presence list
* allow/deny a request to add to a presence list
* perform a combined presence state push and subscription request
For each sending user ID, the message contains their new status.
For each receiving user ID, the message should contain an indication on
whether the sending server is also interested in receiving status from that
user; either as an immediate update response now, or as a promise to send
future updates.
Server to Client
----------------
[[TODO(paul): There also needs to be some way for a user's HS to push status
updates of the presence list to clients, but the general server-client event
model currently lacks a space to do that.]]

View File

@@ -1,232 +0,0 @@
========
Profiles
========
A description of Synapse user profile metadata support.
Overview
========
Internally within Synapse users are referred to by an opaque ID, which consists
of some opaque localpart combined with the domain name of their home server.
Obviously this does not yield a very nice user experience; users would like to
see readable names for other users that are in some way meaningful to them.
Additionally, users like to be able to publish "profile" details to inform other
users of other information about them.
It is also conceivable that since we are attempting to provide a
worldwide-applicable messaging system, that users may wish to present different
subsets of information in their profile to different other people, from a
privacy and permissions perspective.
A Profile consists of a display name, an (optional?) avatar picture, and a set
of other metadata fields that the user may wish to publish (email address, phone
numbers, website URLs, etc...). We put no requirements on the display name other
than it being a valid Unicode string. Since it is likely that users will end up
having multiple accounts (perhaps by necessity of being hosted in multiple
places, perhaps by choice of wanting multiple distinct identifies), it would be
useful that a metadata field type exists that can refer to another Synapse User
ID, so that clients and HSes can make use of this information.
Metadata Fields
---------------
[[TODO(paul): Likely this list is incomplete; more fields can be defined as we
think of them. At the very least, any sort of supported ID for the 3rd Party ID
servers should be accounted for here.]]
* Synapse Directory Server username(s)
* Email address
* Phone number - classify "home"/"work"/"mobile"/custom?
* Twitter/Facebook/Google+/... social networks
* Location - keep this deliberately vague to allow people to choose how
granular it is
* "Bio" information - date of birth, etc...
* Synapse User ID of another account
* Web URL
* Freeform description text
Visibility Permissions
======================
A home server implementation could offer the ability to set permissions on
limited visibility of those fields. When another user requests access to the
target user's profile, their own identity should form part of that request. The
HS implementation can then decide which fields to make available to the
requestor.
A particular detail of implementation could allow the user to create one or more
ACLs; where each list is granted permission to see a given set of non-public
fields (compare to Google+ Circles) and contains a set of other people allowed
to use it. By giving these ACLs strong identities within the HS, they can be
referenced in communications with it, granting other users who encounter these
the "ACL Token" to use the details in that ACL.
If we further allow an ACL Token to be present on Room join requests or stored
by 3PID servers, then users of these ACLs gain the extra convenience of not
having to manually curate people in the access list; anyone in the room or with
knowledge of the 3rd Party ID is automatically granted access. Every HS and
client implementation would have to be aware of the existence of these ACL
Token, and include them in requests if present, but not every HS implementation
needs to actually provide the full permissions model. This can be used as a
distinguishing feature among competing implementations. However, servers MUST
NOT serve profile information from a cache if there is a chance that its limited
understanding could lead to information leakage.
Client Concerns of Multiple Accounts
====================================
Because a given person may want to have multiple Synapse User accounts, client
implementations should allow the use of multiple accounts simultaneously
(especially in the field of mobile phone clients, which generally don't support
running distinct instances of the same application). Where features like address
books, presence lists or rooms are presented, the client UI should remember to
make distinct with user account is in use for each.
Directory Servers
=================
Directory Servers can provide a forward mapping from human-readable names to
User IDs. These can provide a service similar to giving domain-namespaced names
for Rooms; in this case they can provide a way for a user to reference their
User ID in some external form (e.g. that can be printed on a business card).
The format for Synapse user name will consist of a localpart specific to the
directory server, and the domain name of that directory server:
@localname:some.domain.name
The localname is separated from the domain name using a colon, so as to ensure
the localname can still contain periods, as users may want this for similarity
to email addresses or the like, which typically can contain them. The format is
also visually quite distinct from email addresses, phone numbers, etc... so
hopefully reasonably "self-describing" when written on e.g. a business card
without surrounding context.
[[TODO(paul): we might have to think about this one - too close to email?
Twitter? Also it suggests a format scheme for room names of
#localname:domain.name, which I quite like]]
Directory server administrators should be able to make some kind of policy
decision on how these are allocated. Servers within some "closed" domain (such
as company-specific ones) may wish to verify the validity of a mapping using
their own internal mechanisms; "public" naming servers can operate on a FCFS
basis. There are overlapping concerns here with the idea of the 3rd party
identity servers as well, though in this specific case we are creating a new
namespace to allocate names into.
It would also be nice from a user experience perspective if the profile that a
given name links to can also declare that name as part of its metadata.
Furthermore as a security and consistency perspective it would be nice if each
end (the directory server and the user's home server) check the validity of the
mapping in some way. This needs investigation from a security perspective to
ensure against spoofing.
One such model may be that the user starts by declaring their intent to use a
given user name link to their home server, which then contacts the directory
service. At some point later (maybe immediately for "public open FCFS servers",
maybe after some kind of human intervention for verification) the DS decides to
honour this link, and includes it in its served output. It should also tell the
HS of this fact, so that the HS can present this as fact when requested for the
profile information. For efficiency, it may further wish to provide the HS with
a cryptographically-signed certificate as proof, so the HS serving the profile
can provide that too when asked, avoiding requesting HSes from constantly having
to contact the DS to verify this mapping. (Note: This is similar to the security
model often applied in DNS to verify PTR <-> A bidirectional mappings).
Identity Servers
================
The identity servers should support the concept of pointing a 3PID being able to
store an ACL Token as well as the main User ID. It is however, beyond scope to
do any kind of verification that any third-party IDs that the profile is
claiming match up to the 3PID mappings.
User Interface and Expectations Concerns
========================================
Given the weak "security" of some parts of this model as compared to what users
might expect, some care should be taken on how it is presented to users,
specifically in the naming or other wording of user interface components.
Most notably mere knowledge of an ACL Pointer is enough to read the information
stored in it. It is possible that Home or Identity Servers could leak this
information, allowing others to see it. This is a security-vs-convenience
balancing choice on behalf of the user who would choose, or not, to make use of
such a feature to publish their information.
Additionally, unless some form of strong end-to-end user-based encryption is
used, a user of ACLs for information privacy has to trust other home servers not
to lie about the identify of the user requesting access to the Profile.
API Requirements
================
The data model presented here puts the following requirements on the APIs:
Client-Server
-------------
Requests that a client can make to its Home Server
* get/set my Display Name
This should return/take a simple "text/plain" field
* get/set my Avatar URL
The avatar image data itself is not stored by this API; we'll just store a
URL to let the clients fetch it. Optionally HSes could integrate this with
their generic content attacmhent storage service, allowing a user to set
upload their profile Avatar and update the URL to point to it.
* get/add/remove my metadata fields
Also we need to actually define types of metadata
* get another user's Display Name / Avatar / metadata fields
[[TODO(paul): At some later stage we should consider the API for:
* get/set ACL permissions on my metadata fields
* manage my ACL tokens
]]
Server-Server
-------------
Requests that Home Servers make to others
* get a user's Display Name / Avatar
* get a user's full profile - name/avatar + MD fields
This request must allow for specifying the User ID of the requesting user,
for permissions purposes. It also needs to take into account any ACL Tokens
the requestor has.
* push a change of Display Name to observers (overlaps with the presence API)
Room Event PDU Types
--------------------
Events that are pushed from Home Servers to other Home Servers or clients.
* user Display Name change
* user Avatar change
[[TODO(paul): should the avatar image itself be stored in all the room
histories? maybe this event should just be a hint to clients that they should
re-fetch the avatar image]]

View File

@@ -1,64 +0,0 @@
PUT /send/abc/ HTTP/1.1
Host: ...
Content-Length: ...
Content-Type: application/json
{
"origin": "localhost:5000",
"pdus": [
{
"content": {},
"context": "tng",
"depth": 12,
"is_state": false,
"origin": "localhost:5000",
"pdu_id": 1404381396854,
"pdu_type": "feedback",
"prev_pdus": [
[
"1404381395883",
"localhost:6000"
]
],
"ts": 1404381427581
}
],
"prev_ids": [
"1404381396852"
],
"ts": 1404381427823
}
HTTP/1.1 200 OK
...
======================================
GET /pull/-1/ HTTP/1.1
Host: ...
Content-Length: 0
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: application/json
{
origin: ...,
prev_ids: ...,
data: [
{
data_id: ...,
prev_pdus: [...],
depth: ...,
ts: ...,
context: ...,
origin: ...,
content: {
...
}
},
...,
]
}

View File

@@ -1,113 +0,0 @@
==================
Room Join Workflow
==================
An outline of the workflows required when a user joins a room.
Discovery
=========
To join a room, a user has to discover the room by some mechanism in order to
obtain the (opaque) Room ID and a candidate list of likely home servers that
contain it.
Sending an Invitation
---------------------
The most direct way a user discovers the existence of a room is from a
invitation from some other user who is a member of that room.
The inviter's HS sets the membership status of the invitee to "invited" in the
"m.members" state key by sending a state update PDU. The HS then broadcasts this
PDU among the existing members in the usual way. An invitation message is also
sent to the invited user, containing the Room ID and the PDU ID of this
invitation state change and potentially a list of some other home servers to use
to accept the invite. The user's client can then choose to display it in some
way to alert the user.
[[TODO(paul): At present, no API has been designed or described to actually send
that invite to the invited user. Likely it will be some facet of the larger
user-user API required for presence, profile management, etc...]]
Directory Service
-----------------
Alternatively, the user may discover the channel via a directory service; either
by performing a name lookup, or some kind of browse or search acitivty. However
this is performed, the end result is that the user's home server requests the
Room ID and candidate list from the directory service.
[[TODO(paul): At present, no API has been designed or described for this
directory service]]
Joining
=======
Once the ID and home servers are obtained, the user can then actually join the
room.
Accepting an Invite
-------------------
If a user has received and accepted an invitation to join a room, the invitee's
home server can now send an invite acceptance message to a chosen candidate
server from the list given in the invitation, citing also the PDU ID of the
invitation as "proof" of their invite. (This is required as due to late message
propagation it could be the case that the acceptance is received before the
invite by some servers). If this message is allowed by the candidate server, it
generates a new PDU that updates the invitee's membership status to "joined",
referring back to the acceptance PDU, and broadcasts that as a state change in
the usual way. The newly-invited user is now a full member of the room, and
state propagation proceeds as usual.
Joining a Public Room
---------------------
If a user has discovered the existence of a room they wish to join but does not
have an active invitation, they can request to join it directly by sending a
join message to a candidate server on the list provided by the directory
service. As this list may be out of date, the HS should be prepared to retry
other candidates if the chosen one is no longer aware of the room, because it
has no users as members in it.
Once a candidate server that is aware of the room has been found, it can
broadcast an update PDU to add the member into the "m.members" key setting their
state directly to "joined" (i.e. bypassing the two-phase invite semantics),
remembering to include the new user's HS in that list.
Knocking on a Semi-Public Room
------------------------------
If a user requests to join a room but the join mode of the room is "knock", the
join is not immediately allowed. Instead, if the user wishes to proceed, they
can instead post a "knock" message, which informs other members of the room that
the would-be joiner wishes to become a member and sets their membership value to
"knocked". If any of them wish to accept this, they can then send an invitation
in the usual way described above. Knowing that the user has already knocked and
expressed an interest in joining, the invited user's home server should
immediately accept that invitation on the user's behalf, and go on to join the
room in the usual way.
[[NOTE(Erik): Though this may confuse users who expect 'X has joined' to
actually be a user initiated action, i.e. they may expect that 'X' is actually
looking at synapse right now?]]
[[NOTE(paul): Yes, a fair point maybe we should suggest HSes don't do that, and
just offer an invite to the user as normal]]
Private and Non-Existent Rooms
------------------------------
If a user requests to join a room but the room is either unknown by the home
server receiving the request, or is known by the join mode is "invite" and the
user has not been invited, the server must respond that the room does not exist.
This is to prevent leaking information about the existence and identity of
private rooms.
Outstanding Questions
=====================
* Do invitations or knocks time out and expire at some point? If so when? Time
is hard in distributed systems.

View File

@@ -1,274 +0,0 @@
===========
Rooms Model
===========
A description of the general data model used to implement Rooms, and the
user-level visible effects and implications.
Overview
========
"Rooms" in Synapse are shared messaging channels over which all the participant
users can exchange messages. Rooms have an opaque persistent identify, a
globally-replicated set of state (consisting principly of a membership set of
users, and other management and miscellaneous metadata), and a message history.
Room Identity and Naming
========================
Rooms can be arbitrarily created by any user on any home server; at which point
the home server will sign the message that creates the channel, and the
fingerprint of this signature becomes the strong persistent identify of the
room. This now identifies the room to any home server in the network regardless
of its original origin. This allows the identify of the room to outlive any
particular server. Subject to appropriate permissions [to be discussed later],
any current member of a room can invite others to join it, can post messages
that become part of its history, and can change the persistent state of the room
(including its current set of permissions).
Home servers can provide a directory service, allowing a lookup from a
convenient human-readable form of room label to a room ID. This mapping is
scoped to the particular home server domain and so simply represents that server
administrator's opinion of what room should take that label; it does not have to
be globally replicated and does not form part of the stored state of that room.
This room name takes the form
#localname:some.domain.name
for similarity and consistency with user names on directories.
To join a room (and therefore to be allowed to inspect past history, post new
messages to it, and read its state), a user must become aware of the room's
fingerprint ID. There are two mechanisms to allow this:
* An invite message from someone else in the room
* A referral from a room directory service
As room IDs are opaque and ephemeral, they can serve as a mechanism to create
"ad-hoc" rooms deliberately unnamed, for small group-chats or even private
one-to-one message exchange.
Stored State and Permissions
============================
Every room has a globally-replicated set of stored state. This state is a set of
key/value or key/subkey/value pairs. The value of every (sub)key is a
JSON-representable object. The main key of a piece of stored state establishes
its meaning; some keys store sub-keys to allow a sub-structure within them [more
detail below]. Some keys have special meaning to Synapse, as they relate to
management details of the room itself, storing such details as user membership,
and permissions of users to alter the state of the room itself. Other keys may
store information to present to users, which the system does not directly rely
on. The key space itself is namespaced, allowing 3rd party extensions, subject
to suitable permission.
Permission management is based on the concept of "power-levels". Every user
within a room has an integer assigned, being their "power-level" within that
room. Along with its actual data value, each key (or subkey) also stores the
minimum power-level a user must have in order to write to that key, the
power-level of the last user who actually did write to it, and the PDU ID of
that state change.
To be accepted as valid, a change must NOT:
* Be made by a user having a power-level lower than required to write to the
state key
* Alter the required power-level for that state key to a value higher than the
user has
* Increase that user's own power-level
* Grant any other user a power-level higher than the level of the user making
the change
[[TODO(paul): consider if relaxations should be allowed; e.g. is the current
outright-winner allowed to raise their own level, to allow for "inflation"?]]
Room State Keys
===============
[[TODO(paul): if this list gets too big it might become necessary to move it
into its own doc]]
The following keys have special semantics or meaning to Synapse itself:
m.member (has subkeys)
Stores a sub-key for every Synapse User ID which is currently a member of
this room. Its value gives the membership type ("knocked", "invited",
"joined").
m.power_levels
Stores a mapping from Synapse User IDs to their power-level in the room. If
they are not present in this mapping, the default applies.
The reason to store this as a single value rather than a value with subkeys
is that updates to it are atomic; allowing a number of colliding-edit
problems to be avoided.
m.default_level
Gives the default power-level for members of the room that do not have one
specified in their membership key.
m.invite_level
If set, gives the minimum power-level required for members to invite others
to join, or to accept knock requests from non-members requesting access. If
absent, then invites are not allowed. An invitation involves setting their
membership type to "invited", in addition to sending the invite message.
m.join_rules
Encodes the rules on how non-members can join the room. Has the following
possibilities:
"public" - a non-member can join the room directly
"knock" - a non-member cannot join the room, but can post a single "knock"
message requesting access, which existing members may approve or deny
"invite" - non-members cannot join the room without an invite from an
existing member
"private" - nobody who is not in the 'may_join' list or already a member
may join by any mechanism
In any of the first three modes, existing members with sufficient permission
can send invites to non-members if allowed by the "m.invite_level" key. A
"private" room is not allowed to have the "m.invite_level" set.
A client may use the value of this key to hint at the user interface
expectations to provide; in particular, a private chat with one other use
might warrant specific handling in the client.
m.may_join
A list of User IDs that are always allowed to join the room, regardless of any
of the prevailing join rules and invite levels. These apply even to private
rooms. These are stored in a single list with normal update-powerlevel
permissions applied; users cannot arbitrarily remove themselves from the list.
m.add_state_level
The power-level required for a user to be able to add new state keys.
m.public_history
If set and true, anyone can request the history of the room, without needing
to be a member of the room.
m.archive_servers
For "public" rooms with public history, gives a list of home servers that
should be included in message distribution to the room, even if no users on
that server are present. These ensure that a public room can still persist
even if no users are currently members of it. This list should be consulted by
the dirctory servers as the candidate list they respond with.
The following keys are provided by Synapse for user benefit, but their value is
not otherwise used by Synapse.
m.name
Stores a short human-readable name for the room, such that clients can display
to a user to assist in identifying which room is which.
This name specifically is not the strong ID used by the message transport
system to refer to the room, because it may be changed from time to time.
m.topic
Stores the current human-readable topic
Room Creation Templates
=======================
A client (or maybe home server?) could offer a few templates for the creation of
new rooms. For example, for a simple private one-to-one chat the channel could
assign the creator a power-level of 1, requiring a level of 1 to invite, and
needing an invite before members can join. An invite is then sent to the other
party, and if accepted and the other user joins, the creator's power-level can
now be reduced to 0. This now leaves a room with two participants in it being
unable to add more.
Rooms that Continue History
===========================
An option that could be considered for room creation, is that when a new room is
created the creator could specify a PDU ID into an existing room, as the history
continuation point. This would be stored as an extra piece of meta-data on the
initial PDU of the room's creation. (It does not appear in the normal previous
PDU linkage).
This would allow users in rooms to "fork" a room, if it is considered that the
conversations in the room no longer fit its original purpose, and wish to
diverge. Existing permissions on the original room would continue to apply of
course, for viewing that history. If both rooms are considered "public" we might
also want to define a message to post into the original room to represent this
fork point, and give a reference to the new room.
User Direct Message Rooms
=========================
There is no need to build a mechanism for directly sending messages between
users, because a room can handle this ability. To allow direct user-to-user chat
messaging we simply need to be able to create rooms with specific set of
permissions to allow this direct messaging.
Between any given pair of user IDs that wish to exchange private messages, there
will exist a single shared Room, created lazily by either side. These rooms will
need a certain amount of special handling in both home servers and display on
clients, but as much as possible should be treated by the lower layers of code
the same as other rooms.
Specially, a client would likely offer a special menu choice associated with
another user (in room member lists, presence list, etc..) as "direct chat". That
would perform all the necessary steps to create the private chat room. Receiving
clients should display these in a special way too as the room name is not
important; instead it should distinguish them on the Display Name of the other
party.
Home Servers will need a client-API option to request setting up a new user-user
chat room, which will then need special handling within the server. It will
create a new room with the following
m.member: the proposing user
m.join_rules: "private"
m.may_join: both users
m.power_levels: empty
m.default_level: 0
m.add_state_level: 0
m.public_history: False
Having created the room, it can send an invite message to the other user in the
normal way - the room permissions state that no users can be set to the invited
state, but because they're in the may_join list then they'd be allowed to join
anyway.
In this arrangement there is now a room with both users may join but neither has
the power to invite any others. Both users now have the confidence that (at
least within the messaging system itself) their messages remain private and
cannot later be provably leaked to a third party. They can freely set the topic
or name if they choose and add or edit any other state of the room. The update
powerlevel of each of these fixed properties should be 1, to lock out the users
from being able to alter them.
Anti-Glare
==========
There exists the possibility of a race condition if two users who have no chat
history with each other simultaneously create a room and invite the other to it.
This is called a "glare" situation. There are two possible ideas for how to
resolve this:
* Each Home Server should persist the mapping of (user ID pair) to room ID, so
that duplicate requests can be suppressed. On receipt of a room creation
request that the HS thinks there already exists a room for, the invitation to
join can be rejected if:
a) the HS believes the sending user is already a member of the room (and
maybe their HS has forgotten this fact), or
b) the proposed room has a lexicographically-higher ID than the existing
room (to resolve true race condition conflicts)
* The room ID for a private 1:1 chat has a special form, determined by
concatenting the User IDs of both members in a deterministic order, such that
it doesn't matter which side creates it first; the HSes can just ignore
(or merge?) received PDUs that create the room twice.

View File

@@ -1,86 +0,0 @@
===========
Terminology
===========
A list of definitions of specific terminology used among these documents.
These terms were originally taken from the server-server documentation, and may
not currently match the exact meanings used in other places; though as a
medium-term goal we should encourage the unification of this terminology.
Terms
=====
Backfilling:
The process of synchronising historic state from one home server to another,
to backfill the event storage so that scrollback can be presented to the
client(s). (Formerly, and confusingly, called 'pagination')
Context:
A single human-level entity of interest (currently, a chat room)
EDU (Ephemeral Data Unit):
A message that relates directly to a given pair of home servers that are
exchanging it. EDUs are short-lived messages that related only to one single
pair of servers; they are not persisted for a long time and are not forwarded
on to other servers. Because of this, they have no internal ID nor previous
EDUs reference chain.
Event:
A record of activity that records a single thing that happened on to a context
(currently, a chat room). These are the "chat messages" that Synapse makes
available.
[[NOTE(paul): The current server-server implementation calls these simply
"messages" but the term is too ambiguous here; I've called them Events]]
PDU (Persistent Data Unit):
A message that relates to a single context, irrespective of the server that
is communicating it. PDUs either encode a single Event, or a single State
change. A PDU is referred to by its PDU ID; the pair of its origin server
and local reference from that server.
PDU ID:
The pair of PDU Origin and PDU Reference, that together globally uniquely
refers to a specific PDU.
PDU Origin:
The name of the origin server that generated a given PDU. This may not be the
server from which it has been received, due to the way they are copied around
from server to server. The origin always records the original server that
created it.
PDU Reference:
A local ID used to refer to a specific PDU from a given origin server. These
references are opaque at the protocol level, but may optionally have some
structured meaning within a given origin server or implementation.
Presence:
The concept of whether a user is currently online, how available they declare
they are, and so on. See also: doc/model/presence
Profile:
A set of metadata about a user, such as a display name, provided for the
benefit of other users. See also: doc/model/profiles
Room ID:
An opaque string (of as-yet undecided format) that identifies a particular
room and used in PDUs referring to it.
Room Alias:
A human-readable string of the form #name:some.domain that users can use as a
pointer to identify a room; a Directory Server will map this to its Room ID
State:
A set of metadata maintained about a Context, which is replicated among the
servers in addition to the history of Events.
User ID:
A string of the form @localpart:domain.name that identifies a user for
wire-protocol purposes. The localpart is meaningless outside of a particular
home server. This takes a human-readable form that end-users can use directly
if they so wish, avoiding the 3PIDs.
Transaction:
A message which relates to the communication between a given pair of servers.
A transaction contains possibly-empty lists of PDUs and EDUs.

View File

@@ -1,108 +0,0 @@
======================
Third Party Identities
======================
A description of how email addresses, mobile phone numbers and other third
party identifiers can be used to authenticate and discover users in Matrix.
Overview
========
New users need to authenticate their account. An email or SMS text message can
be a convenient form of authentication. Users already have email addresses
and phone numbers for contacts in their address book. They want to communicate
with those contacts in Matrix without manually exchanging a Matrix User ID with
them.
Third Party IDs
---------------
[[TODO(markjh): Describe the format of a 3PID]]
Third Party ID Associations
---------------------------
An Associaton is a binding between a Matrix User ID and a Third Party ID (3PID).
Each 3PID can be associated with one Matrix User ID at a time.
[[TODO(markjh): JSON format of the association.]]
Verification
------------
An Assocation must be verified by a trusted Verification Server. Email
addresses and phone numbers can be verified by sending a token to the address
which a client can supply to the verifier to confirm ownership.
An email Verification Server may be capable of verifying all email 3PIDs or may
be restricted to verifying addresses for a particular domain. A phone number
Verification Server may be capable of verifying all phone numbers or may be
restricted to verifying numbers for a given country or phone prefix.
Verification Servers fulfil a similar role to Certificate Authorities in PKI so
a similar level of vetting should be required before clients trust their
signatures.
A Verification Server may wish to check for existing Associations for a 3PID
before creating a new Association.
Discovery
---------
Users can discover Associations using a trusted Identity Server. Each
Association will be signed by the Identity Server. An Identity Server may store
the entire space of Associations or may delegate to other Identity Servers when
looking up Associations.
Each Association returned from an Identity Server must be signed by a
Verification Server. Clients should check these signatures.
Identity Servers fulfil a similar role to DNS servers.
Privacy
-------
A User may publish the association between their phone number and Matrix User ID
on the Identity Server without publishing the number in their Profile hosted on
their Home Server.
Identity Servers should refrain from publishing reverse mappings and should
take steps, such as rate limiting, to prevent attackers enumerating the space of
mappings.
Federation
==========
Delegation
----------
Verification Servers could delegate signing to another server by issuing
certificate to that server allowing it to verify and sign a subset of 3PID on
its behalf. It would be necessary to provide a language for describing which
subset of 3PIDs that server had authority to validate. Alternatively it could
delegate the verification step to another server but sign the resulting
association itself.
The 3PID space will have a heirachical structure like DNS so Identity Servers
can delegate lookups to other servers. An Identity Server should be prepared
to host or delegate any valid association within the subset of the 3PIDs it is
resonsible for.
Multiple Root Verification Servers
----------------------------------
There can be multiple root Verification Servers and an Association could be
signed by multiple servers if different clients trust different subsets of
the verification servers.
Multiple Root Identity Servers
------------------------------
There can be be multiple root Identity Servers. Clients will add each
Association to all root Identity Servers.
[[TODO(markjh): Describe how clients find the list of root Identity Servers]]

View File

@@ -1,59 +0,0 @@
Transaction
===========
Required keys:
============ =================== ===============================================
Key Type Description
============ =================== ===============================================
origin String DNS name of homeserver making this transaction.
ts Integer Timestamp in milliseconds on originating
homeserver when this transaction started.
previous_ids List of Strings List of transactions that were sent immediately
prior to this transaction.
pdus List of Objects List of updates contained in this transaction.
============ =================== ===============================================
PDU
===
Required keys:
============ ================== ================================================
Key Type Description
============ ================== ================================================
context String Event context identifier
origin String DNS name of homeserver that created this PDU.
pdu_id String Unique identifier for PDU within the context for
the originating homeserver.
ts Integer Timestamp in milliseconds on originating
homeserver when this PDU was created.
pdu_type String PDU event type.
prev_pdus List of Pairs The originating homeserver and PDU ids of the
of Strings most recent PDUs the homeserver was aware of for
this context when it made this PDU.
depth Integer The maximum depth of the previous PDUs plus one.
============ ================== ================================================
Keys for state updates:
================== ============ ================================================
Key Type Description
================== ============ ================================================
is_state Boolean True if this PDU is updating state.
state_key String Optional key identifying the updated state within
the context.
power_level Integer The asserted power level of the user performing
the update.
min_update Integer The required power level needed to replace this
update.
prev_state_id String The homeserver of the update this replaces
prev_state_origin String The PDU id of the update this replaces.
user String The user updating the state.
================== ============ ================================================

View File

@@ -1,231 +0,0 @@
===========================
Matrix Server-to-Server API
===========================
A description of the protocol used to communicate between Matrix home servers;
also known as Federation.
Overview
========
The server-server API is a mechanism by which two home servers can exchange
Matrix event messages, both as a real-time push of current events, and as a
historic fetching mechanism to synchronise past history for clients to view. It
uses HTTP connections between each pair of servers involved as the underlying
transport. Messages are exchanged between servers in real-time by active pushing
from each server's HTTP client into the server of the other. Queries to fetch
historic data for the purpose of back-filling scrollback buffers and the like
can also be performed.
{ Matrix clients } { Matrix clients }
^ | ^ |
| events | | events |
| V | V
+------------------+ +------------------+
| |---------( HTTP )---------->| |
| Home Server | | Home Server |
| |<--------( HTTP )-----------| |
+------------------+ +------------------+
There are three main kinds of communication that occur between home servers:
* Queries
These are single request/response interactions between a given pair of
servers, initiated by one side sending an HTTP request to obtain some
information, and responded by the other. They are not persisted and contain
no long-term significant history. They simply request a snapshot state at the
instant the query is made.
* EDUs - Ephemeral Data Units
These are notifications of events that are pushed from one home server to
another. They are not persisted and contain no long-term significant history,
nor does the receiving home server have to reply to them.
* PDUs - Persisted Data Units
These are notifications of events that are broadcast from one home server to
any others that are interested in the same "context" (namely, a Room ID).
They are persisted to long-term storage and form the record of history for
that context.
Where Queries are presented directly across the HTTP connection as GET requests
to specific URLs, EDUs and PDUs are further wrapped in an envelope called a
Transaction, which is transferred from the origin to the destination home server
using a PUT request.
Transactions and EDUs/PDUs
==========================
The transfer of EDUs and PDUs between home servers is performed by an exchange
of Transaction messages, which are encoded as JSON objects with a dict as the
top-level element, passed over an HTTP PUT request. A Transaction is meaningful
only to the pair of home servers that exchanged it; they are not globally-
meaningful.
Each transaction has an opaque ID and timestamp (UNIX epoch time in
milliseconds) generated by its origin server, an origin and destination server
name, a list of "previous IDs", and a list of PDUs - the actual message payload
that the Transaction carries.
{"transaction_id":"916d630ea616342b42e98a3be0b74113",
"ts":1404835423000,
"origin":"red",
"destination":"blue",
"prev_ids":["e1da392e61898be4d2009b9fecce5325"],
"pdus":[...],
"edus":[...]}
The "previous IDs" field will contain a list of previous transaction IDs that
the origin server has sent to this destination. Its purpose is to act as a
sequence checking mechanism - the destination server can check whether it has
successfully received that Transaction, or ask for a retransmission if not.
The "pdus" field of a transaction is a list, containing zero or more PDUs.[*]
Each PDU is itself a dict containing a number of keys, the exact details of
which will vary depending on the type of PDU. Similarly, the "edus" field is
another list containing the EDUs. This key may be entirely absent if there are
no EDUs to transfer.
(* Normally the PDU list will be non-empty, but the server should cope with
receiving an "empty" transaction, as this is useful for informing peers of other
transaction IDs they should be aware of. This effectively acts as a push
mechanism to encourage peers to continue to replicate content.)
All PDUs have an ID, a context, a declaration of their type, a list of other PDU
IDs that have been seen recently on that context (regardless of which origin
sent them), and a nested content field containing the actual event content.
[[TODO(paul): Update this structure so that 'pdu_id' is a two-element
[origin,ref] pair like the prev_pdus are]]
{"pdu_id":"a4ecee13e2accdadf56c1025af232176",
"context":"#example.green",
"origin":"green",
"ts":1404838188000,
"pdu_type":"m.text",
"prev_pdus":[["blue","99d16afbc857975916f1d73e49e52b65"]],
"content":...
"is_state":false}
In contrast to the transaction layer, it is important to note that the prev_pdus
field of a PDU refers to PDUs that any origin server has sent, rather than
previous IDs that this origin has sent. This list may refer to other PDUs sent
by the same origin as the current one, or other origins.
Because of the distributed nature of participants in a Matrix conversation, it
is impossible to establish a globally-consistent total ordering on the events.
However, by annotating each outbound PDU at its origin with IDs of other PDUs it
has received, a partial ordering can be constructed allowing causallity
relationships to be preserved. A client can then display these messages to the
end-user in some order consistent with their content and ensure that no message
that is semantically in reply of an earlier one is ever displayed before it.
PDUs fall into two main categories: those that deliver Events, and those that
synchronise State. For PDUs that relate to State synchronisation, additional
keys exist to support this:
{...,
"is_state":true,
"state_key":TODO
"power_level":TODO
"prev_state_id":TODO
"prev_state_origin":TODO}
[[TODO(paul): At this point we should probably have a long description of how
State management works, with descriptions of clobbering rules, power levels, etc
etc... But some of that detail is rather up-in-the-air, on the whiteboard, and
so on. This part needs refining. And writing in its own document as the details
relate to the server/system as a whole, not specifically to server-server
federation.]]
EDUs, by comparison to PDUs, do not have an ID, a context, or a list of
"previous" IDs. The only mandatory fields for these are the type, origin and
destination home server names, and the actual nested content.
{"edu_type":"m.presence",
"origin":"blue",
"destination":"orange",
"content":...}
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/
Body: JSON encoding of a single Transaction
Response: [[TODO(paul): I don't actually understand what
ReplicationLayer.on_transaction() is doing here, so I'm not sure what the
response ought to be]]
The transaction_id path argument will override any ID given in the JSON body.
The destination name will be set to that of the receiving server itself. Each
embedded PDU in the transaction body will be processed.
To fetch a particular PDU:
GET .../pdu/:origin/:pdu_id/
Response: JSON encoding of a single Transaction containing one PDU
Retrieves a given PDU from the server. The response will contain a single new
Transaction, inside which will be the requested PDU.
To fetch all the state of a given context:
GET .../state/:context/
Response: JSON encoding of a single Transaction containing multiple PDUs
Retrieves a snapshot of the entire current state of the given context. The
response will contain a single Transaction, inside which will be a list of
PDUs that encode the state.
To backfill events on a given context:
GET .../backfill/:context/
Query args: v, limit
Response: JSON encoding of a single Transaction containing multiple PDUs
Retrieves a sliding-window history of previous PDUs that occurred on the
given context. Starting from the PDU ID(s) given in the "v" argument, the
PDUs that preceeded it are retrieved, up to a total number given by the
"limit" argument. These are then returned in a new Transaction containing all
off the PDUs.
To stream events all the events:
GET .../pull/
Query args: origin, v
Response: JSON encoding of a single Transaction consisting of multiple PDUs
Retrieves all of the transactions later than any version given by the "v"
arguments. [[TODO(paul): I'm not sure what the "origin" argument does because
I think at some point in the code it's got swapped around.]]
To make a query:
GET .../query/:query_type
Query args: as specified by the individual query types
Response: JSON encoding of a response object
Performs a single query request on the receiving home server. The Query Type
part of the path specifies the kind of query being made, and its query
arguments have a meaning specific to that kind of query. The response is a
JSON-encoded object whose meaning also depends on the kind of query.

View File

@@ -1,11 +0,0 @@
Versioning is, like, hard for backfilling backwards because of the number of Home Servers involved.
The way we solve this is by doing versioning as an acyclic directed graph of PDUs. For backfilling purposes, this is done on a per context basis.
When we send a PDU we include all PDUs that have been received for that context that hasn't been subsequently listed in a later PDU. The trivial case is a simple list of PDUs, e.g. A <- B <- C. However, if two servers send out a PDU at the same to, both B and C would point at A - a later PDU would then list both B and C.
Problems with opaque version strings:
- How do you do clustering without mandating that a cluster can only have one transaction in flight to a given remote home server at a time.
If you have multiple transactions sent at once, then you might drop one transaction, receive another with a version that is later than the dropped transaction and which point ARGH WE LOST A TRANSACTION.
- How do you do backfilling? A version string defines a point in a stream w.r.t. a single home server, not a point in the context.
We only need to store the ends of the directed graph, we DO NOT need to do the whole one table of nodes and one of edges.

File diff suppressed because it is too large Load Diff

1
docs/sphinx/README.rst Normal file
View File

@@ -0,0 +1 @@
TODO: how (if at all) is this actually maintained?

93
docs/turn-howto.rst Normal file
View File

@@ -0,0 +1,93 @@
How to enable VoIP relaying on your Home Server with TURN
Overview
--------
The synapse Matrix Home Server supports integration with TURN server via the
TURN server REST API
(http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00). This allows
the Home Server to generate credentials that are valid for use on the TURN
server through the use of a secret shared between the Home Server and the
TURN server.
This document described how to install coturn
(https://code.google.com/p/coturn/) which also supports the TURN REST API,
and integrate it with synapse.
coturn Setup
============
1. Check out coturn::
svn checkout http://coturn.googlecode.com/svn/trunk/ coturn
cd coturn
2. Configure it::
./configure
You may need to install libevent2: if so, you should do so
in the way recommended by your operating system.
You can ignore warnings about lack of database support: a
database is unnecessary for this purpose.
3. Build and install it::
make
make install
4. Make a config file in /etc/turnserver.conf. You can customise
a config file from turnserver.conf.default. The relevant
lines, with example values, are::
lt-cred-mech
use-auth-secret
static-auth-secret=[your secret key here]
realm=turn.myserver.org
See turnserver.conf.default for explanations of the options.
One way to generate the static-auth-secret is with pwgen::
pwgen -s 64 1
5. Ensure youe firewall allows traffic into the TURN server on
the ports you've configured it to listen on (remember to allow
both TCP and UDP if you've enabled both).
6. If you've configured coturn to support TLS/DTLS, generate or
import your private key and certificate.
7. Start the turn server::
bin/turnserver -o
synapse Setup
=============
Your home server configuration file needs the following extra keys:
1. "turn_uris": This needs to be a yaml list
of public-facing URIs for your TURN server to be given out
to your clients. Add separate entries for each transport your
TURN server supports.
2. "turn_shared_secret": This is the secret shared between your Home
server and your TURN server, so you should set it to the same
string you used in turnserver.conf.
3. "turn_user_lifetime": This is the amount of time credentials
generated by your Home Server are valid for (in milliseconds).
Shorter times offer less potential for abuse at the expense
of increased traffic between web clients and your home server
to refresh credentials. The TURN REST API specification recommends
one day (86400000).
As an example, here is the relevant section of the config file for
matrix.org::
turn_uris: turn:turn.matrix.org:3478?transport=udp,turn:turn.matrix.org:3478?transport=tcp
turn_shared_secret: n0t4ctuAllymatr1Xd0TorgSshar3d5ecret4obvIousreAsons
turn_user_lifetime: 86400000
Now, restart synapse::
cd /where/you/run/synapse
./synctl restart
...and your Home Server now supports VoIP relaying!

138
graph/graph2.py Normal file
View File

@@ -0,0 +1,138 @@
# Copyright 2014 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sqlite3
import pydot
import cgi
import json
import datetime
import argparse
from synapse.events import FrozenEvent
def make_graph(db_name, room_id, file_prefix):
conn = sqlite3.connect(db_name)
c = conn.execute(
"SELECT json FROM event_json where room_id = ?",
(room_id,)
)
events = [FrozenEvent(json.loads(e[0])) for e in c.fetchall()]
events.sort(key=lambda e: e.depth)
node_map = {}
state_groups = {}
graph = pydot.Dot(graph_name="Test")
for event in events:
c = conn.execute(
"SELECT state_group FROM event_to_state_groups "
"WHERE event_id = ?",
(event.event_id,)
)
res = c.fetchone()
state_group = res[0] if res else None
if state_group is not None:
state_groups.setdefault(state_group, []).append(event.event_id)
t = datetime.datetime.fromtimestamp(
float(event.origin_server_ts) / 1000
).strftime('%Y-%m-%d %H:%M:%S,%f')
content = json.dumps(event.get_dict()["content"])
label = (
"<"
"<b>%(name)s </b><br/>"
"Type: <b>%(type)s </b><br/>"
"State key: <b>%(state_key)s </b><br/>"
"Content: <b>%(content)s </b><br/>"
"Time: <b>%(time)s </b><br/>"
"Depth: <b>%(depth)s </b><br/>"
"State group: %(state_group)s<br/>"
">"
) % {
"name": event.event_id,
"type": event.type,
"state_key": event.get("state_key", None),
"content": cgi.escape(content, quote=True),
"time": t,
"depth": event.depth,
"state_group": state_group,
}
node = pydot.Node(
name=event.event_id,
label=label,
)
node_map[event.event_id] = node
graph.add_node(node)
for event in events:
for prev_id, _ in event.prev_events:
try:
end_node = node_map[prev_id]
except:
end_node = pydot.Node(
name=prev_id,
label="<<b>%s</b>>" % (prev_id,),
)
node_map[prev_id] = end_node
graph.add_node(end_node)
edge = pydot.Edge(node_map[event.event_id], end_node)
graph.add_edge(edge)
for group, event_ids in state_groups.items():
if len(event_ids) <= 1:
continue
cluster = pydot.Cluster(
str(group),
label="<State Group: %s>" % (str(group),)
)
for event_id in event_ids:
cluster.add_node(node_map[event_id])
graph.add_subgraph(cluster)
graph.write('%s.dot' % file_prefix, format='raw', prog='dot')
graph.write_svg("%s.svg" % file_prefix, prog='dot')
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate a PDU graph for a given room by talking "
"to the given homeserver to get the list of PDUs. \n"
"Requires pydot."
)
parser.add_argument(
"-p", "--prefix", dest="prefix",
help="String to prefix output files with"
)
parser.add_argument('db')
parser.add_argument('room')
args = parser.parse_args()
make_graph(args.db, args.room, args.prefix)

View File

@@ -19,7 +19,12 @@ $('.login').live('click', function() {
showLoggedIn(data);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});

View File

@@ -58,7 +58,12 @@ $('.login').live('click', function() {
showLoggedIn(data);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});

View File

@@ -0,0 +1,7 @@
name: Example Matrix Client
description: Includes login, live event streaming, creating rooms, sending messages and viewing member lists.
authors:
- matrix.org
resources:
- http://matrix.org
normalize_css: no

View File

@@ -110,7 +110,7 @@ $('.register').live('click', function() {
url: "http://localhost:8008/_matrix/client/api/v1/register",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user_id: user, password: password }),
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
onLoggedIn(data);

View File

@@ -14,13 +14,18 @@ $('.register').live('click', function() {
url: "http://localhost:8008/_matrix/client/api/v1/register",
type: "POST",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ user_id: user, password: password }),
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
dataType: "json",
success: function(data) {
showLoggedIn(data);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});
@@ -36,7 +41,12 @@ var login = function(user, password) {
showLoggedIn(data);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
};

View File

@@ -28,7 +28,12 @@ $('.login').live('click', function() {
showLoggedIn(data);
},
error: function(err) {
alert(JSON.stringify($.parseJSON(err.responseText)));
var errMsg = "To try this, you need a home server running!";
var errJson = $.parseJSON(err.responseText);
if (errJson) {
errMsg = JSON.stringify(errJson);
}
alert(errMsg);
}
});
});

280
pylint.cfg Normal file
View File

@@ -0,0 +1,280 @@
[MASTER]
# Specify a configuration file.
#rcfile=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Profiled execution.
profile=no
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Pickle collected data for later comparisons.
persistent=yes
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
[MESSAGES CONTROL]
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time. See also the "--disable" option for examples.
#enable=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=missing-docstring
[REPORTS]
# Set the output format. Available formats are text, parseable, colorized, msvs
# (visual studio) and html. You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Put messages in a separate file for each module / package specified on the
# command line instead of printing them on stdout. Reports (if any) will be
# written in a file name "pylint_global.[txt|html]".
files-output=no
# Tells whether to display a full report or only the messages
reports=yes
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Add a comment according to your evaluation note. This is used by the global
# evaluation report (RP0004).
comment=no
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
#msg-template=
[TYPECHECK]
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
ignored-classes=SQLObject
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
zope=no
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E0201 when accessed. Python regular
# expressions are accepted.
generated-members=REQUEST,acl_users,aq_parent
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,XXX,TODO
[SIMILARITIES]
# Minimum lines number of a similarity.
min-similarity-lines=4
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
[VARIABLES]
# Tells whether we should check for unused import in __init__ files.
init-import=no
# A regular expression matching the beginning of the name of dummy variables
# (i.e. not used).
dummy-variables-rgx=_$|dummy
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
[BASIC]
# Required attributes for module, separated by a comma
required-attributes=
# List of builtins function names that should not be used, separated by a comma
bad-functions=map,filter,apply,input
# Regular expression which should only match correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression which should only match correct module level names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression which should only match correct class names
class-rgx=[A-Z_][a-zA-Z0-9]+$
# Regular expression which should only match correct function names
function-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct method names
method-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct instance attribute names
attr-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct argument names
argument-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct variable names
variable-rgx=[a-z_][a-z0-9_]{2,30}$
# Regular expression which should only match correct attribute names in class
# bodies
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression which should only match correct list comprehension /
# generator expression variable names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Good variable names which should always be accepted, separated by a comma
good-names=i,j,k,ex,Run,_
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=__.*__
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
[FORMAT]
# Maximum number of characters on a single line.
max-line-length=80
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
# List of optional constructs for which whitespace checking is disabled
no-space-check=trailing-comma,dict-separator
# Maximum number of lines in a module
max-module-lines=1000
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of statements in function / method body
max-statements=50
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
[IMPORTS]
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=regsub,TERMIOS,Bastion,rexec
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
[CLASSES]
# List of interface methods to ignore, separated by a comma. This is used for
# instance to not check methods defines in Zope's Interface base class.
ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

View File

@@ -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;
}
}

View File

@@ -0,0 +1,50 @@
from synapse.crypto.event_signing import *
from syutil.base64util import encode_base64
import argparse
import hashlib
import sys
import json
class dictobj(dict):
def __init__(self, *args, **kargs):
dict.__init__(self, *args, **kargs)
self.__dict__ = self
def get_dict(self):
return dict(self)
def get_full_dict(self):
return dict(self)
def get_pdu_json(self):
return dict(self)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("input_json", nargs="?", type=argparse.FileType('r'),
default=sys.stdin)
args = parser.parse_args()
logging.basicConfig()
event_json = dictobj(json.load(args.input_json))
algorithms = {
"sha256": hashlib.sha256,
}
for alg_name in event_json.hashes:
if check_event_content_hash(event_json, algorithms[alg_name]):
print "PASS content hash %s" % (alg_name,)
else:
print "FAIL content hash %s" % (alg_name,)
for algorithm in algorithms.values():
name, h_bytes = compute_event_reference_hash(event_json, algorithm)
print "Reference hash %s: %s" % (name, encode_base64(h_bytes))
if __name__=="__main__":
main()

View File

@@ -0,0 +1,73 @@
from syutil.crypto.jsonsign import verify_signed_json
from syutil.crypto.signing_key import (
decode_verify_key_bytes, write_signing_keys
)
from syutil.base64util import decode_base64
import urllib2
import json
import sys
import dns.resolver
import pprint
import argparse
import logging
def get_targets(server_name):
if ":" in server_name:
target, port = server_name.split(":")
yield (target, int(port))
return
try:
answers = dns.resolver.query("_matrix._tcp." + server_name, "SRV")
for srv in answers:
yield (srv.target, srv.port)
except dns.resolver.NXDOMAIN:
yield (server_name, 8448)
def get_server_keys(server_name, target, port):
url = "https://%s:%i/_matrix/key/v1" % (target, port)
keys = json.load(urllib2.urlopen(url))
verify_keys = {}
for key_id, key_base64 in keys["verify_keys"].items():
verify_key = decode_verify_key_bytes(key_id, decode_base64(key_base64))
verify_signed_json(keys, server_name, verify_key)
verify_keys[key_id] = verify_key
return verify_keys
def main():
parser = argparse.ArgumentParser()
parser.add_argument("signature_name")
parser.add_argument("input_json", nargs="?", type=argparse.FileType('r'),
default=sys.stdin)
args = parser.parse_args()
logging.basicConfig()
server_name = args.signature_name
keys = {}
for target, port in get_targets(server_name):
try:
keys = get_server_keys(server_name, target, port)
print "Using keys from https://%s:%s/_matrix/key/v1" % (target, port)
write_signing_keys(sys.stdout, keys.values())
break
except:
logging.exception("Error talking to %s:%s", target, port)
json_to_check = json.load(args.input_json)
print "Checking JSON:"
for key_id in json_to_check["signatures"][args.signature_name]:
try:
key = keys[key_id]
verify_signed_json(json_to_check, args.signature_name, key)
print "PASS %s" % (key_id,)
except:
logging.exception("Check for key %s failed" % (key_id,))
print "FAIL %s" % (key_id,)
if __name__ == '__main__':
main()

View File

@@ -14,7 +14,7 @@
# limitations under the License.
$copyright = <<EOT;
# Copyright 2014 OpenMarket Ltd
# Copyright 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.

View File

@@ -0,0 +1,143 @@
import nacl.signing
import json
import base64
import requests
import sys
import srvlookup
def encode_base64(input_bytes):
"""Encode bytes as a base64 string without any padding."""
input_len = len(input_bytes)
output_len = 4 * ((input_len + 2) // 3) + (input_len + 2) % 3 - 2
output_bytes = base64.b64encode(input_bytes)
output_string = output_bytes[:output_len].decode("ascii")
return output_string
def decode_base64(input_string):
"""Decode a base64 string to bytes inferring padding from the length of the
string."""
input_bytes = input_string.encode("ascii")
input_len = len(input_bytes)
padding = b"=" * (3 - ((input_len + 3) % 4))
output_len = 3 * ((input_len + 2) // 4) + (input_len + 2) % 4 - 2
output_bytes = base64.b64decode(input_bytes + padding)
return output_bytes[:output_len]
def encode_canonical_json(value):
return json.dumps(
value,
# Encode code-points outside of ASCII as UTF-8 rather than \u escapes
ensure_ascii=False,
# Remove unecessary white space.
separators=(',',':'),
# Sort the keys of dictionaries.
sort_keys=True,
# Encode the resulting unicode as UTF-8 bytes.
).encode("UTF-8")
def sign_json(json_object, signing_key, signing_name):
signatures = json_object.pop("signatures", {})
unsigned = json_object.pop("unsigned", None)
signed = signing_key.sign(encode_canonical_json(json_object))
signature_base64 = encode_base64(signed.signature)
key_id = "%s:%s" % (signing_key.alg, signing_key.version)
signatures.setdefault(signing_name, {})[key_id] = signature_base64
json_object["signatures"] = signatures
if unsigned is not None:
json_object["unsigned"] = unsigned
return json_object
NACL_ED25519 = "ed25519"
def decode_signing_key_base64(algorithm, version, key_base64):
"""Decode a base64 encoded signing key
Args:
algorithm (str): The algorithm the key is for (currently "ed25519").
version (str): Identifies this key out of the keys for this entity.
key_base64 (str): Base64 encoded bytes of the key.
Returns:
A SigningKey object.
"""
if algorithm == NACL_ED25519:
key_bytes = decode_base64(key_base64)
key = nacl.signing.SigningKey(key_bytes)
key.version = version
key.alg = NACL_ED25519
return key
else:
raise ValueError("Unsupported algorithm %s" % (algorithm,))
def read_signing_keys(stream):
"""Reads a list of keys from a stream
Args:
stream : A stream to iterate for keys.
Returns:
list of SigningKey objects.
"""
keys = []
for line in stream:
algorithm, version, key_base64 = line.split()
keys.append(decode_signing_key_base64(algorithm, version, key_base64))
return keys
def lookup(destination, path):
if ":" in destination:
return "https://%s%s" % (destination, path)
else:
srv = srvlookup.lookup("matrix", "tcp", destination)[0]
return "https://%s:%d%s" % (srv.host, srv.port, path)
def get_json(origin_name, origin_key, destination, path):
request_json = {
"method": "GET",
"uri": path,
"origin": origin_name,
"destination": destination,
}
signed_json = sign_json(request_json, origin_key, origin_name)
authorization_headers = []
for key, sig in signed_json["signatures"][origin_name].items():
authorization_headers.append(bytes(
"X-Matrix origin=%s,key=\"%s\",sig=\"%s\"" % (
origin_name, key, sig,
)
))
result = requests.get(
lookup(destination, path),
headers={"Authorization": authorization_headers[0]},
verify=False,
)
return result.json()
def main():
origin_name, keyfile, destination, path = sys.argv[1:]
with open(keyfile) as f:
key = read_signing_keys(f)[0]
result = get_json(
origin_name, key, destination, "/_matrix/federation/v1/" + path
)
json.dump(result, sys.stdout)
if __name__ == "__main__":
main()

View File

@@ -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

69
scripts/hash_history.py Normal file
View File

@@ -0,0 +1,69 @@
from synapse.storage.pdu import PduStore
from synapse.storage.signatures import SignatureStore
from synapse.storage._base import SQLBaseStore
from synapse.federation.units import Pdu
from synapse.crypto.event_signing import (
add_event_pdu_content_hash, compute_pdu_event_reference_hash
)
from synapse.api.events.utils import prune_pdu
from syutil.base64util import encode_base64, decode_base64
from syutil.jsonutil import encode_canonical_json
import sqlite3
import sys
class Store(object):
_get_pdu_tuples = PduStore.__dict__["_get_pdu_tuples"]
_get_pdu_content_hashes_txn = SignatureStore.__dict__["_get_pdu_content_hashes_txn"]
_get_prev_pdu_hashes_txn = SignatureStore.__dict__["_get_prev_pdu_hashes_txn"]
_get_pdu_origin_signatures_txn = SignatureStore.__dict__["_get_pdu_origin_signatures_txn"]
_store_pdu_content_hash_txn = SignatureStore.__dict__["_store_pdu_content_hash_txn"]
_store_pdu_reference_hash_txn = SignatureStore.__dict__["_store_pdu_reference_hash_txn"]
_store_prev_pdu_hash_txn = SignatureStore.__dict__["_store_prev_pdu_hash_txn"]
_simple_insert_txn = SQLBaseStore.__dict__["_simple_insert_txn"]
store = Store()
def select_pdus(cursor):
cursor.execute(
"SELECT pdu_id, origin FROM pdus ORDER BY depth ASC"
)
ids = cursor.fetchall()
pdu_tuples = store._get_pdu_tuples(cursor, ids)
pdus = [Pdu.from_pdu_tuple(p) for p in pdu_tuples]
reference_hashes = {}
for pdu in pdus:
try:
if pdu.prev_pdus:
print "PROCESS", pdu.pdu_id, pdu.origin, pdu.prev_pdus
for pdu_id, origin, hashes in pdu.prev_pdus:
ref_alg, ref_hsh = reference_hashes[(pdu_id, origin)]
hashes[ref_alg] = encode_base64(ref_hsh)
store._store_prev_pdu_hash_txn(cursor, pdu.pdu_id, pdu.origin, pdu_id, origin, ref_alg, ref_hsh)
print "SUCCESS", pdu.pdu_id, pdu.origin, pdu.prev_pdus
pdu = add_event_pdu_content_hash(pdu)
ref_alg, ref_hsh = compute_pdu_event_reference_hash(pdu)
reference_hashes[(pdu.pdu_id, pdu.origin)] = (ref_alg, ref_hsh)
store._store_pdu_reference_hash_txn(cursor, pdu.pdu_id, pdu.origin, ref_alg, ref_hsh)
for alg, hsh_base64 in pdu.hashes.items():
print alg, hsh_base64
store._store_pdu_content_hash_txn(cursor, pdu.pdu_id, pdu.origin, alg, decode_base64(hsh_base64))
except:
print "FAILED_", pdu.pdu_id, pdu.origin, pdu.prev_pdus
def main():
conn = sqlite3.connect(sys.argv[1])
cursor = conn.cursor()
select_pdus(cursor)
conn.commit()
if __name__=='__main__':
main()

View File

@@ -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;
}

View File

@@ -0,0 +1,331 @@
from synapse.storage import SCHEMA_VERSION, read_schema
from synapse.storage._base import SQLBaseStore
from synapse.storage.signatures import SignatureStore
from synapse.storage.event_federation import EventFederationStore
from syutil.base64util import encode_base64, decode_base64
from synapse.crypto.event_signing import compute_event_signature
from synapse.events.builder import EventBuilder
from synapse.events.utils import prune_event
from synapse.crypto.event_signing import check_event_content_hash
from syutil.crypto.jsonsign import (
verify_signed_json, SignatureVerifyException,
)
from syutil.crypto.signing_key import decode_verify_key_bytes
from syutil.jsonutil import encode_canonical_json
import argparse
# import dns.resolver
import hashlib
import httplib
import json
import sqlite3
import syutil
import urllib2
delta_sql = """
CREATE TABLE IF NOT EXISTS event_json(
event_id TEXT NOT NULL,
room_id TEXT NOT NULL,
internal_metadata NOT NULL,
json BLOB NOT NULL,
CONSTRAINT ev_j_uniq UNIQUE (event_id)
);
CREATE INDEX IF NOT EXISTS event_json_id ON event_json(event_id);
CREATE INDEX IF NOT EXISTS event_json_room_id ON event_json(room_id);
PRAGMA user_version = 10;
"""
class Store(object):
_get_event_signatures_txn = SignatureStore.__dict__["_get_event_signatures_txn"]
_get_event_content_hashes_txn = SignatureStore.__dict__["_get_event_content_hashes_txn"]
_get_event_reference_hashes_txn = SignatureStore.__dict__["_get_event_reference_hashes_txn"]
_get_prev_event_hashes_txn = SignatureStore.__dict__["_get_prev_event_hashes_txn"]
_get_prev_events_and_state = EventFederationStore.__dict__["_get_prev_events_and_state"]
_get_auth_events = EventFederationStore.__dict__["_get_auth_events"]
cursor_to_dict = SQLBaseStore.__dict__["cursor_to_dict"]
_simple_select_onecol_txn = SQLBaseStore.__dict__["_simple_select_onecol_txn"]
_simple_select_list_txn = SQLBaseStore.__dict__["_simple_select_list_txn"]
_simple_insert_txn = SQLBaseStore.__dict__["_simple_insert_txn"]
def _generate_event_json(self, txn, rows):
events = []
for row in rows:
d = dict(row)
d.pop("stream_ordering", None)
d.pop("topological_ordering", None)
d.pop("processed", None)
if "origin_server_ts" not in d:
d["origin_server_ts"] = d.pop("ts", 0)
else:
d.pop("ts", 0)
d.pop("prev_state", None)
d.update(json.loads(d.pop("unrecognized_keys")))
d["sender"] = d.pop("user_id")
d["content"] = json.loads(d["content"])
if "age_ts" not in d:
# For compatibility
d["age_ts"] = d.get("origin_server_ts", 0)
d.setdefault("unsigned", {})["age_ts"] = d.pop("age_ts")
outlier = d.pop("outlier", False)
# d.pop("membership", None)
d.pop("state_hash", None)
d.pop("replaces_state", None)
b = EventBuilder(d)
b.internal_metadata.outlier = outlier
events.append(b)
for i, ev in enumerate(events):
signatures = self._get_event_signatures_txn(
txn, ev.event_id,
)
ev.signatures = {
n: {
k: encode_base64(v) for k, v in s.items()
}
for n, s in signatures.items()
}
hashes = self._get_event_content_hashes_txn(
txn, ev.event_id,
)
ev.hashes = {
k: encode_base64(v) for k, v in hashes.items()
}
prevs = self._get_prev_events_and_state(txn, ev.event_id)
ev.prev_events = [
(e_id, h)
for e_id, h, is_state in prevs
if is_state == 0
]
# ev.auth_events = self._get_auth_events(txn, ev.event_id)
hashes = dict(ev.auth_events)
for e_id, hash in ev.prev_events:
if e_id in hashes and not hash:
hash.update(hashes[e_id])
#
# if hasattr(ev, "state_key"):
# ev.prev_state = [
# (e_id, h)
# for e_id, h, is_state in prevs
# if is_state == 1
# ]
return [e.build() for e in events]
store = Store()
# def get_key(server_name):
# print "Getting keys for: %s" % (server_name,)
# targets = []
# if ":" in server_name:
# target, port = server_name.split(":")
# targets.append((target, int(port)))
# try:
# answers = dns.resolver.query("_matrix._tcp." + server_name, "SRV")
# for srv in answers:
# targets.append((srv.target, srv.port))
# except dns.resolver.NXDOMAIN:
# targets.append((server_name, 8448))
# except:
# print "Failed to lookup keys for %s" % (server_name,)
# return {}
#
# for target, port in targets:
# url = "https://%s:%i/_matrix/key/v1" % (target, port)
# try:
# keys = json.load(urllib2.urlopen(url, timeout=2))
# verify_keys = {}
# for key_id, key_base64 in keys["verify_keys"].items():
# verify_key = decode_verify_key_bytes(
# key_id, decode_base64(key_base64)
# )
# verify_signed_json(keys, server_name, verify_key)
# verify_keys[key_id] = verify_key
# print "Got keys for: %s" % (server_name,)
# return verify_keys
# except urllib2.URLError:
# pass
# except urllib2.HTTPError:
# pass
# except httplib.HTTPException:
# pass
#
# print "Failed to get keys for %s" % (server_name,)
# return {}
def reinsert_events(cursor, server_name, signing_key):
print "Running delta: v10"
cursor.executescript(delta_sql)
cursor.execute(
"SELECT * FROM events ORDER BY rowid ASC"
)
print "Getting events..."
rows = store.cursor_to_dict(cursor)
events = store._generate_event_json(cursor, rows)
print "Got events from DB."
algorithms = {
"sha256": hashlib.sha256,
}
key_id = "%s:%s" % (signing_key.alg, signing_key.version)
verify_key = signing_key.verify_key
verify_key.alg = signing_key.alg
verify_key.version = signing_key.version
server_keys = {
server_name: {
key_id: verify_key
}
}
i = 0
N = len(events)
for event in events:
if i % 100 == 0:
print "Processed: %d/%d events" % (i,N,)
i += 1
# for alg_name in event.hashes:
# if check_event_content_hash(event, algorithms[alg_name]):
# pass
# else:
# pass
# print "FAIL content hash %s %s" % (alg_name, event.event_id, )
have_own_correctly_signed = False
for host, sigs in event.signatures.items():
pruned = prune_event(event)
for key_id in sigs:
if host not in server_keys:
server_keys[host] = {} # get_key(host)
if key_id in server_keys[host]:
try:
verify_signed_json(
pruned.get_pdu_json(),
host,
server_keys[host][key_id]
)
if host == server_name:
have_own_correctly_signed = True
except SignatureVerifyException:
print "FAIL signature check %s %s" % (
key_id, event.event_id
)
# TODO: Re sign with our own server key
if not have_own_correctly_signed:
sigs = compute_event_signature(event, server_name, signing_key)
event.signatures.update(sigs)
pruned = prune_event(event)
for key_id in event.signatures[server_name]:
verify_signed_json(
pruned.get_pdu_json(),
server_name,
server_keys[server_name][key_id]
)
event_json = encode_canonical_json(
event.get_dict()
).decode("UTF-8")
metadata_json = encode_canonical_json(
event.internal_metadata.get_dict()
).decode("UTF-8")
store._simple_insert_txn(
cursor,
table="event_json",
values={
"event_id": event.event_id,
"room_id": event.room_id,
"internal_metadata": metadata_json,
"json": event_json,
},
or_replace=True,
)
def main(database, server_name, signing_key):
conn = sqlite3.connect(database)
cursor = conn.cursor()
# Do other deltas:
cursor.execute("PRAGMA user_version")
row = cursor.fetchone()
if row and row[0]:
user_version = row[0]
# Run every version since after the current version.
for v in range(user_version + 1, 10):
print "Running delta: %d" % (v,)
sql_script = read_schema("delta/v%d" % (v,))
cursor.executescript(sql_script)
reinsert_events(cursor, server_name, signing_key)
conn.commit()
print "Success!"
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("database")
parser.add_argument("server_name")
parser.add_argument(
"signing_key", type=argparse.FileType('r'),
)
args = parser.parse_args()
signing_key = syutil.crypto.signing_key.read_signing_keys(
args.signing_key
)
main(args.database, args.server_name, signing_key[0])

View File

@@ -26,32 +26,41 @@ def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
name="SynapseHomeServer",
version="0.0.1",
packages=find_packages(exclude=["tests"]),
name="matrix-synapse",
version=read("VERSION").strip(),
packages=find_packages(exclude=["tests", "tests.*"]),
description="Reference Synapse Home Server",
install_requires=[
"syutil==0.0.1",
"syutil==0.0.2",
"matrix_angular_sdk>=0.6.1",
"Twisted>=14.0.0",
"service_identity>=1.0.0",
"pyopenssl>=0.14",
"pyyaml",
"pyasn1",
"pynacl",
"daemonize",
"py-bcrypt",
"frozendict>=0.4",
"pillow",
],
dependency_links=[
"git+ssh://git@github.com/matrix-org/syutil.git#egg=syutil-0.0.1",
"https://github.com/matrix-org/syutil/tarball/v0.0.2#egg=syutil-0.0.2",
"https://github.com/pyca/pynacl/tarball/d4d3175589b892f6ea7c22f466e0e223853516fa#egg=pynacl-0.3.0",
"https://github.com/matrix-org/matrix-angular-sdk/tarball/v0.6.1/#egg=matrix_angular_sdk-0.6.1",
],
setup_requires=[
"setuptools_trial",
"setuptools>=1.0.0", # Needs setuptools that supports git+ssh. It's not obvious when support for this was introduced.
"setuptools>=1.0.0", # Needs setuptools that supports git+ssh.
# TODO: Do we need this now? we don't use git+ssh.
"mock"
],
include_package_data=True,
zip_safe=False,
long_description=read("README.rst"),
entry_points="""
[console_scripts]
synapse-homeserver=synapse.app.homeserver:run
synctl=synapse.app.synctl:main
synapse-homeserver=synapse.app.homeserver:main
"""
)

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
# Copyright 2014, 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,4 +16,4 @@
""" This is a reference implementation of a synapse home server.
"""
__version__ = "0.2.2"
__version__ = "0.6.1c"

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
# Copyright 2014, 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,4 +12,3 @@
# 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.

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
# Copyright 2014, 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,10 +17,10 @@
from twisted.internet import defer
from synapse.api.constants import Membership, JoinRules
from synapse.api.constants import EventTypes, Membership, JoinRules
from synapse.api.errors import AuthError, StoreError, Codes, SynapseError
from synapse.api.events.room import RoomMemberEvent, RoomPowerLevelsEvent
from synapse.util.logutils import log_function
from synapse.util.async import run_on_reactor
import logging
@@ -32,66 +32,98 @@ class Auth(object):
def __init__(self, hs):
self.hs = hs
self.store = hs.get_datastore()
self.state = hs.get_state_handler()
@defer.inlineCallbacks
def check(self, event, snapshot, raises=False):
def check(self, event, auth_events):
""" Checks if this event is correctly authed.
Returns:
True if the auth checks pass.
Raises:
AuthError if there was a problem authorising this event. This will
be raised only if raises=True.
"""
try:
if hasattr(event, "room_id"):
is_state = hasattr(event, "state_key")
if not hasattr(event, "room_id"):
raise AuthError(500, "Event has no room_id: %s" % event)
if auth_events is None:
# Oh, we don't know what the state of the room was, so we
# are trusting that this is allowed (at least for now)
logger.warn("Trusting event: %s", event.event_id)
return True
if event.type == RoomMemberEvent.TYPE:
yield self._can_replace_state(event)
allowed = yield self.is_membership_change_allowed(event)
defer.returnValue(allowed)
return
if event.type == EventTypes.Create:
# FIXME
return True
self._check_joined_room(
member=snapshot.membership_state,
user_id=snapshot.user_id,
room_id=snapshot.room_id,
# FIXME: Temp hack
if event.type == EventTypes.Aliases:
return True
logger.debug("Auth events: %s", auth_events)
if event.type == EventTypes.Member:
allowed = self.is_membership_change_allowed(
event, auth_events
)
if is_state:
# TODO (erikj): This really only should be called for *new*
# state
yield self._can_add_state(event)
yield self._can_replace_state(event)
if allowed:
logger.debug("Allowing! %s", event)
else:
yield self._can_send_event(event)
logger.debug("Denying! %s", event)
return allowed
if event.type == RoomPowerLevelsEvent.TYPE:
yield self._check_power_levels(event)
self.check_event_sender_in_room(event, auth_events)
self._can_send_event(event, auth_events)
defer.returnValue(True)
else:
raise AuthError(500, "Unknown event: %s" % event)
if event.type == EventTypes.PowerLevels:
self._check_power_levels(event, auth_events)
if event.type == EventTypes.Redaction:
self._check_redaction(event, auth_events)
logger.debug("Allowing! %s", event)
except AuthError as e:
logger.info("Event auth check failed on event %s with msg: %s",
event, e.msg)
if raises:
raise e
defer.returnValue(False)
logger.info(
"Event auth check failed on event %s with msg: %s",
event, e.msg
)
logger.info("Denying! %s", event)
raise
@defer.inlineCallbacks
def check_joined_room(self, room_id, user_id):
try:
member = yield self.store.get_room_member(
room_id=room_id,
user_id=user_id
)
self._check_joined_room(member, user_id, room_id)
defer.returnValue(member)
except AttributeError:
pass
defer.returnValue(None)
member = yield self.state.get_current_state(
room_id=room_id,
event_type=EventTypes.Member,
state_key=user_id
)
self._check_joined_room(member, user_id, room_id)
defer.returnValue(member)
@defer.inlineCallbacks
def check_host_in_room(self, room_id, host):
curr_state = yield self.state.get_current_state(room_id)
for event in curr_state:
if event.type == EventTypes.Member:
try:
if self.hs.parse_userid(event.state_key).domain != host:
continue
except:
logger.warn("state_key not user_id: %s", event.state_key)
continue
if event.content["membership"] == Membership.JOIN:
defer.returnValue(True)
defer.returnValue(False)
def check_event_sender_in_room(self, event, auth_events):
key = (EventTypes.Member, event.user_id, )
member_event = auth_events.get(key)
return self._check_joined_room(
member_event,
event.user_id,
event.room_id
)
def _check_joined_room(self, member, user_id, room_id):
if not member or member.membership != Membership.JOIN:
@@ -99,46 +131,79 @@ class Auth(object):
user_id, room_id, repr(member)
))
@defer.inlineCallbacks
def is_membership_change_allowed(self, event):
target_user_id = event.state_key
# does this room even exist
room = yield self.store.get_room(event.room_id)
if not room:
raise AuthError(403, "Room does not exist")
# get info about the caller
try:
caller = yield self.store.get_room_member(
user_id=event.user_id,
room_id=event.room_id)
except:
caller = None
caller_in_room = caller and caller.membership == "join"
# get info about the target
try:
target = yield self.store.get_room_member(
user_id=target_user_id,
room_id=event.room_id)
except:
target = None
target_in_room = target and target.membership == "join"
@log_function
def is_membership_change_allowed(self, event, auth_events):
membership = event.content["membership"]
join_rule = yield self.store.get_room_join_rule(event.room_id)
if not join_rule:
# Check if this is the room creator joining:
if len(event.prev_events) == 1 and Membership.JOIN == membership:
# Get room creation event:
key = (EventTypes.Create, "", )
create = auth_events.get(key)
if create and event.prev_events[0][0] == create.event_id:
if create.content["creator"] == event.state_key:
return True
target_user_id = event.state_key
# get info about the caller
key = (EventTypes.Member, event.user_id, )
caller = auth_events.get(key)
caller_in_room = caller and caller.membership == Membership.JOIN
caller_invited = caller and caller.membership == Membership.INVITE
# get info about the target
key = (EventTypes.Member, target_user_id, )
target = auth_events.get(key)
target_in_room = target and target.membership == Membership.JOIN
key = (EventTypes.JoinRules, "", )
join_rule_event = auth_events.get(key)
if join_rule_event:
join_rule = join_rule_event.content.get(
"join_rule", JoinRules.INVITE
)
else:
join_rule = JoinRules.INVITE
user_level = self._get_power_level_from_event_state(
event,
event.user_id,
auth_events,
)
ban_level, kick_level, redact_level = (
self._get_ops_level_from_event_state(
event,
auth_events,
)
)
logger.debug(
"is_membership_change_allowed: %s",
{
"caller_in_room": caller_in_room,
"caller_invited": caller_invited,
"target_in_room": target_in_room,
"membership": membership,
"join_rule": join_rule,
"target_user_id": target_user_id,
"event.user_id": event.user_id,
}
)
if Membership.INVITE == membership:
# TODO (erikj): We should probably handle this more intelligently
# PRIVATE join rules.
# Invites are valid iff caller is in the room and target isn't.
if not caller_in_room: # caller isn't joined
raise AuthError(403, "You are not in room %s." % event.room_id)
raise AuthError(
403,
"%s not in room %s." % (event.user_id, event.room_id,)
)
elif target_in_room: # the target is already in the room.
raise AuthError(403, "%s is already in the room." %
target_user_id)
@@ -148,13 +213,10 @@ class Auth(object):
# joined: It's a NOOP
if event.user_id != target_user_id:
raise AuthError(403, "Cannot force another user to join.")
elif join_rule == JoinRules.PUBLIC or room.is_public:
elif join_rule == JoinRules.PUBLIC:
pass
elif join_rule == JoinRules.INVITE:
if (
not caller or caller.membership not in
[Membership.INVITE, Membership.JOIN]
):
if not caller_in_room and not caller_invited:
raise AuthError(403, "You are not invited to this room.")
else:
# TODO (erikj): may_join list
@@ -164,31 +226,21 @@ class Auth(object):
# TODO (erikj): Implement kicks.
if not caller_in_room: # trying to leave a room you aren't joined
raise AuthError(403, "You are not in room %s." % event.room_id)
elif target_user_id != event.user_id:
user_level = yield self.store.get_power_level(
event.room_id,
event.user_id,
raise AuthError(
403,
"%s not in room %s." % (target_user_id, event.room_id,)
)
_, kick_level = yield self.store.get_ops_levels(event.room_id)
elif target_user_id != event.user_id:
if kick_level:
kick_level = int(kick_level)
else:
kick_level = 50
kick_level = 50 # FIXME (erikj): What should we do here?
if user_level < kick_level:
raise AuthError(
403, "You cannot kick user %s." % target_user_id
)
elif Membership.BAN == membership:
user_level = yield self.store.get_power_level(
event.room_id,
event.user_id,
)
ban_level, _ = yield self.store.get_ops_levels(event.room_id)
if ban_level:
ban_level = int(ban_level)
else:
@@ -199,8 +251,38 @@ class Auth(object):
else:
raise AuthError(500, "Unknown membership %s" % membership)
defer.returnValue(True)
return True
def _get_power_level_from_event_state(self, event, user_id, auth_events):
key = (EventTypes.PowerLevels, "", )
power_level_event = auth_events.get(key)
level = None
if power_level_event:
level = power_level_event.content.get("users", {}).get(user_id)
if not level:
level = power_level_event.content.get("users_default", 0)
else:
key = (EventTypes.Create, "", )
create_event = auth_events.get(key)
if (create_event is not None and
create_event.content["creator"] == user_id):
return 100
return level
def _get_ops_level_from_event_state(self, event, auth_events):
key = (EventTypes.PowerLevels, "", )
power_level_event = auth_events.get(key)
if power_level_event:
return (
power_level_event.content.get("ban", 50),
power_level_event.content.get("kick", 50),
power_level_event.content.get("redact", 50),
)
return None, None, None,
@defer.inlineCallbacks
def get_user_by_req(self, request):
""" Get a registered user's ID.
@@ -213,7 +295,25 @@ class Auth(object):
"""
# Can optionally look elsewhere in the request (e.g. headers)
try:
return self.get_user_by_token(request.args["access_token"][0])
access_token = request.args["access_token"][0]
user_info = yield self.get_user_by_token(access_token)
user = user_info["user"]
ip_addr = self.hs.get_ip_from_request(request)
user_agent = request.requestHeaders.getRawHeaders(
"User-Agent",
default=[""]
)[0]
if user and access_token and ip_addr:
yield self.store.insert_client_ip(
user=user,
access_token=access_token,
device_id=user_info["device_id"],
ip=ip_addr,
user_agent=user_agent
)
defer.returnValue(user)
except KeyError:
raise AuthError(403, "Missing access token.")
@@ -222,34 +322,121 @@ class Auth(object):
""" Get a registered user's ID.
Args:
token (str)- The access token to get the user by.
token (str): The access token to get the user by.
Returns:
UserID : User ID object of the user who has that access token.
dict : dict that includes the user, device_id, and whether the
user is a server admin.
Raises:
AuthError if no user by that token exists or the token is invalid.
"""
try:
user_id = yield self.store.get_user_by_token(token=token)
if not user_id:
ret = yield self.store.get_user_by_token(token=token)
if not ret:
raise StoreError()
defer.returnValue(self.hs.parse_userid(user_id))
user_info = {
"admin": bool(ret.get("admin", False)),
"device_id": ret.get("device_id"),
"user": self.hs.parse_userid(ret.get("name")),
}
defer.returnValue(user_info)
except StoreError:
raise AuthError(403, "Unrecognised access token.",
errcode=Codes.UNKNOWN_TOKEN)
def is_server_admin(self, user):
return self.store.is_server_admin(user)
@defer.inlineCallbacks
def add_auth_events(self, builder, context):
yield run_on_reactor()
if builder.type == EventTypes.Create:
builder.auth_events = []
return
auth_ids = []
key = (EventTypes.PowerLevels, "", )
power_level_event = context.current_state.get(key)
if power_level_event:
auth_ids.append(power_level_event.event_id)
key = (EventTypes.JoinRules, "", )
join_rule_event = context.current_state.get(key)
key = (EventTypes.Member, builder.user_id, )
member_event = context.current_state.get(key)
key = (EventTypes.Create, "", )
create_event = context.current_state.get(key)
if create_event:
auth_ids.append(create_event.event_id)
if join_rule_event:
join_rule = join_rule_event.content.get("join_rule")
is_public = join_rule == JoinRules.PUBLIC if join_rule else False
else:
is_public = False
if builder.type == EventTypes.Member:
e_type = builder.content["membership"]
if e_type in [Membership.JOIN, Membership.INVITE]:
if join_rule_event:
auth_ids.append(join_rule_event.event_id)
if e_type == Membership.JOIN:
if member_event and not is_public:
auth_ids.append(member_event.event_id)
else:
if member_event:
auth_ids.append(member_event.event_id)
elif member_event:
if member_event.content["membership"] == Membership.JOIN:
auth_ids.append(member_event.event_id)
auth_events_entries = yield self.store.add_event_hashes(
auth_ids
)
builder.auth_events = auth_events_entries
context.auth_events = {
k: v
for k, v in context.current_state.items()
if v.event_id in auth_ids
}
@log_function
def _can_send_event(self, event):
send_level = yield self.store.get_send_event_level(event.room_id)
def _can_send_event(self, event, auth_events):
key = (EventTypes.PowerLevels, "", )
send_level_event = auth_events.get(key)
send_level = None
if send_level_event:
send_level = send_level_event.content.get("events", {}).get(
event.type
)
if not send_level:
if hasattr(event, "state_key"):
send_level = send_level_event.content.get(
"state_default", 50
)
else:
send_level = send_level_event.content.get(
"events_default", 0
)
if send_level:
send_level = int(send_level)
else:
send_level = 0
user_level = yield self.store.get_power_level(
event.room_id,
user_level = self._get_power_level_from_event_state(
event,
event.user_id,
auth_events,
)
if user_level:
@@ -259,78 +446,55 @@ class Auth(object):
if user_level < send_level:
raise AuthError(
403, "You don't have permission to post to the room"
403,
"You don't have permission to post that to the room. " +
"user_level (%d) < send_level (%d)" % (user_level, send_level)
)
defer.returnValue(True)
# Check state_key
if hasattr(event, "state_key"):
if not event.state_key.startswith("_"):
if event.state_key.startswith("@"):
if event.state_key != event.user_id:
raise AuthError(
403,
"You are not allowed to set others state"
)
else:
sender_domain = self.hs.parse_userid(
event.user_id
).domain
@defer.inlineCallbacks
def _can_add_state(self, event):
add_level = yield self.store.get_add_state_level(event.room_id)
if sender_domain != event.state_key:
raise AuthError(
403,
"You are not allowed to set others state"
)
if not add_level:
defer.returnValue(True)
return True
add_level = int(add_level)
user_level = yield self.store.get_power_level(
event.room_id,
def _check_redaction(self, event, auth_events):
user_level = self._get_power_level_from_event_state(
event,
event.user_id,
auth_events,
)
user_level = int(user_level)
_, _, redact_level = self._get_ops_level_from_event_state(
event,
auth_events,
)
if user_level < add_level:
if user_level < redact_level:
raise AuthError(
403, "You don't have permission to add state to the room"
403,
"You don't have permission to redact events"
)
defer.returnValue(True)
@defer.inlineCallbacks
def _can_replace_state(self, event):
current_state = yield self.store.get_current_state(
event.room_id,
event.type,
event.state_key,
)
if current_state:
current_state = current_state[0]
user_level = yield self.store.get_power_level(
event.room_id,
event.user_id,
)
if user_level:
user_level = int(user_level)
else:
user_level = 0
logger.debug(
"Checking power level for %s, %s", event.user_id, user_level
)
if current_state and hasattr(current_state, "required_power_level"):
req = current_state.required_power_level
logger.debug("Checked power level for %s, %s", event.user_id, req)
if user_level < req:
raise AuthError(
403,
"You don't have permission to change that state"
)
@defer.inlineCallbacks
def _check_power_levels(self, event):
for k, v in event.content.items():
if k == "default":
continue
# FIXME (erikj): We don't want hsob_Ts in content.
if k == "hsob_ts":
continue
def _check_power_levels(self, event, auth_events):
user_list = event.content.get("users", {})
# Validate users
for k, v in user_list.items():
try:
self.hs.parse_userid(k)
except:
@@ -341,80 +505,69 @@ class Auth(object):
except:
raise SynapseError(400, "Not a valid power level: %s" % (v,))
current_state = yield self.store.get_current_state(
event.room_id,
event.type,
event.state_key,
)
key = (event.type, event.state_key, )
current_state = auth_events.get(key)
if not current_state:
return
else:
current_state = current_state[0]
user_level = yield self.store.get_power_level(
event.room_id,
user_level = self._get_power_level_from_event_state(
event,
event.user_id,
auth_events,
)
if user_level:
user_level = int(user_level)
else:
user_level = 0
# Check other levels:
levels_to_check = [
("users_default", []),
("events_default", []),
("ban", []),
("redact", []),
("kick", []),
]
old_list = current_state.content
old_list = current_state.content.get("users")
for user in set(old_list.keys() + user_list.keys()):
levels_to_check.append(
(user, ["users"])
)
# FIXME (erikj)
old_people = {k: v for k, v in old_list.items() if k.startswith("@")}
new_people = {
k: v for k, v in event.content.items()
if k.startswith("@")
}
old_list = current_state.content.get("events")
new_list = event.content.get("events")
for ev_id in set(old_list.keys() + new_list.keys()):
levels_to_check.append(
(ev_id, ["events"])
)
removed = set(old_people.keys()) - set(new_people.keys())
added = set(old_people.keys()) - set(new_people.keys())
same = set(old_people.keys()) & set(new_people.keys())
old_state = current_state.content
new_state = event.content
for r in removed:
if int(old_list.content[r]) > user_level:
raise AuthError(
403,
"You don't have permission to remove user: %s" % (r, )
)
for level_to_check, dir in levels_to_check:
old_loc = old_state
for d in dir:
old_loc = old_loc.get(d, {})
for n in added:
if int(event.content[n]) > user_level:
new_loc = new_state
for d in dir:
new_loc = new_loc.get(d, {})
if level_to_check in old_loc:
old_level = int(old_loc[level_to_check])
else:
old_level = None
if level_to_check in new_loc:
new_level = int(new_loc[level_to_check])
else:
new_level = None
if new_level is not None and old_level is not None:
if new_level == old_level:
continue
if old_level > user_level or new_level > user_level:
raise AuthError(
403,
"You don't have permission to add ops level greater "
"than your own"
)
for s in same:
if int(event.content[s]) != int(old_list[s]):
if int(event.content[s]) > user_level:
raise AuthError(
403,
"You don't have permission to add ops level greater "
"than your own"
)
if "default" in old_list:
old_default = int(old_list["default"])
if old_default > user_level:
raise AuthError(
403,
"You don't have permission to add ops level greater than "
"your own"
)
if "default" in event.content:
new_default = int(event.content["default"])
if new_default > user_level:
raise AuthError(
403,
"You don't have permission to add ops level greater "
"than your own"
)

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
# Copyright 2014, 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -50,3 +50,27 @@ class JoinRules(object):
KNOCK = u"knock"
INVITE = u"invite"
PRIVATE = u"private"
class LoginType(object):
PASSWORD = u"m.login.password"
OAUTH = u"m.login.oauth2"
EMAIL_CODE = u"m.login.email.code"
EMAIL_URL = u"m.login.email.url"
EMAIL_IDENTITY = u"m.login.email.identity"
RECAPTCHA = u"m.login.recaptcha"
class EventTypes(object):
Member = "m.room.member"
Create = "m.room.create"
JoinRules = "m.room.join_rules"
PowerLevels = "m.room.power_levels"
Aliases = "m.room.aliases"
Redaction = "m.room.redaction"
Feedback = "m.room.message.feedback"
# These are used for validation
Message = "m.room.message"
Topic = "m.room.topic"
Name = "m.room.name"

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
# Copyright 2014, 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,8 +17,11 @@
import logging
logger = logging.getLogger(__name__)
class Codes(object):
UNAUTHORIZED = "M_UNAUTHORIZED"
FORBIDDEN = "M_FORBIDDEN"
BAD_JSON = "M_BAD_JSON"
NOT_JSON = "M_NOT_JSON"
@@ -31,13 +34,14 @@ class Codes(object):
LIMIT_EXCEEDED = "M_LIMIT_EXCEEDED"
CAPTCHA_NEEDED = "M_CAPTCHA_NEEDED"
CAPTCHA_INVALID = "M_CAPTCHA_INVALID"
TOO_LARGE = "M_TOO_LARGE"
class CodeMessageException(Exception):
"""An exception with integer code and message string attributes."""
def __init__(self, code, msg):
logging.error("%s: %s, %s", type(self).__name__, code, msg)
logger.info("%s: %s, %s", type(self).__name__, code, msg)
super(CodeMessageException, self).__init__("%d: %s" % (code, msg))
self.code = code
self.msg = msg
@@ -53,7 +57,7 @@ class SynapseError(CodeMessageException):
"""Constructs a synapse error.
Args:
code (int): The integer error code (typically an HTTP response code)
code (int): The integer error code (an HTTP response code)
msg (str): The human-readable error message.
err (str): The error code e.g 'M_FORBIDDEN'
"""
@@ -66,6 +70,7 @@ class SynapseError(CodeMessageException):
self.errcode,
)
class RoomError(SynapseError):
"""An error raised when a room event fails."""
pass
@@ -116,6 +121,7 @@ class InvalidCaptchaError(SynapseError):
error_url=self.error_url,
)
class LimitExceededError(SynapseError):
"""A client has sent too many requests and is being throttled.
"""
@@ -137,7 +143,8 @@ def cs_exception(exception):
if isinstance(exception, CodeMessageException):
return exception.error_dict()
else:
logging.error("Unknown exception type: %s", type(exception))
logger.error("Unknown exception type: %s", type(exception))
return {}
def cs_error(msg, code=Codes.UNKNOWN, **kwargs):
@@ -155,3 +162,37 @@ def cs_error(msg, code=Codes.UNKNOWN, **kwargs):
for key, value in kwargs.iteritems():
err[key] = value
return err
class FederationError(RuntimeError):
""" This class is used to inform remote home servers about erroneous
PDUs they sent us.
FATAL: The remote server could not interpret the source event.
(e.g., it was missing a required field)
ERROR: The remote server interpreted the event, but it failed some other
check (e.g. auth)
WARN: The remote server accepted the event, but believes some part of it
is wrong (e.g., it referred to an invalid event)
"""
def __init__(self, level, code, reason, affected, source=None):
if level not in ["FATAL", "ERROR", "WARN"]:
raise ValueError("Level is not valid: %s" % (level,))
self.level = level
self.code = code
self.reason = reason
self.affected = affected
self.source = source
msg = "%s %s: %s" % (level, code, reason,)
super(FederationError, self).__init__(msg)
def get_dict(self):
return {
"level": self.level,
"code": self.code,
"reason": self.reason,
"affected": self.affected,
"source": self.source if self.source else self.affected,
}

View File

@@ -1,168 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from synapse.api.errors import SynapseError, Codes
from synapse.util.jsonobject import JsonEncodedObject
class SynapseEvent(JsonEncodedObject):
"""Base class for Synapse events. These are JSON objects which must abide
by a certain well-defined structure.
"""
# Attributes that are currently assumed by the federation side:
# Mandatory:
# - event_id
# - room_id
# - type
# - is_state
#
# Optional:
# - state_key (mandatory when is_state is True)
# - prev_events (these can be filled out by the federation layer itself.)
# - prev_state
valid_keys = [
"event_id",
"type",
"room_id",
"user_id", # sender/initiator
"content", # HTTP body, JSON
"state_key",
"required_power_level",
]
internal_keys = [
"is_state",
"prev_events",
"prev_state",
"depth",
"destinations",
"origin",
"outlier",
"power_level",
]
required_keys = [
"event_id",
"room_id",
"content",
]
def __init__(self, raises=True, **kwargs):
super(SynapseEvent, self).__init__(**kwargs)
if "content" in kwargs:
self.check_json(self.content, raises=raises)
def get_content_template(self):
""" Retrieve the JSON template for this event as a dict.
The template must be a dict representing the JSON to match. Only
required keys should be present. The values of the keys in the template
are checked via type() to the values of the same keys in the actual
event JSON.
NB: If loading content via json.loads, you MUST define strings as
unicode.
For example:
Content:
{
"name": u"bob",
"age": 18,
"friends": [u"mike", u"jill"]
}
Template:
{
"name": u"string",
"age": 0,
"friends": [u"string"]
}
The values "string" and 0 could be anything, so long as the types
are the same as the content.
"""
raise NotImplementedError("get_content_template not implemented.")
def check_json(self, content, raises=True):
"""Checks the given JSON content abides by the rules of the template.
Args:
content : A JSON object to check.
raises: True to raise a SynapseError if the check fails.
Returns:
True if the content passes the template. Returns False if the check
fails and raises=False.
Raises:
SynapseError if the check fails and raises=True.
"""
# recursively call to inspect each layer
err_msg = self._check_json(content, self.get_content_template())
if err_msg:
if raises:
raise SynapseError(400, err_msg, Codes.BAD_JSON)
else:
return False
else:
return True
def _check_json(self, content, template):
"""Check content and template matches.
If the template is a dict, each key in the dict will be validated with
the content, else it will just compare the types of content and
template. This basic type check is required because this function will
be recursively called and could be called with just strs or ints.
Args:
content: The content to validate.
template: The validation template.
Returns:
str: An error message if the validation fails, else None.
"""
if type(content) != type(template):
return "Mismatched types: %s" % template
if type(template) == dict:
for key in template:
if key not in content:
return "Missing %s key" % key
if type(content[key]) != type(template[key]):
return "Key %s is of the wrong type." % key
if type(content[key]) == dict:
# we must go deeper
msg = self._check_json(content[key], template[key])
if msg:
return msg
elif type(content[key]) == list:
# make sure each item type in content matches the template
for entry in content[key]:
msg = self._check_json(entry, template[key][0])
if msg:
return msg
class SynapseStateEvent(SynapseEvent):
valid_keys = SynapseEvent.valid_keys + [
"prev_content",
]
def __init__(self, **kwargs):
if "state_key" not in kwargs:
kwargs["state_key"] = ""
super(SynapseStateEvent, self).__init__(**kwargs)

View File

@@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from synapse.api.events.room import (
RoomTopicEvent, MessageEvent, RoomMemberEvent, FeedbackEvent,
InviteJoinEvent, RoomConfigEvent, RoomNameEvent, GenericEvent,
RoomPowerLevelsEvent, RoomJoinRulesEvent, RoomOpsPowerLevelsEvent,
RoomCreateEvent, RoomAddStateLevelEvent, RoomSendEventLevelEvent
)
from synapse.util.stringutils import random_string
class EventFactory(object):
_event_classes = [
RoomTopicEvent,
RoomNameEvent,
MessageEvent,
RoomMemberEvent,
FeedbackEvent,
InviteJoinEvent,
RoomConfigEvent,
RoomPowerLevelsEvent,
RoomJoinRulesEvent,
RoomCreateEvent,
RoomAddStateLevelEvent,
RoomSendEventLevelEvent,
RoomOpsPowerLevelsEvent,
]
def __init__(self, hs):
self._event_list = {} # dict of TYPE to event class
for event_class in EventFactory._event_classes:
self._event_list[event_class.TYPE] = event_class
self.clock = hs.get_clock()
self.hs = hs
def create_event(self, etype=None, **kwargs):
kwargs["type"] = etype
if "event_id" not in kwargs:
kwargs["event_id"] = "%s@%s" % (
random_string(10), self.hs.hostname
)
if "ts" not in kwargs:
kwargs["ts"] = int(self.clock.time_msec())
if etype in self._event_list:
handler = self._event_list[etype]
else:
handler = GenericEvent
return handler(**kwargs)

View File

@@ -1,182 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from synapse.api.constants import Feedback, Membership
from synapse.api.errors import SynapseError
from . import SynapseEvent, SynapseStateEvent
class GenericEvent(SynapseEvent):
def get_content_template(self):
return {}
class RoomTopicEvent(SynapseEvent):
TYPE = "m.room.topic"
internal_keys = SynapseEvent.internal_keys + [
"topic",
]
def __init__(self, **kwargs):
kwargs["state_key"] = ""
if "topic" in kwargs["content"]:
kwargs["topic"] = kwargs["content"]["topic"]
super(RoomTopicEvent, self).__init__(**kwargs)
def get_content_template(self):
return {"topic": u"string"}
class RoomNameEvent(SynapseEvent):
TYPE = "m.room.name"
internal_keys = SynapseEvent.internal_keys + [
"name",
]
def __init__(self, **kwargs):
kwargs["state_key"] = ""
if "name" in kwargs["content"]:
kwargs["name"] = kwargs["content"]["name"]
super(RoomNameEvent, self).__init__(**kwargs)
def get_content_template(self):
return {"name": u"string"}
class RoomMemberEvent(SynapseEvent):
TYPE = "m.room.member"
valid_keys = SynapseEvent.valid_keys + [
# target is the state_key
"membership", # action
]
def __init__(self, **kwargs):
if "membership" not in kwargs:
kwargs["membership"] = kwargs.get("content", {}).get("membership")
if not kwargs["membership"] in Membership.LIST:
raise SynapseError(400, "Bad membership value.")
super(RoomMemberEvent, self).__init__(**kwargs)
def get_content_template(self):
return {"membership": u"string"}
class MessageEvent(SynapseEvent):
TYPE = "m.room.message"
valid_keys = SynapseEvent.valid_keys + [
"msg_id", # unique per room + user combo
]
def __init__(self, **kwargs):
super(MessageEvent, self).__init__(**kwargs)
def get_content_template(self):
return {"msgtype": u"string"}
class FeedbackEvent(SynapseEvent):
TYPE = "m.room.message.feedback"
valid_keys = SynapseEvent.valid_keys
def __init__(self, **kwargs):
super(FeedbackEvent, self).__init__(**kwargs)
if not kwargs["content"]["type"] in Feedback.LIST:
raise SynapseError(400, "Bad feedback value.")
def get_content_template(self):
return {
"type": u"string",
"target_event_id": u"string"
}
class InviteJoinEvent(SynapseEvent):
TYPE = "m.room.invite_join"
valid_keys = SynapseEvent.valid_keys + [
# target_user_id is the state_key
"target_host",
]
def __init__(self, **kwargs):
super(InviteJoinEvent, self).__init__(**kwargs)
def get_content_template(self):
return {}
class RoomConfigEvent(SynapseEvent):
TYPE = "m.room.config"
def __init__(self, **kwargs):
kwargs["state_key"] = ""
super(RoomConfigEvent, self).__init__(**kwargs)
def get_content_template(self):
return {}
class RoomCreateEvent(SynapseStateEvent):
TYPE = "m.room.create"
def get_content_template(self):
return {}
class RoomJoinRulesEvent(SynapseStateEvent):
TYPE = "m.room.join_rules"
def get_content_template(self):
return {}
class RoomPowerLevelsEvent(SynapseStateEvent):
TYPE = "m.room.power_levels"
def get_content_template(self):
return {}
class RoomAddStateLevelEvent(SynapseStateEvent):
TYPE = "m.room.add_state_level"
def get_content_template(self):
return {}
class RoomSendEventLevelEvent(SynapseStateEvent):
TYPE = "m.room.send_event_level"
def get_content_template(self):
return {}
class RoomOpsPowerLevelsEvent(SynapseStateEvent):
TYPE = "m.room.ops_levels"
def get_content_template(self):
return {}
class RoomAliasesEvent(SynapseStateEvent):
TYPE = "m.room.aliases"
def get_content_template(self):
return {}

View File

@@ -1,4 +1,4 @@
# Copyright 2014 OpenMarket Ltd
# Copyright 2014, 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright 2014 OpenMarket Ltd
# Copyright 2014, 2015 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -18,4 +18,6 @@
CLIENT_PREFIX = "/_matrix/client/api/v1"
FEDERATION_PREFIX = "/_matrix/federation/v1"
WEB_CLIENT_PREFIX = "/_matrix/client"
CONTENT_REPO_PREFIX = "/_matrix/content"
CONTENT_REPO_PREFIX = "/_matrix/content"
SERVER_KEY_PREFIX = "/_matrix/key/v1"
MEDIA_PREFIX = "/_matrix/media/v1"

Some files were not shown because too many files have changed in this diff Show More