"""
TCP server that receives config requests from freeswitch.
"""
- def __init__(self, parent=None):
- super(FreeswitchConfigServer, self).__init__(parent)
+ 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, window):
+ super(FreeswitchConfigServer, self).__init__(window)
self.host = None
self.port = None
+ self.connection_id = None
self.is_running = False
+ self.generators = [
+ GenClass(window, self) for GenClass in (
+ SofiaConfGenerator,)]
- self.httpRequestParser = HttpRequestParser()
+ self.httpRequestParser = HttpRequestParser(self)
- def setSocketData(self, host, port):
+ def setSocketData(self, host, port, connection_id):
"""
Set host and port for socket to listen on.
+
+ If the settings differ from previous values, server gets restarted.
"""
+ # Check if restart is needed before new settings are applied.
+ needs_restart = (
+ (host, port) != (self.host, self.port)) and connection_id
+
+ # Save new settings.
self.host = host
self.port = port
+ if connection_id:
+ self.connection_id = connection_id
+
+ # Restart server if necessary.
+ if needs_restart:
+ self.restartServer()
def startServer(self):
"""
def receiveData(self):
# TODO: read in chunks.
for line in str(self.socket.readAll()).split('\r\n'):
+ print self.httpRequestParser.i, line
self.httpRequestParser.handle(line)
+ response = self.httpRequestParser.result or self.configNotFound
+ http_response = self.responseTemplate % (len(response), response)
+ self.socket.write(http_response)
+ print
+ print http_response
+ print
+ 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 = range(5)
- HTTP_STATES = ['NONE', 'REQUEST', 'HEADERS', 'EMPTY', 'MESSAGE']
+ HTTP_NONE, HTTP_REQUEST, HTTP_HEADERS, HTTP_EMPTY, HTTP_MESSAGE, \
+ HTTP_DONE = range(6)
+ HTTP_STATES = ['NONE', 'REQUEST', 'HEADERS', 'EMPTY', 'MESSAGE', 'DONE']
- def __init__(self):
- super(HttpRequestParser, self).__init__()
+ def __init__(self, parent):
+ self.parent = parent
+ self.i = 0
self.reset()
def reset(self):
"""
Reset parser to initial state.
"""
+ self.i += 1
# Initial values for request data.
self.method = None
self.request_path = None
self.http_version = None
self.headers = {}
self.data = {}
+ self.result = None
# Set initial state.
self.state = self.HTTP_NONE
"""
for state in self.HTTP_STATES:
if getattr(self, 'HTTP_%s' % state) == self.state:
- print self.state, line
getattr(self, 'handle%s' % state.title())(line)
break
else:
"""
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.data = dict(pair.split('=', 2) for pair in line.split('&'))
- for k, v in self.data.items():
- print k, '=>', v
- print
+ #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
+ self.result = etree.tostring(generator.generateConfig(
+ self.headers))
+ break
class FreeswitchConfigGenerator(object):
"""
param_match = {}
- section_name = None
- def __init__(self, model):
+ def __init__(self, model, parent):
self.model = model
+ self.parent = parent
- def check_params(self, params):
+ def database(self):
+ """
+ Return database instance.
+ """
+ return self.model.controllers['connection'].model.database()
+ database = property(database)
+
+ def canHandle(self, params):
for key, value in self.param_match.iteritems():
if params.get(key, None) != value:
return False
else:
return True
- def base_elements(self):
- root_elt = etree.Element('document')
+ def baseElements(self):
+ 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
- base_elements = property(base_elements)
+ baseElements = property(baseElements)
- def generate_config(self, params):
+ def generateConfig(self, params):
return NotImplemented
- def add_params(parent_elt, params):
+ @staticmethod
+ def addParams(parent_elt, params):
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 generate_config(self, params):
+ def generateConfig(self, params):
# Get base elements.
- root_elt, section_elt = self.base_elements
+ root_elt, section_elt = self.baseElements
# Create configuration, settings and profiles elements.
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(self.settings_elt, 'profiles')
+ profiles_elt = etree.SubElement(configuration_elt, 'profiles')
+ database = self.database
+
# Create all profiles for current host.
- for profile in self.parent.get_profiles():
- profile_elt = etree.SubElement(profiles_elt, 'profile')
+ profiles_query = database.exec_(
+ '''
+ 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():
+ profile_id, _ok = profiles_query.value(0).toInt()
+ 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')
- for domain in self.parent.get_domains_for_profile(profile):
+ domains_query = database.exec_(
+ '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=domain.host_name,
- 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),
- ('nonce-ttl'. '60'),
+ ('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'),
('debug', '1'),
('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():
+ 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