managerimpl.cpp 61.1 KB
Newer Older
jpbl's avatar
jpbl committed
1
/*
2
3
 *  Copyright (C) 2004-2007 Savoir-Faire Linux inc.
 *  Author: Alexandre Bourget <alexandre.bourget@savoirfairelinux.com>
jpbl's avatar
jpbl committed
4
 *  Author: Yan Morin <yan.morin@savoirfairelinux.com>
5
 *  Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com>
6
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
7
 *  Author: Guillaume Carmel-Archambault <guillaume.carmel-archambault@savoirfairelinux.com>
8
 *
jpbl's avatar
jpbl committed
9
10
 *  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
11
 *  the Free Software Foundation; either version 3 of the License, or
jpbl's avatar
jpbl committed
12
 *  (at your option) any later version.
13
 *
jpbl's avatar
jpbl committed
14
15
16
17
 *  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.
18
 *
jpbl's avatar
jpbl committed
19
20
21
22
23
24
25
26
27
28
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <errno.h>
#include <time.h>
#include <cstdlib>
#include <iostream>
#include <fstream>
29
#include <sstream>  
jpbl's avatar
jpbl committed
30
31
32
33
34
35
36
37
38
#include <sys/types.h> // mkdir(2)
#include <sys/stat.h>	// mkdir(2)

#include <cc++/socket.h>   // why do I need this here?
#include <ccrtp/channel.h> // why do I need this here?
#include <ccrtp/rtp.h>     // why do I need this here?
#include <cc++/file.h>

#include "manager.h"
39
#include "account.h"
40
#include "sipaccount.h"
jpbl's avatar
jpbl committed
41
#include "audio/audiolayer.h"
42
43
#include "audio/alsalayer.h"
#include "audio/pulselayer.h"
jpbl's avatar
jpbl committed
44
45
#include "audio/tonelist.h"

yanmorin's avatar
   
yanmorin committed
46
#include "accountcreator.h" // create new account
47
48
#include "sipvoiplink.h"

jpbl's avatar
jpbl committed
49
50
51
52
53
54
55
#include "user_cfg.h"

#define fill_config_str(name, value) \
  (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_str)))
#define fill_config_int(name, value) \
  (_config.addConfigTreeItem(section, Conf::ConfigTreeItem(std::string(name), std::string(value), type_int)))

Yun Liu's avatar
Yun Liu committed
56
57
58
59
ManagerImpl::ManagerImpl (void) 
	: _hasTriedToRegister(false)
        , _config()
	, _currentCallId2()
60
        , _currentCallMutex()
Yun Liu's avatar
Yun Liu committed
61
62
63
64
        , _codecBuilder(NULL)
        , _audiodriver(NULL)
        , _dtmfKey(NULL)
        , _codecDescriptorMap()
65
        , _toneMutex()
Yun Liu's avatar
Yun Liu committed
66
67
68
69
        , _telephoneTone(NULL)
        , _audiofile()
        , _spkr_volume(0)
        , _mic_volume(0)
70
        , _mutex()
Yun Liu's avatar
Yun Liu committed
71
72
	, _dbus(NULL)
        , _waitingCall()
73
        , _waitingCallMutex()
Yun Liu's avatar
Yun Liu committed
74
75
76
77
78
79
80
81
        , _nbIncomingWaitingCall(0)
        , _path("")
        , _exist(0)
        , _setupLoaded(false)
        , _firewallPort()
        , _firewallAddr("")
        , _hasZeroconf(false)
        , _callAccountMap()
82
        , _callAccountMapMutex()
Yun Liu's avatar
Yun Liu committed
83
        , _accountMap()
jpbl's avatar
jpbl committed
84
{
85
86
87
  
    // initialize random generator for call id
    srand (time(NULL));
yanmorin's avatar
   
yanmorin committed
88
89
90
91
92
93
94
95
96

#ifdef TEST
  testAccountMap();
  loadAccountMap();
  testCallAccountMap();
  unloadAccountMap();
#endif

  // should be call before initConfigFile
97
  // loadAccountMap();, called in init() now.
jpbl's avatar
jpbl committed
98
99
100
101
102
}

// never call if we use only the singleton...
ManagerImpl::~ManagerImpl (void) 
{
103
104
    terminate();
    _debug("%s stop correctly.\n", PROGNAME);
jpbl's avatar
jpbl committed
105
106
}

107
  void 
jpbl's avatar
jpbl committed
108
109
ManagerImpl::init() 
{
110
111
    // Load accounts, init map
    loadAccountMap();
112
 
113
    initVolume();
114

115
116
117
    if (_exist == 0) {
        _debug("Cannot create config file in your home directory\n");
    }
jpbl's avatar
jpbl committed
118

119
120
    initAudioDriver();
    selectAudioDriver();
121

122
123
    // Initialize the list of supported audio codecs
    initAudioCodec();
jpbl's avatar
jpbl committed
124

125
    AudioLayer *audiolayer = getAudioDriver();
126
    
127
128
    if (audiolayer!=0) {
        unsigned int sampleRate = audiolayer->getSampleRate();
jpbl's avatar
jpbl committed
129

130
131
132
        _debugInit("Load Telephone Tone");
        std::string country = getConfigString(PREFERENCES, ZONE_TONE);
        _telephoneTone = new TelephoneTone(country, sampleRate);
jpbl's avatar
jpbl committed
133

134
135
136
        _debugInit("Loading DTMF key");
        _dtmfKey = new DTMF(sampleRate);
    }
jpbl's avatar
jpbl committed
137
138
139
140
}

void ManagerImpl::terminate()
{
141
    saveConfig();
jpbl's avatar
jpbl committed
142

143
    unloadAccountMap();
144
  
145
146
    _debug("Unload DTMF Key\n");
    delete _dtmfKey;
jpbl's avatar
jpbl committed
147

148
149
    _debug("Unload Audio Driver\n");
    delete _audiodriver; _audiodriver = NULL;
jpbl's avatar
jpbl committed
150

151
152
    _debug("Unload Telephone Tone\n");
    delete _telephoneTone; _telephoneTone = NULL;
153

154
155
    _debug("Unload Audio Codecs\n");
    _codecDescriptorMap.deleteHandlePointer();
jpbl's avatar
jpbl committed
156
157
}

yanmorin's avatar
   
yanmorin committed
158
159
160
bool
ManagerImpl::isCurrentCall(const CallID& callId) {
  return (_currentCallId2 == callId ? true : false);
jpbl's avatar
jpbl committed
161
162
}

yanmorin's avatar
   
yanmorin committed
163
164
bool
ManagerImpl::hasCurrentCall() {
Emmanuel Milou's avatar
Bug fix    
Emmanuel Milou committed
165
  _debug("Current call ID = %s\n", _currentCallId2.c_str());
yanmorin's avatar
   
yanmorin committed
166
167
  if ( _currentCallId2 != "") {
    return true;
jpbl's avatar
jpbl committed
168
  }
yanmorin's avatar
   
yanmorin committed
169
  return false;
jpbl's avatar
jpbl committed
170
171
}

yanmorin's avatar
   
yanmorin committed
172
173
174
const CallID& 
ManagerImpl::getCurrentCallId() {
  return _currentCallId2;
jpbl's avatar
jpbl committed
175
176
177
}

void
yanmorin's avatar
   
yanmorin committed
178
ManagerImpl::switchCall(const CallID& id ) {
179
  ost::MutexLock m(_currentCallMutex);
yanmorin's avatar
   
yanmorin committed
180
  _currentCallId2 = id;
jpbl's avatar
jpbl committed
181
182
183
184
185
186
}


///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
yanmorin's avatar
   
yanmorin committed
187
/* Main Thread */ 
188
  bool
yanmorin's avatar
   
yanmorin committed
189
190
ManagerImpl::outgoingCall(const std::string& accountid, const CallID& id, const std::string& to)
{
Emmanuel Milou's avatar
Emmanuel Milou committed
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
    if (!accountExists(accountid)) {
        _debug("! Manager Error: Outgoing Call: account doesn't exist\n");
        return false;
    }
  
    if (getAccountFromCall(id) != AccountNULL) {
        _debug("! Manager Error: Outgoing Call: call id already exists\n");
        return false;
    }
  
    if (hasCurrentCall()) {
        _debug("* Manager Info: there is currently a call, try to hold it\n");
        onHoldCall(getCurrentCallId());
    }
  
    _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data());
yanmorin's avatar
   
yanmorin committed
207
    associateCallToAccount( id, accountid );
Emmanuel Milou's avatar
Emmanuel Milou committed
208
209
210
211
212
213
214
215
    if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) {
        switchCall(id);
        return true;
    } else {
        callFailure(id);
        _debug("! Manager Error: An error occur, the call was not created\n");
    }
    return false;
jpbl's avatar
jpbl committed
216
217
}

yanmorin's avatar
   
yanmorin committed
218
//THREAD=Main : for outgoing Call
219
  bool
yanmorin's avatar
   
yanmorin committed
220
ManagerImpl::answerCall(const CallID& id)
jpbl's avatar
jpbl committed
221
{
222
  stopTone(false); 
223
  _debug("Try to answer call: %s\n", id.data());
yanmorin's avatar
   
yanmorin committed
224
225
226
227
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("Answering Call: Call doesn't exists\n");
    return false;
jpbl's avatar
jpbl committed
228
  }
yanmorin's avatar
   
yanmorin committed
229

230
  if (id != getCurrentCallId()) {
Yun Liu's avatar
Yun Liu committed
231
232
233
234
    _debug("* Manager Info: there is currently a call, try to hold it\n");
    onHoldCall(getCurrentCallId());
  }

yanmorin's avatar
   
yanmorin committed
235
236
237
238
  if (!getAccountLink(accountid)->answer(id)) {
    // error when receiving...
    removeCallAccount(id);
    return false;
jpbl's avatar
jpbl committed
239
  }
240
  
yanmorin's avatar
   
yanmorin committed
241
  // if it was waiting, it's waiting no more
242
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
yanmorin's avatar
   
yanmorin committed
243
244
245
  removeWaitingCall(id);
  switchCall(id);
  return true;
jpbl's avatar
jpbl committed
246
247
}

yanmorin's avatar
   
yanmorin committed
248
//THREAD=Main
249
  bool
yanmorin's avatar
   
yanmorin committed
250
ManagerImpl::hangupCall(const CallID& id)
jpbl's avatar
jpbl committed
251
{
252
  _debug("ManagerImpl::hangupCall(): This function is called when user hangup \n");
253
254
255
    PulseLayer *pulselayer;
    AccountID accountid;
    bool returnValue;
Emmanuel Milou's avatar
Emmanuel Milou committed
256

257
    stopTone(true);
yanmorin's avatar
   
yanmorin committed
258

259
260
261
262
263
264
265
266
267
268
    /* Broadcast a signal over DBus */
    if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
  
    accountid = getAccountFromCall( id );
    if (accountid == AccountNULL) {
        /** @todo We should tell the GUI that the call doesn't exist, so
        * it clears up. This can happen. */
        _debug("! Manager Hangup Call: Call doesn't exists\n");
        return false;
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
269

270
    returnValue = getAccountLink(accountid)->hangup(id);
271

272
273
274
    _debug("After voip link hungup!\n");
    removeCallAccount(id);
    switchCall("");
275

276
277
278
279
280
281
    if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) {
        pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver());
        if(pulselayer)  pulselayer->restorePulseAppsVolume();
    }
  
    return returnValue;
jpbl's avatar
jpbl committed
282
283
}

yanmorin's avatar
   
yanmorin committed
284
//THREAD=Main
285
  bool
yanmorin's avatar
   
yanmorin committed
286
ManagerImpl::cancelCall (const CallID& id)
jpbl's avatar
jpbl committed
287
{
288
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
289
290
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
291
    _debug("! Manager Cancel Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
292
    return false;
jpbl's avatar
jpbl committed
293
  }
yanmorin's avatar
   
yanmorin committed
294
295
296
297
298
299

  bool returnValue = getAccountLink(accountid)->cancel(id);
  // it could be a waiting call?
  removeWaitingCall(id);
  removeCallAccount(id);
  switchCall("");
300

yanmorin's avatar
   
yanmorin committed
301
  return returnValue;
jpbl's avatar
jpbl committed
302
303
}

yanmorin's avatar
   
yanmorin committed
304
//THREAD=Main
305
  bool
yanmorin's avatar
   
yanmorin committed
306
ManagerImpl::onHoldCall(const CallID& id)
jpbl's avatar
jpbl committed
307
{
308
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
309
310
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
311
    _debug("5 Manager On Hold Call: Account ID %s or callid %s desn't exists\n", accountid.c_str(), id.c_str());
yanmorin's avatar
   
yanmorin committed
312
    return false;
jpbl's avatar
jpbl committed
313
  }
yanmorin's avatar
   
yanmorin committed
314

315
316
  _debug("Setting ONHOLD, Account %s, callid %s\n", accountid.c_str(), id.c_str());

yanmorin's avatar
   
yanmorin committed
317
  bool returnValue = getAccountLink(accountid)->onhold(id);
318

yanmorin's avatar
   
yanmorin committed
319
  removeWaitingCall(id);
320
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HOLD");
yanmorin's avatar
   
yanmorin committed
321
  switchCall("");
322

yanmorin's avatar
   
yanmorin committed
323
324
325
326
  return returnValue;
}

//THREAD=Main
327
  bool
yanmorin's avatar
   
yanmorin committed
328
329
ManagerImpl::offHoldCall(const CallID& id)
{
330
  stopTone(false);
yanmorin's avatar
   
yanmorin committed
331
332
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
333
    _debug("5 Manager OffHold Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
334
    return false;
jpbl's avatar
jpbl committed
335
  }
336

337
338
339
340
341
  //Place current call on hold if it isn't
  if (hasCurrentCall()) 
  { 
    onHoldCall(getCurrentCallId());
  }
342

343
344
  _debug("Setting OFFHOLD, Account %s, callid %s\n", accountid.c_str(), id.c_str());

yanmorin's avatar
   
yanmorin committed
345
  bool returnValue = getAccountLink(accountid)->offhold(id);
346
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "UNHOLD");
yanmorin's avatar
   
yanmorin committed
347
  switchCall(id);
348

jpbl's avatar
jpbl committed
349
350
351
  return returnValue;
}

yanmorin's avatar
   
yanmorin committed
352
//THREAD=Main
353
  bool
yanmorin's avatar
   
yanmorin committed
354
ManagerImpl::transferCall(const CallID& id, const std::string& to)
jpbl's avatar
jpbl committed
355
{
356
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
357
358
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
359
    _debug("! Manager Transfer Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
360
    return false;
jpbl's avatar
jpbl committed
361
  }
yanmorin's avatar
   
yanmorin committed
362
363
364
  bool returnValue = getAccountLink(accountid)->transfer(id, to);
  removeWaitingCall(id);
  removeCallAccount(id);
365
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
   
yanmorin committed
366
367
  switchCall("");
  return returnValue;
jpbl's avatar
jpbl committed
368
369
}

yanmorin's avatar
   
yanmorin committed
370
//THREAD=Main : Call:Incoming
371
  bool
yanmorin's avatar
   
yanmorin committed
372
ManagerImpl::refuseCall (const CallID& id)
jpbl's avatar
jpbl committed
373
{
374
  _debug("ManagerImpl::refuseCall(): method called");
375
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
376
377
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
378
    _debug("! Manager OffHold Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
379
380
381
    return false;
  }
  bool returnValue = getAccountLink(accountid)->refuse(id);
yanmorin's avatar
   
yanmorin committed
382
383
384
385
386
  // if the call was outgoing or established, we didn't refuse it
  // so the method did nothing
  if (returnValue) {
    removeWaitingCall(id);
    removeCallAccount(id);
387
    if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
   
yanmorin committed
388
389
    switchCall("");
  }
yanmorin's avatar
   
yanmorin committed
390
  return returnValue;
jpbl's avatar
jpbl committed
391
392
}

yanmorin's avatar
   
yanmorin committed
393
//THREAD=Main
394
  bool
jpbl's avatar
jpbl committed
395
396
397
398
399
400
401
402
403
404
ManagerImpl::saveConfig (void)
{
  _debug("Saving Configuration...\n");
  setConfig(AUDIO, VOLUME_SPKR, getSpkrVolume());
  setConfig(AUDIO, VOLUME_MICRO, getMicVolume());

  _setupLoaded = _config.saveConfigTree(_path.data());
  return _setupLoaded;
}

yanmorin's avatar
   
yanmorin committed
405
//THREAD=Main
406
 int 
407
ManagerImpl::initRegisterAccounts() 
jpbl's avatar
jpbl committed
408
{
409
    int status; 
410
    bool flag = true;
411
412
413
414
415
    AccountMap::iterator iter;

    _debugInit("Initiate VoIP Links Registration");
    iter = _accountMap.begin();

416
    /* Loop on the account map previously loaded */
417
    while( iter != _accountMap.end() ) {
418
419
420
421
422
423
424
425
426
        if ( iter->second ) {
            iter->second->loadConfig();
            /* If the account is set as enabled, try to register */
            if ( iter->second->isEnabled() ) {
	            status = iter->second->registerVoIPLink();
	            if (status != SUCCESS){
		            flag = false;
                }
            }
427
        }
428
        iter++;
Emmanuel Milou's avatar
Emmanuel Milou committed
429
    }
430

431
432
433
434
    // calls the client notification here in case of errors at startup...
    if( _audiodriver -> getErrorMessage() != -1 )
      notifyErrClient( _audiodriver -> getErrorMessage() );
    
435
    ASSERT( flag, true );
436
    return SUCCESS;
jpbl's avatar
jpbl committed
437
438
}

yanmorin's avatar
   
yanmorin committed
439
//THREAD=Main
440
  bool 
yanmorin's avatar
   
yanmorin committed
441
ManagerImpl::sendDtmf(const CallID& id, char code)
jpbl's avatar
jpbl committed
442
{
yanmorin's avatar
   
yanmorin committed
443
444
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
Emmanuel Milou's avatar
Emmanuel Milou committed
445
446
    //_debug("Send DTMF: call doesn't exists\n");
    playDtmf(code, false);
yanmorin's avatar
   
yanmorin committed
447
448
449
    return false;
  }

jpbl's avatar
jpbl committed
450
  int sendType = getConfigInt(SIGNALISATION, SEND_DTMF_AS);
yanmorin's avatar
   
yanmorin committed
451
  bool returnValue = false;
jpbl's avatar
jpbl committed
452
  switch (sendType) {
453
    case 0: // SIP INFO
Emmanuel Milou's avatar
Emmanuel Milou committed
454
      playDtmf(code , true);
455
456
457
458
459
460
461
462
463
      returnValue = getAccountLink(accountid)->carryingDTMFdigits(id, code);
      break;

    case 1: // Audio way
      break;
    case 2: // rfc 2833
      break;
    default: // unknown - error config?
      break;
jpbl's avatar
jpbl committed
464
465
466
467
  }
  return returnValue;
}

yanmorin's avatar
   
yanmorin committed
468
//THREAD=Main | VoIPLink
469
  bool
Emmanuel Milou's avatar
Emmanuel Milou committed
470
ManagerImpl::playDtmf(char code, bool isTalking)
jpbl's avatar
jpbl committed
471
{
472
473
474
475
476
477
478
479
480
481
    int hasToPlayTone, pulselen, layer, size;
    bool ret = false;
    AudioLayer *audiolayer;
    SFLDataFormat *buf;
  
    stopTone(false);
    
    hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF);
    if (!hasToPlayTone) 
        return false;
jpbl's avatar
jpbl committed
482

483
484
485
486
    // length in milliseconds
    pulselen = getConfigInt(SIGNALISATION, PULSE_LENGTH);
    if (!pulselen)
        return false;
487

488
489
490
491
    // numbers of int = length in milliseconds / 1000 (number of seconds)
    //                = number of seconds * SAMPLING_RATE by SECONDS
    audiolayer = getAudioDriver();
    layer = audiolayer->getLayerType();
jpbl's avatar
jpbl committed
492

493
494
495
    // fast return, no sound, so no dtmf
    if (audiolayer==0 || _dtmfKey == 0)
        return false;
496

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
    // number of data sampling in one pulselen depends on samplerate
    // size (n sampling) = time_ms * sampling/s 
    //                     ---------------------
    //                            ms/s
    size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000));

    // this buffer is for mono
    // TODO <-- this should be global and hide if same size
    buf = new SFLDataFormat[size];

    // Handle dtmf
    _dtmfKey->startTone(code);

    // copy the sound
    if ( _dtmfKey->generateDTMF(buf, size) ) {
        // Put buffer to urgentRingBuffer 
        // put the size in bytes...
        // so size * 1 channel (mono) * sizeof (bytes for the data)
        audiolayer->putUrgent (buf, size * sizeof(SFLDataFormat));
    }
    ret = true;
518

519
    // TODO Cache the DTMF
Emmanuel Milou's avatar
Emmanuel Milou committed
520

521
522
523
    delete[] buf; buf = 0;
    
    return ret;
jpbl's avatar
jpbl committed
524
525
}

yanmorin's avatar
   
yanmorin committed
526
// Multi-thread 
jpbl's avatar
jpbl committed
527
528
529
530
531
532
bool
ManagerImpl::incomingCallWaiting() {
  return (_nbIncomingWaitingCall > 0) ? true : false;
}

void
yanmorin's avatar
   
yanmorin committed
533
ManagerImpl::addWaitingCall(const CallID& id) {
534
  ost::MutexLock m(_waitingCallMutex);
yanmorin's avatar
   
yanmorin committed
535
  _waitingCall.insert(id);
jpbl's avatar
jpbl committed
536
537
538
539
  _nbIncomingWaitingCall++;
}

void
yanmorin's avatar
   
yanmorin committed
540
ManagerImpl::removeWaitingCall(const CallID& id) {
541
  ost::MutexLock m(_waitingCallMutex);
yanmorin's avatar
   
yanmorin committed
542
543
544
  // should return more than 1 if it erase a call
  if (_waitingCall.erase(id)) {
    _nbIncomingWaitingCall--;
jpbl's avatar
jpbl committed
545
546
547
548
  }
}

bool
yanmorin's avatar
   
yanmorin committed
549
550
551
552
ManagerImpl::isWaitingCall(const CallID& id) {
  CallIDSet::iterator iter = _waitingCall.find(id);
  if (iter != _waitingCall.end()) {
    return false;
jpbl's avatar
jpbl committed
553
  }
yanmorin's avatar
   
yanmorin committed
554
  return true;
jpbl's avatar
jpbl committed
555
556
}

yanmorin's avatar
   
yanmorin committed
557
558
559
560
///////////////////////////////////////////////////////////////////////////////
// Management of event peer IP-phone 
////////////////////////////////////////////////////////////////////////////////
// SipEvent Thread 
561
  bool 
yanmorin's avatar
   
yanmorin committed
562
ManagerImpl::incomingCall(Call* call, const AccountID& accountId) 
jpbl's avatar
jpbl committed
563
{
564
565
    PulseLayer *pulselayer;
    std::string from, number;
566

567
    _debug("Incoming call %s\n", call->getCallId().data());
568

569
    associateCallToAccount(call->getCallId(), accountId);
jpbl's avatar
jpbl committed
570

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
    if ( !hasCurrentCall() ) {
        call->setConnectionState(Call::Ringing);
        ringtone();
        switchCall(call->getCallId());
    } else {
        addWaitingCall(call->getCallId());
    }

    from = call->getPeerName();
    number = call->getPeerNumber();

    if (from != "" && number != "") {
        from.append(" <");
        from.append(number);
        from.append(">");
    } else if ( from.empty() ) {
        from.append("<");
        from.append(number);
        from.append(">");
    }
591
  
592
593
    /* Broadcast a signal over DBus */
    _dbus->getCallManager()->incomingCall(accountId, call->getCallId(), from);
594
  
595
596
597
598
599
    // Reduce volume of the other pulseaudio-connected audio applications
    if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) {
        pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver());
        if(pulselayer)  pulselayer->reducePulseAppsVolume();
    }
600
  
601
    return true;
jpbl's avatar
jpbl committed
602
603
}

yanmorin's avatar
   
yanmorin committed
604
605
606
//THREAD=VoIP
void
ManagerImpl::incomingMessage(const AccountID& accountId, const std::string& message) {
607
608
  if (_dbus) {
    _dbus->getCallManager()->incomingMessage(accountId, message);
jpbl's avatar
jpbl committed
609
610
611
  }
}

yanmorin's avatar
   
yanmorin committed
612
//THREAD=VoIP CALL=Outgoing
613
  void
yanmorin's avatar
   
yanmorin committed
614
ManagerImpl::peerAnsweredCall(const CallID& id)
jpbl's avatar
jpbl committed
615
{
yanmorin's avatar
   
yanmorin committed
616
  if (isCurrentCall(id)) {
617
    stopTone(false);
jpbl's avatar
jpbl committed
618
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
619
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
jpbl's avatar
jpbl committed
620
621
}

yanmorin's avatar
   
yanmorin committed
622
//THREAD=VoIP Call=Outgoing
623
  void
yanmorin's avatar
   
yanmorin committed
624
ManagerImpl::peerRingingCall(const CallID& id)
jpbl's avatar
jpbl committed
625
{
yanmorin's avatar
   
yanmorin committed
626
  if (isCurrentCall(id)) {
jpbl's avatar
jpbl committed
627
628
    ringback();
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
629
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "RINGING");
jpbl's avatar
jpbl committed
630
631
}

yanmorin's avatar
   
yanmorin committed
632
//THREAD=VoIP Call=Outgoing/Ingoing
633
  void
yanmorin's avatar
   
yanmorin committed
634
ManagerImpl::peerHungupCall(const CallID& id)
jpbl's avatar
jpbl committed
635
{
636
    _debug("ManagerImpl::peerHungupCall():this function is called when peer hangup \n");
637
638
639
640
641
642
643
644
    PulseLayer *pulselayer;
    AccountID accountid;

    accountid = getAccountFromCall( id );
    if (accountid == AccountNULL) {
        _debug("peerHungupCall: Call doesn't exists\n");
        return;
    }
Emmanuel Milou's avatar
Emmanuel Milou committed
645
  
646
647
648
649
650
651
652
653
654
655
    /* Broadcast a signal over DBus */
    if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
    
    if (isCurrentCall(id)) {
        stopTone(true);
        switchCall("");
    }

    removeWaitingCall(id);
    removeCallAccount(id);
Emmanuel Milou's avatar
Emmanuel Milou committed
656
  
657
658
659
660
    if( _audiodriver->getLayerType() == PULSEAUDIO && getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) ) {
        pulselayer = dynamic_cast<PulseLayer *> (getAudioDriver());
        if(pulselayer)  pulselayer->restorePulseAppsVolume();
    }
jpbl's avatar
jpbl committed
661
662
}

yanmorin's avatar
   
yanmorin committed
663
//THREAD=VoIP
jpbl's avatar
jpbl committed
664
void
yanmorin's avatar
   
yanmorin committed
665
666
ManagerImpl::callBusy(const CallID& id) {
  _debug("Call busy\n");
667

668
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "BUSY");
yanmorin's avatar
   
yanmorin committed
669
670
671
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
jpbl's avatar
jpbl committed
672
  }
yanmorin's avatar
   
yanmorin committed
673
674
  removeCallAccount(id);
  removeWaitingCall(id);
jpbl's avatar
jpbl committed
675
676
}

yanmorin's avatar
   
yanmorin committed
677
//THREAD=VoIP
678
  void
yanmorin's avatar
   
yanmorin committed
679
680
ManagerImpl::callFailure(const CallID& id) 
{
681
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "FAILURE");
Emmanuel Milou's avatar
Emmanuel Milou committed
682
  _debug("CALL ID = %s\n" , id.c_str());
yanmorin's avatar
   
yanmorin committed
683
684
685
686
687
688
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
  }
  removeCallAccount(id);
  removeWaitingCall(id);
689

jpbl's avatar
jpbl committed
690
691
}

yanmorin's avatar
   
yanmorin committed
692
//THREAD=VoIP
693
  void
Emmanuel Milou's avatar
Emmanuel Milou committed
694
ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, int nb_msg)
jpbl's avatar
jpbl committed
695
{
Emmanuel Milou's avatar
Emmanuel Milou committed
696
  if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, nb_msg) ;
jpbl's avatar
jpbl committed
697
698
}

699
void ManagerImpl::connectionStatusNotification(  )
700
{
701
702
703
704
    if (_dbus)
        _dbus->getConfigurationManager()->accountsChanged();
    else
        _debug("Error: DBus connection not found\n");
Emmanuel Milou's avatar
nothing    
Emmanuel Milou committed
705
706
}

jpbl's avatar
jpbl committed
707
708
709
/**
 * Multi Thread
 */
710
711
712
713
714
715
bool ManagerImpl::playATone(Tone::TONEID toneId) 
{
    int hasToPlayTone;
    AudioLoop *audioloop;
    AudioLayer *audiolayer;
    unsigned int nbSamples;
yanmorin's avatar
   
yanmorin committed
716

717
718
719
720
721
    hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
    if (!hasToPlayTone) 
        return false;
    
    audiolayer = getAudioDriver();
jpbl's avatar
jpbl committed
722

723
724
725
726
727
728
729
730
731
732
733
734
735
    if (_telephoneTone != 0) {
        _toneMutex.enterMutex();
        _telephoneTone->setCurrentTone(toneId);
        _toneMutex.leaveMutex();

        audioloop = getTelephoneTone();
        nbSamples = audioloop->getSize();
        SFLDataFormat buf[nbSamples];
    
        if ( audiolayer ){ 
            audiolayer->putUrgent( buf, nbSamples );
        } else 
            return false;
jpbl's avatar
jpbl committed
736
  }
737
  return true;
jpbl's avatar
jpbl committed
738
739
740
741
742
}

/**
 * Multi Thread
 */
743
744
745
746
void ManagerImpl::stopTone (bool stopAudio=true)
{
    int hasToPlayTone;
    AudioLayer *audiolayer;
yanmorin's avatar
   
yanmorin committed
747

748
749
750
    hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
    if (!hasToPlayTone) 
        return;
751

752
753
754
755
    if (stopAudio) {
        audiolayer = getAudioDriver();
        if (audiolayer) audiolayer->stopStream();
    }
jpbl's avatar
jpbl committed
756

757
758
759
760
761
    _toneMutex.enterMutex();
    if (_telephoneTone != 0) {
        _telephoneTone->setCurrentTone(Tone::TONE_NULL);
    }
    _toneMutex.leaveMutex();
jpbl's avatar
jpbl committed
762

763
764
765
766
    // for ringing tone..
    _toneMutex.enterMutex();
    _audiofile.stop();
    _toneMutex.leaveMutex();
jpbl's avatar
jpbl committed
767
768
769
770
771
}

/**
 * Multi Thread
 */
772
  bool
jpbl's avatar
jpbl committed
773
774
ManagerImpl::playTone()
{
775
  playATone(Tone::TONE_DIALTONE);
Yun Liu's avatar
Yun Liu committed
776
  return true;
jpbl's avatar
jpbl committed
777
778
}

779
780
781
782
783
784
785
/**
 * Multi Thread
 */
  bool
ManagerImpl::playToneWithMessage()
{
  playATone(Tone::TONE_CONGESTION);
Yun Liu's avatar
Yun Liu committed
786
  return true;
787
788
}

jpbl's avatar
jpbl committed
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
/**
 * Multi Thread
 */
void
ManagerImpl::congestion () {
  playATone(Tone::TONE_CONGESTION);
}

/**
 * Multi Thread
 */
void
ManagerImpl::ringback () {
  playATone(Tone::TONE_RINGTONE);
}

/**
 * Multi Thread
 */
808
  void
jpbl's avatar
jpbl committed
809
810
ManagerImpl::ringtone() 
{
811
812
813
814
815
816
    std::string ringchoice;
    AudioLayer *audiolayer;
    AudioCodec *codecForTone;
    int layer, samplerate;
    bool loadFile;

817
818
819
820
821
    if( isRingtoneEnabled() )
    {
        //TODO Comment this because it makes the daemon crashes since the main thread
        //synchronizes the ringtone thread.
        
822
        ringchoice = getConfigString(AUDIO, RING_CHOICE);
823
824
825
826
827
828
        //if there is no / inside the path
        if ( ringchoice.find(DIR_SEPARATOR_CH) == std::string::npos ) {
            // check inside global share directory
            ringchoice = std::string(PROGSHAREDIR) + DIR_SEPARATOR_STR + RINGDIR + DIR_SEPARATOR_STR + ringchoice; 
        }

829
830
831
832
833
834
835
        audiolayer = getAudioDriver();
        layer = audiolayer->getLayerType();
        if (audiolayer == 0)
            return;

        samplerate  = audiolayer->getSampleRate();
        codecForTone = _codecDescriptorMap.getFirstCodecAvailable();
Emmanuel Milou's avatar
Emmanuel Milou committed
836

837
        _toneMutex.enterMutex(); 
838
        loadFile = _audiofile.loadFile(ringchoice, codecForTone , samplerate);
839
        _toneMutex.leaveMutex(); 
Emmanuel Milou's avatar
Emmanuel Milou committed
840

841
        if (loadFile) {
842
            _toneMutex.enterMutex(); 
843
            _audiofile.start();
844
            _toneMutex.leaveMutex(); 
845
            if(CHECK_INTERFACE( layer, ALSA )){
846
                //ringback();
847
848
849
850
851
852
            }
            else{
                audiolayer->startStream();
            }
        } else {
            ringback();
853
        }
854
855
856
857
858
    
    }
    else
    {
        ringback();
Emmanuel Milou's avatar
Emmanuel Milou committed
859
    }
jpbl's avatar
jpbl committed
860
861
}

862
  AudioLoop*
jpbl's avatar
jpbl committed
863
864
865
ManagerImpl::getTelephoneTone()
{
  if(_telephoneTone != 0) {
866
    ost::MutexLock m(_toneMutex);
jpbl's avatar
jpbl committed
867
868
869
870
871
872
873
    return _telephoneTone->getCurrentTone();
  }
  else {
    return 0;
  }
}

874
  AudioLoop*
jpbl's avatar
jpbl committed
875
876
ManagerImpl::getTelephoneFile()
{
877
  ost::MutexLock m(_toneMutex);
jpbl's avatar
jpbl committed
878
879
880
881
882
883
884
  if(_audiofile.isStarted()) {
    return &_audiofile;
  } else {
    return 0;
  }
}

885
886
887
void ManagerImpl::notificationIncomingCall(void) 
{
    AudioLayer *audiolayer;
jpbl's avatar
jpbl committed
888
    std::ostringstream frequency;
889
890
891
892
893
894
895
896
897
898
899
900
901
    unsigned int samplerate, nbSampling;

    audiolayer = getAudioDriver();
    if (audiolayer != 0) {
        samplerate = audiolayer->getSampleRate();
        frequency << "440/" << FRAME_PER_BUFFER;
        Tone tone(frequency.str(), samplerate);
        nbSampling = tone.getSize();
        SFLDataFormat buf[nbSampling];
        tone.getNext(buf, tone.getSize());
        /* Put the data in the urgent ring buffer */
        audiolayer->putUrgent (buf, sizeof(SFLDataFormat)*nbSampling);
    }
jpbl's avatar
jpbl committed
902
903
904
905
906
}

/**
 * Multi Thread
 */
907
  bool
yanmorin's avatar
   
yanmorin committed
908
ManagerImpl::getStunInfo (StunAddress4& stunSvrAddr, int port) 
jpbl's avatar
jpbl committed
909
910
911
912
{
  StunAddress4 mappedAddr;
  struct in_addr in;
  char* addr;
yanmorin's avatar
   
yanmorin committed
913
914
915
916
917

  //int fd3, fd4;
  // bool ok = stunOpenSocketPair(stunSvrAddr, &mappedAddr, &fd3, &fd4, port);
  int fd1 = stunOpenSocket(stunSvrAddr, &mappedAddr, port);
  bool ok = (fd1 == -1 || fd1 == INVALID_SOCKET) ? false : true;
jpbl's avatar
jpbl committed
918
  if (ok) {
yanmorin's avatar
   
yanmorin committed
919
920
921
    closesocket(fd1);
    //closesocket(fd3);
    //closesocket(fd4);
jpbl's avatar
jpbl committed
922
923
924
925
926
927
    _firewallPort = mappedAddr.port;
    // Convert ipv4 address to host byte ordering
    in.s_addr = ntohl (mappedAddr.addr);
    addr = inet_ntoa(in);
    _firewallAddr = std::string(addr);
    _debug("STUN Firewall: [%s:%d]\n", _firewallAddr.data(), _firewallPort);
yanmorin's avatar
   
yanmorin committed
928
    return true;
jpbl's avatar
jpbl committed
929
930
931
  } else {
    _debug("Opening a stun socket pair failed\n");
  }
yanmorin's avatar
   
yanmorin committed
932
  return false;
jpbl's avatar
jpbl committed
933
934
}

935
  bool
yanmorin's avatar
   
yanmorin committed
936
ManagerImpl::behindNat(const std::string& svr, int port)
yanmorin's avatar
   
yanmorin committed
937
938
939
{
  StunAddress4 stunSvrAddr;
  stunSvrAddr.addr = 0;
940

yanmorin's avatar
   
yanmorin committed
941
942
943
944
945
  // Convert char* to StunAddress4 structure
  bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr);
  if (!ret) {
    _debug("SIP: Stun server address (%s) is not valid\n", svr.data());
    return 0;
jpbl's avatar
jpbl committed
946
  }
947

yanmorin's avatar
   
yanmorin committed
948
  // Firewall address
949
  _debug("STUN server: %s\n", svr.data());
yanmorin's avatar
   
yanmorin committed
950
  return getStunInfo(stunSvrAddr, port);
jpbl's avatar
jpbl committed
951
952
}

yanmorin's avatar
   
yanmorin committed
953

jpbl's avatar
jpbl committed
954
955
956
957
958
959
///////////////////////////////////////////////////////////////////////////////
// Private functions
///////////////////////////////////////////////////////////////////////////////
/**
 * Initialization: Main Thread
 * @return 1: ok
960
 -1: error directory
jpbl's avatar
jpbl committed
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
 */
int
ManagerImpl::createSettingsPath (void) {
  _path = std::string(HOMEDIR) + DIR_SEPARATOR_STR + "." + PROGDIR;

  if (mkdir (_path.data(), 0755) != 0) {
    // If directory	creation failed
    if (errno != EEXIST) {
      _debug("Cannot create directory: %s\n", strerror(errno));
      return -1;
    }
  }

  // Load user's configuration
  _path = _path + DIR_SEPARATOR_STR + PROGNAME + "rc";
976
  return 1;
jpbl's avatar
jpbl committed
977
978
979
980
981
}

/**
 * Initialization: Main Thread
 */
982
  void
983
ManagerImpl::initConfigFile ( bool load_user_value ) 
jpbl's avatar
jpbl committed
984
{
985
986
987
  std::string mes = gettext("Init config file\n");
  _debug("%s",mes.c_str());

jpbl's avatar
jpbl committed
988
989
990
991
  std::string type_str("string");
  std::string type_int("int");

  std::string section;
992
993
994

  // Default values, that will be overwritten by the call to
  // 'populateFromFile' below.
Emmanuel Milou's avatar
Emmanuel Milou committed
995
  section = SIGNALISATION;
jpbl's avatar
jpbl committed
996
  fill_config_int(SYMMETRIC, YES_STR);
yanmorin's avatar
   
yanmorin committed
997
  fill_config_int(PLAY_DTMF, YES_STR);
jpbl's avatar
jpbl committed
998
999
1000
  fill_config_int(PLAY_TONES, YES_STR);
  fill_config_int(PULSE_LENGTH, DFT_PULSE_LENGTH_STR);
  fill_config_int(SEND_DTMF_AS, SIP_INFO_STR);