diff --git a/.gitignore b/.gitignore index af90668c8..960183a79 100644 --- a/.gitignore +++ b/.gitignore @@ -26,17 +26,19 @@ htmlcov demo/*.db demo/*.log +demo/*.log.* demo/*.pid +demo/media_store.* demo/etc -graph/*.svg -graph/*.png -graph/*.dot - -**/webclient/config.js -**/webclient/test/coverage/ -**/webclient/test/environment-protractor.js - uploads .idea/ +media_store/ + +*.tac + +build/ + +localhost-800*/ +static/client/register/register_config.js diff --git a/CHANGES.rst b/CHANGES.rst index 297ae914f..f89542a2b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,54 +1,135 @@ +Changes in synapse v0.8.0 (2015-03-06) +====================================== + +General: + +* Add support for registration fallback. This is a page hosted on the server + which allows a user to register for an account, regardless of what client + they are using (e.g. mobile devices). + +* Added new default push rules and made them configurable by clients: + + * Suppress all notice messages. + * Notify when invited to a new room. + * Notify for messages that don't match any rule. + * Notify on incoming call. + +Federation: + +* Added per host server side rate-limiting of incoming federation requests. +* Added a ``/get_missing_events/`` API to federation to reduce number of + ``/events/`` requests. + +Configuration: + +* Added configuration option to disable registration: + ``disable_registration``. +* Added configuration option to change soft limit of number of open file + descriptors: ``soft_file_limit``. +* Make ``tls_private_key_path`` optional when running with ``no_tls``. + +Application services: + +* Application services can now poll on the CS API ``/events`` for their events, + by providing their application service ``access_token``. +* Added exclusive namespace support to application services API. + + +Changes in synapse v0.7.1 (2015-02-19) +====================================== + +* Initial alpha implementation of parts of the Application Services API. + Including: + + - AS Registration / Unregistration + - User Query API + - Room Alias Query API + - Push transport for receiving events. + - User/Alias namespace admin control + +* Add cache when fetching events from remote servers to stop repeatedly + fetching events with bad signatures. +* Respect the per remote server retry scheme when fetching both events and + server keys to reduce the number of times we send requests to dead servers. +* Inform remote servers when the local server fails to handle a received event. +* Turn off python bytecode generation due to problems experienced when + upgrading from previous versions. + +Changes in synapse v0.7.0 (2015-02-12) +====================================== + +* Add initial implementation of the query auth federation API, allowing + servers to agree on whether an event should be allowed or rejected. +* Persist events we have rejected from federation, fixing the bug where + servers would keep requesting the same events. +* Various federation performance improvements, including: + + - Add in memory caches on queries such as: + + * Computing the state of a room at a point in time, used for + authorization on federation requests. + * Fetching events from the database. + * User's room membership, used for authorizing presence updates. + + - Upgraded JSON library to improve parsing and serialisation speeds. + +* Add default avatars to new user accounts using pydenticon library. +* Correctly time out federation requests. +* Retry federation requests against different servers. +* Add support for push and push rules. +* Add alpha versions of proposed new CSv2 APIs, including ``/sync`` API. + 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. +* 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. +* 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. +* 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. +* 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. +* 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. +* 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) ===================================== diff --git a/MANIFEST.in b/MANIFEST.in index a1a77ff54..8243a942e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,14 @@ -recursive-include docs * -recursive-include tests *.py +include synctl +include LICENSE +include VERSION +include *.rst +include demo/README + recursive-include synapse/storage/schema *.sql -recursive-include syweb/webclient * + +recursive-include demo *.dh +recursive-include demo *.py +recursive-include demo *.sh +recursive-include docs * +recursive-include scripts * +recursive-include tests *.py diff --git a/README.rst b/README.rst index 768da3df6..c2af7c933 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ VoIP. The basics you need to know to get up and running are: - 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``. + like ``#matrix:matrix.org`` or ``#test:localhost:8448``. - Matrix user IDs look like ``@matthew:matrix.org`` (although in the future you will normally refer to yourself and others using a 3PID: email @@ -95,27 +95,36 @@ Installing prerequisites on Ubuntu or Debian:: $ sudo apt-get install build-essential python2.7-dev libffi-dev \ python-pip python-setuptools sqlite3 \ - libssl-dev + libssl-dev python-virtualenv libjpeg-dev + +Installing prerequisites on ArchLinux:: + + $ sudo pacman -S base-devel python2 python-pip \ + python-setuptools python-virtualenv sqlite3 Installing prerequisites on Mac OS X:: $ xcode-select --install + $ sudo pip install virtualenv To install the synapse homeserver run:: - $ pip install --user --process-dependency-links https://github.com/matrix-org/synapse/tarball/master + $ virtualenv ~/.synapse + $ source ~/.synapse/bin/activate + $ pip install --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. +This installs synapse, along with the libraries it uses, into a virtual +environment under ``~/.synapse``. -Your python may not give priority to locally installed libraries over system -libraries, in which case you must add your local packages to your python path:: +To set up your homeserver, run (in your virtualenv, as before):: - $ # on Linux: - $ export PYTHONPATH=$HOME/.local/lib/python2.7/site-packages:$PYTHONPATH + $ cd ~/.synapse + $ python -m synapse.app.homeserver \ + --server-name machine.my.domain.name \ + --config-path homeserver.yaml \ + --generate-config - $ # on OSX: - $ export PYTHONPATH=$HOME/Library/Python/2.7/lib/python/site-packages:$PYTHONPATH +Substituting your host and domain name as appropriate. For reliable VoIP calls to be routed via this homeserver, you MUST configure a TURN server. See docs/turn-howto.rst for details. @@ -128,23 +137,57 @@ 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 + $ pip install twisted On OSX, if you encounter clang: error: unknown argument: '-mno-fused-madd' you will need to export CFLAGS=-Qunused-arguments. +ArchLinux +--------- + +Installation on ArchLinux may encounter a few hiccups as Arch defaults to +python 3, but synapse currently assumes python 2.7 by default. + +pip may be outdated (6.0.7-1 and needs to be upgraded to 6.0.8-1 ):: + + $ sudo pip2.7 install --upgrade pip + +You also may need to explicitly specify python 2.7 again during the install +request:: + + $ pip2.7 install --process-dependency-links \ + https://github.com/matrix-org/synapse/tarball/master + +If you encounter an error with lib bcrypt causing an Wrong ELF Class: +ELFCLASS32 (x64 Systems), you may need to reinstall py-bcrypt to correctly +compile it under the right architecture. (This should not be needed if +installing under virtualenv):: + + $ sudo pip2.7 uninstall py-bcrypt + $ sudo pip2.7 install py-bcrypt + +During setup of homeserver you need to call python2.7 directly again:: + + $ cd ~/.synapse + $ python2.7 -m synapse.app.homeserver \ + --server-name machine.my.domain.name \ + --config-path homeserver.yaml \ + --generate-config + +...substituting your host and domain name as appropriate. + Windows Install --------------- Synapse can be installed on Cygwin. It requires the following Cygwin packages: @@ -155,7 +198,7 @@ Synapse can be installed on Cygwin. It requires the following Cygwin packages: - 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 @@ -182,23 +225,13 @@ 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 + $ source ./bin/activate + $ 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. @@ -214,6 +247,14 @@ fix try re-installing from PyPI or directly from $ # Install from github $ pip install --user https://github.com/pyca/pynacl/tarball/master +ArchLinux +--------- + +If running `$ synctl start` fails wit 'returned non-zero exit status 1', you will need to explicitly call Python2.7 - either running as:: + + $ python2.7 -m synapse.app.homeserver --daemonize -c homeserver.yaml --pid-file homeserver.pid + +...or by editing synctl with the correct python executable. Homeserver Development ====================== @@ -225,13 +266,15 @@ directory of your choice:: $ cd synapse The homeserver has a number of external dependencies, that are easiest -to install by making setup.py do so, in --user mode:: +to install using pip and a virtualenv:: - $ python setup.py develop --user + $ virtualenv env + $ source env/bin/activate + $ python synapse/python_dependencies.py | xargs -n1 pip install + $ pip install setuptools_trial mock -This will run a process of downloading and installing into your -user's .local/lib directory all of the required dependencies that are -missing. +This will run a process of downloading and installing all the needed +dependencies into a virtual env. Once this is done, you may wish to run the homeserver's unit tests, to check that everything is installed as it should be:: @@ -252,7 +295,7 @@ 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 +by ``pip install --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. @@ -279,9 +322,9 @@ For the first form, simply pass the required hostname (of the machine) as the $ python -m synapse.app.homeserver \ --server-name machine.my.domain.name \ - --config-path homeserver.config \ + --config-path homeserver.yaml \ --generate-config - $ python -m synapse.app.homeserver --config-path homeserver.config + $ python -m synapse.app.homeserver --config-path homeserver.yaml Alternatively, you can run ``synctl start`` to guide you through the process. @@ -301,9 +344,9 @@ SRV record, as that is the name other machines will expect it to have:: $ python -m synapse.app.homeserver \ --server-name YOURDOMAIN \ --bind-port 8448 \ - --config-path homeserver.config \ + --config-path homeserver.yaml \ --generate-config - $ python -m synapse.app.homeserver --config-path homeserver.config + $ python -m synapse.app.homeserver --config-path homeserver.yaml You may additionally want to pass one or more "-v" options, in order to diff --git a/UPGRADE.rst b/UPGRADE.rst index 0f81f3e11..87dd6e04a 100644 --- a/UPGRADE.rst +++ b/UPGRADE.rst @@ -1,3 +1,32 @@ +Upgrading to v0.8.0 +=================== + +Servers which use captchas will need to add their public key to:: + + static/client/register/register_config.js + + window.matrixRegistrationConfig = { + recaptcha_public_key: "YOUR_PUBLIC_KEY" + }; + +This is required in order to support registration fallback (typically used on +mobile devices). + + +Upgrading to v0.7.0 +=================== + +New dependencies are: + +- pydenticon +- simplejson +- syutil +- matrix-angular-sdk + +To pull in these dependencies in a virtual env, run:: + + python synapse/python_dependencies.py | xargs -n 1 pip install + Upgrading to v0.6.0 =================== diff --git a/VERSION b/VERSION deleted file mode 100644 index 3b3e72317..000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.6.1b diff --git a/contrib/graph/graph2.py b/contrib/graph/graph2.py index 6b551d42e..d0d2cfe7c 100644 --- a/contrib/graph/graph2.py +++ b/contrib/graph/graph2.py @@ -21,6 +21,7 @@ import datetime import argparse from synapse.events import FrozenEvent +from synapse.util.frozenutils import unfreeze def make_graph(db_name, room_id, file_prefix, limit): @@ -70,7 +71,7 @@ def make_graph(db_name, room_id, file_prefix, limit): float(event.origin_server_ts) / 1000 ).strftime('%Y-%m-%d %H:%M:%S,%f') - content = json.dumps(event.get_dict()["content"]) + content = json.dumps(unfreeze(event.get_dict()["content"])) label = ( "<" diff --git a/contrib/jitsimeetbridge/jitsimeetbridge.py b/contrib/jitsimeetbridge/jitsimeetbridge.py index dbc6f6ffa..15f8e1c48 100644 --- a/contrib/jitsimeetbridge/jitsimeetbridge.py +++ b/contrib/jitsimeetbridge/jitsimeetbridge.py @@ -39,43 +39,43 @@ 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 __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 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 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 + 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 @@ -83,178 +83,178 @@ 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']] - + 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 __init__(self, matrixRoom, userId): + self.rid = 0 + self.matrixRoom = matrixRoom + self.userId = userId + self.running = True - def stop(self): - self.running = False + def stop(self): + self.running = False - def nextRid(self): - self.rid += 1 - return '%d' % (self.rid) + def nextRid(self): + self.rid += 1 + return '%d' % (self.rid) - def sendIq(self, xml): - fullXml = "
%s" % (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 sendIq(self, xml): + fullXml = "%s" % (self.nextRid(), self.sid, xml) + #print "\t>>>%s" % (fullXml) + return self.xmppPoke(fullXml) - 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 + 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 - gevent.joinall([ - gevent.spawn(self.advertiseSsrcs) - ]) - - def advertiseSsrcs(self): + 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 = "