Skip to content

Commit

Permalink
Use Plugin architecture for Features.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben Weaver committed Feb 5, 2010
1 parent 83a819b commit 7868298
Show file tree
Hide file tree
Showing 10 changed files with 646 additions and 442 deletions.
29 changes: 14 additions & 15 deletions examples/ping-pong.py
Expand Up @@ -72,9 +72,6 @@ class Client(xmpp.Plugin):

def __init__(self):
self.pongs = 0

@xmpp.bind(xmpp.SessionStarted)
def send_ping(self):
self.plugin(PingPong).send_ping()

@xmpp.bind(ReceivedPong)
Expand All @@ -96,6 +93,7 @@ def loop(cls):
del cls.SCHEDULE[0]
if callback:
callback(data)
done and done()
elif isinstance(data, float) and data < time.time():
if cls.SCHEDULE:
cls.SCHEDULE.append((callback, data, done))
Expand All @@ -117,7 +115,7 @@ def __init__(self, name, app, dest):
self.io = self.IO()

print '%s: OPEN' % self.name
self.target = app.Core(('127.0.0.1', 0), self, **app.settings)
self.target = app.Core(self, **app.settings)

def read(self, callback):
self.reader = callback
Expand All @@ -131,7 +129,7 @@ def on_close(self, callback):
self.closed = callback

def write(self, data, callback=None):
print '%s:' % self.name, data
# print '%s:' % self.name, data
if self.dest:
self.SCHEDULE.append((self.dest, data, callback))
return self
Expand All @@ -143,23 +141,24 @@ def close(self):
if __name__ == '__main__':
client = xmpp.Client({
'plugins': [PingPong, Client],
'auth': xmpp.ClientAuth('xmpp', 'example.net', 'user@example.net', 'secret'),
'resources': xmpp.state.Resources()
'host': 'example.net',
'username': 'user@example.net',
'password': 'secret'
})

server = xmpp.Server({
'plugins': [PingPong],
'auth': xmpp.ServerAuth('xmpp', 'example.net', { 'user@example.net': 'secret' }),
'resources': xmpp.state.Resources(),
'host': 'example.net',
'users': { 'user@example.net': 'secret' },
'certfile': os.path.join(os.path.dirname(__file__), 'certs/self.crt'),
'keyfile': os.path.join(os.path.dirname(__file__), 'certs/self.key')
})

CP = Stream('C', client, lambda d: SP.reader(d))
SP = Stream('S', server, lambda d: CP.reader(d))
Stream.loop()
# CP = Stream('C', client, lambda d: SP.reader(d))
# SP = Stream('S', server, lambda d: CP.reader(d))
# Stream.loop()

# SP = xmpp.TCPServer(server).bind('127.0.0.1', '9000')
# CP = xmpp.TCPClient(client).connect('127.0.0.1', '9000')
# xmpp.start([SP, CP])
SP = xmpp.TCPServer(server).bind('127.0.0.1', '9000')
CP = xmpp.TCPClient(client).connect('127.0.0.1', '9000')
xmpp.start([SP, CP])

121 changes: 116 additions & 5 deletions xmpp/application.py
@@ -1,28 +1,95 @@
## Copyright (c) 2010, Coptix, Inc. All rights reserved.
## See the LICENSE file for license terms and warranty disclaimer.

"""application -- XMPP application constructors"""
"""application -- XMPP application constructors
These are shortcuts for creating XMPP applications. All that's really
necessary to create an XMPP server is:
xmppstream.XMPPHandler(core.ServerCore, {
'jid': 'example.net',
'plugins': plugin.CompiledPlugins([...]),
'features': plugin.CompiledFeatures([...])
})
These shortcuts process settings into the form above.
"""

from __future__ import absolute_import
import sasl
from . import core, xmppstream, plugin
import sasl, socket
from . import core, xmppstream, plugin, state
from .prelude import *

__all__ = ('Server', 'Client', 'Application', 'ServerAuth', 'ClientAuth')


### Applications

def Server(settings):
return Application(core.ServerCore, settings)
return Application(core.ServerCore, server_settings(settings))

def Client(settings):
return Application(core.ClientCore, settings)
return Application(core.ClientCore, client_settings(settings))

def Application(Core, settings):
"""Declare an XMPP Application. An application is an XMLHandler
that dispatches to stanza handlers.
"""

settings['features'] = plugin.CompiledFeatures(settings.pop('features', ()))
settings['plugins'] = plugin.CompiledPlugins(settings.pop('plugins', ()))

return xmppstream.XMPPHandler(Core, settings)

def default_settings(settings, defaults):
"""Create missing settings by making default values."""

for (name, default) in defaults:
if name not in settings:
settings[name] = default(settings)
return settings


### Server

def server_settings(settings):
return default_settings(settings, (
('jid', server_jid),
('features', server_features)
))

def server_jid(settings):
return settings.get('host') or socket.gethostname()

def server_features(settings):
auth = server_auth(settings)
resources = default_resources(settings)
return (
(core.TLS, dict(ipop(settings, 'certfile', 'keyfile'), server_side=True)),
(core.SASL, dict(ipop(settings, 'mechanisms'), auth=auth)),
(core.Bind, dict(resources=resources)),
core.Session
)

def server_auth(settings):
auth = pop(settings, 'auth')
if not auth:
users = pop(settings, 'users')
if not (users and isinstance(users, Mapping)):
raise ValueError(
'To make a server, add a "users" setting. It should be '
'a dictionary of (username, password) items.'
)
auth = ServerAuth(
settings.pop('service', 'xmpp'),
pop(settings, 'host') or socket.gethostname(),
users
)
return auth

def default_resources(settings):
return pop(settings, 'resources') or state.Resources()

def ServerAuth(serv_type, host, users):

def user():
Expand All @@ -40,6 +107,50 @@ def password():
lambda: host
)


### Client

def client_settings(settings):
return default_settings(settings, (
('jid', client_jid),
('features', client_features)
))

def client_jid(settings):
jid = settings.get('host')
if not jid:
raise ValueError('Missing required "host" setting.')
return jid

def client_features(settings):
auth = client_auth(settings)
resources = default_resources(settings)
return (
core.TLS,
(core.SASL, dict(ipop(settings, 'mechanisms'), auth=auth)),
(core.Bind, dict(resources=resources)),
core.Session
)

def client_auth(settings):
auth = pop(settings, 'auth')
if not auth:
(username, password) = pop(settings, 'username', 'password')
if username is None or password is None:
raise ValueError('Missing "username" or "password".')

host = pop(settings, 'host')
if not host:
raise ValueError('Missing required "host" setting.')

auth = ClientAuth(
settings.pop('service', 'xmpp'),
host,
username,
password
)
return auth

def ClientAuth(serv_type, host, username, password):

return sasl.SimpleAuth(
Expand Down

0 comments on commit 7868298

Please sign in to comment.