]> git.ipfire.org Git - ipfire.org.git/blob - src/backend/talk.py
people: Drop old SIP page
[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_channels(self, query, *args):
38 res = self.db.query(query, *args)
39
40 channels = []
41 for row in res:
42 c = Channel(self, data=row)
43 channels.append(c)
44
45 return channels
46
47 def get_sip_channels(self, account):
48 return self._get_channels("SELECT * FROM channels \
49 WHERE (direction = %s AND cid_num = %s) OR \
50 (direction = %s AND (callee_num = %s OR callee_num = ANY(%s))) \
51 AND callstate != %s ORDER BY created_epoch",
52 "inbound", account.sip_id, "outbound", account.sip_id,
53 account._all_telephone_numbers, "DOWN")
54
55 def get_conferences(self):
56 res = self.db.query("SELECT DISTINCT application_data AS handle FROM channels \
57 WHERE application = %s AND application_data LIKE %s \
58 ORDER BY application_data", "conference", "%%@ipfire.org")
59
60 conferences = []
61 for row in res:
62 c = Conference(self, row.handle)
63 conferences.append(c)
64
65 return conferences
66
67 def get_agent_status(self, account):
68 res = self.db.get("SELECT status FROM agents \
69 WHERE name = %s", account.sip_url)
70
71 if res:
72 return res.status
73
74 class SIPRegistration(object):
75 def __init__(self, freeswitch, data):
76 self.freeswitch = freeswitch
77 self.data = data
78
79 @lazy_property
80 def protocol(self):
81 m = re.match(r"Registered\(([A-Z]+)(\-NAT)?\)", self.data.status)
82
83 if m:
84 return m.group(1)
85
86 @property
87 def network_ip(self):
88 return ipaddress.ip_address(self.data.network_ip)
89
90 @property
91 def network_port(self):
92 return self.data.network_port
93
94 @property
95 def user_agent(self):
96 return self.data.user_agent
97
98 def is_reachable(self):
99 return self.data.ping_status == "Reachable"
100
101 @lazy_property
102 def latency(self):
103 if self.is_reachable() and self.data.ping_time:
104 return self.data.ping_time / 1000.0
105
106
107 class Channel(object):
108 def __init__(self, freeswitch, data):
109 self.freeswitch = freeswitch
110 self.data = data
111
112 @property
113 def backend(self):
114 return self.freeswitch.backend
115
116 @property
117 def uuid(self):
118 return self.data.uuid
119
120 @property
121 def direction(self):
122 return self.data.direction
123
124 @lazy_property
125 def caller(self):
126 return self.backend.accounts.get_by_sip_id(self.caller_number)
127
128 @property
129 def caller_name(self):
130 return self.data.cid_name
131
132 @property
133 def caller_number(self):
134 return self.data.cid_num
135
136 @lazy_property
137 def callee(self):
138 return self.backend.accounts.get_by_sip_id(self.callee_number)
139
140 @property
141 def callee_name(self):
142 return self.data.callee_name
143
144 @property
145 def callee_number(self):
146 return self.data.callee_num
147
148 @property
149 def called_number(self):
150 return self.data.dest
151
152 @property
153 def state(self):
154 return self.data.callstate
155
156 @property
157 def application(self):
158 return self.data.application
159
160 @property
161 def application_data(self):
162 return self.data.application_data
163
164 @lazy_property
165 def conference(self):
166 if self.application == "conference":
167 return Conference(self.freeswitch, self.application_data)
168
169 @property
170 def duration(self):
171 return time.time() - self.data.created_epoch
172
173 @property
174 def codec(self):
175 # We always assume a symmetric codec
176 return format_codec(self.data.write_codec, int(self.data.write_rate or 0), int(self.data.write_bit_rate or 0))
177
178 def is_secure(self):
179 if self.data.secure:
180 return True
181
182 return False
183
184 @property
185 def secure(self):
186 try:
187 transport_protocol, key_negotiation, cipher_suite = self.data.secure.split(":")
188 except:
189 return
190
191 return "%s: %s" % (key_negotiation.upper(), cipher_suite.replace("_", "-"))
192
193
194 class Conference(object):
195 def __init__(self, freeswitch, handle):
196 self.freeswitch = freeswitch
197 self.handle = handle
198
199 def __repr__(self):
200 return "<%s %s>" % (self.__class__.__name__, self.handle)
201
202 def __len__(self):
203 return len(self.channels)
204
205 def __eq__(self, other):
206 if isinstance(other, self.__class__):
207 return self.handle == other.handle
208
209 def __iter__(self):
210 return iter(self.channels)
211
212 @lazy_property
213 def number(self):
214 m = re.match(r"conf(\d+)@", self.handle)
215 if m:
216 i = m.group(1)
217
218 return int(i)
219
220 @property
221 def sip_id(self):
222 return 900 + self.number
223
224 @property
225 def sip_url(self):
226 return "%s@ipfire.org" % self.sip_id
227
228 @property
229 def phone_numbers(self):
230 return [
231 "+4923636035%s" % self.sip_id,
232 ]
233
234 @lazy_property
235 def channels(self):
236 return self.freeswitch._get_channels("SELECT * FROM channels \
237 WHERE application = %s AND application_data = %s \
238 ORDER BY created_epoch", "conference", self.handle)
239
240
241 class Talk(Object):
242 def init(self):
243 # Connect to FreeSWITCH
244 self.freeswitch = Freeswitch(self.backend)
245
246 @property
247 def conferences(self):
248 return self.freeswitch.get_conferences()
249
250
251 def format_codec(name, bit_rate, bandwidth):
252 if not name:
253 return
254
255 s = [
256 name,
257 ]
258
259 if bit_rate:
260 s.append("%.0f kHz" % (bit_rate / 1000.0))
261
262 if bandwidth:
263 s.append("%.0f kBit/s" % (bandwidth / 1000.0))
264 else:
265 s.append("VBR")
266
267 return " ".join(s)