"""
TCP server that receives config requests from freeswitch.
"""
- def __init__(self, window):
- super(FreeswitchConfigServer, self).__init__(window)
+ configNotFound = '''
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="freeswitch/xml">
+ <section name="result">
+ <result status="not found" />
+ </section>
+</document>
+ '''
+ responseTemplate = '''HTTP/1.1 200 OK
+Content-Type: text/xml; charset=utf-8
+Content-Length: %i
+
+%s'''
+
+ def __init__(self, database, parent):
+ super(FreeswitchConfigServer, self).__init__(parent)
self.host = None
self.port = None
self.connection_id = None
self.is_running = False
self.generators = [
- GenClass(window, self) for GenClass in (
+ GenClass(database, self) for GenClass in (
SofiaConfGenerator,)]
- self.httpRequestParser = HttpRequestParser(self)
+ self.httpRequestParser = HttpRequestParser()
def setSocketData(self, host, port, connection_id):
"""
for line in str(self.socket.readAll()).split('\r\n'):
self.httpRequestParser.handle(line)
+ for generator in self.generators:
+ if generator.canHandle(self.httpRequestParser.data):
+ self.state += 1
+ self.result = etree.tostring(generator.generateConfig(
+ self.httpRequestParser.headers))
+ break
+
+ response = self.httpRequestParser.result or self.configNotFound
+ http_response = self.responseTemplate % (len(response), response)
+ self.socket.write(http_response)
+ self.httpRequestParser.reset()
+ self.socket.close()
+
class HttpParseError(Exception):
"""
"""
A simple state machine for parsing HTTP requests.
"""
- HTTP_NONE, HTTP_REQUEST, HTTP_HEADERS, HTTP_EMPTY, HTTP_MESSAGE, \
- HTTP_DONE = range(6)
- HTTP_STATES = ['NONE', 'REQUEST', 'HEADERS', 'EMPTY', 'MESSAGE', 'DONE']
+ HTTP_NONE, HTTP_REQUEST, HTTP_HEADERS, HTTP_EMPTY, HTTP_BODY, HTTP_DONE = \
+ range(6)
+ HTTP_STATES = ['NONE', 'REQUEST', 'HEADERS', 'EMPTY', 'BODY', 'DONE']
- def __init__(self, parent):
- self.parent = parent
+ def __init__(self):
self.reset()
def reset(self):
self.http_version = None
self.headers = {}
self.data = {}
+ self.result = None
# Set initial state.
self.state = self.HTTP_NONE
"""
Retrieve HTTP method, request path and HTTP version from request.
"""
- self.method, self.request_path, self.http_version = line.split(' ')
- self.state += 1
+ try:
+ self.method, self.request_path, self.http_version = line.split(' ')
+ self.state += 1
+ except ValueError:
+ pass
def handleHeaders(self, line):
"""
"""
self.state += 1
- def handleMessage(self, line):
+ def handleBody(self, line):
"""
Append to message body.
"""
- self.data = dict(pair.split('=', 2) for pair in line.split('&'))
-
- for k, v in self.data.items():
- print k, '=>', v
- print
-
- for generator in self.parent.generators:
- if generator.canHandle(self.data):
- self.state += 1
- print generator.generateConfig(self.headers)
- else:
- print 'No generator found'
+ if self.method != 'POST':
+ raise HttpParseError('Only POST request are supported')
+ self.data = dict(pair.split('=', 2) for pair in line.split('&'))
+ def handleDone(self, line):
+ raise HttpParseError("Can't read past request end")
+
class FreeswitchConfigGenerator(object):
"""
"""
param_match = {}
- section_name = None
- def __init__(self, model, parent):
- self.model = model
+ def __init__(self, database, parent):
+ self.database = database
self.parent = parent
+# def database(self):
+# """
+# Return database instance.
+# """
+# return self.model.controllers['connection'].model.database()
+
def canHandle(self, params):
+ """
+ Check if this generator can handle a request from freeswitch.
+ """
for key, value in self.param_match.iteritems():
- print key, value, params.get(key, None)
if params.get(key, None) != value:
return False
else:
return True
def baseElements(self):
- root_elt = etree.Element('document')
+ root_elt = etree.Element('document', type='freeswitch/xml')
section_elt = etree.SubElement(
- root_elt, 'section', name=self.section_name)
+ root_elt, 'section', name=self.param_match['section'])
return root_elt, section_elt
baseElements = property(baseElements)
@staticmethod
def addParams(parent_elt, params):
+ """
+ Create params element based on data passed in a list.
+ """
for name, value in params:
- etree.SubElement(parent_elt, 'param', name=name, value=value)
+ etree.SubElement(
+ parent_elt, 'param', name=name, value=str(value))
class SofiaConfGenerator(FreeswitchConfigGenerator):
Generates sofia.conf.xml config file.
"""
param_match = {'section': 'configuration', 'key_value': 'sofia.conf'}
- section_name = 'configuration'
config_name = 'sofia.conf'
def generateConfig(self, params):
configuration_elt = etree.SubElement(
section_elt, 'configuration', name=self.config_name,
description='%s config' % self.config_name)
- settings_elt = etree.SubElement(configuration_elt, 'settings')
- profiles_elt = etree.SubElement(settings_elt, 'profiles')
+ profiles_elt = etree.SubElement(configuration_elt, 'profiles')
- database = self.model.controllers['connection'].model.database()
+ database = self.database
# Create all profiles for current host.
profiles_query = database.exec_(
- 'select id from ipypbxweb_sipprofile where connection_id = %i' %
- self.parent.connection_id)
+ '''
+ select id, name, external_sip_ip, external_rtp_ip, sip_ip, rtp_ip,
+ sip_port, accept_blind_registration, authenticate_calls
+ from ipypbxweb_sipprofile where connection_id = %i
+ ''' % self.parent.connection_id)
while profiles_query.next():
+ # Create profile element.
profile_id, _ok = profiles_query.value(0).toInt()
- profile_elt = etree.SubElement(profiles_elt, 'profile')
+ profile_elt = etree.SubElement(
+ profiles_elt, 'profile',
+ name=profiles_query.value(1).toString())
# Create domains for current profile.
domains_elt = etree.SubElement(profile_elt, 'domains')
-
domains_query = database.exec_(
- 'select host_name from ipypbxweb_domain where profile_id = '
+ 'select host_name from ipypbxweb_domain where sip_profile_id = '
'%i' % profile_id)
while domains_query.next():
domain_elt = etree.SubElement(
- domains_elt, 'domain', name=domains_quey.value(0),
- alias='true', parse='true')
+ domains_elt, 'domain',
+ name=domains_query.value(0).toString(), alias='true',
+ parse='true')
+
+
+ profile_sip_port, _ok = profiles_query.value(6).toInt()
# Create settings for current profile.
settings_elt = etree.SubElement(profile_elt, 'settings')
params = (
('dialplan', 'XML,enum'),
- ('ext-sip-ip', profile.ext_sip_ip),
- ('ext-rtp-ip', profile.ext_rtp_ip),
- ('sip-ip', profile.sip_ip),
- ('rtp-ip', profile.rtp_ip),
- ('sip-port', profile.sip_port),
+ ('ext-sip-ip', profiles_query.value(2).toString()),
+ ('ext-rtp-ip', profiles_query.value(3).toString()),
+ ('sip-ip', profiles_query.value(4).toString()),
+ ('rtp-ip', profiles_query.value(5).toString()),
+ ('sip-port', profile_sip_port),
('nonce-ttl', '60'),
('rtp-timer-name', 'soft'),
('codec-prefs', 'PCMU@20i'),
('rfc2833-pt', '1'),
('dtmf-duration', '100'),
('codec-ms', '20'),
- ('accept-blind-reg', profile.accept_blind_registration),
- ('auth-calls', profile.authenticate_calls))
- self.add_params(settings_elt, params)
+ ('accept-blind-reg', profiles_query.value(7).toBool()),
+ ('auth-calls', profiles_query.value(8).toBool()))
+ self.addParams(settings_elt, params)
# Create gateways for current profile.
- gateways_elt = etree.SubElement(profile, 'gateways')
- for gateway in self.parent.get_gateways_for_profile(profile):
- gateway_elt = etree.SubElement(gateways_elt, 'gateway', name=gateway.name)
+ gateways_elt = etree.SubElement(profile_elt, 'gateways')
+ gateways_query = database.exec_(
+ '''
+ select name, username, realm, from_domain, password,
+ retry_in_seconds, expire_in_seconds, caller_id_in_from_field,
+ extension
+ from ipypbxweb_gateway where sip_profile_id = %i
+ ''' % profile_id)
+ while gateways_query.next():
+ # Create gateway element.
+ gateway_elt = etree.SubElement(
+ gateways_elt, 'gateway', name=gateways_query.value(0).toString())
+ retry_seconds, _ok = gateways_query.value(5).toInt()
+ expire_seconds, _ok = gateways_query.value(6).toInt()
params = (
- ('username', gateway.username),
- ('realm', gateway.realm),
- ('from-domain', gateway.from_domain),
- ('password', gateway.password),
- ('retry-seconds', gateway.retry_seconds),
- ('expire-seconds', gateway.expire_seconds),
- ('caller-id-in-from', gateway.caller_id_in_from),
- ('extension', gateway.extension),
+ ('username', gateways_query.value(1).toString()),
+ ('realm', gateways_query.value(2).toString()),
+ ('from-domain', gateways_query.value(3).toString()),
+ ('password', gateways_query.value(4).toString()),
+ ('retry-seconds', retry_seconds),
+ ('expire-seconds', expire_seconds),
+ ('caller-id-in-from', gateways_query.value(7).toBool()),
+ ('extension', gateways_query.value(8).toString()),
# TODO: proxy, register
- ('expire-seconds', gateway.expire_seconds),
- ('retry-seconds', gateway.retry_seconds))
- self.add_params(gateway_elt, params)
+ )
+ self.addParams(gateway_elt, params)
return root_elt
+
+class DirectoryGenerator(FreeswitchConfigGenerator):
+ """
+ Generates user directory.
+ """
+ param_match = {'section': 'directory'}
+
+ def generateConfig(self, params):
+ #Get base elemenets.
+ root_elt, section_elt = self.baseELements
+
+ database = self.database
+
+ # Find profile id from params.
+ profile_query = database.exec_(
+ '''
+ select id from ipypbxweb_sipprofile
+ where name= '%s' and connection_id = %i limit 1
+ ''' % (params['profile'], self.parent.connection_id))
+
+ _ok = False
+ if profile_query.next():
+ profile_id, _ok = profile_query.value(0).toInt()
+
+ if not _ok:
+ # Matching SIP profile not found.
+ return
+
+ # List all domains for this profile.
+ domains_query = database.exec_(
+ '''
+ select id, host_name from ipypbxweb_domain
+ where sip_profile_id = %i
+ ''' % profile_id)
+
+ while domains_query.next():
+ domain_id, _ok = domains_query.value(0).toInt()
+
+ # Create domaim element.
+ domain_elt = etree.SubElement(
+ section_elt, 'domain', name=domains_query.value(1).toString())
+
+ # TODO: add domain params section if we need it, i.e.:
+ #<params>
+ # <param name="dial-string"
+ # value="{presence_id=${dialed_user}@${dialed_domain}}$\
+ # {sofia_contact(${dialed_user}@${dialed_domain})}"/>
+ #</params>
+
+ # For new we put all users into one group called default.
+ groups_elt = etree.SubElement(domain_elt, 'groups')
+ group_elt = etree.SubElement(groups_elt, 'group', name='default')
+
+ users_elt = etree.SubElement(group_elt, 'users')
+
+ users_query = database.exec_(
+ '''
+ select user_id, password from ipypbxweb_endpoint
+ where domain_id = %i
+ ''' % domain_id)
+
+ # Create user entries for all endpoints for this domain.
+ while users_query.next():
+ user_elt = etree.SubElement(
+ users_elt, 'user', id=users_query.value(0).toString())
+
+ # Specify endpoint password.
+ params = (
+ ('password', users_query.value(1).toString()),
+ )
+ self.addParams(user_elt, params)
+
+ return root_elt
+
+
+class DialplanGenerator(FreeswitchConfigGenerator):
+ """
+ Generates XML dialplans.
+ """
+
+ param_match = {'section': 'dialplan'}
+