~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

Viewing changes to tools/pysflphone/sflphonectrlsimple.py

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2012-05-19 21:46:37 UTC
  • mfrom: (1.1.7)
  • Revision ID: package-import@ubuntu.com-20120519214637-la8rbrford5kj6m3
Tags: 1.1.0-1
* New upstream release 
  - Fixes "FTBFS with libccrtp-dev/2.0.2 from experimental" (Closes: #663282)
* NEW Maintainer: Debian VoIP Team - Thanks Francois for your work.
  - (Closes: #665789: O: sflphone -- SIP and IAX2 compatible VoIP phone)
* Added Build-Depends: libdbus-c++-bin
* Add gcc47-fixes.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
#
3
 
# Copyright (C) 2009 by the Free Software Foundation, Inc.
4
 
#
5
 
# Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
6
 
#
7
 
# This program is free software; you can redistribute it and/or
8
 
# modify it under the terms of the GNU General Public License
9
 
# as published by the Free Software Foundation; either version 2
10
 
# of the License, or (at your option) any later version.
11
 
#
12
 
# This program is distributed in the hope that it will be useful,
13
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
# GNU General Public License for more details.
16
 
#
17
 
# You should have received a copy of the GNU General Public License
18
 
# along with this program; if not, write to the Free Software
19
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
 
 
21
 
"""Simple class for controlling SflPhoned through DBUS"""
22
 
 
23
 
import sys
24
 
import os
25
 
import random
26
 
from traceback import print_exc
27
 
 
28
 
import gtk
29
 
import gobject
30
 
from gobject import GObject
31
 
from gobject import MainLoop
32
 
 
33
 
import getopt
34
 
 
35
 
import time
36
 
import hashlib
37
 
 
38
 
from threading import Thread
39
 
from threading import Event
40
 
 
41
 
from Errors import *
42
 
 
43
 
try:
44
 
        import dbus
45
 
        from dbus.mainloop.glib import DBusGMainLoop
46
 
except ImportError, e:
47
 
        raise SflPhoneError("No python-dbus module found")
48
 
 
49
 
 
50
 
class SflPhoneCtrlSimple(Thread):
51
 
    """ Simple class for controlling SflPhoned through DBUS
52
 
 
53
 
        If option testSuite (ts) is put to true,
54
 
        simple actions are implemented on incoming call.
55
 
    """
56
 
 
57
 
    # list of active calls (known by the client)
58
 
    activeCalls = {}
59
 
 
60
 
    def __init__(self, test=False, name=sys.argv[0]):
61
 
        print "Create SFLphone instance"
62
 
        Thread.__init__(self)
63
 
        # current active account
64
 
        self.account = None
65
 
        # client name
66
 
        self.name = name
67
 
        # client registered to sflphoned ?
68
 
        self.registered = False
69
 
        self.register()
70
 
        self.currentCallId = ""
71
 
 
72
 
        self.loop = MainLoop()
73
 
 
74
 
        self.isStop = False
75
 
 
76
 
        self.test = test
77
 
        self.onIncomingCall_cb = None
78
 
        self.event = Event()
79
 
 
80
 
        gobject.threads_init()
81
 
        
82
 
 
83
 
 
84
 
    def __del__(self):
85
 
        if self.registered:
86
 
            self.unregister()
87
 
        self.loop.quit()
88
 
 
89
 
 
90
 
    def stopThread(self):
91
 
        print "Stop PySFLphone"
92
 
        self.isStop = True
93
 
        
94
 
 
95
 
 
96
 
    def register(self):
97
 
        if self.registered:
98
 
            return
99
 
 
100
 
        try:
101
 
            # register the main loop for d-bus events
102
 
            DBusGMainLoop(set_as_default=True)
103
 
            self.bus = dbus.SessionBus()
104
 
        except dbus.DBusException, e:
105
 
            raise SPdbusError("Unable to connect DBUS session bus")
106
 
 
107
 
        dbus_objects = dbus.Interface(self.bus.get_object(
108
 
              'org.freedesktop.DBus', '/org/freedesktop/DBus'),
109
 
                      'org.freedesktop.DBus').ListNames()
110
 
 
111
 
        if not "org.sflphone.SFLphone" in dbus_objects:
112
 
            raise SPdbusError("Unable to find org.sflphone.SFLphone in DBUS. Check if sflphoned is running")
113
 
 
114
 
        try:
115
 
            proxy_instance = self.bus.get_object("org.sflphone.SFLphone",
116
 
                 "/org/sflphone/SFLphone/Instance", introspect=False)
117
 
            proxy_callmgr = self.bus.get_object("org.sflphone.SFLphone",
118
 
                 "/org/sflphone/SFLphone/CallManager", introspect=False)
119
 
            proxy_confmgr = self.bus.get_object("org.sflphone.SFLphone",
120
 
                 "/org/sflphone/SFLphone/ConfigurationManager",
121
 
                        introspect=False)
122
 
 
123
 
            self.instance = dbus.Interface(proxy_instance,
124
 
                          "org.sflphone.SFLphone.Instance")
125
 
            self.callmanager = dbus.Interface(proxy_callmgr,
126
 
                          "org.sflphone.SFLphone.CallManager")
127
 
            self.configurationmanager = dbus.Interface(proxy_confmgr,
128
 
                          "org.sflphone.SFLphone.ConfigurationManager")
129
 
 
130
 
        except dbus.DBusException, e:
131
 
 
132
 
            raise SPdbusError("Unable to bind to sflphoned api, ask core-dev team to implement getVersion method and start to pray.")
133
 
 
134
 
        try:
135
 
            self.instance.Register(os.getpid(), self.name)
136
 
            self.registered = True
137
 
        except:
138
 
            raise SPdaemonError("Client registration failed")
139
 
 
140
 
        try:
141
 
            print "Adding Incoming call method"
142
 
            proxy_callmgr.connect_to_signal('incomingCall', self.onIncomingCall)
143
 
            proxy_callmgr.connect_to_signal('callStateChanged', self.onCallStateChanged)
144
 
        except dbus.DBusException, e:
145
 
            print e
146
 
 
147
 
 
148
 
 
149
 
    def unregister(self):
150
 
 
151
 
        print "Unregister"
152
 
 
153
 
        if not self.registered:
154
 
            return
155
 
            #raise SflPhoneError("Not registered !")
156
 
        try:
157
 
            self.instance.Unregister(os.getpid())
158
 
            self.registered = False
159
 
        except:
160
 
            raise SPdaemonError("Client unregistration failed")
161
 
 
162
 
 
163
 
    def isRegistered(self):
164
 
        return self.registered
165
 
 
166
 
 
167
 
    def getEvent(self):
168
 
        return self.event
169
 
 
170
 
    def wait(self):
171
 
        self.event.wait()
172
 
 
173
 
    def isSet(self):
174
 
        self.event.isSet()
175
 
 
176
 
    def set(self):
177
 
        self.event.set()
178
 
 
179
 
    def clear(self):
180
 
        self.event.clear()
181
 
 
182
 
    #
183
 
    # Signal handling
184
 
    #
185
 
 
186
 
    # On incoming call event, add the call to the list of active calls
187
 
    def onIncomingCall(self, account, callid, to):
188
 
        print "Incoming call: " + account + ", " + callid + ", " + to
189
 
        self.activeCalls[callid] = {'Account': account, 'To': to, 'State': '' }
190
 
        self.currentCallId = callid
191
 
 
192
 
        if(self.test):
193
 
            # TODO fix this bug in daemon, cannot answer too fast
194
 
            time.sleep(0.5)
195
 
            if self.onIncomingCall_cb(self) is not None:
196
 
                self.onIncomingCall_cb(self)
197
 
        
198
 
 
199
 
 
200
 
    # On call state changed event, set the values for new calls,
201
 
    # or delete the call from the list of active calls
202
 
    def onCallStateChanged(self, callid, state):
203
 
        print "Call state changed: " + callid + ", " + state
204
 
        if state == "HUNGUP":
205
 
            try:
206
 
                del self.activeCalls[callid]
207
 
            except KeyError:
208
 
                print "Call " + callid + " didn't exist. Cannot delete."
209
 
 
210
 
        elif state in [ "RINGING", "CURRENT", "INCOMING", "HOLD" ]:
211
 
            try:
212
 
                self.activeCalls[callid]['State'] = state
213
 
            except KeyError, e:
214
 
                print "This call didn't exist!: " + callid + ". Adding it to the list."
215
 
                callDetails = self.getCallDetails(callid)
216
 
                self.activeCalls[callid] = {'Account': callDetails['ACCOUNTID'],
217
 
                                            'To': callDetails['PEER_NUMBER'], 'State': state }
218
 
        elif state in [ "BUSY", "FAILURE" ]:
219
 
            try:
220
 
                del self.activeCalls[callid]
221
 
            except KeyError, e:
222
 
                print "This call didn't exist!: " + callid
223
 
 
224
 
#               elif state == "UNHOLD_CURRENT":
225
 
#                       self.activeCalls[callid]['State'] = "UNHOLD_CURRENT"
226
 
 
227
 
 
228
 
    #
229
 
    # Account management
230
 
    #
231
 
    def addAccount(self, details=None):
232
 
        """Add a new account account
233
 
 
234
 
        Add a new account to the SFLphone-daemon. Default parameters are \
235
 
        used for missing account configuration field.
236
 
 
237
 
        Required parameters are type, alias, hostname, username and password
238
 
 
239
 
        input details
240
 
        
241
 
        """
242
 
 
243
 
        if details is None:
244
 
            raise SPaccountError("Must specifies type, alias, hostname, \
245
 
                                  username and password in \
246
 
                                  order to create a new account")
247
 
 
248
 
        return self.configurationmanager.addAccount(details)
249
 
 
250
 
    def removeAccount(self, accountID=None):
251
 
        """Remove an account from internal list"""
252
 
 
253
 
        if accountID is None:
254
 
            raise SPaccountError("Account ID must be specified")
255
 
 
256
 
        self.configurationmanager.removeAccount(accountID)
257
 
 
258
 
    def getAllAccounts(self):
259
 
        """Return a list with all accounts"""
260
 
        return self.configurationmanager.getAccountList()
261
 
 
262
 
 
263
 
    def getAllEnabledAccounts(self):
264
 
        """Return a list with all enabled accounts"""
265
 
        accounts = self.getAllAccounts()
266
 
        activeaccounts = []
267
 
        for testedaccount in accounts:
268
 
            if self.isAccountEnable(testedaccount):
269
 
                activeaccounts.append(testedaccount)
270
 
        return activeaccounts
271
 
 
272
 
 
273
 
    def getAccountDetails(self, account=None):
274
 
        """Return a list of string. If no account is provided, active account is used"""
275
 
 
276
 
        if account is None:
277
 
            if self.account is None:
278
 
                raise SflPhoneError("No provided or current account !")
279
 
                if checkAccountExists(self.account):
280
 
                    return self.configurationmanager.getAccountDetails(self.account)
281
 
        else:
282
 
            if self.checkAccountExists(account):
283
 
 
284
 
                return self.configurationmanager.getAccountDetails(account)
285
 
 
286
 
 
287
 
    def setAccountByAlias(self, alias):
288
 
        """Define as active the first account who match with the alias"""
289
 
 
290
 
        for testedaccount in self.getAllAccounts():
291
 
            details = self.getAccountDetails(testedaccount)
292
 
            if ( details['Account.enable'] == "TRUE" and
293
 
                              details['Account.alias'] == alias ):
294
 
                self.account = testedaccount
295
 
                return
296
 
        raise SPaccountError("No enabled account matched with alias")
297
 
 
298
 
 
299
 
    def getAccountByAlias(self, alias):
300
 
        """Get account name having its alias"""
301
 
 
302
 
        for account in self.getAllAccounts():
303
 
            details = self.getAccountDetails(account)
304
 
            if details['Account.alias'] == alias:
305
 
                return account
306
 
 
307
 
        raise SPaccountError("No account matched with alias")
308
 
 
309
 
    def setAccount(self, account):
310
 
        """Define the active account
311
 
 
312
 
        The active account will be used when sending a new call
313
 
        """
314
 
 
315
 
        if account in self.getAllAccounts():
316
 
            self.account = account
317
 
        else:
318
 
            print account
319
 
            raise SflPhoneError("Not a valid account")
320
 
 
321
 
    def setFirstRegisteredAccount(self):
322
 
        """Find the first enabled account and define it as active"""
323
 
 
324
 
        rAccounts = self.getAllRegisteredAccounts()
325
 
        if 0 == len(rAccounts):
326
 
            raise SflPhoneError("No registered account !")
327
 
        self.account = rAccounts[0]
328
 
 
329
 
    def setFirstActiveAccount(self):
330
 
        """Find the first enabled account and define it as active"""
331
 
 
332
 
        aAccounts = self.getAllEnabledAccounts()
333
 
        if 0 == len(aAccounts):
334
 
            raise SflPhoneError("No active account !")
335
 
        self.account = aAccounts[0]
336
 
 
337
 
 
338
 
    def getAccount(self):
339
 
        """Return the active account"""
340
 
 
341
 
        return self.account
342
 
 
343
 
 
344
 
    def isAccountRegistered(self, account=None):
345
 
        """Return True if the account is registered. If no account is provided, active account is used"""
346
 
 
347
 
        if account is None:
348
 
                if self.account is None:
349
 
                        raise SflPhoneError("No provided or current account !")
350
 
                account = self.account
351
 
        return self.getAccountDetails(account)['Status'] == "REGISTERED"
352
 
 
353
 
 
354
 
    def isAccountEnable(self, account=None):
355
 
        """Return True if the account is enabled. If no account is provided, active account is used"""
356
 
 
357
 
        if account is None:
358
 
                if self.account is None:
359
 
                        raise SflPhoneError("No provided or current account !")
360
 
                account = self.account
361
 
        return self.getAccountDetails(account)['Account.enable'] == "TRUE"
362
 
 
363
 
    def setAccountEnable(self, account=None, enable=False):
364
 
        """Set account enabled"""
365
 
        if account is None:
366
 
                if self.account is None:
367
 
                        raise SflPhoneError("No provided or current account !")
368
 
                account = self.account
369
 
 
370
 
        if enable == True:
371
 
                details = self.getAccountDetails(account)
372
 
                details['Account.enable'] = "TRUE"
373
 
                self.configurationmanager.setAccountDetails(account, details)
374
 
        else:
375
 
                details = self.getAccountDetails(account)
376
 
                details['Account.enable'] = "FALSE"
377
 
                self.configurationmanager.setAccountDetails(account, details)
378
 
 
379
 
    def checkAccountExists(self, account=None):
380
 
        """ Checks if the account exists """
381
 
        if account is None:
382
 
            raise SflPhoneError("No provided or current account !")
383
 
        return account in self.getAllAccounts()
384
 
                        
385
 
    def getAllRegisteredAccounts(self):
386
 
        """Return a list of registered accounts"""
387
 
 
388
 
        registeredAccountsList = []
389
 
        for account in self.getAllAccounts():
390
 
            if self.isAccountRegistered(account):
391
 
                registeredAccountsList.append(account)
392
 
 
393
 
        return registeredAccountsList
394
 
 
395
 
    def getAllEnabledAccounts(self):
396
 
        """Return a list of enabled accounts"""
397
 
 
398
 
        enabledAccountsList = []
399
 
        for accountName in self.getAllAccounts():
400
 
            if self.getAccountDetails(accountName)['Account.enable'] == "TRUE":
401
 
                 enabledAccountsList.append(accountName)
402
 
 
403
 
        return enabledAccountsList
404
 
 
405
 
    def getAllSipAccounts(self):
406
 
        """Return a list of SIP accounts"""
407
 
        sipAccountsList = []
408
 
        for accountName in self.getAllAccounts():
409
 
            if  self.getAccountDetails(accountName)['Account.type'] == "SIP":
410
 
                sipAccountsList.append(accountName)
411
 
 
412
 
        return sipAccountsList
413
 
 
414
 
    def getAllIaxAccounts(self):
415
 
        """Return a list of IAX accounts"""
416
 
 
417
 
        iaxAccountsList = []
418
 
        for accountName in self.getAllAccounts():
419
 
            if  self.getAccountDetails(accountName)['Account.type'] == "IAX":
420
 
                iaxAccountsList.append(accountName)
421
 
 
422
 
        return iaxAccountsList
423
 
 
424
 
    def setAccountRegistered(self, account=None, register=False):
425
 
        """ Tries to register the account """
426
 
 
427
 
        if account is None:
428
 
                if self.account is None:
429
 
                        raise SflPhoneError("No provided or current account !")
430
 
                account = self.account
431
 
 
432
 
        try:
433
 
                if register:
434
 
                        self.configurationmanager.sendRegister(account, int(1))
435
 
                        #self.setAccount(account)
436
 
                else:
437
 
                        self.configurationmanager.sendRegister(account, int(0))
438
 
                        #self.setFirstRegisteredAccount()
439
 
        except SflPhoneError, e:
440
 
                print e
441
 
 
442
 
    #
443
 
    # Codec manager
444
 
    #
445
 
 
446
 
    def getCodecList(self):
447
 
        """ Return the codec list """
448
 
        return self.configurationmanager.getCodecList()
449
 
 
450
 
    def getActiveCodecList(self):
451
 
        """ Return the active codec list """
452
 
        return self.configurationmanager.getActiveCodecList()
453
 
 
454
 
 
455
 
 
456
 
    #
457
 
    # Call management
458
 
    #
459
 
 
460
 
    def getCurrentCallID(self):
461
 
        """Return the callID of the current call if any"""
462
 
 
463
 
        return self.callmanager.getCurrentCallID()
464
 
 
465
 
 
466
 
    def getCurrentCallDetails(self):
467
 
        """Return informations on the current call if any"""
468
 
 
469
 
        return self.callmanager.getCallDetails(self.getCurrentCallID())
470
 
 
471
 
    def getCallDetails(self, callid):
472
 
        """Return informations on this call if exists"""
473
 
 
474
 
        return self.callmanager.getCallDetails(callid)
475
 
 
476
 
    def printClientCallList(self):
477
 
        print "Client active call list:"
478
 
        print "------------------------"
479
 
        for call in self.activeCalls:
480
 
            print "\t" + call
481
 
 
482
 
    #
483
 
    # Action
484
 
    #
485
 
    def Call(self, dest):
486
 
        """Start a call and return a CallID
487
 
 
488
 
        Use the current account previously set using setAccount().
489
 
        If no account specified, first registered one in account list is used.
490
 
 
491
 
        For phone number prefixed using SIP scheme (i.e. sip: or sips:),
492
 
        IP2IP profile is automatically selected and set as the default account
493
 
 
494
 
        return callID Newly generated callidentifier for this call
495
 
        """
496
 
 
497
 
        if dest is None or dest == "":
498
 
            raise SflPhoneError("Invalid call destination")
499
 
        
500
 
        # Set the account to be used for this call
501
 
        if dest.find('sip:') is 0 or dest.find('sips:') is 0:
502
 
            print "Ip 2 IP call"
503
 
            self.setAccount("IP2IP")
504
 
        elif not self.account:
505
 
            self.setFirstRegisteredAccount()
506
 
 
507
 
        if self.account is "IP2IP" and self.isAccountRegistered():
508
 
            raise SflPhoneError("Can't place a call without a registered account")
509
 
 
510
 
        # Generate a call ID for this call
511
 
        callid = self.GenerateCallID()  
512
 
 
513
 
        # Add the call to the list of active calls and set status to SENT
514
 
        self.activeCalls[callid] = {'Account': self.account, 'To': dest, 'State': 'SENT' }
515
 
 
516
 
        # Send the request to the CallManager
517
 
        self.callmanager.placeCall(self.account, callid, dest)
518
 
 
519
 
        return callid
520
 
 
521
 
 
522
 
    def HangUp(self, callid):
523
 
        """End a call identified by a CallID"""
524
 
        if not self.account:
525
 
            self.setFirstRegisteredAccount()
526
 
 
527
 
        # if not self.isAccountRegistered() and self.accout is not "IP2IP":
528
 
        #    raise SflPhoneError("Can't hangup a call without a registered account")
529
 
 
530
 
        if callid is None or callid == "":
531
 
            pass # just to see
532
 
            #raise SflPhoneError("Invalid callID")
533
 
 
534
 
        self.callmanager.hangUp(callid)
535
 
 
536
 
 
537
 
    def Transfer(self, callid, to):
538
 
        """Transfert a call identified by a CallID"""
539
 
        # if not self.account:
540
 
        #    self.setFirstRegisteredAccount()
541
 
 
542
 
        # if not self.isAccountRegistered():
543
 
        #     raise SflPhoneError("Can't transfert a call without a registered account")
544
 
 
545
 
        if callid is None or callid == "":
546
 
            raise SflPhoneError("Invalid callID")
547
 
 
548
 
        self.callmanager.transfert(callid, to)
549
 
 
550
 
 
551
 
    def Refuse(self, callid):
552
 
        """Refuse an incoming call identified by a CallID"""
553
 
 
554
 
        print "Refuse call " + callid
555
 
 
556
 
        # if not self.account:
557
 
        #     self.setFirstRegisteredAccount()
558
 
 
559
 
        # if not self.isAccountRegistered():
560
 
        #     raise SflPhoneError("Can't refuse a call without a registered account")
561
 
 
562
 
        if callid is None or callid == "":
563
 
            raise SflPhoneError("Invalid callID")
564
 
 
565
 
        self.callmanager.refuse(callid)
566
 
 
567
 
 
568
 
    def Accept(self, callid):
569
 
        """Accept an incoming call identified by a CallID"""
570
 
        print "Accept call " + callid
571
 
        if not self.account:
572
 
            self.setFirstRegisteredAccount()
573
 
 
574
 
        if not self.isAccountRegistered():
575
 
            raise SflPhoneError("Can't accept a call without a registered account")
576
 
 
577
 
        if callid is None or callid == "":
578
 
            raise SflPhoneError("Invalid callID")
579
 
        
580
 
        self.callmanager.accept(callid)
581
 
 
582
 
 
583
 
    def Hold(self, callid):
584
 
        """Hold a call identified by a CallID"""
585
 
        # if not self.account:
586
 
        #    self.setFirstRegisteredAccount()
587
 
 
588
 
        # if not self.isAccountRegistered():
589
 
        #    raise SflPhoneError("Can't hold a call without a registered account")
590
 
 
591
 
        if callid is None or callid == "":
592
 
            raise SflPhoneError("Invalid callID")
593
 
 
594
 
        self.callmanager.hold(callid)
595
 
 
596
 
 
597
 
    def UnHold(self, callid):
598
 
        """Unhold an incoming call identified by a CallID"""
599
 
        # if not self.account:
600
 
        #    self.setFirstRegisteredAccount()
601
 
 
602
 
        # if not self.isAccountRegistered():
603
 
        #    raise SflPhoneError("Can't unhold a call without a registered account")
604
 
 
605
 
        if callid is None or callid == "":
606
 
            raise SflPhoneError("Invalid callID")
607
 
 
608
 
        self.callmanager.unhold(callid)
609
 
 
610
 
 
611
 
    def Dtmf(self, key):
612
 
        """Send a DTMF"""
613
 
        self.callmanager.playDTMF(key)
614
 
 
615
 
 
616
 
    def GenerateCallID(self):
617
 
        """Generate Call ID"""
618
 
        m = hashlib.md5()
619
 
        t = long( time.time() * 1000 )
620
 
        r = long( random.random()*100000000000000000L )
621
 
        m.update(str(t) + str(r))
622
 
        callid = m.hexdigest()
623
 
        return callid
624
 
 
625
 
    def run(self):
626
 
        """Processing method for this thread"""
627
 
 
628
 
        context = self.loop.get_context()
629
 
 
630
 
        while True:
631
 
            context.iteration(True)
632
 
 
633
 
            if self.isStop:
634
 
                return