Page MenuHomePhabricator

No OneTemporary

diff --git a/sylk/applications/xmppgateway/configuration.py b/sylk/applications/xmppgateway/configuration.py
index 8afe6ee..c8eda62 100644
--- a/sylk/applications/xmppgateway/configuration.py
+++ b/sylk/applications/xmppgateway/configuration.py
@@ -1,22 +1,25 @@
from application.system import host
from application.configuration import ConfigSection, ConfigSetting
from application.configuration.datatypes import StringList
from sipsimple.configuration.datatypes import NonNegativeInteger
from sylk.configuration.datatypes import IPAddress, Port
class XMPPGatewayConfig(ConfigSection):
__cfgfile__ = 'xmppgateway.ini'
__section__ = 'general'
local_ip = ConfigSetting(type=IPAddress, value=IPAddress(host.default_ip))
local_port = ConfigSetting(type=Port, value=5269)
trace_xmpp = False
log_presence = False
+ transport = ConfigSetting(type=str, value='tls')
+ ca_file = ConfigSetting(type=str, value='/etc/sylkserver/tls/ca.crt')
+ certificate = ConfigSetting(type=str, value='/etc/sylkserver/tls/default.crt')
domains = ConfigSetting(type=StringList, value=[])
muc_prefix = 'conference'
sip_session_timeout = ConfigSetting(type=NonNegativeInteger, value=86400)
use_msrp_for_chat = True
diff --git a/sylk/applications/xmppgateway/xmpp/__init__.py b/sylk/applications/xmppgateway/xmpp/__init__.py
index 0936249..9d3f7c4 100644
--- a/sylk/applications/xmppgateway/xmpp/__init__.py
+++ b/sylk/applications/xmppgateway/xmpp/__init__.py
@@ -1,330 +1,361 @@
+import os
+
from application.notification import IObserver, NotificationCenter, NotificationData
from application.python import Null
from application.python.types import Singleton
-from twisted.internet import reactor
+from twisted.internet import reactor, ssl
from wokkel.disco import DiscoClientProtocol
from wokkel.generic import FallbackHandler, VersionHandler
from wokkel.ping import PingHandler
from wokkel.server import ServerService, XMPPS2SServerFactory
from zope.interface import implements
+from sylk.configuration.datatypes import Path
from sylk import __version__ as SYLK_VERSION
from sylk.applications.xmppgateway.configuration import XMPPGatewayConfig
from sylk.applications.xmppgateway.datatypes import FrozenURI
from sylk.applications.xmppgateway.logger import log
from sylk.applications.xmppgateway.xmpp.jingle.session import JingleSession, JingleSessionManager
from sylk.applications.xmppgateway.xmpp.protocols import DiscoProtocol, JingleProtocol, MessageProtocol, MUCServerProtocol, MUCPresenceProtocol, PresenceProtocol
from sylk.applications.xmppgateway.xmpp.server import SylkInternalComponent, SylkRouter
from sylk.applications.xmppgateway.xmpp.session import XMPPChatSessionManager, XMPPMucSessionManager
from sylk.applications.xmppgateway.xmpp.subscription import XMPPSubscriptionManager
-
class XMPPManager(object):
__metaclass__ = Singleton
implements(IObserver)
def __init__(self):
config = XMPPGatewayConfig
self.stopped = False
self.domains = set(config.domains)
self.muc_domains = set('%s.%s' % (config.muc_prefix, domain) for domain in self.domains)
router = SylkRouter()
self._server_service = ServerService(router)
self._server_service.domains = self.domains | self.muc_domains
self._server_service.logTraffic = False # done manually
self._s2s_factory = XMPPS2SServerFactory(self._server_service)
self._s2s_factory.logTraffic = False # done manually
# Setup internal components
self._internal_component = SylkInternalComponent(router)
self._internal_component.domains = self.domains
self._internal_component.manager = self
self._muc_component = SylkInternalComponent(router)
self._muc_component.domains = self.muc_domains
self._muc_component.manager = self
# Setup protocols
self.message_protocol = MessageProtocol()
self.message_protocol.setHandlerParent(self._internal_component)
self.presence_protocol = PresenceProtocol()
self.presence_protocol.setHandlerParent(self._internal_component)
self.disco_protocol = DiscoProtocol()
self.disco_protocol.setHandlerParent(self._internal_component)
self.disco_client_protocol = DiscoClientProtocol()
self.disco_client_protocol.setHandlerParent(self._internal_component)
self.muc_protocol = MUCServerProtocol()
self.muc_protocol.setHandlerParent(self._muc_component)
self.muc_presence_protocol = MUCPresenceProtocol()
self.muc_presence_protocol.setHandlerParent(self._muc_component)
self.disco_muc_protocol = DiscoProtocol()
self.disco_muc_protocol.setHandlerParent(self._muc_component)
self.version_protocol = VersionHandler('SylkServer', SYLK_VERSION)
self.version_protocol.setHandlerParent(self._internal_component)
self.fallback_protocol = FallbackHandler()
self.fallback_protocol.setHandlerParent(self._internal_component)
self.fallback_muc_protocol = FallbackHandler()
self.fallback_muc_protocol.setHandlerParent(self._muc_component)
self.ping_protocol = PingHandler()
self.ping_protocol.setHandlerParent(self._internal_component)
self.jingle_protocol = JingleProtocol()
self.jingle_protocol.setHandlerParent(self._internal_component)
self.jingle_coin_protocol = JingleProtocol()
self.jingle_coin_protocol.setHandlerParent(self._muc_component)
self._s2s_listener = None
self.chat_session_manager = XMPPChatSessionManager()
self.muc_session_manager = XMPPMucSessionManager()
self.subscription_manager = XMPPSubscriptionManager()
self.jingle_session_manager = JingleSessionManager()
def start(self):
self.stopped = False
# noinspection PyUnresolvedReferences
- self._s2s_listener = reactor.listenTCP(XMPPGatewayConfig.local_port, self._s2s_factory, interface=XMPPGatewayConfig.local_ip)
+ interface = XMPPGatewayConfig.local_ip
+ port = XMPPGatewayConfig.local_port
+ cert_path = Path(XMPPGatewayConfig.certificate).normalized if XMPPGatewayConfig.certificate else None
+ cert_chain_path = Path(XMPPGatewayConfig.ca_file).normalized if XMPPGatewayConfig.ca_file else None
+
+ if XMPPGatewayConfig.transport == 'tls':
+ if cert_path is not None:
+ if not os.path.isfile(cert_path):
+ log.error('Certificate file %s could not be found' % cert_path)
+ return
+ try:
+ ssl_ctx_factory = ssl.DefaultOpenSSLContextFactory(cert_path, cert_path)
+ except Exception:
+ log.exception('Creating TLS context')
+ return
+ if cert_chain_path is not None:
+ if not os.path.isfile(cert_chain_path):
+ log.error('Certificate chain file %s could not be found' % cert_chain_path)
+ return
+ ssl_ctx = ssl_ctx_factory.getContext()
+ try:
+ ssl_ctx.use_certificate_chain_file(cert_chain_path)
+ except Exception:
+ log.exception('Setting TLS certificate chain file')
+ return
+ self._s2s_listener = reactor.listenSSL(port, self._s2s_factory, ssl_ctx_factory, interface=interface)
+ else:
+ self._s2s_listener = reactor.listenTCP(port, self._s2s_factory, interface=interface)
+
+ port = self._s2s_listener.getHost().port
listen_address = self._s2s_listener.getHost()
- log.info("XMPP listener started on %s:%d" % (listen_address.host, listen_address.port))
+ log.info("XMPP S2S component listening on %s:%d (%s)" % (listen_address.host, listen_address.port, XMPPGatewayConfig.transport.upper()))
self.chat_session_manager.start()
self.muc_session_manager.start()
self.subscription_manager.start()
self.jingle_session_manager.start()
notification_center = NotificationCenter()
notification_center.add_observer(self, sender=self._internal_component)
notification_center.add_observer(self, sender=self._muc_component)
self._internal_component.startService()
self._muc_component.startService()
def stop(self):
self.stopped = True
self._s2s_listener.stopListening()
self.jingle_session_manager.stop()
self.subscription_manager.stop()
self.muc_session_manager.stop()
self.chat_session_manager.stop()
self._internal_component.stopService()
self._muc_component.stopService()
notification_center = NotificationCenter()
notification_center.remove_observer(self, sender=self._internal_component)
notification_center.remove_observer(self, sender=self._muc_component)
def send_stanza(self, stanza):
if self.stopped:
return
self._internal_component.send(stanza.to_xml_element())
def send_muc_stanza(self, stanza):
if self.stopped:
return
self._muc_component.send(stanza.to_xml_element())
def handle_notification(self, notification):
handler = getattr(self, '_NH_%s' % notification.name, Null)
handler(notification)
# Process message stanzas
def _NH_XMPPGotChatMessage(self, notification):
message = notification.data.message
try:
session = self.chat_session_manager.sessions[(message.recipient.uri, message.sender.uri)]
except KeyError:
notification.center.post_notification('XMPPGotChatMessage', sender=self, data=notification.data)
else:
session.channel.send(message)
def _NH_XMPPGotNormalMessage(self, notification):
notification.center.post_notification('XMPPGotNormalMessage', sender=self, data=notification.data)
def _NH_XMPPGotComposingIndication(self, notification):
composing_indication = notification.data.composing_indication
try:
session = self.chat_session_manager.sessions[(composing_indication.recipient.uri, composing_indication.sender.uri)]
except KeyError:
notification.center.post_notification('XMPPGotComposingIndication', sender=self, data=notification.data)
else:
session.channel.send(composing_indication)
def _NH_XMPPGotErrorMessage(self, notification):
error_message = notification.data.error_message
try:
session = self.chat_session_manager.sessions[(error_message.recipient.uri, error_message.sender.uri)]
except KeyError:
notification.center.post_notification('XMPPGotErrorMessage', sender=self, data=notification.data)
else:
session.channel.send(error_message)
def _NH_XMPPGotReceipt(self, notification):
receipt = notification.data.receipt
try:
session = self.chat_session_manager.sessions[(receipt.recipient.uri, receipt.sender.uri)]
except KeyError:
pass
else:
session.channel.send(receipt)
# Process presence stanzas
def _NH_XMPPGotPresenceAvailability(self, notification):
stanza = notification.data.presence_stanza
if stanza.recipient.uri.resource is not None:
# Skip directed presence
return
sender_uri = stanza.sender.uri
sender_uri_bare = FrozenURI(sender_uri.user, sender_uri.host)
try:
subscription = self.subscription_manager.outgoing_subscriptions[(stanza.recipient.uri, sender_uri_bare)]
except KeyError:
# Ignore incoming presence stanzas if there is no subscription
pass
else:
subscription.channel.send(stanza)
def _NH_XMPPGotPresenceSubscriptionStatus(self, notification):
stanza = notification.data.presence_stanza
if stanza.sender.uri.resource is not None or stanza.recipient.uri.resource is not None:
# Skip directed presence
return
if stanza.type in ('subscribed', 'unsubscribed'):
try:
subscription = self.subscription_manager.outgoing_subscriptions[(stanza.recipient.uri, stanza.sender.uri)]
except KeyError:
pass
else:
subscription.channel.send(stanza)
elif stanza.type in ('subscribe', 'unsubscribe'):
try:
subscription = self.subscription_manager.incoming_subscriptions[(stanza.recipient.uri, stanza.sender.uri)]
except KeyError:
if stanza.type == 'subscribe':
notification.center.post_notification('XMPPGotPresenceSubscriptionRequest', sender=self, data=NotificationData(stanza=stanza))
else:
subscription.channel.send(stanza)
def _NH_XMPPGotPresenceProbe(self, notification):
stanza = notification.data.presence_stanza
if stanza.recipient.uri.resource is not None:
# Skip directed presence
return
sender_uri = stanza.sender.uri
sender_uri_bare = FrozenURI(sender_uri.user, sender_uri.host)
try:
subscription = self.subscription_manager.incoming_subscriptions[(stanza.recipient.uri, sender_uri_bare)]
except KeyError:
notification.center.post_notification('XMPPGotPresenceSubscriptionRequest', sender=self, data=NotificationData(stanza=stanza))
else:
subscription.channel.send(stanza)
# Process muc stanzas
def _NH_XMPPMucGotGroupChat(self, notification):
message = notification.data.message
muc_uri = FrozenURI(message.recipient.uri.user, message.recipient.uri.host)
try:
session = self.muc_session_manager.incoming[(muc_uri, message.sender.uri)]
except KeyError:
# Ignore groupchat messages if there was no session created
pass
else:
session.channel.send(message)
def _NH_XMPPMucGotSubject(self, notification):
message = notification.data.message
muc_uri = FrozenURI(message.recipient.uri.user, message.recipient.uri.host)
try:
session = self.muc_session_manager.incoming[(muc_uri, message.sender.uri)]
except KeyError:
# Ignore groupchat messages if there was no session created
pass
else:
session.channel.send(message)
def _NH_XMPPMucGotPresenceAvailability(self, notification):
stanza = notification.data.presence_stanza
if not stanza.sender.uri.resource:
return
muc_uri = FrozenURI(stanza.recipient.uri.user, stanza.recipient.uri.host)
try:
session = self.muc_session_manager.incoming[(muc_uri, stanza.sender.uri)]
except KeyError:
if stanza.available:
notification.center.post_notification('XMPPGotMucJoinRequest', sender=self, data=NotificationData(stanza=stanza))
else:
notification.center.post_notification('XMPPGotMucLeaveRequest', sender=self, data=NotificationData(stanza=stanza))
else:
session.channel.send(stanza)
def _NH_XMPPMucGotInvitation(self, notification):
invitation = notification.data.invitation
data = NotificationData(sender=invitation.sender, recipient=invitation.recipient, participant=invitation.invited_user)
notification.center.post_notification('XMPPGotMucAddParticipantRequest', sender=self, data=data)
# Jingle
def _NH_XMPPGotJingleSessionInitiate(self, notification):
stanza = notification.data.stanza
try:
self.jingle_session_manager.sessions[stanza.jingle.sid]
except KeyError:
session = JingleSession(notification.data.protocol)
session.init_incoming(stanza)
session.send_ring_indication()
def _NH_XMPPGotJingleSessionTerminate(self, notification):
stanza = notification.data.stanza
try:
session = self.jingle_session_manager.sessions[stanza.jingle.sid]
except KeyError:
return
session.handle_notification(notification)
def _NH_XMPPGotJingleSessionInfo(self, notification):
stanza = notification.data.stanza
try:
session = self.jingle_session_manager.sessions[stanza.jingle.sid]
except KeyError:
return
session.handle_notification(notification)
def _NH_XMPPGotJingleSessionAccept(self, notification):
stanza = notification.data.stanza
try:
session = self.jingle_session_manager.sessions[stanza.jingle.sid]
except KeyError:
return
session.handle_notification(notification)
def _NH_XMPPGotJingleDescriptionInfo(self, notification):
stanza = notification.data.stanza
try:
session = self.jingle_session_manager.sessions[stanza.jingle.sid]
except KeyError:
return
session.handle_notification(notification)
def _NH_XMPPGotJingleTransportInfo(self, notification):
stanza = notification.data.stanza
try:
session = self.jingle_session_manager.sessions[stanza.jingle.sid]
except KeyError:
return
session.handle_notification(notification)
diff --git a/xmppgateway.ini.sample b/xmppgateway.ini.sample
index 83a4c55..01cbd9c 100644
--- a/xmppgateway.ini.sample
+++ b/xmppgateway.ini.sample
@@ -1,39 +1,45 @@
; SylkServer XMPP gateway application configuration file
[general]
; Comma-separated list of Internet domains for which this server is
; responsible. Beside this setting, you must also add the xmpp-server records
; into the DNS server responsable for these Internet domains, e.g.:
; _xmpp-server._tcp.example.com. IN SRV 0 0 5269 sylkserver.example.com.
; At least one domain is required, replace the example below with your own domain:
domains = example.com
; The following settings are the default used by the software, uncomment
; them only if you want to make changes
; Prefix that will be appended to all Internet domains in the above setting,
; which will be used as a Multi User Chat (MUC) component. Beside this
; setting, you must also add the xmpp-server DNS records into the DNS server
; for the conference domains, e.g.:
; _xmpp-server._tcp.conference.example.com. IN SRV 0 0 5269 sylkserver.example.com.
; muc_prefix = conference
; IP address used for listening to XMPP connections; empty string or any means listen on interface used
; by the default route
; local_ip =
; local_port = 5269
+; transport can be tcp or tls
+
+; transport = tls
+; ca_file = /etc/sylkserver/tls/ca.crt
+; certificate = /etc/sylkserver/tls/default.crt
; If set to True (default) MSRP will be used to translate XMPP Instant Messaging, else SIP MESSAGE will be used
; Note: XMPP 'normal' messages (not chat messages) are always translated to SIP MESSAGE requests
; use_msrp_for_chat = True
; Timeout to terminate a SIP session if no chat traffic was received
; sip_session_timeout = 600
; Enable verbose logging of XMPP stantzas
; trace_xmpp = False
; Log presence activity (can get very verbose)
; log_presence = False
+

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 3:28 AM (19 h, 36 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408703
Default Alt Text
(19 KB)

Event Timeline