]> git.ipfire.org Git - ipfire.org.git/blame - src/backend/talk.py
talk: List active registrations
[ipfire.org.git] / src / backend / talk.py
CommitLineData
66862195
MT
1#!/usr/bin/python
2
917434b8
MT
3import ipaddress
4import logging
77431b9c
MT
5import re
6
5ac74b02 7from . import database
66862195 8
11347e46 9from .misc import Object
13e89ccc
MT
10from .decorators import *
11
12class Freeswitch(Object):
13 @lazy_property
14 def db(self):
15 credentials = {
16 "host" : self.settings.get("freeswitch_database_host"),
17 "database" : self.settings.get("freeswitch_database_name", "freeswitch"),
18 "user" : self.settings.get("freeswitch_database_user"),
19 "password" : self.settings.get("freeswitch_database_password"),
20 }
21
22 return database.Connection(**credentials)
23
917434b8
MT
24 def get_sip_registrations(self, sip_uri):
25 logging.debug("Fetching SIP registrations for %s" % sip_uri)
26
27 user, delim, domain = sip_uri.partition("@")
28
29 res = self.db.query("SELECT * FROM sip_registrations \
30 WHERE sip_user = %s AND sip_host = %s AND expires >= EXTRACT(epoch FROM CURRENT_TIMESTAMP) \
31 ORDER BY contact", user, domain)
32
33 for row in res:
34 yield SIPRegistration(self, data=row)
35
36
37class SIPRegistration(object):
38 def __init__(self, freeswitch, data):
39 self.freeswitch = freeswitch
40 self.data = data
41
42 @lazy_property
43 def protocol(self):
44 m = re.match(r"Registered\(([A-Z]+)(\-NAT)?\)", self.data.status)
45
46 if m:
47 return m.group(1)
48
49 @property
50 def network_ip(self):
51 return ipaddress.ip_address(self.data.network_ip)
52
53 @property
54 def network_port(self):
55 return self.data.network_port
56
57 @property
58 def user_agent(self):
59 return self.data.user_agent
60
61 def is_reachable(self):
62 return self.data.ping_status == "Reachable"
63
64 @lazy_property
65 def latency(self):
66 if self.is_reachable():
67 return self.data.ping_time / 1000.0
68
69
66862195
MT
70
71class Talk(Object):
13e89ccc
MT
72 def init(self):
73 # Connect to FreeSWITCH
74 self.freeswitch = Freeswitch(self.backend)
75
66862195
MT
76 def get_phonebook(self, account=None):
77 accounts = []
78 for a in self.accounts.list():
79 if account and a == account:
80 continue
81
82 if not a.is_talk_enabled():
83 continue
84
85 accounts.append(a)
86
87 return accounts
88
89 def user_is_online(self, sip_id):
77431b9c 90 res = self.db.get("SELECT 1 FROM location WHERE username = %s \
66862195
MT
91 AND expires >= NOW() LIMIT 1", sip_id)
92
93 if res:
94 return True
95
96 return False
97
77431b9c
MT
98 def get_lines(self, account=None):
99 accounts_cache = {}
66862195 100
77431b9c 101 if account and not account.is_talk_enabled():
66862195
MT
102 return []
103
77431b9c
MT
104 if account:
105 res = self.db.query("SELECT contact AS location, expires, user_agent, socket \
106 FROM location WHERE domain = %s AND username = %s ORDER BY expires DESC",
107 "ipfire.org", account.sip_id)
108 else:
109 res = self.db.query("SELECT username, domain, contact AS location, \
110 expires, user_agent, socket FROM location ORDER BY username, expires DESC")
66862195
MT
111
112 result = []
113 for row in res:
77431b9c
MT
114 if account:
115 row.account = account
116 elif row.username:
117 try:
118 row.account = accounts_cache[row.username]
119 except KeyError:
120 row.account = accounts_cache[row.username] = \
121 self.accounts.get_by_sip_id(row.username)
122 else:
123 row.account = None
66862195 124
77431b9c
MT
125 # Check if TLS is used
126 row.tls_enabled = row.socket.startswith("tls:")
66862195
MT
127
128 # Cut off everything after ;
129 row.location, sep, rest = row.location.partition(";")
130
131 result.append(row)
132
133 return result
134
77431b9c
MT
135 def _process_sip_uri(self, sip_uri):
136 sip_uri, delimiter, rest = sip_uri.partition(";")
137
138 m = re.match(r"^sip:([0-9]{4})@ipfire\.org", sip_uri)
139 if m:
140 return m.group(1)
141
142 # Remove trailing default port
143 if sip_uri.endswith(":5060"):
144 sip_uri = sip_uri.replace(":5060", "")
145
146 return sip_uri
147
148 def _process_cdr(self, entries, replace_sip_uris=False):
66862195
MT
149 accounts_cache = {}
150
151 result = []
152 for row in entries:
77431b9c
MT
153 if replace_sip_uris:
154 row["caller"] = self._process_sip_uri(row.caller)
155
66862195 156 try:
77431b9c 157 row["caller_account"] = accounts_cache[row.caller]
66862195 158 except KeyError:
77431b9c 159 row["caller_account"] = accounts_cache[row.caller] = \
66862195
MT
160 self.accounts.get_by_sip_id(row.caller)
161
77431b9c
MT
162 if replace_sip_uris:
163 row["called"] = self._process_sip_uri(row.called)
164
66862195 165 try:
77431b9c 166 row["called_account"] = accounts_cache[row.called]
66862195 167 except KeyError:
77431b9c 168 row["called_account"] = accounts_cache[row.called] = \
66862195
MT
169 self.accounts.get_by_sip_id(row.called)
170
171 if not row.called_account:
77431b9c 172 row["called_conference"] = self.get_conference(row.called)
66862195 173 else:
77431b9c
MT
174 row["called_conference"] = None
175
176 try:
177 row["time"] = datetime.datetime.fromutctimestamp(row.time)
178 except:
179 pass
66862195
MT
180
181 result.append(row)
182
183 return result
184
185 def get_call_log(self, account, limit=25):
77431b9c
MT
186 if account:
187 res = self.db.query("SELECT * FROM cdr WHERE (caller = %s OR called = %s) \
188 ORDER BY time DESC LIMIT %s", account.sip_id, account.sip_id, limit)
189 else:
190 res = self.db.query("SELECT * FROM cdr ORDER BY time DESC LIMIT %s", limit)
66862195
MT
191
192 return self._process_cdr(res)
193
a69e87a1
MT
194 def get_channels(self, account=None):
195 channels = []
196 for c in a.list_channels():
197 if account and not account.sip_id in (c.caller, c.callee):
198 continue
5ac74b02 199
a69e87a1 200 channels.append(c)
5ac74b02 201
a69e87a1 202 return sorted(channels)
5ac74b02 203
a69e87a1
MT
204 def get_channel(self, channel_id, account=None):
205 channels = self.get_channels(account=account)
40818cf2 206
a69e87a1
MT
207 for channel in channels:
208 if channel.id == channel_id:
209 return channel
40818cf2 210
66862195
MT
211 def get_ongoing_calls(self, account=None, sip_id=None):
212 if account and sip_id is None:
213 sip_id = account.sip_id
214
77431b9c
MT
215 # If the given account does not have a SIP ID,
216 # we not need to search for any active sessions
217 if sip_id is None:
218 return []
219
66862195 220 if sip_id:
77431b9c
MT
221 sip_id = "sip:%s@%%" % sip_id
222
223 res = self.db.query("SELECT from_uri AS caller, \
224 to_uri AS called, start_time AS time FROM dialog \
225 WHERE from_uri LIKE %s OR to_uri LIKE %s ORDER BY time",
226 sip_id, sip_id)
66862195 227 else:
77431b9c
MT
228 res = self.db.query("SELECT from_uri AS caller, to_uri AS called, \
229 start_time AS time FROM dialog ORDER BY time")
66862195 230
77431b9c 231 return self._process_cdr(res, replace_sip_uris=True)
66862195 232
66862195
MT
233 # Favourites
234
235 def get_favourite_contacts(self, account, limit=6):
77431b9c
MT
236 res = self.db.query("SELECT src_user AS caller, dst_ouser AS called, \
237 COUNT(*) AS count FROM acc WHERE method = %s AND src_user = %s AND \
238 dst_ouser BETWEEN %s AND %s AND time >= NOW() - INTERVAL '1 year' \
239 GROUP BY caller, called ORDER BY count DESC LIMIT %s",
240 "INVITE", account.sip_id, "1000", "1999", limit)
66862195
MT
241
242 return self._process_cdr(res)
243
244 # Conferences
245
246 @property
247 def conferences(self):
248 conferences = []
249
250 for no in range(1, 10):
251 conference = Conference(self.backend, no)
252 conferences.append(conference)
253
254 return conferences
255
256 def get_conference(self, id):
257 for c in self.conferences:
258 if not c.sip_id == id:
259 continue
260
261 return c
262
263
264class Conference(Object):
265 def __init__(self, backend, no):
266 Object.__init__(self, backend)
267 self.no = no
268
269 def __cmp__(self, other):
270 return cmp(self.no, other.no)
271
272 @property
273 def name(self):
274 return "IPFire Conference Room %s" % self.no
275
276 @property
277 def sip_id(self):
278 return "%s" % (9000 + self.no)
279
280 @property
281 def sip_url(self):
282 return "%s@ipfire.org" % self.sip_id
283
284 @property
285 def participants(self):
286 if not hasattr(self, "_participants"):
287 self._participants = self.backend.talk.get_ongoing_calls(sip_id=self.sip_id)
288
289 return self._participants