+++ /dev/null
-#!/usr/bin/python
-
-import ipaddress
-import logging
-import re
-import time
-
-from . import database
-
-from .misc import Object
-from .decorators import *
-
-class Freeswitch(Object):
- @lazy_property
- def db(self):
- credentials = {
- "host" : self.settings.get("freeswitch_database_host"),
- "database" : self.settings.get("freeswitch_database_name", "freeswitch"),
- "user" : self.settings.get("freeswitch_database_user"),
- "password" : self.settings.get("freeswitch_database_password"),
- }
-
- return database.Connection(**credentials)
-
- def get_sip_registrations(self, sip_uri):
- logging.debug("Fetching SIP registrations for %s" % sip_uri)
-
- user, delim, domain = sip_uri.partition("@")
-
- res = self.db.query("SELECT * FROM sip_registrations \
- WHERE sip_user = %s AND sip_host = %s AND expires >= EXTRACT(epoch FROM CURRENT_TIMESTAMP) \
- ORDER BY contact", user, domain)
-
- for row in res:
- yield SIPRegistration(self, data=row)
-
- def _get_channels(self, query, *args):
- res = self.db.query(query, *args)
-
- channels = []
- for row in res:
- c = Channel(self, data=row)
- channels.append(c)
-
- return channels
-
- def get_sip_channels(self, account):
- return self._get_channels("SELECT * FROM channels \
- WHERE (direction = %s AND cid_num = %s) OR \
- (direction = %s AND (callee_num = %s OR callee_num = ANY(%s))) \
- AND callstate != %s ORDER BY created_epoch",
- "inbound", account.sip_id, "outbound", account.sip_id,
- account._all_telephone_numbers, "DOWN")
-
- def get_conferences(self):
- res = self.db.query("SELECT DISTINCT application_data AS handle FROM channels \
- WHERE application = %s AND application_data LIKE %s \
- ORDER BY application_data", "conference", "%%@ipfire.org")
-
- conferences = []
- for row in res:
- c = Conference(self, row.handle)
- conferences.append(c)
-
- return conferences
-
- def get_agent_status(self, account):
- res = self.db.get("SELECT status FROM agents \
- WHERE name = %s", account.sip_url)
-
- if res:
- return res.status
-
-class SIPRegistration(object):
- def __init__(self, freeswitch, data):
- self.freeswitch = freeswitch
- self.data = data
-
- @lazy_property
- def protocol(self):
- m = re.match(r"Registered\(([A-Z]+)(\-NAT)?\)", self.data.status)
-
- if m:
- return m.group(1)
-
- @property
- def network_ip(self):
- return ipaddress.ip_address(self.data.network_ip)
-
- @property
- def network_port(self):
- return self.data.network_port
-
- @property
- def user_agent(self):
- return self.data.user_agent
-
- def is_reachable(self):
- return self.data.ping_status == "Reachable"
-
- @lazy_property
- def latency(self):
- if self.is_reachable() and self.data.ping_time:
- return self.data.ping_time / 1000.0
-
-
-class Channel(object):
- def __init__(self, freeswitch, data):
- self.freeswitch = freeswitch
- self.data = data
-
- @property
- def backend(self):
- return self.freeswitch.backend
-
- @property
- def uuid(self):
- return self.data.uuid
-
- @property
- def direction(self):
- return self.data.direction
-
- @lazy_property
- def caller(self):
- return self.backend.accounts.get_by_sip_id(self.caller_number)
-
- @property
- def caller_name(self):
- return self.data.cid_name
-
- @property
- def caller_number(self):
- return self.data.cid_num
-
- @lazy_property
- def callee(self):
- return self.backend.accounts.get_by_sip_id(self.callee_number)
-
- @property
- def callee_name(self):
- return self.data.callee_name
-
- @property
- def callee_number(self):
- return self.data.callee_num
-
- @property
- def called_number(self):
- return self.data.dest
-
- @property
- def state(self):
- return self.data.callstate
-
- @property
- def application(self):
- return self.data.application
-
- @property
- def application_data(self):
- return self.data.application_data
-
- @lazy_property
- def conference(self):
- if self.application == "conference":
- return Conference(self.freeswitch, self.application_data)
-
- @property
- def duration(self):
- return time.time() - self.data.created_epoch
-
- @property
- def codec(self):
- # We always assume a symmetric codec
- return format_codec(self.data.write_codec, int(self.data.write_rate or 0), int(self.data.write_bit_rate or 0))
-
- def is_secure(self):
- if self.data.secure:
- return True
-
- return False
-
- @property
- def secure(self):
- try:
- transport_protocol, key_negotiation, cipher_suite = self.data.secure.split(":")
- except:
- return
-
- return "%s: %s" % (key_negotiation.upper(), cipher_suite.replace("_", "-"))
-
-
-class Conference(object):
- def __init__(self, freeswitch, handle):
- self.freeswitch = freeswitch
- self.handle = handle
-
- def __repr__(self):
- return "<%s %s>" % (self.__class__.__name__, self.handle)
-
- def __len__(self):
- return len(self.channels)
-
- def __eq__(self, other):
- if isinstance(other, self.__class__):
- return self.handle == other.handle
-
- def __iter__(self):
- return iter(self.channels)
-
- @lazy_property
- def number(self):
- m = re.match(r"conf(\d+)@", self.handle)
- if m:
- i = m.group(1)
-
- return int(i)
-
- @property
- def sip_id(self):
- return 900 + self.number
-
- @property
- def sip_url(self):
- return "%s@ipfire.org" % self.sip_id
-
- @property
- def phone_numbers(self):
- return [
- "+4923636035%s" % self.sip_id,
- ]
-
- @lazy_property
- def channels(self):
- return self.freeswitch._get_channels("SELECT * FROM channels \
- WHERE application = %s AND application_data = %s \
- ORDER BY created_epoch", "conference", self.handle)
-
-
-class Talk(Object):
- def init(self):
- # Connect to FreeSWITCH
- self.freeswitch = Freeswitch(self.backend)
-
- @property
- def conferences(self):
- return self.freeswitch.get_conferences()
-
-
-def format_codec(name, bit_rate, bandwidth):
- if not name:
- return
-
- s = [
- name,
- ]
-
- if bit_rate:
- s.append("%.0f kHz" % (bit_rate / 1000.0))
-
- if bandwidth:
- s.append("%.0f kBit/s" % (bandwidth / 1000.0))
- else:
- s.append("VBR")
-
- return " ".join(s)