Changeset 678
- Timestamp:
- 06/15/08 15:37:53 (2 months ago)
- Files:
-
- oss/headstock/headstock/example/microblog/config.xml (modified) (2 diffs)
- oss/headstock/headstock/example/microblog/design (added)
- oss/headstock/headstock/example/microblog/design/default (added)
- oss/headstock/headstock/example/microblog/design/default/css (added)
- oss/headstock/headstock/example/microblog/design/default/css/style.css (added)
- oss/headstock/headstock/example/microblog/design/default/images (added)
- oss/headstock/headstock/example/microblog/design/default/js (added)
- oss/headstock/headstock/example/microblog/design/default/templates (added)
- oss/headstock/headstock/example/microblog/design/default/templates/footer.mako (added)
- oss/headstock/headstock/example/microblog/design/default/templates/header.mako (added)
- oss/headstock/headstock/example/microblog/design/default/templates/index.mako (added)
- oss/headstock/headstock/example/microblog/design/default/templates/newaccount_step2.mako (added)
- oss/headstock/headstock/example/microblog/design/default/templates/signin.mako (added)
- oss/headstock/headstock/example/microblog/design/default/templates/speakup.mako (added)
- oss/headstock/headstock/example/microblog/design/default/templates/welcome.mako (added)
- oss/headstock/headstock/example/microblog/etc (added)
- oss/headstock/headstock/example/microblog/etc/password (added)
- oss/headstock/headstock/example/microblog/launcher.py (modified) (1 diff)
- oss/headstock/headstock/example/microblog/logs (added)
- oss/headstock/headstock/example/microblog/microblog/atompub (added)
- oss/headstock/headstock/example/microblog/microblog/atompub/__init__.py (added)
- oss/headstock/headstock/example/microblog/microblog/atompub/application.py (added)
- oss/headstock/headstock/example/microblog/microblog/atompub/resource.py (added)
- oss/headstock/headstock/example/microblog/microblog/jabber/__init__.py (modified) (1 diff)
- oss/headstock/headstock/example/microblog/microblog/jabber/atomhandler.py (added)
- oss/headstock/headstock/example/microblog/microblog/jabber/client.py (modified) (32 diffs)
- oss/headstock/headstock/example/microblog/microblog/jabber/monitor.py (added)
- oss/headstock/headstock/example/microblog/microblog/jabber/profile.py (added)
- oss/headstock/headstock/example/microblog/microblog/jabber/pubsub.py (modified) (14 diffs)
- oss/headstock/headstock/example/microblog/microblog/profile (added)
- oss/headstock/headstock/example/microblog/microblog/profile/__init__.py (added)
- oss/headstock/headstock/example/microblog/microblog/profile/manager.py (added)
- oss/headstock/headstock/example/microblog/microblog/profile/user.py (added)
- oss/headstock/headstock/example/microblog/microblog/web/__init__.py (modified) (1 diff)
- oss/headstock/headstock/example/microblog/microblog/web/atompub.py (modified) (6 diffs)
- oss/headstock/headstock/example/microblog/service.xml (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
oss/headstock/headstock/example/microblog/config.xml
r675 r678 4 4 <storage type="filesystem" target="member"> 5 5 <encoding>utf-8</encoding> 6 <basepath>repository</basepath> 7 <!-- Uncomment the following line if you want to add memcached caching support --> 6 <basepath>repository/home/jabber.defuze.org</basepath> 8 7 <!--<cache ref="cache0" />--> 9 8 </storage> 10 9 <storage type="filesystem" target="media"> 11 10 <encoding>utf-8</encoding> 12 <basepath>repository </basepath>11 <basepath>repository/home/jabber.defuze.org</basepath> 13 12 </storage> 14 13 <cache id="cache0"> … … 17 16 </servers> 18 17 </cache> 19 <!--20 <storage type="subversion" target="">21 <repository-uri />22 <workingcopy-path />23 <username />24 <password />25 </storage>26 <storage type="zodb" target="">27 <fstype />28 <fspath />29 <address />30 <top-level-node />31 </storage>32 <storage type="s3" target="">33 <public-key />34 <private-key />35 <bucket-prefix />36 <encoding>utf-8</encoding>37 <aws-key-lookup>38 <module />39 <callable />40 <aws-keys-path />41 </aws-key-lookup>42 </storage>43 <storage type="dejavu" target="">44 <dbtype />45 <capitalize />46 <encoding>ISO-8859-1</encoding>47 <dbconf />48 </storage>49 <storage type="tarball" target="">50 <basepath />51 <compression />52 <encoding>utf-8</encoding>53 </storage>54 -->55 18 </store> 56 19 </config> oss/headstock/headstock/example/microblog/launcher.py
r675 r678 2 2 import sys 3 3 import os 4 import socket 5 import threading 6 from optparse import OptionParser 4 import tempfile 7 5 8 from cherrypy.process import bus 9 from cherrypy.process import plugins, servers 6 import cherrypy 7 from selector4cherrypy import SelectorDispatcher 8 from openid.store import filestore 10 9 11 from microblog.jabber.client import Client 12 from microblog.web import setup_atompub 10 from mako.template import Template 11 from mako.lookup import TemplateLookup 12 13 import microblog.web.profiletool 14 from microblog.web.oidtool import OpenIDTool 15 from microblog.web.application import WebApplication 16 from microblog.web.oid import OpenIDWebApplication 17 from microblog.web.atompub import AtomPubWebApplication 18 from microblog.web.profile import UserProfileAtomPubWebApplication 19 from microblog.profile.manager import ProfileManager 20 21 from microblog.atompub.application import AtomPubApplication 13 22 14 23 base_dir = os.getcwd() 15 24 16 def parse_commandline():17 from optparse import OptionParser18 parser = OptionParser()19 parser.add_option("-d", "--xmpp-domain", dest="domain",20 help="XMPP server domain (default: localhost)")21 parser.set_defaults(domain='localhost')22 parser.add_option("-a", "--address", dest="address", action="store",23 help="XMPP server address (default: localhost:5222) ")24 parser.set_defaults(address='localhost:5222')25 parser.add_option("-u", "--username", dest="username",26 help="XMPP username", action="store")27 parser.set_defaults(username=None)28 parser.add_option("-p", "--password", action="store", dest="password",29 help="XMPP password. You may also be prompted for it if you do not pass this parameter")30 parser.set_defaults(password=None)31 parser.add_option("-r", "--register", action="store_true", dest="register",32 help="Register the user if the server supports in-band registration (default: False)")33 parser.set_defaults(register=False)34 parser.add_option("-t", "--usetls", dest="usetls", action="store_true",35 help="Use TLS (default: False)")36 parser.set_defaults(usetls=False)37 parser.add_option("-w", "--web-only", dest="webonly", action="store_true",38 help="Web server only (default: False)")39 parser.set_defaults(webonly=False)40 (options, args) = parser.parse_args()41 42 return options43 44 25 class Server(object): 45 26 def __init__(self): 46 self.options = parse_commandline() 47 self.client = None 27 self.setup_mako() 28 self.setup_atompub() 29 self.setup_profiles() 30 self.setup_openid() 31 self.setup_web() 32 self.setup_cherrypy() 48 33 49 atompub = setup_atompub(base_dir) 50 if not self.options.webonly: 51 if not self.options.password: 52 from getpass import getpass 53 self.options.password = getpass() 54 host, port = self.options.address.split(':') 55 self.client = Client(atompub, unicode(self.options.username), 56 unicode(self.options.password), 57 unicode(self.options.domain), 58 server=host, port=int(port), 59 usetls=self.options.usetls, 60 register=self.options.register) 34 def run(self): 35 cherrypy.engine.start() 36 cherrypy.engine.block() 61 37 62 def start(self): 63 if self.client: 64 self.client.activate() 65 start.priority = 90 38 def setup_cherrypy(self): 39 cherrypy.config.update({'engine.autoreload_on' : False, 40 'server.socket_port' : 8080, 41 'server.socket_host': '127.0.0.1', 42 'server.socket_queue_size': 15, 43 'log.screen': True, 44 'log.access_file': os.path.join(base_dir, 'logs', 'http_access.log'), 45 'log.error_file': os.path.join(base_dir, 'logs', 'http_error.log'), 46 'checker.on': False,}) 47 d = SelectorDispatcher() 48 d.add('/service[/]', GET=self.atompubapp.service_get, 49 HEAD=self.atompubapp.service_head) 66 50 67 def stop(self): 68 if self.client: 69 self.client.shutdown() 51 # OpenID controllers 52 d.add('/auth/login', GET=self.oidapp.login) 53 d.add('/auth/logout', GET=self.oidapp.logout) 54 d.add('/auth/failure', GET=self.oidapp.failure) 55 d.add('/auth/error', GET=self.oidapp.error) 56 d.add('/auth/cancelled', GET=self.oidapp.cancelled) 70 57 71 def exit(self): 72 self.stop() 58 # New profiles controllers 59 d.add('/profile/new/feed', GET=self.newprofileapp.feed) 60 d.add('/profile/new/', POST=self.newprofileapp.create) 61 d.add('/profile/new/{id}', GET=self.newprofileapp.retrieve, 62 HEAD=self.newprofileapp.retrieve_head, 63 PUT=self.newprofileapp.replace, 64 DELETE=self.newprofileapp.remove) 73 65 74 def setup(server): 75 plugins.SignalHandler(bus) 76 bus.subscribe('start', server.start) 77 bus.subscribe('graceful', server.stop) 78 bus.subscribe('exit', server.exit) 66 # Profiles controllers 67 d.add('/profile/feed', GET=self.existingprofileapp.feed) 68 d.add('/profile/', POST=self.existingprofileapp.create) 69 d.add('/profile/{id}', GET=self.existingprofileapp.retrieve, 70 HEAD=self.existingprofileapp.retrieve_head, 71 PUT=self.existingprofileapp.replace, 72 DELETE=self.existingprofileapp.remove) 79 73 80 def serve_http_only(): 81 import cherrypy 82 cherrypy.quickstart() 74 # Main web application controllers 75 d.add('/signin[/]', GET=self.webapp.signin) 76 d.add('/signup[/]', GET=self.webapp.signup) 77 d.add('/signup/complete', POST=self.webapp.signup_complete) 78 d.add('/signout[/]', GET=self.webapp.signout) 79 d.add('/', GET=self.webapp.index) 83 80 84 def serve(): 85 bus.start() 86 try: 87 try: 88 from Axon.Scheduler import scheduler 89 scheduler.run.runThreads(slowmo=0.01) 90 except (KeyboardInterrupt, IOError): 91 pass 92 except SystemExit: 93 pass 94 finally: 95 bus.exit() 81 cherrypy.tree.mount(self.webapp, '/', {'/': { 'request.dispatch': d, 82 'tools.etags.on': True, 83 'tools.etags.autotags': True, 84 'tools.sessions.on': True, 85 'tools.sessions.storage_type': 'memcached',}, 86 '/signup': {'tools.openid.on': True,}, 87 '/profile/feed': {'tools.openid.on': False, 88 'tools.etags.on': True, 89 'tools.etags.autotags': True,}, 90 '/profile/new': {'tools.openid.on': False, 91 'tools.etags.on': True, 92 'tools.etags.autotags': False,}, 93 '/js': {'tools.openid.on': False, 94 'tools.staticdir.on': True, 95 'tools.staticdir.dir': os.path.join(base_dir, 'design', 96 'default', 'js')}, 97 '/images': {'tools.openid.on': False, 98 'tools.staticdir.on': True, 99 'tools.staticdir.dir': os.path.join(base_dir, 'design', 100 'default', 'images')}, 101 '/css': {'tools.openid.on': False, 102 'tools.staticdir.on': True, 103 'tools.staticdir.dir': os.path.join(base_dir, 'design', 104 'default', 'css')}}) 105 106 def setup_web(self): 107 self.webapp = WebApplication(base_dir, self.atompub, self.tpl_lookup) 108 self.oidapp = OpenIDWebApplication(self.tpl_lookup) 109 self.atompubapp = AtomPubWebApplication(base_dir, self.atompub, self.tpl_lookup) 110 111 collection = self.atompub.service.get_collection_by_xml_id('collection-profile') 112 self.existingprofileapp = UserProfileAtomPubWebApplication(base_dir, self.atompub, 113 collection, self.tpl_lookup) 114 collection = self.atompub.service.get_collection_by_xml_id('collection-profile-new') 115 self.newprofileapp = UserProfileAtomPubWebApplication(base_dir, self.atompub, 116 collection, self.tpl_lookup) 117 self.webapp.new_profiles_atompub_app = self.newprofileapp 118 self.webapp.profiles_atompub_app = self.newprofileapp 119 120 def setup_profiles(self): 121 self.profiles = ProfileManager.load_profiles(base_dir, self.atompub) 122 123 def setup_atompub(self): 124 self.atompub = AtomPubApplication(base_dir) 125 126 def setup_openid(self): 127 store = filestore.FileOpenIDStore(tempfile.gettempdir()) 128 cherrypy.tools.openid = OpenIDTool(store, '/auth') 129 130 def setup_mako(self): 131 tpl_directory = os.path.join(base_dir, 'design', 'default', 'templates') 132 tpl_cache_directory = os.path.join(tpl_directory, 'cache') 133 134 self.tpl_lookup = TemplateLookup(directories=[tpl_directory], 135 module_directory=tpl_cache_directory, 136 collection_size=70) 137 96 138 97 139 if __name__ == '__main__': 98 140 s = Server() 99 setup(s) 100 if s.options.webonly: 101 serve_http_only() 102 else: 103 serve() 141 s.run() oss/headstock/headstock/example/microblog/microblog/jabber/__init__.py
r675 r678 1 # -*- coding: utf-8 -*- oss/headstock/headstock/example/microblog/microblog/jabber/client.py
r675 r678 27 27 28 28 """ 29 from Axon.Ipc import * 29 30 from Axon.Component import component 31 from Axon.Ipc import shutdownMicroprocess, producerFinished 30 32 from Kamaelia.Chassis.Graphline import Graphline 31 33 from Kamaelia.Chassis.Pipeline import Pipeline … … 33 35 from Kamaelia.Util.Backplane import PublishTo, SubscribeTo 34 36 from Kamaelia.Internet.TCPClient import TCPClient 35 from Kamaelia.Util.Console import ConsoleReader 36 from Axon.Ipc import shutdownMicroprocess, producerFinished 37 from Kamaelia.Protocol.HTTP.HTTPClient import SimpleHTTPClient 37 38 38 39 from headstock.protocol.core.stream import ClientStream, StreamError, SaslError … … 45 46 from headstock.protocol.extension.discovery import FeaturesDiscovery 46 47 from headstock.protocol.extension.pubsub import * 48 from headstock.api import Entity 47 49 from headstock.api.jid import JID 48 from headstock.api.im import Message, Body, Event 50 from headstock.api.im import Message, Body, Event, XHTMLBody 49 51 from headstock.api.contact import Presence, Roster, Item 50 from headstock.api import Entity51 52 from headstock.api.activity import Activity 52 53 from headstock.api.registration import Registration 53 54 from headstock.lib.parser import XMLIncrParser 54 55 from headstock.lib.logger import Logger 55 from headstock.lib.utils import generate_unique 56 from headstock.lib.utils import generate_unique, remove_BOM 56 57 57 58 from bridge import Element as E 58 59 from bridge.common import XMPP_CLIENT_NS, XMPP_ROSTER_NS, \ 59 60 XMPP_LAST_NS, XMPP_DISCO_INFO_NS, XMPP_IBR_NS, \ 60 XMPP_DISCO_ITEMS_NS, XMPP_PUBSUB_NS, XMPP_PUBSUB_EVENT_NS 61 XMPP_DISCO_ITEMS_NS, XMPP_PUBSUB_NS, XMPP_PUBSUB_EVENT_NS,\ 62 XMPP_PUBSUB_OWNER_NS 61 63 62 64 from microblog.jabber.pubsub import DiscoHandler, ItemsHandler, MessageHandler … … 77 79 "activity" : "headstock.api.activity.Activity instance to send to the server"} 78 80 79 def __init__(self, from_jid ):81 def __init__(self, from_jid, session_id): 80 82 super(RosterHandler, self).__init__() 81 83 self.from_jid = from_jid 82 84 self.roster = None 85 self.session_id = session_id 83 86 84 87 def initComponents(self): … … 86 89 # that will inform us when the server has 87 90 # returned the per-session jid 88 sub = SubscribeTo("JID ")91 sub = SubscribeTo("JID.%s" % self.session_id) 89 92 self.link((sub, 'outbox'), (self, 'jid')) 90 93 self.addChildren(sub) … … 116 119 roster = self.recv("inbox") 117 120 self.roster = roster 118 print "Your contacts:"119 121 for nodeid in roster.items: 120 122 contact = roster.items[nodeid] 121 print " ", contact.jid122 123 123 124 if self.dataReady('ask-activity'): … … 141 142 142 143 Outboxes = {"outbox" : "headstock.api.im.Message to send to the client", 143 "signal" : "Shutdown signal"} 144 145 def __init__(self): 144 "signal" : "Shutdown signal", 145 "PI" : "Publish item", 146 "DI" : "Retract item", 147 "PN" : "Purge node from items", 148 "CN" : "Create node", 149 "DN" : "Delete node", 150 "CCN" : "Create collection node", 151 "DCN" : "Delete collection node", 152 "SN" : "Subscribe to node", 153 "UN" : "Unsubscribe from node"} 154 155 def __init__(self, session_id, profile): 146 156 super(DummyMessageHandler, self).__init__() 147 157 self.from_jid = None 158 self.session_id = session_id 159 self.profile = profile 148 160 149 161 def initComponents(self): 150 sub = SubscribeTo("JID ")162 sub = SubscribeTo("JID.%s" % self.session_id) 151 163 self.link((sub, 'outbox'), (self, 'jid')) 152 self.addChildren(sub)153 sub.activate()154 155 sub = SubscribeTo("CONSOLE")156 self.link((sub, 'outbox'), (self, 'inbox'))157 164 self.addChildren(sub) 158 165 sub.activate() … … 175 182 if self.dataReady("inbox"): 176 183 m = self.recv("inbox") 177 # in this first case, we want to send the message178 # typed in the console.179 # The message is of the form:180 # contant_jid message181 184 if isinstance(m, str) and m != '': 182 try: 183 contact_jid, message = m.split(' ', 1) 184 except ValueError: 185 print "Messages format: contact_jid message" 186 continue 187 m = Message(unicode(self.from_jid), unicode(contact_jid), 188 type=u'chat', stanza_id=generate_unique()) 189 m.event = Event.composing # note the composing event status 190 m.bodies.append(Body(unicode(message))) 191 self.send(m, "outbox") 192 193 # Right after we sent the first message 194 # we send another one reseting the event status 195 m = Message(unicode(self.from_jid), unicode(contact_jid), 196 type=u'chat', stanza_id=generate_unique()) 197 self.send(m, "outbox") 185 pass 198 186 # In this case we actually received a message 199 187 # from a contact, we print it. 200 188 elif isinstance(m, Message): 201 189 for body in m.bodies: 202 print m.from_jid, ": ", str(body) 190 message = remove_BOM(body.plain_body).strip() 191 print repr(message) 192 if message == 'help': 193 m = Message(self.from_jid, m.from_jid) 194 b = """<ul> 195 <li>PI text</li> 196 <li>PI text</li> 197 <li>DI text</li> 198 <li>CN text</li> 199 <li>DN text</li> 200 <li>PN text</li> 201 <li>SN text</li> 202 <li>UN text</li> 203 </ul>""" 204 b = E.load(b).xml_root 205 m.bodies.append(XHTMLBody(b)) 206 self.send(m, 'outbox') 207 else: 208 try: 209 action, data = message.split(' ', 1) 210 except ValueError: 211 action = 'PI' 212 data = message 213 214 if action in self.outboxes: 215 self.send(data, action) 203 216 204 217 if not self.anyReady(): … … 220 233 } 221 234 222 def __init__(self ):235 def __init__(self, session_id): 223 236 super(ActivityHandler, self).__init__() 237 self.session_id = session_id 224 238 225 239 def initComponents(self): 226 sub = SubscribeTo("DISCO_FEAT ")240 sub = SubscribeTo("DISCO_FEAT.%s" % self.session_id) 227 241 self.link((sub, 'outbox'), (self, 'inbox')) 228 242 self.addChildren(sub) … … 244 258 disco = self.recv("inbox") 245 259 support = disco.has_feature(XMPP_LAST_NS) 246 print "Activity support: ", support247 260 if support: 248 261 self.send('', "activity-supported") … … 256 269 Inboxes = {"inbox" : "headstock.api.contact.Presence instance", 257 270 "control" : "Shutdown the client stream", 271 "jid" : "headstock.api.jid.JID instance received from the server", 258 272 "subscribe" : "", 259 273 "unsubscribe" : "",} … … 264 278 "log" : "log",} 265 279 266 def __init__(self ):280 def __init__(self, session_id): 267 281 super(PresenceHandler, self).__init__() 282 self.session_id = session_id 283 284 def initComponents(self): 285 sub = SubscribeTo("JID.%s" % self.session_id) 286 self.link((sub, 'outbox'), (self, 'jid')) 287 self.addChildren(sub) 288 sub.activate() 289 290 return 1 268 291 269 292 def main(self): 293 yield self.initComponents() 294 270 295 while 1: 271 296 if self.dataReady("control"): … … 275 300 self.send(producerFinished(), "signal") 276 301 break 302 303 if self.dataReady("jid"): 304 self.from_jid = self.recv('jid') 305 306 if '.microblogging' not in unicode(self.from_jid): 307 sibling = JID(unicode('%s.microblogging' % self.from_jid.node), 308 self.from_jid.domain) 309 p = Presence(from_jid=self.from_jid, to_jid=unicode(sibling), 310 type=u'subscribe') 311 self.send(p, "outbox") 277 312 278 313 if self.dataReady("subscribe"): … … 325 360 Inboxes = {"inbox" : "headstock.api.registration.Registration", 326 361 "error" : "headstock.api.registration.Registration", 327 "control" : "Shutdown the client stream",} 362 "control" : "Shutdown the client stream", 363 "_response": ""} 328 364 329 365 Outboxes = {"outbox" : "headstock.api.registration.Registration", 330 366 "signal" : "Shutdown signal", 331 "log" : "log",} 332 333 def __init__(self, username, password): 367 "log" : "log", 368 "_request": ""} 369 370 def __init__(self, username, password, session_id, profile): 334 371 super(RegistrationHandler, self).__init__() 335 372 self.username = username 336 373 self.password = password 374 self.profile = profile 337 375 self.registration_id = None 376 self.session_id = session_id 377 378 def initComponents(self): 379 self.client = SimpleHTTPClient() 380 self.addChildren(self.client) 381 self.link((self, '_request'), (self.client, 'inbox')) 382 self.link((self.client, 'outbox'), (self, '_response')) 383 self.client.activate() 338 384 339 385 def main(self): 386 yield self.initComponents() 387 340 388 while 1: 341 389 if self.dataReady("control"): … … 346 394 break 347 395 396 if self.dataReady("_response"): 397 self.recv("_response") 398 348 399 if self.dataReady("inbox"): 349 400 r = self.recv('inbox') 350 401 if r.registered: 351 print "'%s' is already a registered username." % self.username 402 self.send("'%s' is already a registered username." % r.infos[u'username'], 'log') 403 c = Client.Sessions[r.infos[u'username']] 404 c.shutdown() 405 del Client.Sessions[r.infos[u'username']] 352 406 elif self.registration_id == r.stanza_id: 353 print "'%s' is now a registered user."\ 354 "Please restart the client without the register flag." % self.username 407 c = Client.Sessions[r.infos[u'username']] 408 c.shutdown() 409 del Client.Sessions[r.infos[u'username']] 410 411 if '.microblogging' not in r.infos[u'username']: 412 body = self.profile.xml() 413 params = {'url': 'http://localhost:8080/profile/', 414 'method': 'POST', 'postbody': body, 415 'extraheaders': {'content-type': 'application/xml', 416 'slug': self.profile.username, 417 'content-length': len(body)}} 418 self.send(params, '_request') 355 419 else: 356 420 if 'username' in r.infos and 'password' in r.infos: … … 363 427 if self.dataReady("error"): 364 428 r = self.recv('error') 365 print r.error 429 if r.error.code == '409': 430 self.send("'%s' is already a registered username." % r.infos[u'username'], 'log') 431 c = Client.Sessions[r.infos[u'username']] 432 c.shutdown() 433 del Client.Sessions[r.infos[u'username']] 434 self.send(r.error, 'log') 366 435 367 436 if not self.anyReady(): … … 374 443 "jid" : "", 375 444 "streamfeat" : "", 445 "connected" : "", 446 "unhandled" : "", 376 447 "control" : "Shutdown the client stream"} 377 448 … … 383 454 "doregistration" : ""} 384 455 456 Domain = None 457 Host = u'localhost' 458 Port = 5222 459 460 Sessions = {} 461 385 462 def __init__(self, atompub, username, password, domain, resource=u"headstock-client1", 386 server=u'localhost', port=5222, usetls=False, register=False ):463 server=u'localhost', port=5222, usetls=False, register=False, session_id=None, profile=None): 387 464 super(Client, self).__init__() 465 self.running = False 466 self.connected = False 388 467 self.atompub = atompub 389 self.jid = JID(username, domain, resource) 468 if not session_id: 469 session_id = generate_unique() 470 self.session_id = session_id 471 self.backplanes = [] 390 472 self.username = username 391 473 self.password = password 474 self.jid = JID(self.username, domain, '%s!%s' % (resource, session_id)) 392 475 self.server = server 393 476 self.port = port … … 397 480 self.usetls = usetls 398 481 self.register = register 482 self.restartable = False 483 self.profile = profile 484 485 @staticmethod 486 def start_clients(atompub, users): 487 for username, password in users: 488 profile = atompub.load_profile(username) 489 Client.connect_jabber_user(atompub, username, password, profile) 490 491 @staticmethod 492 def register_jabber_user(atompub, username, password, profile): 493 c = Client(atompub, unicode(username), unicode(password), 494 domain=Client.Domain, server=Client.Host, port=Client.Port, 495 usetls=True, register=True, profile=profile) 496 Client.Sessions[c.username] = c 497 c.activate() 498 499 username = unicode('%s.microblogging' % username) 500 c = Client(atompub, unicode(username), unicode(password), 501 domain=Client.Domain, server=Client.Host, port=Client.Port, 502 usetls=True, register=True, profile=profile) 503 Client.Sessions[c.username] = c 504 c.activate() 505 506 @staticmethod 507 def connect_jabber_user(atompub, username, password, profile): 508 c = Client(atompub, unicode(username), unicode(password), 509 domain=Client.Domain, server=Client.Host, port=Client.Port, 510 usetls=True, register=False, profile=profile) 511 Client.Sessions[c.username] = c 512 c.activate() 513 514 username = unicode('%s.microblogging' % username) 515 c = Client(atompub, unicode(username), unicode(password), 516 domain=Client.Domain, server=Client.Host, port=Client.Port, 517 usetls=True, register=False, profile=profile) 518 Client.Sessions[c.username] = c 519 c.activate() 520 521 @staticmethod 522 def disconnect_jabber_user(username): 523 if username in Client.Sessions: 524 c = Client.Sessions[username] 525 del Client.Sessions[username] 526 c.shutdown() 527 528 @staticmethod 529 def get_status(username): 530 return username in Client.Sessions 531 532 @staticmethod 533 def is_registered(username): 534 if username.lower() in Client.Sessions: 535 return Client.Sessions[username.lower()] 536 537 return False 399 538 400 539 def passwordLookup(self, jid): … … 402 541 403 542 def shutdown(self): 404 self.send(Presence.to_element(Presence(self.jid, type=u'unavailable')), 'forward')543 #self.send(Presence.to_element(Presence(self.jid, type=u'unavailable')), 'forward') 405 544 self.send('OUTGOING : </stream:stream>', 'log') 406 self.send('</stream:stream>', 'outbox') 545 self.send('</stream:stream>', 'outbox') 546 self.running = False 407 547 408 548 def abort(self): 409 549 self.send('OUTGOING : </stream:stream>', 'log') 410 self.send('</stream:stream>', 'outbox') 550 self.send('</stream:stream>', 'outbox') 551 self.running = False 411 552 412 553 def setup(self): 554 self.running = True 555 413 556 # Backplanes are like a global entry points that 414 557 # can be accessible both for publishing and … … 426 569 # the server returns the per-session JID that 427 570 # is of interest for most other components). 428 Backplane("CONSOLE").activate() 429 Backplane("JID").activate() 571 self.backplanes.append(Backplane("JID.%s" % self.session_id).activate()) 430 572 # Used to inform components that the session is now active 431 Backplane("BOUND").activate()573 self.backplanes.append(Backplane("BOUND.%s" % self.session_id).activate()) 432 574 # Used to inform components of the supported features 433 Backplane("DISCO_FEAT").activate()434 435 sub = SubscribeTo("JID ")575 self.backplanes.append(Backplane("DISCO_FEAT.%s" % self.session_id).activate()) 576 577 sub = SubscribeTo("JID.%s" % self.session_id) 436 578 self.link((sub, 'outbox'), (self, 'jid')) 437 579 self.addChildren(sub) 438 580 sub.activate() 439 581 440 # We pipe everything typed into the console 441 # directly to the console backplane so that 442 # every components subscribed to the console 443 # backplane inbox will get the typed data and 444 # will decide it it's of concern or not. 445 Pipeline(ConsoleReader(), PublishTo('CONSOLE')).activate() 582 sub = SubscribeTo("BOUND.%s" % self.session_id) 583 self.link((sub, 'outbox'), (self, 'connected')) 584 self.addChildren(sub) 585 sub.activate() 446 586 447 587 # Add two outboxes ro the ClientSteam to support specific extensions. … … 453 593 ClientStream.Outboxes["%s.unsubscribe" % XMPP_PUBSUB_NS] = "Pubsub unsubscription handler" 454 594 ClientStream.Outboxes["%s.subscriptions" % XMPP_PUBSUB_NS] = "Pubsub subscriptions handler" 595 ClientStream.Outboxes["%s.affiliations" % XMPP_PUBSUB_NS] = "Pubsub affiliations handler" 455 596 ClientStream.Outboxes["%s.create" % XMPP_PUBSUB_NS] = "Pubsub node creation handler" 456 ClientStream.Outboxes["%s.delete" % XMPP_PUBSUB_NS] = "Pubsub node deletion handler" 597 ClientStream.Outboxes["%s.purge" % XMPP_PUBSUB_OWNER_NS] = "Pubsub node purge handler" 598 ClientStream.Outboxes["%s.delete" % XMPP_PUBSUB_OWNER_NS] = "Pubsub node delete handler" 457 599 ClientStream.Outboxes["%s.publish" % XMPP_PUBSUB_NS] = "Pubsub item publication handler" 458 600 ClientStream.Outboxes["%s.retract" % XMPP_PUBSUB_NS] = "Pubsub item deletion handler" … … 461 603 462 604 self.client = ClientStream(self.jid, self.passwordLookup, use_tls=self.usetls) 463 605 self.addChildren(self.client) 606 self.client.activate() 607 464 608 self.graph = Graphline(client = self, 465 console = SubscribeTo('CONSOLE'),466 logger = Logger(path=None, stdout=True),609 logger = Logger(path='./logs/%s.log' % self.username, 610 stdout=False, name=self.session_id), 467 611 tcp = TCPClient(self.server, self.port), 468 612 xmlparser = XMLIncrParser(), … … 470 614 streamerr = StreamError(), 471 615 saslerr = SaslError(), 472 discohandler = DiscoHandler(self.jid, self.atompub, self.domain), 473 activityhandler = ActivityHandler(), 474 rosterhandler = RosterHandler(self.jid), 475 registerhandler = RegistrationHandler(self.username, self.password), 476 msgdummyhandler = DummyMessageHandler(), 477 presencehandler = PresenceHandler(), 478 itemshandler = ItemsHandler(self.jid, self.atompub, self.domain), 479 pubsubmsgeventhandler = MessageHandler(self.jid, self.atompub, self.domain), 616 discohandler = DiscoHandler(self.jid, self.atompub, self.domain, 617 session_id=self.session_id, 618 profile=self.profile), 619 activityhandler = ActivityHandler(session_id=self.session_id), 620 rosterhandler = RosterHandler(self.jid, session_id=self.session_id), 621 registerhandler = RegistrationHandler(self.username, self.password, 622
