Hide extension.endpoint_id display for now
[ipypbx] / src / ipypbx / http.py
index afb3fb3..3dfe0b8 100644 (file)
@@ -15,6 +15,7 @@
 # You should have received a copy of the GNU General Public License
 # along with IPyPBX.  If not, see <http://www.gnu.org/licenses/>.
 
+import xml.etree.ElementTree as etree
 from PyQt4 import QtCore, QtNetwork
 
 
@@ -22,22 +23,35 @@ class FreeswitchConfigServer(QtNetwork.QTcpServer):
     """
     TCP server that receives config requests from freeswitch.
     """
-    def __init__(self, parent=None):
+    def __init__(self, parent):
         super(FreeswitchConfigServer, self).__init__(parent)
 
         self.host = None
         self.port = None
         self.is_running = False
+        self.generators = [
+            GenClass(self.parent().model) for GenClass in (
+                SofiaConfGenerator,)]
         
-        self.httpRequestParser = HttpRequestParser()
+        self.httpRequestParser = HttpRequestParser(self)
         
-    def setSocket(self, host, port):
+    def setSocketData(self, host, port):
         """
         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)
+
+        # Save new settings.
         self.host = host
         self.port = port
 
+        # Restart server if necessary.
+        if needs_restart:
+            self.restartServer()
+
     def startServer(self):
         """
         Start listening on our socket.
@@ -63,18 +77,19 @@ class FreeswitchConfigServer(QtNetwork.QTcpServer):
         self.stopServer()
         self.startServer()
 
-    def clientConnecting(self, socket):
+    def clientConnecting(self):
         """
         Handle client connection.
         """
         if self.hasPendingConnections():
-             connectingClient = self.server.nextPendingConnection()
-             connectingClient.readyRead.connect(self.receiveData)
+            self.socket = self.nextPendingConnection()
+            self.socket.readyRead.connect(self.receiveData)
+
+    def receiveData(self):
+        # TODO: read in chunks.
+        for line in str(self.socket.readAll()).split('\r\n'):
+            self.httpRequestParser.handle(line)
 
-    def receiveData(self, socket):
-        while socket.canReadLine():
-            line = socket.readLine().strip()
-            
 
 class HttpParseError(Exception):
     """
@@ -86,11 +101,13 @@ class HttpRequestParser(object):
     """
     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(HttpParser, self).__init__()
+    def __init__(self, parent):
+        self.parent = parent
+        self.reset()
 
     def reset(self):
         """
@@ -100,18 +117,19 @@ class HttpRequestParser(object):
         self.method = None
         self.request_path = None
         self.http_version = None
-        self.message = ''
+        self.headers = {}
+        self.data = {}
         
         # Set initial state.
-        self.state = HTTP_NONE        
+        self.state = self.HTTP_NONE        
 
     def handle(self, line):
         """
         Dispatch line to current state handler.
         """
-        for state in HTTP_STATES:
+        for state in self.HTTP_STATES:
             if getattr(self, 'HTTP_%s' % state) == self.state:
-                getattr(self, 'handle%s' % state.title()).(line)
+                getattr(self, 'handle%s' % state.title())(line)
                 break
         else:
             raise HttpParseError('Unknown HTTP state')
@@ -139,6 +157,7 @@ class HttpRequestParser(object):
             self.headers[key] = value
         else:
             self.state += 1
+            self.handle(line)
 
     def handleEmpty(self, line):
         """
@@ -150,4 +169,120 @@ class HttpRequestParser(object):
         """
         Append to message body.
         """
-        self.message += line
+        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.headers):
+                self.state += 1
+                print generator.generateConfig(self.headers)
+        else:
+            print 'No generator found'
+            
+
+
+class FreeswitchConfigGenerator(object):
+    """
+    Base class for generating XML configs.
+    """
+    
+    param_match = {}
+    section_name = None
+
+    def __init__(self, model):
+        self.model = model
+
+    def canHandle(self, params):
+        for key, value in self.param_match.iteritems():
+            if params.get(key, None) != value:
+                return False
+        else:
+            return True
+
+    def baseElements(self):
+        root_elt = etree.Element('document')
+        section_elt = etree.SubElement(
+            root_elt, 'section', name=self.section_name)
+        return root_elt, section_elt
+    baseElements = property(baseElements)
+
+    def generateConfig(self, params):
+        return NotImplemented
+
+    def addParams(parent_elt, params):
+        for name, value in params:
+            etree.SubElement(parent_elt, 'param', name=name, value=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):
+        # Get 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')
+
+        # Create all profiles for current host.
+        for profile in self.parent.get_profiles():
+            profile_elt = etree.SubElement(profiles_elt, 'profile')
+
+            # Create domains for current profile.
+            domains_elt = etree.SubElement(profile_elt, 'domains')
+            for domain in self.parent.get_domains_for_profile(profile):
+                domain_elt = etree.SubElement(
+                    domains_elt, 'domain', name=domain.host_name,
+                    alias='true', parse='true')
+
+            # 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'),
+                ('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)
+
+            # 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)
+                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),
+                    # TODO: proxy, register
+                    ('expire-seconds', gateway.expire_seconds),
+                    ('retry-seconds', gateway.retry_seconds))
+                self.add_params(gateway_elt, params)
+
+        return root_elt