1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
#!/usr/bin/python
# -*- encoding: utf-8 -*-
from __future__ import with_statement
import base64
import ConfigParser
import optparse
import MySQLdb
import re
import SimpleXMLRPCServer
import socket
import sys
import uuid
class AsteriskOpenSimServerException(Exception):
pass
class AsteriskOpenSimServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
'''Subclassed SimpleXMLRPCServer to be able to muck around with the socket options.
'''
def __init__(self, config):
baseURL = config.get('xmlrpc', 'baseurl')
match = reURLParser.match(baseURL)
if not match:
raise AsteriskOpenSimServerException('baseURL "%s" is not a well-formed URL' % (baseURL))
host = 'localhost'
port = 80
path = None
if match.group('host'):
host = match.group('host')
port = int(match.group('port'))
else:
host = match.group('hostonly')
self.__host = host
self.__port = port
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self,(host, port))
def host(self):
return self.__host
def port(self):
return self.__port
def server_bind(self):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self)
class AsteriskFrontend(object):
'''AsteriskFrontend serves as an XmlRpc function dispatcher.
'''
def __init__(self, config, db):
'''Constructor to take note of the AsteriskDB object.
'''
self.__db = db
try:
self.__debug = config.getboolean('dispatcher', 'debug')
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
self.__debug = False
def account_update(self, request):
'''update (or create) the SIP account data in the Asterisk RealTime DB.
OpenSim's AsteriskVoiceModule will call this method each
time it receives a ProvisionVoiceAccount request.
'''
print '[asterisk-opensim] account_update: new request'
for p in ['admin_password', 'username', 'password']:
if p not in request:
print '[asterisk-opensim] account_update: failed: missing password'
return { 'success': 'false', 'error': 'missing parameter "%s"' % (p)}
# turn base64 binary UUID into proper UUID
user = request['username'].partition('@')[0]
user = user.lstrip('x').replace('-','+').replace('_','/')
user = uuid.UUID(bytes = base64.standard_b64decode(user))
if self.__debug: print '[asterisk-opensim]: account_update: user %s' % user
error = self.__db.AccountUpdate(user = user, password = request['password'])
if error:
print '[asterisk-opensim]: DB.AccountUpdate failed'
return { 'success': 'false', 'error': error}
print '[asterisk-opensim] account_update: done'
return { 'success': 'true'}
def region_update(self, request):
'''update (or create) a VoIP conference call for a region.
OpenSim's AsteriskVoiceModule will call this method each time it
receives a ParcelVoiceInfo request.
'''
print '[asterisk-opensim] region_update: new request'
for p in ['admin_password', 'region']:
if p not in request:
print '[asterisk-opensim] region_update: failed: missing password'
return { 'success': 'false', 'error': 'missing parameter "%s"' % (p)}
region = request['region'].partition('@')[0]
if self.__debug: print '[asterisk-opensim]: region_update: region %s' % user
error = self.__db.RegionUpdate(region = region)
if error:
print '[asterisk-opensim]: DB.RegionUpdate failed'
return { 'success': 'false', 'error': error}
print '[asterisk-opensim] region_update: done'
return { 'success': 'true' }
class AsteriskDBException(Exception):
pass
class AsteriskDB(object):
'''AsteriskDB maintains the connection to Asterisk's MySQL database.
'''
def __init__(self, config):
# configure from config object
self.__server = config.get('mysql', 'server')
self.__database = config.get('mysql', 'database')
self.__user = config.get('mysql', 'user')
self.__password = config.get('mysql', 'password')
try:
self.__debug = config.getboolean('mysql', 'debug')
self.__debug_t = config.getboolean('mysql-templates', 'debug')
except ConfigParser.NoOptionError:
self.__debug = False
self.__tablesTemplate = self.__loadTemplate(config, 'tables')
self.__userTemplate = self.__loadTemplate(config, 'user')
self.__regionTemplate = self.__loadTemplate(config, 'region')
self.__mysql = MySQLdb.connect(host = self.__server, db = self.__database,
user = self.__user, passwd = self.__password)
if self.__assertDBExists():
raise AsteriskDBException('could not initialize DB')
def __loadTemplate(self, config, templateName):
template = config.get('mysql-templates', templateName)
t = ''
with open(template, 'r') as templateFile:
for line in templateFile:
line = line.rstrip('\n')
t += line
return t.split(';')
def __assertDBExists(self):
'''Assert that DB tables exist.
'''
try:
cursor = self.__mysql.cursor()
for sql in self.__tablesTemplate[:]:
if not sql: continue
sql = sql % { 'database': self.__database }
if self.__debug: print 'AsteriskDB.__assertDBExists: %s' % sql
cursor.execute(sql)
cursor.fetchall()
cursor.close()
except MySQLdb.Error, e:
if self.__debug: print 'AsteriskDB.__assertDBExists: Error %d: %s' % (e.args[0], e.args[1])
return e.args[1]
return None
def AccountUpdate(self, user, password):
print 'AsteriskDB.AccountUpdate: user %s' % (user)
try:
cursor = self.__mysql.cursor()
for sql in self.__userTemplate[:]:
if not sql: continue
sql = sql % { 'database': self.__database, 'username': user, 'password': password }
if self.__debug_t: print 'AsteriskDB.AccountUpdate: sql: %s' % sql
cursor.execute(sql)
cursor.fetchall()
cursor.close()
except MySQLdb.Error, e:
if self.__debug: print 'AsteriskDB.RegionUpdate: Error %d: %s' % (e.args[0], e.args[1])
return e.args[1]
return None
def RegionUpdate(self, region):
print 'AsteriskDB.RegionUpdate: region %s' % (region)
try:
cursor = self.__mysql.cursor()
for sql in self.__regionTemplate[:]:
if not sql: continue
sql = sql % { 'database': self.__database, 'regionname': region }
if self.__debug_t: print 'AsteriskDB.RegionUpdate: sql: %s' % sql
cursor.execute(sql)
res = cursor.fetchall()
except MySQLdb.Error, e:
if self.__debug: print 'AsteriskDB.RegionUpdate: Error %d: %s' % (e.args[0], e.args[1])
return e.args[1]
return None
reURLParser = re.compile(r'^http://((?P<host>[^/]+):(?P<port>\d+)|(?P<hostonly>[^/]+))/', re.IGNORECASE)
# main
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option('-c', '--config', dest = 'config', help = 'config file', metavar = 'CONFIG')
(options, args) = parser.parse_args()
if not options.config:
parser.error('missing option config')
sys.exit(1)
config = ConfigParser.ConfigParser()
config.readfp(open(options.config))
server = AsteriskOpenSimServer(config)
server.register_introspection_functions()
server.register_instance(AsteriskFrontend(config, AsteriskDB(config)))
# get cracking
print '[asterisk-opensim] server ready on %s:%d' % (server.host(), server.port())
server.serve_forever()
sys.exit(0)
|