Files
jami-daemon/tools/pysflphone/sflphonectrl.py
2012-04-15 10:27:51 -04:00

706 lines
20 KiB
Python
Executable File

#!/usr/bin/env python
#
# Copyright (C) 2009 by the Free Software Foundation, Inc.
#
# Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Simple class for controlling SflPhoned through DBUS"""
import sys
import os
import random
from traceback import print_exc
import gtk
import gobject
from gobject import GObject
from gobject import MainLoop
import getopt
import time
import hashlib
from threading import Thread
from threading import Event
from Errors import *
try:
import dbus
from dbus.mainloop.glib import DBusGMainLoop
except ImportError, e:
raise SflPhoneError("No python-dbus module found")
class SflPhoneCtrl(Thread):
""" class for controlling SflPhoned through DBUS
Classes deriving this class should reimplement signal handlers,
more especially:
onIncomingCall_cb
onCallHangup_cb
onCallRinging_cb
onCallHold_cb
onCallCurrent_cb
onCallBusy_cb
onCallFailure_cb
onConferenceCreated_cb
"""
# list of active calls (known by the client)
activeCalls = {}
# list of active conferences
activeConferences = {}
def __init__(self, name=sys.argv[0]):
Thread.__init__(self)
# current active account
self.account = None
# client name
self.name = name
self.currentCallId = ""
self.currentConfId = ""
self.isStop = False
# client registered to sflphoned ?
self.registered = False
self.register()
# Glib MainLoop for processing callbacks
self.loop = MainLoop()
gobject.threads_init()
def __del__(self):
if self.registered:
self.unregister()
self.loop.quit()
def stopThread(self):
self.isStop = True
def register(self):
if self.registered:
return
try:
# register the main loop for d-bus events
DBusGMainLoop(set_as_default=True)
self.bus = dbus.SessionBus()
except dbus.DBusException, e:
raise SPdbusError("Unable to connect DBUS session bus")
dbus_objects = dbus.Interface(self.bus.get_object(
'org.freedesktop.DBus', '/org/freedesktop/DBus'),
'org.freedesktop.DBus').ListNames()
if not "org.sflphone.SFLphone" in dbus_objects:
raise SPdbusError("Unable to find org.sflphone.SFLphone in DBUS. Check if sflphoned is running")
try:
proxy_instance = self.bus.get_object("org.sflphone.SFLphone",
"/org/sflphone/SFLphone/Instance", introspect=False)
proxy_callmgr = self.bus.get_object("org.sflphone.SFLphone",
"/org/sflphone/SFLphone/CallManager", introspect=False)
proxy_confmgr = self.bus.get_object("org.sflphone.SFLphone",
"/org/sflphone/SFLphone/ConfigurationManager",
introspect=False)
self.instance = dbus.Interface(proxy_instance,
"org.sflphone.SFLphone.Instance")
self.callmanager = dbus.Interface(proxy_callmgr,
"org.sflphone.SFLphone.CallManager")
self.configurationmanager = dbus.Interface(proxy_confmgr,
"org.sflphone.SFLphone.ConfigurationManager")
except dbus.DBusException, e:
raise SPdbusError("Unable to bind to sflphoned api, ask core-dev team to implement getVersion method and start to pray.")
try:
self.instance.Register(os.getpid(), self.name)
self.registered = True
except:
raise SPdaemonError("Client registration failed")
try:
print "Adding Incoming call method"
proxy_callmgr.connect_to_signal('incomingCall', self.onIncomingCall)
proxy_callmgr.connect_to_signal('callStateChanged', self.onCallStateChanged)
proxy_callmgr.connect_to_signal('conferenceCreated', self.onConferenceCreated)
except dbus.DBusException, e:
print e
def unregister(self):
if not self.registered:
return
try:
self.instance.Unregister(os.getpid())
self.registered = False
except:
raise SPdaemonError("Client unregistration failed")
def isRegistered(self):
return self.registered
#
# Signal handling
#
def onIncomingCall_cb(self):
pass
def onCallHangup_cb(self, callId):
pass
def onCallRinging_cb(self):
pass
def onCallHold_cb(self):
pass
def onCallCurrent_cb(self):
pass
def onCallBusy_cb(self):
pass
def onCallFailure_cb(self):
pass
def onIncomingCall(self, account, callid, to):
""" On incoming call event, add the call to the list of active calls """
self.activeCalls[callid] = {'Account': account,
'To': to,
'State': ''}
self.currentCallId = callid
self.onIncomingCall_cb()
def onCallHangUp(self, callid):
""" Remove callid from call list """
self.onCallHangup_cb(callid)
self.currentCallId = ""
del self.activeCalls[callid]
def onCallRinging(self, callid, state):
""" Update state for this call to Ringing """
self.activeCalls[callid]['State'] = state
self.onCallRinging_cb()
def onCallHold(self, callid, state):
""" Update state for this call to Hold """
self.activeCalls[callid]['State'] = state
self.onCallHold_cb()
def onCallCurrent(self, callid, state):
""" Update state for this call to current """
self.activeCalls[callid]['State'] = state
self.onCallCurrent_cb()
def onCallBusy(self, callid, state):
""" Update state for this call to busy """
self.activeCalls[callid]['State'] = state
self.onCallBusy_cb()
def onCallFailure(self, callid, state):
""" Handle call failure """
self.onCallFailure_cb(self)
del self.activeCalls[callid]
def onCallStateChanged(self, callid, state):
""" On call state changed event, set the values for new calls,
or delete the call from the list of active calls
"""
print "On call state changed " + callid + " " + state
if callid not in self.activeCalls:
print "This call didn't exist!: " + callid + ". Adding it to the list."
callDetails = self.getCallDetails(callid)
self.activeCalls[callid] = {'Account': callDetails['ACCOUNTID'],
'To': callDetails['PEER_NUMBER'],
'State': state }
self.currentCallId = callid
if state == "HUNGUP":
self.onCallHangUp(callid)
elif state == "RINGING":
self.onCallRinging(callid, state)
elif state == "CURRENT":
self.onCallCurrent(callid, state)
elif state == "HOLD":
self.onCallHold(callid, state)
elif state == "BUSY":
self.onCallBusy(callid, state)
elif state == "FAILURE":
self.onCallFailure(self, callid, state)
else:
print "unknown state"
def onConferenceCreated_cb(self):
pass
def onConferenceCreated(self, confId):
self.currentConfId = confId
self.onConferenceCreated_cb()
#
# Account management
#
def addAccount(self, details=None):
"""Add a new account account
Add a new account to the SFLphone-daemon. Default parameters are \
used for missing account configuration field.
Required parameters are type, alias, hostname, username and password
input details
"""
if details is None:
raise SPaccountError("Must specifies type, alias, hostname, \
username and password in \
order to create a new account")
return self.configurationmanager.addAccount(details)
def removeAccount(self, accountID=None):
"""Remove an account from internal list"""
if accountID is None:
raise SPaccountError("Account ID must be specified")
self.configurationmanager.removeAccount(accountID)
def getAllAccounts(self):
"""Return a list with all accounts"""
return self.configurationmanager.getAccountList()
def getAllEnabledAccounts(self):
"""Return a list with all enabled accounts"""
accounts = self.getAllAccounts()
activeaccounts = []
for testedaccount in accounts:
if self.isAccountEnable(testedaccount):
activeaccounts.append(testedaccount)
return activeaccounts
def getAccountDetails(self, account=None):
"""Return a list of string. If no account is provided, active account is used"""
if account is None:
if self.account is None:
raise SflPhoneError("No provided or current account !")
if checkAccountExists(self.account):
return self.configurationmanager.getAccountDetails(self.account)
else:
if self.checkAccountExists(account):
return self.configurationmanager.getAccountDetails(account)
def setAccountByAlias(self, alias):
"""Define as active the first account who match with the alias"""
for testedaccount in self.getAllAccounts():
details = self.getAccountDetails(testedaccount)
if ( details['Account.enable'] == "TRUE" and
details['Account.alias'] == alias ):
self.account = testedaccount
return
raise SPaccountError("No enabled account matched with alias")
def getAccountByAlias(self, alias):
"""Get account name having its alias"""
for account in self.getAllAccounts():
details = self.getAccountDetails(account)
if details['Account.alias'] == alias:
return account
raise SPaccountError("No account matched with alias")
def setAccount(self, account):
"""Define the active account
The active account will be used when sending a new call
"""
if account in self.getAllAccounts():
self.account = account
else:
print account
raise SflPhoneError("Not a valid account")
def setFirstRegisteredAccount(self):
"""Find the first enabled account and define it as active"""
rAccounts = self.getAllRegisteredAccounts()
if 0 == len(rAccounts):
raise SflPhoneError("No registered account !")
self.account = rAccounts[0]
def setFirstActiveAccount(self):
"""Find the first enabled account and define it as active"""
aAccounts = self.getAllEnabledAccounts()
if 0 == len(aAccounts):
raise SflPhoneError("No active account !")
self.account = aAccounts[0]
def getAccount(self):
"""Return the active account"""
return self.account
def isAccountRegistered(self, account=None):
"""Return True if the account is registered. If no account is provided, active account is used"""
if account is None:
if self.account is None:
raise SflPhoneError("No provided or current account !")
account = self.account
return self.getAccountDetails(account)['Registration.Status'] == "REGISTERED"
def isAccountEnable(self, account=None):
"""Return True if the account is enabled. If no account is provided, active account is used"""
if account is None:
if self.account is None:
raise SflPhoneError("No provided or current account !")
account = self.account
return self.getAccountDetails(account)['Account.enable'] == "TRUE"
def setAccountEnable(self, account=None, enable=False):
"""Set account enabled"""
if account is None:
if self.account is None:
raise SflPhoneError("No provided or current account !")
account = self.account
if enable == True:
details = self.getAccountDetails(account)
details['Account.enable'] = "true"
self.configurationmanager.setAccountDetails(account, details)
else:
details = self.getAccountDetails(account)
details['Account.enable'] = "false"
self.configurationmanager.setAccountDetails(account, details)
def checkAccountExists(self, account=None):
""" Checks if the account exists """
if account is None:
raise SflPhoneError("No provided or current account !")
return account in self.getAllAccounts()
def getAllRegisteredAccounts(self):
"""Return a list of registered accounts"""
registeredAccountsList = []
for account in self.getAllAccounts():
if self.isAccountRegistered(account):
registeredAccountsList.append(account)
return registeredAccountsList
def getAllEnabledAccounts(self):
"""Return a list of enabled accounts"""
enabledAccountsList = []
for accountName in self.getAllAccounts():
if self.getAccountDetails(accountName)['Account.enable'] == "TRUE":
enabledAccountsList.append(accountName)
return enabledAccountsList
def getAllSipAccounts(self):
"""Return a list of SIP accounts"""
sipAccountsList = []
for accountName in self.getAllAccounts():
if self.getAccountDetails(accountName)['Account.type'] == "SIP":
sipAccountsList.append(accountName)
return sipAccountsList
def getAllIaxAccounts(self):
"""Return a list of IAX accounts"""
iaxAccountsList = []
for accountName in self.getAllAccounts():
if self.getAccountDetails(accountName)['Account.type'] == "IAX":
iaxAccountsList.append(accountName)
return iaxAccountsList
def setAccountRegistered(self, account=None, register=False):
""" Tries to register the account """
if account is None:
if self.account is None:
raise SflPhoneError("No provided or current account !")
account = self.account
try:
if register:
self.configurationmanager.sendRegister(account, int(1))
else:
self.configurationmanager.sendRegister(account, int(0))
except SflPhoneError, e:
print e
#
# Codec manager
#
def getCodecList(self):
""" Return the codec list """
return self.configurationmanager.getCodecList()
def getActiveCodecList(self):
""" Return the active codec list """
return self.configurationmanager.getActiveCodecList()
#
# Call management
#
def getCurrentCallID(self):
"""Return the callID of the current call if any"""
return self.callmanager.getCurrentCallID()
def getCurrentCallDetails(self):
"""Return informations on the current call if any"""
return self.callmanager.getCallDetails(self.getCurrentCallID())
def getCallDetails(self, callid):
"""Return informations on this call if exists"""
return self.callmanager.getCallDetails(callid)
def printClientCallList(self):
print "Client active call list:"
print "------------------------"
for call in self.activeCalls:
print "\t" + call
def Call(self, dest):
"""Start a call and return a CallID
Use the current account previously set using setAccount().
If no account specified, first registered one in account list is used.
For phone number prefixed using SIP scheme (i.e. sip: or sips:),
IP2IP profile is automatically selected and set as the default account
return callID Newly generated callidentifier for this call
"""
if dest is None or dest == "":
raise SflPhoneError("Invalid call destination")
# Set the account to be used for this call
if dest.find('sip:') is 0 or dest.find('sips:') is 0:
print "Ip 2 IP call"
self.setAccount("IP2IP")
elif not self.account:
self.setFirstRegisteredAccount()
if self.account is "IP2IP" and self.isAccountRegistered():
raise SflPhoneError("Can't place a call without a registered account")
# Generate a call ID for this call
callid = self.GenerateCallID()
# Add the call to the list of active calls and set status to SENT
self.activeCalls[callid] = {'Account': self.account, 'To': dest, 'State': 'SENT' }
# Send the request to the CallManager
self.callmanager.placeCall(self.account, callid, dest)
return callid
def HangUp(self, callid):
"""End a call identified by a CallID"""
if not self.account:
self.setFirstRegisteredAccount()
if callid is None or callid == "":
pass # just to see
self.callmanager.hangUp(callid)
def Transfer(self, callid, to):
"""Transfert a call identified by a CallID"""
if callid is None or callid == "":
raise SflPhoneError("Invalid callID")
self.callmanager.transfert(callid, to)
def Refuse(self, callid):
"""Refuse an incoming call identified by a CallID"""
print "Refuse call " + callid
if callid is None or callid == "":
raise SflPhoneError("Invalid callID")
self.callmanager.refuse(callid)
def Accept(self, callid):
"""Accept an incoming call identified by a CallID"""
print "Accept call " + callid
if not self.account:
self.setFirstRegisteredAccount()
if not self.isAccountRegistered():
raise SflPhoneError("Can't accept a call without a registered account")
if callid is None or callid == "":
raise SflPhoneError("Invalid callID")
self.callmanager.accept(callid)
def Hold(self, callid):
"""Hold a call identified by a CallID"""
if callid is None or callid == "":
raise SflPhoneError("Invalid callID")
self.callmanager.hold(callid)
def UnHold(self, callid):
"""Unhold an incoming call identified by a CallID"""
if callid is None or callid == "":
raise SflPhoneError("Invalid callID")
self.callmanager.unhold(callid)
def Dtmf(self, key):
"""Send a DTMF"""
self.callmanager.playDTMF(key)
def GenerateCallID(self):
"""Generate Call ID"""
m = hashlib.md5()
t = long( time.time() * 1000 )
r = long( random.random()*100000000000000000L )
m.update(str(t) + str(r))
callid = m.hexdigest()
return callid
def createConference(self, call1Id, call2Id):
""" Create a conference given the two call ids """
self.callmanager.joinParticipant(call1Id, call2Id)
def hangupConference(self, confId):
""" Hang up each call for this conference """
self.callmanager.hangUpConference(confId)
def run(self):
"""Processing method for this thread"""
context = self.loop.get_context()
while True:
context.iteration(True)
if self.isStop:
print "++++++++++++++++++++++++++++++++++++++++"
print "++++++++++++++++++++++++++++++++++++++++"
print "++++++++++++++++++++++++++++++++++++++++"
print "++++++++++++++++++++++++++++++++++++++++"
return