]> git.ipfire.org Git - ipfire.org.git/blob - src/backend/talk.py
ed0b1210bd50e773a43d1cd1c8fdc9eb494495e6
[ipfire.org.git] / src / backend / talk.py
1 #!/usr/bin/python
2
3 import ipaddress
4 import logging
5 import re
6 import time
7
8 from . import database
9
10 from .misc import Object
11 from .decorators import *
12
13 class Freeswitch(Object):
14 @lazy_property
15 def db(self):
16 credentials = {
17 "host" : self.settings.get("freeswitch_database_host"),
18 "database" : self.settings.get("freeswitch_database_name", "freeswitch"),
19 "user" : self.settings.get("freeswitch_database_user"),
20 "password" : self.settings.get("freeswitch_database_password"),
21 }
22
23 return database.Connection(**credentials)
24
25 def get_sip_registrations(self, sip_uri):
26 logging.debug("Fetching SIP registrations for %s" % sip_uri)
27
28 user, delim, domain = sip_uri.partition("@")
29
30 res = self.db.query("SELECT * FROM sip_registrations \
31 WHERE sip_user = %s AND sip_host = %s AND expires >= EXTRACT(epoch FROM CURRENT_TIMESTAMP) \
32 ORDER BY contact", user, domain)
33
34 for row in res:
35 yield SIPRegistration(self, data=row)
36
37 def get_sip_channels(self, account):
38 res = self.db.query("SELECT * FROM channels \
39 WHERE (direction = %s AND cid_num = %s) OR \
40 (direction = %s AND callee_num = %s) ORDER BY created_epoch",
41 "inbound", account.sip_id, "outbound", account.sip_id)
42
43 channels = []
44
45 for row in res:
46 c = Channel(self, data=row)
47 channels.append(c)
48
49 return channels
50
51 def get_cdr_by_account(self, account, limit=None):
52 res = self.db.query("SELECT * FROM cdr \
53 WHERE (caller_id_number = %s AND bleg_uuid IS NOT NULL) \
54 OR (destination_number = %s AND bleg_uuid IS NULL) \
55 ORDER BY end_stamp DESC LIMIT %s", account.sip_id, account.sip_id, limit)
56
57 for row in res:
58 yield CDR(self, data=row)
59
60
61 class SIPRegistration(object):
62 def __init__(self, freeswitch, data):
63 self.freeswitch = freeswitch
64 self.data = data
65
66 @lazy_property
67 def protocol(self):
68 m = re.match(r"Registered\(([A-Z]+)(\-NAT)?\)", self.data.status)
69
70 if m:
71 return m.group(1)
72
73 @property
74 def network_ip(self):
75 return ipaddress.ip_address(self.data.network_ip)
76
77 @property
78 def network_port(self):
79 return self.data.network_port
80
81 @property
82 def user_agent(self):
83 return self.data.user_agent
84
85 def is_reachable(self):
86 return self.data.ping_status == "Reachable"
87
88 @lazy_property
89 def latency(self):
90 if self.is_reachable() and self.data.ping_time:
91 return self.data.ping_time / 1000.0
92
93
94 class Channel(object):
95 def __init__(self, freeswitch, data):
96 self.freeswitch = freeswitch
97 self.data = data
98
99 @property
100 def backend(self):
101 return self.freeswitch.backend
102
103 @property
104 def uuid(self):
105 return self.data.uuid
106
107 @property
108 def direction(self):
109 return self.data.direction
110
111 @lazy_property
112 def caller(self):
113 return self.backend.accounts.get_by_sip_id(self.caller_number)
114
115 @property
116 def caller_name(self):
117 return self.data.cid_name
118
119 @property
120 def caller_number(self):
121 return self.data.cid_num
122
123 @lazy_property
124 def callee(self):
125 return self.backend.accounts.get_by_sip_id(self.callee_number)
126
127 @property
128 def callee_name(self):
129 return self.data.callee_name
130
131 @property
132 def callee_number(self):
133 return self.data.callee_num
134
135 @property
136 def called_number(self):
137 return self.data.dest
138
139 @property
140 def application(self):
141 return self.data.application
142
143 @property
144 def application_data(self):
145 return self.data.application_data
146
147 @property
148 def duration(self):
149 return time.time() - self.data.created_epoch
150
151 @property
152 def codec(self):
153 # We always assume a symmetric codec
154 s = [
155 self.data.write_codec,
156 ]
157
158 if self.data.write_rate:
159 s.append("%.0f kHz" % (int(self.data.write_rate) / 1000.0))
160
161 if self.data.write_bit_rate == "0":
162 s.append("VBR")
163 else:
164 s.append("%.0f kBit/s" % (int(self.data.write_bit_rate) / 1000.0))
165
166 return " ".join(s)
167
168 def is_secure(self):
169 if self.data.secure:
170 return True
171
172 return False
173
174 @property
175 def secure(self):
176 try:
177 transport_protocol, key_negotiation, cipher_suite = self.data.secure.split(":")
178 except:
179 return
180
181 return "%s: %s" % (key_negotiation.upper(), cipher_suite.replace("_", "-"))
182
183
184 class CDR(object):
185 def __init__(self, freeswitch, data):
186 self.freeswitch = freeswitch
187 self.data = data
188
189 @property
190 def backend(self):
191 return self.freeswitch.backend
192
193 @property
194 def direction(self):
195 if self.data.bleg_uuid:
196 return "inbound"
197
198 return "outbound"
199
200 @lazy_property
201 def caller(self):
202 return self.backend.accounts.get_by_phone_number(self.data.caller_id_number)
203
204 @property
205 def caller_number(self):
206 return self.data.caller_id_number
207
208 @lazy_property
209 def callee(self):
210 return self.backend.accounts.get_by_phone_number(self.data.destination_number)
211
212 @property
213 def callee_number(self):
214 return self.data.destination_number
215
216 @property
217 def time_start(self):
218 return self.data.start_stamp
219
220 @property
221 def time_answered(self):
222 return self.data.answer_stamp
223
224 @property
225 def duration(self):
226 return self.data.duration
227
228 @property
229 def codec(self):
230 return self.data.write_codec
231
232 @property
233 def user_agent(self):
234 return self.data.user_agent.replace("_", " ")
235
236 @property
237 def size(self):
238 return sum((self.data.rtp_audio_in_raw_bytes or 0, self.data.rtp_audio_out_raw_bytes or 0))
239
240 @property
241 def mos(self):
242 return self.data.rtp_audio_in_mos
243
244
245 class Talk(Object):
246 def init(self):
247 # Connect to FreeSWITCH
248 self.freeswitch = Freeswitch(self.backend)
249
250 # Conferences
251
252 @property
253 def conferences(self):
254 conferences = []
255
256 for no in range(1, 10):
257 conference = Conference(self.backend, no)
258 conferences.append(conference)
259
260 return conferences
261
262 def get_conference(self, id):
263 for c in self.conferences:
264 if not c.sip_id == id:
265 continue
266
267 return c
268
269
270 class Conference(Object):
271 def __init__(self, backend, no):
272 Object.__init__(self, backend)
273 self.no = no
274
275 def __cmp__(self, other):
276 return cmp(self.no, other.no)
277
278 @property
279 def name(self):
280 return "IPFire Conference Room %s" % self.no
281
282 @property
283 def sip_id(self):
284 return "%s" % (9000 + self.no)
285
286 @property
287 def sip_url(self):
288 return "%s@ipfire.org" % self.sip_id
289
290 @property
291 def participants(self):
292 if not hasattr(self, "_participants"):
293 self._participants = self.backend.talk.get_ongoing_calls(sip_id=self.sip_id)
294
295 return self._participants