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